Browsing articles in "PHP"

“concrete5 cannot parse the PATH_INFO or ORIG_PATH_INFO information provided by your server.”

Thought I’d take a look into concrete5 to see what is was like, and it’s the first open source PHP project installer I’ve come across in ages that complained about anything other than filesystem permissions. PATH_INFO? Most other projects use different mechanisms/variables to do a similar job I assume…

Anyways, go to the concrete5 install directory and create a .htaccess file there with the following contents:

php_flag display_errors off
AcceptPathInfo On

If you don’t suppress PHP errors it won’t work, which I read may be related to an Ajax callback failing because the response is full of errors (the installer isn’t written properly? I’ve no idea…)

I’ve also heard that some hosting providers (like GoDaddy) don’t play well with .htaccess files, which unfortunately if that’s the case you’ll have to get in touch with them!

Using PHP late static binding to define static arrays as subsets for child classes

OK, the title might sound a little confusing, but the concept it relatively simple. Basically it’s using an array in a child class that contains some of the items that appear in the array in the parent class.

Which just means you have a list of all of the things in the parent, and a list of some of those things in every child. Like a list of options such as colours, types, etc.

Imagine that you have a class that represents all the cars you sell:

class Cars

To make sure that an enumerated database field matches up when you pick the colour of the car, you use class constants; this is so that when you set you use the same constant, as opposed to typing it in each time (which reduces error, and means you can change it in one place only)

class Car
{
const COLOUR_GREEN = 'green';
const COLOUR_RED = 'red';
const COLOUR_YELLOW = 'yellow';
const COLOUR_BLUE = 'blue';

If you want to find out what colours the cars are available in, you could use an array. Because you can’t declare constant arrays (because they aren’t scalar) you could use a variable; using a static variable will allow you to use it in static methods.

class Car
{

const COLOUR_GREEN = 'green';
const COLOUR_RED = 'red';
const COLOUR_YELLOW = 'yellow';
const COLOUR_BLUE = 'blue';

static array $colours = array(
COLOUR_GREEN,
COLOUR_RED,
COLOUR_YELLOW,
COLOUR_BLUE,
);

}

Then, if you want to make child classes of the cars class, to represent each type of car, you don’t need to redine the constants; all the types of cars can be those colours. However, each type of car might not be available in every colour; the colours are the same but which colours are available are different.

For example if you had trucks that were available in green, red and yellow, and vans that were available in red and blue.

class Truck extends Car
{

static array $colours = array(
COLOUR_GREEN,
COLOUR_RED,
COLOUR_YELLOW,
);

}

class Van extends Car
{

static array $colours = array(
COLOUR_RED,
COLOUR_BLUE,
);

}

This might look all well and dandy, but the problem comes when you check what is in self::$colours in a static method of the child class. Despite the values being different in the child class, it will still contain the values in the class where it was originally declared.

Therefore, if you ran the following method:

static method colours()
{

print_r(self::$colours);

}

You would get the following result in all 3 classes, even if you declared self::colours() in every class.

Array
(
    [0] => green
    [1] => red
    [2] => yellow
    [3] => blue
)

To make sure that PHP gets the values from the most recently declared (in the class hierarchy) version of that array, is to use the static keyword.

static method colours()
{

print_r(static::$colours);

}

Using the static keyword, you would only need to write the static colours() in the parent class, as it will still use the static variable in the child class if it is called from the child class, even if the method is declared in the parent class. Calling Car::colours() will tell you all of the colours that are available, Truck::colours() will tell you what colours trucks come in, and likewise with Van::colours()

Note that you do need PHP 5.3 to make this work.

p.s. I wouldn’t actually put colour constants in a car class! It was just the first example that I could think of that wasn’t what I actually used it for at work…

Zend Framework 1.8+ Module/Library Autoloading

Given the problems I had for the 1st Zend Framework website I did, I figured the next time I started one from scratch I would document how to get the autoloading working, since almost nowhere has it documented correctly. I forgot then too, so 3rd time lucky…

The following code assumes the following:

  • You have no model, controller or view directories at the root of application/ because they should all be within application/modules/modulename/
  • The default module is located at application/modules/default/
  • The default module is not namespaced, so the index controller is called IndexController, not Default_IndexController
You can change all of the above in application.ini and/or Bootstrap.php if so desired.

The key things you need to add, excluding the directory structure that will be created for you by Zend Tool is the following entry in application.ini in application/configs/

resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"

Then put the following code in the Application (not any of the Modules) Bootstrap.php in application/

protected function _initAutoloader()
{

	$moduleAutoloader =
		new Zend_Application_Module_Autoloader(array(
			'namespace' => '',
			'basePath' => APPLICATION_PATH.'/modules/default'
		));

}

Why? No idea, as according to Zend you can do it all from setting the Module Resource in application.ini, although I have never gotten that to work with a variety of different configuration options…

If you need to register a namespace for the library you can add this to Bootstrap.php also

$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('MyNamespace_');

Testing and timing RDS/MySQL scheduled downtime with PHP

Something I was asked at work recently was “if we do this to the server, how long will it be unavailable for?” and usually the answer would be easy… however in certain circumstances you may not know. One of these is Amazon RDS, whereby you don’t really know what happens when you reboot or change the configuration of an instance, it just sort-of happens and eventually becomes available again.

For Amazon RDS an example would be when you modify the DB Instance Class of your main instance, which will cause the server to be “modified” which will cause some downtime; of course a reboot will cause downtime also.

Therefore I needed to time how long the website would go down for, at what point connections to the database would fail, or running a query would fail, etc.

I wrote a PHP CLI script to record how long it will take (in seconds) for the database to recover for any given operation that will cause downtime. Start the script, and it will keep outputting ‘+’ to the command line. Start the operation, and eventually the ‘+’ will turn into ‘-’, and once they turn into ‘+’ again it means the downtime is over and the database(s) will be available again, and it will tell you how long it took.

The output will look something like this…

user@server:~$ php test_upgrade.php
Starting...
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + - - - - - - - - - - - - - - - -
- +

Database down for 262 seconds

The script is as follows…

<?php

error_reporting(0);

$host = 'myhost.us-east-1.rds.amazonaws.com';
$port = 3306;
$database = 'mydb';
$username = 'user';
$password = 'supersecretpassword';

$sql = "SELECT count(*) FROM users";

echo "Starting...\n";

$disconnectTime = 0;
$reconnectTime = 0;
$reconnected = null;

while ($reconnected !== true) {

	$link = mysql_connect($host.':'.$port, $username, $password);
	if (! $link) {
		flagConnection(false);
		$reconnected = false;
		continue;
	}

	$db_selected = mysql_select_db($database, $link);
	if (! $db_selected) {
    	flagConnection(false);
		$reconnected = false;
		continue;
	}

	$result = mysql_query($sql);
	if (! $result) {
		flagConnection(false);
		$reconnected = false;
		continue;
	}
	$row = mysql_fetch_row($result);
	if (! $row) {
		flagConnection(false);
		$reconnected = false;
		continue;
	}

	// If everything works, flag as connected
	flagConnection(true);

	// If was previously disconnected, exit the loop
	if ($reconnected === false) {
		$reconnected = true;
	}

	mysql_close($link);

}

$outageTime = $reconnectedTime - $disconnectedTime;

echo "\n\nDatabase down for $outageTime seconds\n";

function flagConnection($flag)
{
	global $disconnectedTime;
	global $reconnectedTime;
	global $reconnected;

	echo ($flag ? '+' : '-').' ';
	$log[] = $flag;

	if ($flag == false && is_null($reconnected)) {
		$disconnectedTime = time();
	}

	if ($flag == true && $reconnected === false) {
		$reconnectedTime = time();
	}

	sleep(1);

}

?>

Google Geocoding API with PHP

If you want to know where a particular address is by latitude and longitude, but only have the normal address details like street, city, zip, country, etc. then the Google Geocoding API should be able to help. By passing it an address string it will try to work out (just like Google Maps) whereabouts the address is, and return you a JSON or XML response with what it managed to figure out.

Below is a basic PHP example using cURL (based on an example from Stack Overflow by Jack W)

<pre>
<?php

function lookup($string){

   $string = str_replace (" ", "+", urlencode($string));
   $details_url = "http://maps.googleapis.com/maps/api/geocode/json?address=".$string."&sensor=false";

   $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL, $details_url);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
   $response = json_decode(curl_exec($ch), true);

   // If Status Code is ZERO_RESULTS, OVER_QUERY_LIMIT, REQUEST_DENIED or INVALID_REQUEST
   if ($response['status'] != 'OK') {
   	return null;
   }

   print_r($response);
   $geometry = $response['results'][0]['geometry'];

	$longitude = $geometry['location']['lng'];
	$latitude = $geometry['location']['lat'];

	$array = array(
		'latitude' => $geometry['location']['lng'],
		'longitude' => $geometry['location']['lat'],
		'location_type' => $geometry['location_type'],
	);

	return $array;

}

$city = 'San Francisco, USA';

$array = lookup($city);
print_r($array);

?>
</pre>

Use phpMyAdmin with a remote MySQL server

Been doing relatively proprietary work recently, so not much to share other than this little tip. If you want to use phpMyAdmin but for whatever reason the MySQL server you’re using isn’t local, you can change the host by editing line 104 in config.inc.php in the root of phpMyAdmin:

$cfg['Servers'][$i]['host'] = 'localhost';

… and optionally the port if required

$cfg['Servers'][$i]['port'] = '';

Zend Framework, Zend_Db_Table ORDER BY sorting NULL as last instead of first

If you have a group of fields, some of which may be null (such as an address, in which the 1st line of the address may or may not be filled in) then you may want to sort them all so that if null appears in a field, it’s sorted to the bottom of the list as opposed to the top (which is the default behaviour)

$select = $table->select();
$rowset = $table->fetchAll($select->order('IF(ISNULL(line1),1,0), line1', 'line2', 'city', 'country'));

I grabbed the MySQL code from the MySQL Forums

WordPress 3.2 upgrade, WYSIWYG editor no longer loads? Now requires JSON extension

An issue I encountered recently doing a WordPress upgrade from 3.1 to 3.2, is that afterwards everything appeared to work with the exception of the editor for Posts and Pages. After working through the checklist for the manual install and checking each plugin individually, I took a look at the PHP error log and noticed that PHP didn’t have the JSON extension bundled.

If you get an error mentioning that json_encode() is missing, it will be because PHP wasn’t compiled with it and WordPress now uses it. If you use SuSE (which this particular server is) you need to install the php5_json package using a command similar to:

zypper install php5_json

Otherwise you will need to see if your distro has a JSON-specific package for PHP, or you may have to compile it from source.

NOTE: PHP usually has the JSON extension compiled into it by default since 5.2, so this is an unusual case

Access the non-direct View Helper methods in Zend Framework

Something that was temporarily puzzling me so I thought I would post about it. As in most examples to access the direct() method of a View Helper, you simply call the class name on the View Object:

$view->baseUrl();

But if you want to access any of the methods other than direct() you’ve got to get the Helper first, as unlike most Action Helpers they don’t implement a fluent interface because they return bits of the view instead (usually HTML)

$this->getHelper('BaseUrl')->setBaseUrl();

Configuring Zend Server CE on Mac OS X

Unlike Windows which doesn’t come with Apache by default, or distributions of Linux which (usually) have their own package management systems, Mac OS X comes with an outdated version of Apache which isn’t handled by package management. This means it’s a pain to either update or remove it. Any Mac PHP developer will probably have tried MAMP which gives you a relatively pain-free installation, but for anything complex it can be a bit tricky due to the way it’s self-contained. As a Zend Framework developer I use Zend Server and Zend Server CE at work now and figured now that I’ve got to do some work at home I should look to get it installed and configured…

Just to check that everything is working OK to start with, go to http://localhost/ and you should see the default Mac OS X homepage which will be located in your Sites directory.

Installation

Download Zend Server CE from Zend and install as usual.

Configuration

Disable the default Apache installation

apachectl stop

Replace the default apachectl with Zend Server’s apachectl

Easiest way to do this (without uninstalling anything) is to replace the default apachectl with Zend Server’s apachectl, that way it will just appear to be launching Apache as per usual when OS X starts up.

Firstly move apachectl to a safe place, such as your home directory:

cd /usr/sbin/
sudo mv apachectl /Users/yourusername/

Then create a symbolic link to Zend Server’s apachectl:

sudo ln -s /usr/local/zend/apache2/bin/apachectl ./ 

Start Zend Server’s Apache:

sudo apachectl start

You should then see:

/usr/sbin/apachectl start [OK]

This will indicate that you are running Zend Server’s Apache as opposed to Mac OS X’s Apache (as the latter doesn’t output to the terminal when you start/stop it) although if you go to http://localhost/ you will notice that you will get a 404.

Configure Apache to use port 80

I’m assuming to avoid any conflict with the default Apache installation that Zend Studio’s Apache comes preconfigured to use port 10088. To change this simply edit the configuration:

sudo vi /usr/local/zend/apache2/conf/httpd.conf

… or if you dont use Vi, use another text editor such as Nano:

sudo nano /usr/local/zend/apache2/conf/httpd.conf

Look for the line Listen 10088, then either comment it out and add a new line or edit the port to be port 80.

#Listen 10088
Listen 80

You may also need to change the DocumentRoot and first Directory parameters to your webroot (/Users/username/Sites for example)

Save httpd.conf (Ctrl + x in Nano to exit then y to save) and restart Apache:

sudo apachectl restart

If you go to http://localhost/ in your browser you should now see the default Mac OS X homepage, as httpd.conf should already be setup to point to your Sites directory by default.

If you get any complaints about the FQDN when you start Apache, such as:

httpd: Could not reliably determine the server's fully qualified domain name, using iMac.local for ServerName

Then you need to explicitely set the name of the domain to be used (in this case, localhost)

#ServerName www.example.com:10088
ServerName localhost:80

Configure Zend Framework

sudo ln -s /usr/local/zend/share/ZendFramework/bin/zf.sh ./zf

Configure Pear

sudo ln -s /usr/local/zend/bin/pear ./
sudo pear upgrade pear

Configure MySQL

You can create symbolic links for the typical MySQL commands if you wish (although you can change the include paths for the shell if you so desire)

sudo ln -s /usr/local/zend/mysql/bin/mysql ./
sudo ln -s /usr/local/zend/mysql/bin/mysqladmin ./
sudo vi /usr/local/zend/mysql/data/my.cnf
#socket = /usr/local/zend/mysql/tmp/mysql.sock
socket = /tmp/mysql.sock

Configure phpMyAdmin

If you change the web root for Apache, you will have to create a Directory entry and Alias for phpMyAdmin, since it exists somewhere else:

sudo vi /usr/local/zend/apache2/conf/httpd.conf

Alias /phpMyAdmin /usr/local/zend/share/phpmyadmin

<Directory "/usr/local/zend/share/phpmyadmin">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

Install PHPUnit

sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover components.ez.no
sudo pear channel-discover pear.symfony-project.com
sudo pear update-channels
sudo pear install --alldeps phpunit/PHPUnit
sudo ln -s /usr/local/zend/bin/phpunit ./
sudo pear install --alldeps phpunit/PHPUnit
Pages:12»