Browsing articles in "Development"

Use Zend_Log_Writer_Firebug to debug AJAX requests using Firebug

Debugging a page request in PHP, simple right? Either output to the page or command-line using something like var_dump, or use a step-through debugger to go through line-by-line. The advantage of the former is that it’s quick, and although the latter is more thorough it can be quite time-consuming.

However when you’re debugging an AJAX request, you can’t simply output as it’ll usually break the response that your JavaScript is expecting. Triggering AJAX events for step-by-step debugging can be tricky too. So what do you do?

Output log messages to Firebug!

$writer = new Zend_Log_Writer_Firebug();
$logger = new Zend_Log($writer);
$logger->info('Started debugging');

Setup the logger (somewhere like the bootstrap) and enable the Net panel in Firebug. Then you can simply output log messages as usual, but when using this writer they’ll appear in the Firebug console as opposed to a file or database.

Using Zend_Db_Profiler_Firebug to show database queries per page

Although ZFDebug is a great tool, it does require you to show the toolbar when you need to check something. Whilst this is fine for developers or administrators, it’ll probably confuse regular users. Therefore if you’re trying to debug something a regular user is doing, you may need another way to see what database queries are being run.

Luckily Zend Framework has both a logger and a database profiler that can send info straight to Firebug. By using Zend_Db_Profiler_Firebug you can profile the database and send that information to the Firebug console.

$profiler = new Zend_Db_Profiler_Firebug('My database queries');
$profiler->setEnabled(true);
$resource = $this->getPluginResource('multidb');
$database = $resource->getDb('default');
$database->setProfiler($profiler);

Pretty much like setting up a normal Zend_Db profiler, except now you’ll see the results in Firebug. You will need to enable the Net panel in Firebug in order to get it to output the queries to the console.

Checking $mixed parameter type for PHP methods

Something that we came across this morning, and something to be wary of in the future, is how you check the parameter type of a $mixed variable in a method signature.

i.e. Is the parameter a string or a number?

If you’re not careful you can get unexpected results…

As we’ve discovered, using ctype functions can be dangerous as ctype_digit() for example expects a string as a parameter, so if you pass it an integer it will convert it to the ASCII equivalent, thus:

ctype_digit(65) = false

Because it’s basically doing:

ctype_digit('A') = false

And A isn’t a number. The exact opposite problem exists with ctype_alpha

ctype_alpha(65) = true

Because again it’s basically doing:

ctype_alpha('A') = true

One way to get around this, although of course you must be careful too, is to use is_numeric() to determine whether a variable is a number or not. Do not use is_string() or is_integer() as they have exactly the same problems as above, in that:

is_integer("65") = false
is_string("65") = true

Whereas:

is_numeric("65") = true
is_numeric(65) = true

However as has been mentioned to me since I wrote this, is that you have to be careful when handling floating point numbers, as if they end up being represented with a mantissa and exponent you can run into similar types of problems…

“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!

VirtualBox Guest Additions with Shared Folders for Linux Virtual Machines on Mac OS X

In order to access certain aspects of a virtual machine in VirtualBox, you will need to install Guest Additions (similar to VMWare Tools I guess)

There is a manual page for installing Guest Additions but not all of it is self-explanatory…

First you need to log into your Linux virtual machine (VM) and install/configure x, y and z.

Install DKMS

This part is relatively straightforward, just install using whatever package manager your Linux distribution has. The below example uses APT that comes with Debian-based distros:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install dkms

Where to find VBoxGuestAdditions.iso

Usefully Oracle don’t tell you in their guide, but it is available with the rest of the downloads at http://download.virtualbox.org/virtualbox/

So for example for version 4.1.6 the VBoxGuestAdditions_4.1.6.iso is located at http://download.virtualbox.org/virtualbox/4.1.6/VBoxGuestAdditions_4.1.6.iso

You need this image to install the VirtualBox Guest Additions themselves onto your VM, so you can either download it to your Linux VM and mount it there, or (which is what I did) download it to your Mac OS X host and mount it in the DVD drive using the VirtualBox Manager.

How to mount the image

You need to mount the image on the Linux VM, so that you can install VirtualBox Guest Additions from it. If you have mounted it in the VirtualBox Manager in the DVD drive then you will still need to mount it in the Linux VM; Since I can’t remember the last time I had to mount something in *nix from the command-line, here’s a quick way.

sudo mkdir /dev/dvd
sudo mount /dev/dvd1 /mnt/dvd/
cd /mnt/dvd

Then you should see VBoxLinuxAdditions in that directory, which you need to run as per the manual. If the above doesn’t work it might be because your DVD drive in VirtualBox is called something else, like dvd (instead of dvd1) which probably differs depending on which distro you’re using.

Then you need to run the installer.

sudo sh ./VBoxLinuxAdditions.run

Once that’s done you can restart.

sudo reboot now

Everything should be finished now installation/configuration-wise, but you might encounter some problems… (otherwise skip to Mount the host folder)

Kernel header problems

I got some missing kernel header problems when trying to install Guest Additions, which if building the main Guest Additions module fails will be logged.

cat /var/log/vboxadd-install.log

If you see something like this…

Error! Your kernel headers for kernel 2.6.35-28-generic cannot be found at
/lib/modules/2.6.35-28-generic/build or 
/lib/modules/2.6.35-28-generic/source.
You can use the --kernelsourcedir option to tell DKMS where it's located,
or you could install the linux-headers-2.6.35-28-generic package.

So you can do just that!

sudo apt-get install linux-headers-2.6.35-28-generic
sudo ./VBoxLinuxAdditions.run

Hopefully this should install now (although the XFree86 bit will fail, assuming you’re using the command-line) and you may need to restart the VM, although I’m not sure.

Mount the host folder

You can create the host folder in the VirtualBox Manager in the Shared Folders tab on the Settings for that VM. If you add it on the command-line it’ll appear under the machine folders anyways. If you want to type it though, here’s what you’d type into the Mac OS X terminal (note this is the only thing that you’d type into the host itself)

VBoxManage sharedfolder add "my-ubuntu-vm" \
--name "websites" --hostpath "/Users/andrew/pizza"

To mount the Shared Folder from within the guest, the instructions from Ubuntu (as my guest is Ubuntu, although I think this is a better way to mount it anyways) were very useful.

sharename="whatever.you.want.to.call.it";
sudo mkdir /mnt/$sharename \
sudo chmod 777 /mnt/$sharename \
sudo mount -t vboxsf -o uid=1000,gid=1000 $sharename /mnt/$sharename \
ln -s /mnt/$sharename $HOME/Desktop/$sharename

Just change the target to be wherever you want the Shared Folder to be mounted in the guest. Now in theory if you go to that path in the guest, it should be the same as the directory you shared from the host!

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 Server lighttp not running (phpMyAdmin) but Apache is? How to start it manually

If for some reason you’ve powered on and while Apache seems to be running OK, lighttp isn’t (for example phpMyAdmin isn’t working on localhost:10081/phpmyadmin) then you will need to start it manually. This is probably caused by a dirty restart, such as a power failure. On Mac OS X run this command…

sudo /usr/local/zend/bin/lighttpdctl.sh start

For Windows, I’m afraid I will have to find that out!

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>
Pages:123»