Ep #1: WordPress things you should never do

Here is a somewhat random list of rules containing things I hope you never do in PHP or WordPress.

Do not ignore your error_ and access_ logs

If you don’t have your Apache error_log running 100% of the time you are coding, your site is probably full of bugs. Your error_log should only contain intentional messages you have included to benchmark or log specific activity. If should NEVER contain PHP Errors, Warnings or Notices. Those should be corrected immediately. Once your site rolls to production and you start getting a lot of traffic, monitor your logs there as well. Production will expose every edge case you could possibly dream up.

You should regularly check your Apache access_log as well for requests to your domain that don’t return something less than HTTP error code 400. If any of your files or scripts return 400, 404, or 500 in WordPress, you may get into a loop of infinite recursion that your Web Server will choke on after about 10 times through this circle of hell.

To tail your error_log for PHP-only errors, etc (using MacPorts):

tail -f /opt/local/apache2/logs/error_log | grep "PHP " &

Also make sure your wp-config.php contains the following:

// turn on all errors
error_reporting( -1 );

Don’t try to access Array / Object properties that don’t exist

90% of the errors that inexperienced PHP developers generate are based on doing something like this:

// $data might be empty
$data = give_me_what_I_want();

echo $data[0]['items'][7]->fail();

The second you try to access the 0th index of $data, your code will fire off a PHP Notice. DO NOT IGNORE IT. You have 2 choices with arrays to avoid this:

// check that the whole path is cool

if ( isset( $data[0]['items'][7] ) )
     $data[0]['items'][7]->fail();

// be safe all the way down

if ( !empty( $data ) )
    if ( !empty( $data['items'] ) )
        if ( is_a( $data[0]['items'][7], 'SomeObj' ) )
            $data[0]['items'][7]->fail();

You don’t have to get ridiculous, but you also don’t have to be careless.

isset() won’t throw any log notices when you’re checking for depth in an array that may not have it.

empty() is a great utility. It will return a boolean for 0, empty string, empty array, false, or null. The best way to check a variable that might be an array, but might be nothing. You should always check an array for empty before iterating over it, this will prevent  an error being thrown when you start your foreach loop:

// do this

if ( !empty( $might_be_empty_array ) ) foreach ( $might_be_empty_array as $item ) {
    blame_nacin( $item );
}

// not this

foreach ( $im_empty_and_logging_it as $item ) {
     hope_its_not_empty( $item );
}

Don’t use mysql_real_escape_string

If you think you can just call this function out of nowhere to escape some text to be used in a SQL statement, you server is probably barfing and you don’t know it:

$whoops = mysql_real_escape_string( 'escape me!' );

// your logs are blowing up while server
// tries to connect to MySQL with default connection params
// because you didn't pass in an open connection to MySQL
// as the 2nd parameter

// use me instead

$cool = $wpdb->escape( 'escape me!' );

Most functions that start with mysql_* are the procedural counterpart to the object-oriented functions made available by the MySQL extension to PHP. Most of the procedural functions (similar to Memcache functions in this way) require the connection, or current resource instance, passed in as an argument as well. Even if you did pass in the resource, you should be using the mysqli functions instead (MySQL Improved Interface). WordPress uses mysql_* everywhere, so I guess forget what I just said….

ereg_*

This should be blatantly obvious by now, but if you aren’t a whiz at RegEx and Google that wrong site, you may end up pasting in some deprecated POSIX regular expressions when you should be using PCRE = Perl-compatible Regular Expressions.

POSIX = ereg_* functions
PCRE = preg_* functions

serialize

I am going to dare you to name me a good reason to create serialized arrays in your code. Since you can’t find one, I am going to ask you how great of an idea it is to store serialized arrays in the database. Since you don’t know the answer, I am going to ask you how knowledgable you are about ASCII, UTF-*, and ISO-*. Since you have no clue why I am asking you that, I want you to heed my warning: DO NOT USE SERIALIZED ARRAYS. If you do use serialized arrays, store NUMBERS and CHARs only. DO NOT STORE STRINGS of any length that constitute any amount of whitespace.

Here’s why:

Serialized arrays that contain strings bind those strings to their string length, making them as non-portable as possible. If you import / export data and have ANY weird characters that were pasted in from Word or worse, your strings may become invisibly altered and won’t match your bound string lengths. This is super important because of the way we get our data into a usable format when it is stored this way is through unserialize. unserialize fails easy and often when dealing with weird strings.

In WordPress, maybe_unserialize is a function invoked to do this to strings stored in the wp_*meta tables. maybe_unserialize will fail as well, easily and often, but does so….SILENTLY.

You might ask yourself, when would I ever do this anyways?

Does this look familiar?

$data = array( 'format' => 'long', 'color' => 'red' );
update_post_meta( $post_id, 'stuff', $data );

My example is harmless – but let’s say you are using Post Meta to store some SEO text, you are susceptible to maybe_unserialize failing. maybe_unserialize won’t return mangled text when this happens, it will return quite literally nothing.

Another huge problem is the version of MySQL you might be running. MySQL 5.5 is WAY more forgiving with invisible characters in UTF-8 strings with illegal bytes. If you are running any flavor of MySQL 5.1.* and you import data from a MySQL 5.5 database, you mean get bombarded with foreign characters you didn’t have in your 5.5 environment. If you were storing that data in serialized arrays, the data will cause unserialize to fail.

Be careful using $_SESSION

Using $_SESSIONs with Memcached can be palatable, but for most people that don’t know what they are doing, using PHP sessions can be extremely problematic. In almost every case, PHP will store your session_id( ) for a user as a Cookie, it will then throw no-cache headers with every request. Which sucks.

When no-cache is triggered by the session_start() function, this is what the HTTP headers look like:

Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache

Those headers instruct your browser to re-request the page every time you visit it in your browser. Luckily for us, this little nugget tell sessions to not use a cache limiter. It will allow Batcache to work as well. Without it, Batcache will not work:

To turn off the no-cache headers:

session_cache_limiter( false );
session_start();

One thought on “Ep #1: WordPress things you should never do

  1. Thank you for this information. (Specifically, the info on serialization.) You just helped me figure out why a plugin I’m using was failing after I cloned a site to my local machine. Now I can let the developer know what’s going on.

Comments are closed.