PHP Gotchas: Part 1
PHP is a remarkably easy language to get started with but from there, if my own experience is anything to go by, developers seem to experience a “rollercoaster ride” in terms of productivity. Some people refer to PHP as the “Visual Basic of Open Source”, which is both a complaint and a complement. A quote attributed to Bjarne Stroustrup (designer of C++); “There are only two kinds of programming languages: those people always ***** about and those nobody uses.”…
Over the next few weeks (perhaps months) will be attempting to highlight PHP “gotchas”; things that lead to developer slow-down and *****ing, when working with PHP. In other words the types of problem which aren’t obvious up front and only become clear once you’ve “been there”. Some will be purely technical issues (PHP configuration, legacy headaches etc.) while others will be more theoretical (what “works” and what doesn’t in terms of code design).
The purpose is signpost “gotchas” to developers getting started with PHP and, hopefully, prevent frustration before it happens. Will be based primarily on my own experiences, after almost five years of PHP, as well as things I’ve seen on Sitepoint’s PHP forums. Further input / insight much appreciated, as are requests for subjects.
PHP Environment and Portability Gotchas
Kicking off, these are some of the common php.ini related gotchas. When talking about “portability” here, I’m referring to running code under different PHP installations, as opposed to operating system portability or backwards compatibility with older PHP versions, both of which need examining seperately.
Some of these are already covered here so excuse me re-iterating; think it’s worth attempting to put together a complete list as I see some of these problems over and over again, looking at Open Source PHP projects.
The basic misconception seems to be the assumption that all PHP installations are equal; code that runs under one should run fine under all. While that’s largely true, some key PHP configuration settings and legacy issues conspire to make headaches. It is possible to write code that runs fine under any PHP installation (assuming comparable PHP versions) but a care is needed.
Controlling Runtime Configuration
First up, you need to know how to change PHP’s runtime configuration (runtime as opposed to compile time configuration when PHP is built and installed).
There are, essentially, four basic mechanisms to control PHP’s runtime configuration; the php.ini file, Apaches httpd.conf file (or similar, such as the Windows registry), using Apache .htaccess files or within the scripts themselves using functions like ini_set(). It’s worth reading the manual on Runtime Configuration as well as browsing the core directives and the more or less complete reference found under ini_set(). Further notes can be found commented in the php.ini file itself.
The key point to note here is on a shared web server (your typical PHP host) users will only be to changes settings via the scripts themselves and possibly using .htaccess files (few hosts will let users change php.ini or httpd.conf). Changing settings with a .htaccess requires Apache configured to provide users the “AllowOverride Options” or “AllowOverride All” privileges (normally placed in httpd.conf under
The mechanism by which a runtime configuration setting can be changed depends on the setting itself. Looking at the list found under ini_set(), you’ll notice values in the “Changeable” column like PHP_INI_PERDIR and PHP_INI_SYSTEM. These are actually constants defined as follows;
– PHP_INI_USER: the configuration option can be change inside a PHP script (in fact you’ll never see this listed – it falls under PHP_INI_ALL below).
– PHP_INI_PERDIR: the setting can be changed in php.ini, httpd.conf or a .htaccess file.
– PHP_INI_SYSTEM: the setting can only be changed in php.ini or httpd.conf.
– PHP_INI_ALL: the setting can be changed by all available mechanisms, include a users script.
In other words, for portability, avoid writing code that relies on PHP_INI_SYSTEM and be aware that PHP_INI_PERDIR may be a problem for some users.
Apache Directives
The are two Apache directives, which can be used in httpd.conf and .htaccess files, available for changing configuration settings, namely php_value for settings which have string values and php_flag for settings which have boolean (0 or 1 in fact) values. An example .htaccess file containing one of both;
# Switch off register_globals
php_flag "register_globals" 0
# Set the include_path - Unix! See below...
php_value "include_path" ".:/usr/local/lib/php"
Place this in some directory on your server and place a PHP script containing;
You should see that the local values for these settings have been changed (the global values are those set in php.ini or httpd.conf).
Note for sysadmins – there are also two more directives, php_admin_value and php_admin_flag described here.
Script Configuration
To change configuration settings within a PHP script, the main functions are ini_set() to change a configuration value, ini_get() to get the current local value of a configuration setting, get_cfg_var() to get the global value from php.ini, ini_get_all() for a giant array of all settings, containing both local and global values and ini_restore() to revert a local option to it’s global value (overriding .htaccess files as well). Other functions, such as set_include_path() act as aliases for a specific configuration option, but pay close attention to the PHP version information in the manual, when using these.
An example to append a value to the include path, from within a PHP script;
Development Settings
The settings described below are things you should set in php.ini itself, in your development environment. Some are a pain, in that code modification may be required if you've written stuff without them, but it's worth the effort in fixing if your code will be used by other people.
Error Reporting: E_ALL
When developing, switch error_reporting to E_ALL. In particular this catches E_NOTICE type errors, which you can normally get away but may display error messages to users with this setting and may break code when it comes to sending HTTP headers. It will mean code updates e.g. where you used to write;
You'll need to write;
if ( isset($_GET['doSomething']) && $_GET['doSomething'] == 'yes' ) {
// do something
} else {
// do the default
}
To prevent error notices when $_GET['doSomething'] isn't set. Note that using the @ operator to suppress error messages is generally slower than using the isset() construct.
PHP Tags
Switch short_open_tag off and avoid asp_tags. Code modification may be required e.g.;
Becomes;
The problem with short_open_tag is the PHP interpreter will be confused by XML tags (plus anyone with it switched off will see the tags as HTML) e.g.;
PHP will trip on the XML declaration, thinking it's PHP. The short_open_tag setting is, sadly, PHP_INI_PERDIR so there's no way to modify it inside a script (which would be nice to have, IMO but, no doubt, tricky to implement).
Register Globals: Off
Hopefully you've realised that having register_globals switched on is generally bad news for security, as explained here. Will do security "gotchas" another time.
From the point of view of portability, code written with register_globals switched off should run with register_globals switched on (but may not be secure!) - the same probably won't work in reverse.
Cutting a long story short, switch of register_globals!
Call Time Reference Passing: Off
References in PHP4 are a tricky subject that you'll find more on here and probably need their own "gotchas" discussion.
For portability, switch off allow_call_time_pass_reference. This refers to code like;
Switching allow_call_time_pass_reference off will result in PHP warning errors being generated if you attempt to use it. Once you understand how references work, there's no need to do this anyway and it can make code extremely hard to follow.
Magic Quotes
Magic quotes are a tricky subject. They do a lot to prevent beginners shooting themself in the foot but can cause big headaches later. There's more in depth discussion here and here - be aware there are important security concerns to be aware of, regarding magic quotes.
From a portability perspective, it's best to write code that doesn't rely on magic_quotes_gpc being switched on (e.g. use mysql_escape_string()) but can function correctly irrespective of whether magic_quotes_gpc is on or off. A quick way to do this is to execute the something like the following, before the rest of your code;
// Is magic quotes on?
if (get_magic_quotes_gpc()) {
// Yes? Strip the added slashes
$_GET = array_map('stripslashes', $_GET);
$_POST = array_map('stripslashes', $_POST);
$_COOKIE = array_map('stripslashes', $_COOKIE);
}
Include Path Seperator
Although I said I wasn't going to talk about operating system related issues, as I've mentioned it above it's worth being aware that the include_path seperator is different on Unix and Windows. If you're setting it within a PHP script, the trick you've already seen above can help;
if (strtoupper(substr(PHP_OS, 0,3) == 'WIN')) {
$seperator = ';';
} else {
$seperator = ':';
}
[update]
PHP 4.3.4 provides the predefined constant PATH_SEPARATOR which contains the above character needed for include paths.
Thanks Joshelli for tip
[/update]
Safe Mode
Errr - no thanks. Personally don't write code for users running with safe mode on. If anyone want's to fill this blank, please do.
SAPI Issues
PHP has a number of Server APIs, perhaps the two most popular being the Apache API and the CGI API. The new CLI API adds further issues. The PHP function php_sapi_name() can be useful.
There's some discussion of the Apache vs. CGI APIs here, in particular related to the $_SERVER['PATH_TRANSLATED'] variable. Notes on compatibility between the CLI and CGI binaries, when running command line scripts, can be found on the later half of this page.
Enough already for now. Feel free to add / correct - will update this blog with things I've missed.