PHP - Tips & snippets for configuration and development

New Relic

What is New Relic ?

New Relic is an APM software dedicated to web apps (Ruby, PHP, JAVA, .Net, Python, Node.js). I has quite a wide product offer, among which :

PHP APM : Enable the agent :

  1. Install the agent (1, 2)
  2. Enable the New Relic PHP module so that you have : /etc/php5/cgi/conf.d/20-newrelic.ini -> ../mods-available/newrelic.ini :
    cd /etc/php5/cgi/conf.d/; ln -s ../mods-available/newrelic.ini 20-newrelic.ini
  3. Make sure newrelic.appname is set accordingly in /etc/php5/mods-available/newrelic.ini
  4. Restart the webserver. Should be fine !

PHP APM : Configuring the application name (source) :

The application name can be configured system-wide with the newrelic.appname directive in :
  • php.ini
  • /etc/php5/conf.d/newrelic.ini
What if more than one application is installed on the server ?
  • It is possible to specify the appname in the code (source) :
    if (extension_loaded('newrelic')) {
    	newrelic_set_appname (name);
    	}
  • you can define PHP values in Apache vhost (source, Specify a custom php.ini for a website) :
    <VirtualHost 192.168.42.43>
    	ServerName www.myvhost1.com
    	DocumentRoot "/path/to/vhost1/"
    	...
    	<IfModule PHP_MODULE>
    		php_value newrelic.appname "Application 1"
    	</IfModule>
    </VirtualHost>
    
    <VirtualHost 192.168.123.45>
    	ServerName www.myvhost2.com
    	DocumentRoot "/path/to/vhost2/"
    	...
    	<IfModule PHP_MODULE>
    		php_value newrelic.appname "Application 2"
    	</IfModule>
    </VirtualHost>
  • With Lighttpd, it is possible to set per-directory PHP config values in .htaccess files (source) with the htscanner extension.

Upgrade to PHP 7.0

The upgrade from Debian 8.10 to 9.3 involved upgrading to PHP7.0. After that, I got an error from a web application saying :

"Can not connect to database. Make sure the 'mysqli' extension is enabled"

SOLUTION :
	apt install php7.0-cgi php-mysql
	systemctl restart lighttpd

SOURCE :
	https://stackoverflow.com/questions/35424982/how-to-enable-mysqli-extension-in-php-7


VALUES RETURNED BY phpinfo() :
Configuration File (php.ini) Path	/etc/php/7.0/cgi
Loaded Configuration File		/etc/php/7.0/cgi/php.ini

=====================================================================================================

2017-12-13 11:31:10: (mod_fastcgi.c.2543) FastCGI-stderr: PHP Fatal error:  Uncaught Error: Call to undefined function utf8_encode() in /var/www/xxxxxxxx/index.php:552

SOLUTION :
	apt install php7.0-xml && systemctl restart lighttpd

SOURCE :
	https://stackoverflow.com/questions/35701730/utf8-endecode-removed-from-php7#answer-36902105

How to install PHP on Debian via the DotDeb repositories ?

Situation :

There are 2 questions before proceeding :

Solution :

Here's the full procedure :

For PHP7.0 on Debian Jessie (8.x)

  1. Append to /etc/apt/sources.list :
    deb	http://packages.dotdeb.org jessie all
    deb-src	http://packages.dotdeb.org jessie all
  2. As root : wget https://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg && rm dotdeb.gpg
  3. apt-get update
    apt-get install php7.0-common php7.0
  4. After installing/upgrading, there are chances you get the short_open_tag bug

How to set PHP configuration directives in Apache VHost configuration ?

php_flag	log_errors	on
php_value	error_log	/path/to/php_errors.log

PHP snippets

How to perform an HTTP redirect :

<?php
$newDomainName = 'new.example.com';

header(
	"Location: http://$newDomainName".$_SERVER['REQUEST_URI'],
	True,
	301
	);

How to send an email :

Basic version (source) :
php -r 'mail("recipient@domain.com", "subject", "body", "");'
Advanced version (source) :
<?php

$from		= 'test@domain.com';		you can put anything here
$to		= 'john.smith@gmail.com';
$subject	= 'This is the subject';
$message	= 'Hello World !';
$headers	= "From: $from\r\n" .
			"Reply-To: $from\r\n" .
			'X-Mailer: PHP/' . phpversion();

if(mail($to, $subject, $message, $headers)) {
	print "Mail sent\n";
	}
else {
	print "Ooops\n";
	}

How to test a DB connection (source) :

<?php

$dbHost		= 'myServer';
$dbName		= 'myDb';
$dbUser		= 'user';
$dbPassword	= 'password';
$dbTable	= 'myTable';

$dsn		= sprintf("mysql:dbname=$dbName;host=$dbHost");
$driverOptions	= array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8');
$dbConnection	= new PDO($dsn, $dbUser, $dbPassword, $driverOptions);

$sql		= "select count(1) from $dbTable;";
$resultSet	= $dbConnection->query($sql);
foreach($resultSet as $result) {
	print("$result[0]\n");
	}

Details on php.ini

List all known .ini files :
php --ini
Absolute path to currently loaded .ini file :
  • php --ini | grep -i loaded
  • phpinfo()
The result of these commands in environment-dependant : CLI / CGI.

Directives :

date.timezone = Europe/Paris (List of Supported Timezones)
Fixes the PHP Warning: date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. error.
max_execution_time
(explicit)
memory_limit
Maximum amount of memory a script may consume. -1 means "no limit".
expose_php (source)
Boolean (on|off) used to toggle the presence of X-Powered-By: PHP/5.2.6-1+lenny4 HTTP header.
This header testifies that the corresponding response was generated by PHP rather than by the web server itself (Apache / Lighttpd / other), which could be worth knowing when dealing with redirections (HTTP 301, HTTP 302 responses).

Several php.ini files :

  • /etc/php5/apache2/php.ini : used by Apache
  • /etc/php5/cgi/php.ini : used by Lighttpd (source)
  • /etc/php5/cli/php.ini : used by all command line calls to PHP

phpinfo() : details on PHP configuration

For complete details on PHP configuration, you can create a short PHP file containing :

<?php phpinfo(); ?>
and read its result with any web browser.

Alternate methods (source) :

Details on SSL/TLS support :

php -r 'phpinfo();' | grep -i ssl

APC

List installed PHP modules

php -m

Xdebug should appear twice : once under PHP Modules and once under Zend Modules (source).

Override php.ini settings and trigger/show PHP errors

Situation :

I'm developing / testing a script on a (production ?) server where PHP is configured to log errors instead of displaying them. I need these error messages, but can't edit the php.ini.

Details :

Whether to display / hide / log / don't log PHP errors is configured thru the directives display_errors and log_errors from php.ini, that can be set on or off. Errors are logged in the file specified with the error_log directive.

Solution :

Trigger errors

This can be achieved using trigger_error() (This function can only generate user-level errors such as E_USER_NOTICE or E_USER_WARNING.)
trigger_error("ARGL !", E_USER_NOTICE)

Show errors :

You can affect PHP settings from a script with ini_set(). To do so, just insert at the beginning of your script :
ini_set('log_errors', 'off');
ini_set('display_errors', 'on');

Show errors (alternate) :

ini_set('log_errors', 'on');
ini_set('error_log', '/path/to/logFile.log');

/path/to/logFile.log must be writable by the web server's user.

Fiddling with $_SERVER['SERVER_NAME']

$_SERVER['SERVER_NAME'] (explicit) is supposed to handle the "server name", but this name can have several values. It is possible to tune Apache to control which value is affected to this variable.

This is done by editing /etc/httpd/conf/httpd.conf and change the value of the parameter UseCanonicalName :

Value Output Example
Off IP address 172.20.31.28
On system hostname pokhara
Dns system DNS name pokhara.priv.dmz

$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'] is equivalent to $_SERVER['HTTP_HOST']

How to connect to Oracle ?

  1. Enable the corresponding PHP extensions (they are bundled with the PHP installation files) :
  2. php_oci8.dll allows the use of PHP functions such as oci_connect(), but these functions are defined in the Oracle client libraries. Follow this procedure to install these libraries. Basically, you have to :
    1. Download the Instant Client Basic package
    2. On the server running PHP, create a directory to store the Oracle client libraries. Let's say : C:\Oracle
    3. Extract these 3 DLLs into C:\Oracle :
      • oraociei10.dll
      • orannzsbb10.dll
      • oci.dll
    4. Edit system environment variables :
      • Add C:\Oracle to the PATH variable. Place it before any other Oracle-related stuff.
      • Create and set TNS_ADMIN to value C:\Oracle
      • Create and set NLS_LANG to value .AL32UTF8 (UTF-8)
  3. Restart the web service.

PHP complains unexpected end of file and doesn't recognize anymore the <?=myVariable?> syntax

Situation :

A PHP script that used to work fine suddenly starts complaining after being moved to a different server (or after upgrading PHP) :
On recent PHP versions :
PHP Parse error: syntax error, unexpected end of file in /path/to/file.php on line 294
On older ones :
Parse error: syntax error, unexpected $end in /path/to/file.php on line 84
The given line number being the very last line of the faulty script file.

Details :

This is often due to PHP setup defaults. Indeed, some PHP packages come with a default php.ini which is wide-open and not really security minded (perfect for a development server, but not for a production box). Other PHP packages come with a recommended php.ini, which is much more production-oriented, and where some options are disabled.
Here, the disabled option is short_open_tag :
right PHP syntax short_open_tag = On short_open_tag = Off
<?=myVariable?> <?php echo(myVariable); ?>
<?
...
my PHP code
...
?>
<?php
...
my PHP code
...
?>

For better code portability, it is recommended to use the short_open_tag = Off syntax.

Solution :

In php.ini, set short_open_tag to On and restart the webserver to reload parameters.