Several sites on single WordPress installation

development, ideas, php, wordpress 3 Comments »

I have a couple of other WordPress blogs on the same server besides this one. One day I realised that all of them have 3 different WP versions and, as result, different admin areas which is not handy. I decided to make them use the same WordPress installation.

Ok, first of all, I deleted wp-admin and wp-includes folders and created new ones as symbolic links. Though the frontend worked well, I couldn’t log in into admin area, because the browser was redirected to that blog which was the base for all the rest for unknown reason.

The investigation shown, that admin area of WordPress is a separate sub-application, thing in itself, and in my case it resolves the absolute path to its source as the path to the base blog. I wanted each blog to use its own folder because there are config file and uploads folder.

It took me some time to find a solution. It requires two steps.

First, I added this line to the top of the .htaccess to make any PHP request to the blog (the blog front-end and the admin area scripts) call the same script before thier start:

#fix for several sites on the same WP installation
php_value auto_prepend_file "/var/www/site_doc_root/prepend.php"

In this code /var/www/ is the root folder for all my sites, and the site_doc_root is the document root of the current site (folder where all its files are located).

OK, the 2nd step — the contents of the prepend.php script. It is easy — it just must define an absolute path constant which is used all around the WordPress:

<?php
define('ABSPATH', dirname(__FILE__).'/');

OK, after that I decided not to use one of the blogs as source for others, but download a fresh copy of WordPress and make it a source of the symbolic links for all my blogs. This helps to update them.

Then I deleted wp-admin and wp-includes folders and some wp-files and recreated them as symlinks. Attention to wp-config.php — don’t delete it, keep it unique for every site!

To make this task easier, I created setup.sh file, pasted the contents I show below, run this command

chmod 755 setup.sh

then I copied it in every site folder and launched there for every site:


ln -s /var/www/wordpress/wp-admin wp-admin
ln -s /var/www/wordpress/wp-includes wp-includes

ln -s /var/www/wordpress/wp-app.php wp-app.php
ln -s /var/www/wordpress/wp-atom.php wp-atom.php
ln -s /var/www/wordpress/wp-blog-header.php wp-blog-header.php
ln -s /var/www/wordpress/wp-comments-post.php wp-comments-post.php
ln -s /var/www/wordpress/wp-commentsrss2.php wp-commentsrss2.php
ln -s /var/www/wordpress/wp-config-sample.php wp-config-sample.php
ln -s /var/www/wordpress/wp-cron.php wp-cron.php
ln -s /var/www/wordpress/wp-feed.php wp-feed.php
ln -s /var/www/wordpress/wp-links-opml.php wp-links-opml.php
ln -s /var/www/wordpress/wp-load.php wp-load.php
ln -s /var/www/wordpress/wp-login.php wp-login.php
ln -s /var/www/wordpress/wp-mail.php wp-mail.php
ln -s /var/www/wordpress/wp-pass.php wp-pass.php
ln -s /var/www/wordpress/wp-rdf.php wp-rdf.php
ln -s /var/www/wordpress/wp-register.php wp-register.php
ln -s /var/www/wordpress/wp-rss.php wp-rss.php
ln -s /var/www/wordpress/wp-rss2.php wp-rss2.php
ln -s /var/www/wordpress/wp-settings.php wp-settings.php
ln -s /var/www/wordpress/wp-trackback.php wp-trackback.php
ln -s /var/www/wordpress/xmlrpc.php xmlrpc.php

That’s not all ;)

I decided to update my WordPress installation every one or two months.

To do that, in the /var/www/ folder (where all my sites reside) let’s create an update script update_wordpress.sh with the following contents:

wget --timestamping http://wordpress.org/latest.zip
unzip -o latest.zip

This will download a fresh copy of the wordpress if it’s changed (though wordpress team doesn’t show the file Last-Modified header, I think one day they will) and unzip it to /var/www/wordpress/ folder which is the source for our symlinks.

Yes, you got it right — launching this script is all I need to update all my blogs.

Let’s make it periodic:

crontab -e

and then add this line to run the update process automatically every 1st day of every month at 9 AM:

0 9 1 * * /var/www/update_wordpress.sh > mail -s "Wordpress updated" your@email.com

P.S. Of course, SVN checkout can be used for that purpose :)

Documents in repository

development No Comments »

I rent a server in a data center. Why not to use it also for storing my documents in SVN repo?

Advantages:

  • the access from anywhere — home and work, and forget about flash card.
  • SVN client can show changes even for Microsoft Word documents
Drawbacks:
  • a server must be in place. I already have it, so why not to use it.
  • an SVN client must be installed at every machine where I going to work with the documents. However, if I just need to read it, there is a web interface to download it.

What more can you get from bug tracking?

development No Comments »

Some thoughts about bug tracking.

Low priority bugs

Leave low priority tickets for new comers — such bugs let you show internals of your product doing something useful. Among such issues are all simple kinds of work with text (for example, fixing typos).

Underestimated issues

Find the issues that have required much more time than it was estimated (2-3 times and more) — it can mean, that the developer who worked with it faced something unusual, so he can share the experience gained with the rest of the team.

Bugs on paper

Make sure you (or your developers) don’t keep a list of bugs to fix on the sheet of paper. We do it due to the fear of appearing as non productive developers, right? Just don’t do this. Report them.

Developer ratio

There are plugins to bug tracking systems (I saw such one for Jira) that calculate under/over-estimate ratio for every developer, i.e. how much is the difference between the estimate and actual spent time amout. This ratio is multiplied by all further developer’s estimates, so that his manager can find out how much the real estimate is (most adequate developers have this ratio equal to 1, of course). If you show the ratios to the developers, their estimates could become more accurate.

By the way, do you know that Jira appeared in Star Wars? :)

RSS feed of error log

development, ideas, php No Comments »

Your PHP application logs every error to error log. Do you want to keep track of them in your favourite Feed Reader? Then follow up reading this article!

First of all, you must tell to your application to log errors to a log file. Two parameters must be set: what to log and where to save it. First can be done by error_reporting setting, second — by setting error_log PHP value:

error_reporting( E_ALL & ~E_NOTICE ); //all except notices
ini_set( 'error_log', 'temp/error.log' );

Put it in the beginning of your application, for example, at the top of index.php file.

By the way, you can write something custom in the error log by calling error_log function:

error_log('something');

OK, now, how to make a feed from your error log.

You can do this in a few ways.

First, let’s do it manually.

Manual solution

1. Remove ini_set setting described above. Then create .htaccess file in the top folder of your application and place this line in it:

php_value error_log temp/error.log

It will tell to all scripts of your application to take this setting into consideration without manual calling of ini_set function.

2. Create feed.php file. Place this line inside it:

$contents = file_get_contents( ini_get( 'error_log' ) );
echo nl2br( $contents );

It’s dirty for now, but enough as a check.

3. Make sure that your error log file is not empty. Now you can access your feed.php file via browser and check that it’s showing you the contents of your error log.

4. Now you should create an RSS feed from contents of the file. You can rely on your framework or use a custom solution, e.g. download EasyRSS class and fill the contents of the feed with data from the error log (you would need to think about regexp to parse date and error text from it).

After that you could feed your feed reader with the feed address ;]

Ready-made solution

Download a ready PHP class that will do all it for you — RSS Feed. Yes, it’s simple. You can even protect your feed with a password.

Notes:

  • on the production server change your error_reporting value, for example, to log fatal errors only.
  • log errors in the catch() part of the exception handling mechanism:
    try{ ... }
    catch(Exception $e) {
      error_log( $e->getMessage() );
    }

Google Analytics API launched!

api, google 1 Comment »

This week Google has launched Google Analytics API. Now developers can use the analytical power of Google in their own services.

For example, if you want to access Google Analytics by your phone, use Android application by Actual Metrics. Do you want the Analytics on your desktop? There is a Desktop-Reporting for that purpose.

If you are interested in integration of Google Analytics into your own business, take a look at such examples.

MailChimp integrated Google Analytics to email marketing platform.

ShufflePoint® offers the addition of data from Google Analytics to the presentations of PowerPoint®.

Look at youcalc that have created software that allows you to mix up Google Analytics, AdWords, Salesforce.com and other data companies.

Need more examples? Look at samples from the customers on the Google developers website.

Y3K — IT in 3000 year

fun 2 Comments »

It’s many years since Flickr has collapsed — the Flickr chief web designer, who read too much about old days CSS trends, glued together all Flickr images in one single sprite (googol to googol pixels in size) in order to boost site performace.

According to the websites, the most widespread address used in the fake accounts while registration at the websites, is still “Obama street, 1“, since that address is valid anywhere being present in any city of the planet.

Google is on its last legs, slowly but surely. The problems appeared when crio-time traveling became popular (i.e. person is freezed for 100-200 years, while his/her money is raising, and (s)he wakes up rich in the future). Though Google has had a policy to delete a user account after 50 years of inactivity (as it was almost 100% guarantee that the user is dead), now, when people could not check mail for centuries and remain alive, it became clumsy. There is not enough room on the Moon for Google servers farms, but the leadership still believes that giving away 2 zettabyte for an email account is cool.

The minimum length of new email accounts is hundreds of characters (fortunately, Yahoo marketers made a PR campaign and set the fashion having a huge email address. Nevertheless, you can buy a modern concubine robot for the email address up to 20 characters long).

The legendary Soviet module of express-repair is built in all the gadgets: a snarking device resumes to work after you kicked it.

After World Community Grid has found an AIDS cure, its power is addressed to solve alchemical tasks. Lead becomes very expensive — it’s literally worth its weight in gold.

OpenID has risen from the ruins when they guessed to use a user’s DNA as an unique identifier, but termo-rectal crypto-analysis is still working.

Earthmen triumph: Alpha Centauri aliens finally agreed that the numberation system based on integral numbers are much more useful.

Twitter overtook the Wikipedia 3000 by volume of data stored since it started making a copy of the universe of a few parsecs radius around the user at any time, after which it was shown that googol number is not that large.

A usual programmer looks like a pirate — a bandage on one eye is a device with a computer image projected directly into the eye. Autist programmers go with the band on both eyes.

After the nuclear power plant collapse on the Chinese continent of Antarctica the term “Schrödinger Cat” lost its meaning, because animals stopped dying from the external disturbance.

At school, the multiplication table in the first grade must be learned up to “the answer to life the universe and everything“.

Popularity of services to generate the musical albums, where you can make mashaps of singers, drummers and guitarists of all known groups of any stage of its creative work for any language and musical style, fades.

But on the other hand it’s becoming popular to go to the cinema on a bet at least on the “Alien”: the television and movies learned not only how to smell (it was guessed quickly — American films died as a class after this), but also how to transmit the taste and tactile sensations of various intensity degree, so the probability to survive after the watching the “A Nightmare on Elm Street” is about 5%.

The greenhouse effect has increased the oceans and made the land very expensive. Ocean underwater offices are very popular, and the expressions of “office plankton” and “offshore development” got another meaning.

Originally posted in Russian at developers.org.ua. Images are borrowed from It’s funny because it’s true blog.

This week’s links

links No Comments »
  1. The Lazy Manifesto: Do Less. Then, Do Even Less. — “Practice not-doing and everything will fall into place.” – Lao Tzu. You will know the rules of Ultimate Simple Productivity from this article written by Leo Babauta.
  2. 3 Tips for Making Deadlines Your Friend — I often start a task only after the deadline has been reached :) Don’t be me – learn how to work with deadlines.
  3. 7 Ways Being Unbalanced Can Make You More Productive — several ideas how to to stay focused on one thing and make short productive bursts

jQuery plugin: Add2Cart

jquery, plugin 11 Comments »

I added a jQuery plugin called add2cart.

This plugin implements visual effect of adding something to cart (busket). Visually it’s similar to Microsoft Word post-save visual effect, when a gray rectangular of page moves down to the toolbar.

Example of usage:

<input type="button" value="Add to cart"
  onclick="$.add2cart( 'product1_id', 'cart_img_id'  )" />

You can try it out at demo page.

Don’t use scriptlance.com

boo 1 Comment »

Just don’t try, they are stealers.

Internet is full of complaints. Your account becomes suspended — customer support is suspicious and rude, and all your money is lost.

32 tips to speed up your queries

db 1 Comment »

As certified MySQL developer (yes, I’m listed on mysql.com!!! :) ), I would like to share some experience I’ve got during training to the certification. Today I will tell you how to speed up your queries.

  1. Use persistent connections to the database to avoid connection overhead.
  2. Check all tables have PRIMARY KEYs on columns with high cardinality (many rows match the key value). Well,`gender` column has low cardinality (selectivity), unique user id column has high one and is a good candidate to become a primary key.
  3. All references between different tables should usually be done with indices (which also means they must have identical data types so that joins based on the corresponding columns will be faster). Also check that fields that you often need to search in (appear frequently in WHERE, ORDER BY or GROUP BY clauses) have indices, but don’t add too many: the worst thing you can do is to add an index on every column of a table :) (I haven’t seen a table with more than 5 indices for a table, even 20-30 columns big). If you never refer to a column in comparisons, there’s no need to index it.
  4. Using simpler permissions when you issue GRANT statements enables MySQL to reduce permission-checking overhead when clients execute statements.
  5. Use less RAM per row by declaring columns only as large as they need to be to hold the values stored in them.
  6. Use leftmost index prefix — in MySQL you can define index on several columns so that left part of that index can be used a separate one so that you need less indices.
  7. When your index consists of many columns, why not to create a hash column which is short, reasonably unique, and indexed? Then your query will look like:
    SELECT *
    FROM table
    WHERE hash_column = MD5( CONCAT(col1, col2) )
    AND col1='aaa' AND col2='bbb';
  8. Consider running ANALYZE TABLE (or myisamchk --analyze from command line) on a table after it has been loaded with data to help MySQL better optimize queries.
  9. Use CHAR type when possible (instead of VARCHAR, BLOB or TEXT) — when values of a column have constant length: MD5-hash (32 symbols), ICAO or IATA airport code (4 and 3 symbols), BIC bank code (3 symbols), etc. Data in CHAR columns can be found faster rather than in variable length data types columns.
  10. Don’t split a table if you just have too many columns. In accessing a row, the biggest performance hit is the disk seek needed to find the first byte of the row.
  11. A column must be declared as NOT NULL if it really is — thus you speed up table traversing a bit.
  12. If you usually retrieve rows in the same order like expr1, expr2, ..., make ALTER TABLE ... ORDER BY expr1, expr2, ... to optimize the table.
  13. Don’t use PHP loop to fetch rows from database one by one just because you can ;) — use IN instead, e.g.
    SELECT *
    FROM `table`
    WHERE `id` IN (1,7,13,42);
  14. Use column default value, and insert only those values that differs from the default. This reduces the query parsing time.
  15. Use INSERT DELAYED or INSERT LOW_PRIORITY (for MyISAM) to write to your change log table. Also, if it’s MyISAM, you can add DELAY_KEY_WRITE=1 option — this makes index updates faster because they are not flushed to disk until the table is closed
  16. Think of storing users sessions data (or any other non-critical data) in MEMORY table — it’s very fast.
  17. For your web application, images and other binary assets should normally be stored as files. That is, store only a reference to the file rather than the file itself in the database.
  18. If you have to store big amounts of textual data, consider using BLOB column to contain compressed data (MySQL’s COMPRESS() seems to be slow, so gzipping at PHP side may help) and decompressing the contents at application server side. Anyway, it must be benchmarked.
  19. If you often need to calculate COUNT or SUM based on information from a lot of rows (articles rating, poll votes, user registrations count, etc.), it makes sense to create a separate table and update the counter in real time, which is much faster. If you need to collect statistics from huge log tables, take advantage of using a summary table instead of scanning the entire log table every time.
  20. Don’t use REPLACE (which is DELETE+INSERT and wastes ids): use INSERT ... ON DUPLICATE KEY UPDATE instead (i.e. it’s INSERT + UPDATE if conflict takes place). The same technique can be used when you need first make a SELECT to find out if data is already in database, and then run either INSERT or UPDATE. Why to choose yourself — rely on database side.
  21. Tune MySQL caching: allocate enough memory for the buffer (e.g. SET GLOBAL query_cache_size = 1000000) and define query_cache_min_res_unit depending on average query resultset size.
  22. Divide complex queries into several simpler ones — they have more chances to be cached, so will be quicker.
  23. Group several similar INSERTs in one long INSERT with multiple VALUES lists to insert several rows at a time: quiry will be quicker due to fact that connection + sending + parsing a query takes 5-7 times of actual data insertion (depending on row size). If that is not possible, use START TRANSACTION and COMMIT, if your database is InnoDB, otherwise use LOCK TABLES — this benefits performance because the index buffer is flushed to disk only once, after all INSERT statements have completed; in this case unlock your tables each 1000 rows or so to allow other threads access to the table.
  24. When loading a table from a text file, use LOAD DATA INFILE (or my tool for that), it’s 20-100 times faster.
  25. Log slow queries on your dev/beta environment and investigate them. This way you can catch queries which execution time is high, those that don’t use indexes, and also — slow administrative statements (like OPTIMIZE TABLE and ANALYZE TABLE)
  26. Tune your database server parameters: for example, increase buffers size.
  27. If you have lots of DELETEs in your application, or updates of dynamic format rows (if you have VARCHAR, BLOB or TEXT column, the row has dynamic format) of your MyISAM table to a longer total length (which may split the row), schedule running OPTIMIZE TABLE query every weekend by crond. Thus you make the defragmentation, which means more speed of queries. If you don’t use replication, add LOCAL keyword to make it faster.
  28. Don’t use ORDER BY RAND() to fetch several random rows. Fetch 10-20 entries (last by time added or ID) and make array_random() on PHP side. There are also other solutions.
  29. Consider avoiding using of HAVING clause — it’s rather slow.
  30. In most cases, a DISTINCT clause can be considered as a special case of GROUP BY; so the optimizations applicable to GROUP BY queries can be also applied to queries with a DISTINCT clause. Also, if you use DISTINCT, try to use LIMIT (MySQL stops as soon as it finds row_count unique rows) and avoid ORDER BY (it requires a temporary table in many cases).
  31. When I read “Building scalable web sites”, I found that it worth sometimes to de-normalise some tables (Flickr does this), i.e. duplicate some data in several tables to avoid JOINs which are expensive. You can support data integrity with foreign keys or triggers.
  32. If you want to test a specific MySQL function or expression, use BENCHMARK function to do that.

Some of these hints are unapplicable if you use a framework because direct queries are uninvited guests in the case: focus on competent database optimization — tune indexes and server parameters.

More on queries optimization:

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in