====== Writing Portable PHP Code ====== PHP runs in a wide variety of environments. Writing PHP code that runs effectively in many environments can be difficult. This page covers some of the issues or "gotchas" with writing portable PHP code. There are three major portability scenarios. Dedicated applications, portable applications, and libraries. ===== Portability Scenarios ===== ==== Dedicated PHP Applications ==== Dedicated applications are intended to run on only a single server or cluster of servers. They are intended to be the only application running. These applications do not need to be portable and are often tuned for performance instead of portability. The main characteristic of this scenario is that the server is tuned specifically for the application and does not run unrelated applications. ==== Portable PHP Applications ==== These are applications that may be widely distributed and run under a wide variety of environments. Alternatively, these applications may run on a server that also runs a wide variety of PHP applications. In this scenario, the application must maintain a higher degree of independence from its environment. ==== PHP Libraries ==== PHP libraries are designed to run within another application. They may make even fewer assumptions about the environment they run in and may have fewer techniques available for dealing with portability concerns. ===== Portability Challenges ===== ==== PHP Version Differences ==== Different versions of PHP have different capabilities. Portable code may have to avoid using newer PHP features, or to compensate for the lack of those features on older versions of PHP. In PHP, language syntax additions are rarely backward compatible. For language syntax, there may be no alternative but to avoid the feature unless it is supported across all versions of PHP that you wish to support. There are more options available for built in classes, interfaces, functions and constants. === Checking Versions === PHP offers a [[phpfn>version_compare]] function for the purposes of comparing version numbers. The predefined constant ''PHP_VERSION'' contains the version of PHP running that script. The PHP function [[phpfn>phpversion]] function can be used to obtain the version number of any specific extension compiled into PHP. === Checking for Feature Availability === The [[phpfn>function_exists]], [[phpfn>class_exists]], [[phpfn>interface_exists]] or [[phpfn>defined]] functions can be used to determine if a function, class, interface or constant is available, independently of the current version of PHP. === Implementing Missing Features in Native PHP === The [[http://pear.php.net/package/PHP_Compat/|PEAR PHP_Compat]] package backports many built in function implementations to older versions of PHP. === Assessing the Version Requirements of your Code === The [[http://pear.php.net/package/PHP_CompatInfo/|PEAR PHP_CompatInfo]] package can be used to scan code to determine which version of PHP is required for the built in functions that it calls. ==== PHP Extension Dependencies ==== PHP Itself is built with a modular architecture. Many PHP features are implemented as extensions. When PHP is installed, it is configured to include a set of available extensions. Each version of PHP has a set of extensions that are included by default. For dedicated applications, PHP may be configured and compiled specifically for that application. In this scenario, the number of extensions included is limited to only the extensions that the application may actually use. Additionally, the installer may choose to disable extensions that are normally included by default in order to reduce memory requirements and improve performance. On the other hand, in many shared hosting environments, the hosting provider wants to provide many features to their customers and run a very wide variety of applications. They may enable many PHP extensions that are not widely available elsewhere. Some extensions provide functionality which is very hard to live without, but the extension itself cannot be relied upon to be installed in every user's environment. === Checking for Extension Availability === See [[phpfn>extension_loaded]]. === Implement Missing Extensions in Native PHP === It may be possible, in some instances, to provide alternative PHP implementation to the functionality provided by the extensions, at the cost of degraded performance. In such cases it may be best to implement your own API which wraps the extensions functions e.g. for handling utf-8 encoded strings (see [[php:i18n:charsets]]); require_once 'utils/utf-8/' . (extension('iconv') ? 'utf-8_iconv.inc.php': 'utf-8.inc.php'); In the file ''utf-8_iconv.inc.php'' you might have; function utf8_strlen($str) { return iconv_strlen($str,'UTF-8'); } While the fallback file, for when the iconv extensions is not available, might be; /** * utf8_decode() converts characters that are not in ISO-8859-1 * to '?', which, for the purpose of counting, is alright */ function utf8_strlen($str) { return strlen(utf8_decode($str)); } === Dynamic Loading of Extensions === See [[phpfn>dl]] ==== PHP Runtime Configuration Variation ==== PHP has a large number of configuration settings which effect runtime behaviour. See the complete list of [[http://www.php.net/manual/en/ini.php#ini.list|php.ini directives]]. Note, particular, the "Changeable" column which means; ^ Constant ^Meaning ^ |''PHP_INI_USER'' | can be set in user scripts via [[phpfn>ini_set]]. Usually you'll see ''PHP_INI_ALL'' which includes this setting| |''PHP_INI_PERDIR'' | can be set in ''php.ini'', ''.htaccess''((see [[http://www.php.net/manual/en/configuration.changes.php|How to change configuration settings]] for notes on modifying settings with ''.htaccess'')) or ''httpd.conf''| |''PHP_INI_SYSTEM'' | can be set in php.ini or httpd.conf. If you don't control the server, this will require heavy workarounds.| |''PHP_INI_ALL'' | can be set anywhere| In the scenario where a server is dedicated to a specific application, the php.ini may be carefully tuned to the specific application. //What happens when we attempt to set a php.ini setting in .htaccess that is not defined in the current version of PHP? Use a php.ini file with the wrong version of PHP?// === Application Level Configuration Baseline === Portable applications may wish to set a large number of configuration settings in an ''.htaccess'' file local to that application. This gives the application a known baseline to work against. It also provides independence from the ''php.ini'', which may change if the version of PHP is upgrade, or if the application is moved to a different server. A sample baseline from the [[http://fudforum.org/|FUDForum]] application: php_value output_buffering 64000 php_value variables_order GPCS php_flag implicit_flush 0 php_flag register_globals 0 php_flag register_argc_argv 0 php_flag magic_quotes_gpc 0 php_flag session.use_trans_sid 0 php_flag expose_php 0 php_flag display_errors 0 php_flag mbstring.func_overload 0 Some of these settings are intended for performance reasons, not portability reasons. === Locally Scoped Configuration === Portable libraries may not be able to rely on application level configuration settings. They may also not want to impose requirements on their host application. They can protect sensitive operations by setting a configuration value to a known setting before the operation is performed and then restoring it to its original value after the operation is complete. // Set the configuraton to a known state and remember the previous state $oldValue = ini_set('track_errors', 1); // Perform a configuration dependent operation $x = 12 / 0; echo $php_errormsg; // restore the old state ini_set('track_errors', $oldValue); === Configuration Settings and the Operations they Influence === It can be difficult to know which operations may be influenced by which configuration settings. == magic_quotes_sybase == The behaviour of the [[phpfn>addslashes]] function is changed when the ini setting ''magic_quotes_sybase'' is ''On'', so that single quotes are escaped with another single quote. This may be OK if you're using addslashes to escape input for an SQL query but if you're using addslashes to escape strings for other purposes (e.g. escaping a string to make it useful to Javascript's eval() function), it's probably better to use something like the [[phpfn>addcslashes]] function and re-implement the addslashes behaviour like; // 2nd arg is a sequence of OCTAL chrs representing the chars that addslashes escapes $escaped_str = addcslashes($str,"\000\042\047\134"); ==== Server API Differences ==== The notion of "platform" to many programming languages refers to the operating system. For PHP the notion of "platform" really means the operating system //and// the PHP SAPI((SAPI: Server API)) being used. The most popular "plaform" for PHP is Linux + the Apache Module SAPI. Other common SAPIs are CGI, IIS (for Microsofts Internet Information Server) and CLI (for the Command Line Interface). Typically there are SAPIs for every web server that provides "native" support for PHP. See [[http://www.sitepoint.com/blogs/2005/11/09/php-server-api-differences/|PHP Server API Differences]]. ==== Operating System Differences ==== Many PHP applications are developed under Windows, but deployed on Linux.