Zend Server CE and Framework, VHost OS X Dev
So lately I’ve thought about switching my PHP development over to Zend Framework. The main reason for this is that I am just tired of rewriting the wheel. Most of the common problems with developing a PHP application have been solved and for once it would be nice to just build an application, instead of building a framework.
To get started in my efforts, I needed to set myself up with a local development environment. While I do have a production web server hosted with Slice Host (which this website is served from) and a local server at home, I wanted something that was a little less configuration heavy. After looking into several Mac OS X solutions, like XAMPP and Entropy PHP, I ran into Zend Server Community Edition. The first two, while decent solutions, were no where near as complete as the Zend provided solution. It was a clear winner for me.
Fast forward a bit, after downloading, installing and running the Zend Server CE, I was up and running. Much more quickly than I had anticipated actually. Now, the server control panel not only has modules predefined for most of the most common php suspects, it also has several already available that are a little more exotic, like DBLib (for accessing MSSQL servers with PDO). I initially had some questions on how to ensure the MySQL database was working correctly. After consulting a great article on setting up Zend CE in Leopard, I was off and running.
I do suggest one change to his post. In order to have access to the zend command line tools for Zend Framework you path should be adjusted like this in your ~/.bash_profile file:
PATH="/usr/local/zend/bin:/usr/local/zend/mysql/bin:/usr/local/zend/share/ZendFramework/bin:$PATH"
After changing this, be sure to restart your terminal window. You should now have access to the zf.sh file from where ever you are.
Adding Development VHosts
Personally I prefer to use fake local domains for my development. I prefer to type something like http://mysite.local/ in my browser than http://localhost:8181/, which is just non-descriptive and difficult to remember. Especially being that I tend to juggle projects. So, I created some simple instructions for making the Zend Server aware of your local development environment.
My personal taste is to reuse the /Users/Drew/Sites/ directory, as this keeps the information that I want my Time Machine to back up in an easy to find place. Plus it just seem more proper to edit and work with files there, than in the /usr/local/zend/ directory. The first step is to create an apache config file to help find our vhost entries.
$ vim /usr/local/zend/apache2/conf.d/vhosts.conf
Now insert this into that file. Of course change the username to be your own, not mine (Drew).
NameVirtualHost *:80 Include /Users/Drew/Sites/vhosts/*.conf
This will tell Apache to look for any vhost files we create in our home directory to parse. So let’s create a site.
Creating A New Zend Framework Site
Now, assuming that you followed my recommendation for adding Zend Framework into the path, the following should run without any issues.
$ cd ~/Sites $ zf.sh create project mysite $ rm mysite/public/.htaccess
I added the last line in, because we do not need to standard htaccess, as we will be doing this ourselves. Now, let’s create our vhost, so the app can be found by Apache.
$ vim /Users/Drew/Sites/vhosts/mysite.conf
Now insert the following into the file and be sure to adjust the username again.
<VirtualHost *:80>
ServerName mysite.local
DocumentRoot "/Users/Drew/Sites/mysite/public"
SetEnv APPLICATION_ENV development
RewriteEngine off
<Directory /Users/Drew/Sites/mysite/public>
Order deny,allow
Allow from all
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]
</Directory>
</VirtualHost>
Now, I’m assuming the url we’ll be using is http://mysite.local/. In reality this could be anything you want, as our next step is to actually define it. So feel free to change it. In this case I’ve decided to keep the Mac convention of defining local urls with the .local postfix. So, let’s edit the hosts file and add your new host in, so the browser will know how to find it.
$ vim /etc/hosts
Edit the local line and add your url to the end, like so:
127.0.0.1 localhost mysite.local
And finally, we finish it off by restarting Apache:
$ sudo zendctl.sh restart-apache /usr/local/zend/bin/apachectl stop [OK] /usr/local/zend/bin/apachectl start [OK]
And that’s it! You should now be able to go to http://mysite.local in your browser and see your new Zend Framework site ready to go. You can now just start these instructions from the site creation point next time, to set up a new site. Now, you could put all the vhosts in the same file, but I prefer having them in separate files for easier management. I’ll likely be posting some more about using Zend Framework in the near future as I start to learn it and hopefully come up with some tips and tricks. For now though, if you need a resource for getting started, try Zend’s Quickstart Guide.
Creating A Dynamic DNS Script With Slicehost
Note: The script used here is written in Python. I did not write this script. However, in the time honored tradition of the Internet I am going to reiterate it here, as it was a little hard to find a solution via Google. The original script came from a Slicehost forum poster. I just want to make clear that I am not the author, just a fan of his work.
In all of my new server craze, I’ve come to have a need to be able to connect to it from where ever I am. Initially I thought that the best solution for this was to just use the DynDNS.com free service. It does exactly what I need with three exceptions.
- I have to use one of their canned domain names.
- After 1 month of inactivity, they will cancel your account.
- My current AirPort Extreme router does not support dyndns.com updating like my old LinkSys one did.
Now, most of these are either easily overcome or downright nit-picky. #2 and #3 could be easily overcome with a script that updates the IP address periodically. However if I’m going to need a script for this, I might as well go to the next step and use a solution that let’s me take care of #1 as well. Enter SliceHost.
I’ve been a very happy customer of SliceHost for a little over two years at this point. I started with a Gentoo slice and am currently rocking a Ubuntu install, simply so there is a little more fire and forget happening. I recently started using their wonderful DNS management system to add a post.drewbutler.name to point over at the Posterous servers. So I figured why not do the same thing but point it at my home server.
Setting Up A Type A Record On A Zone
In network terminology, your DNS is split into zones and records. The zone is the main domain name itself (drewbutler.name.) and the record is essentially the sub-domain (home). Now, I am going to say that this is an extreme simplification, however for our purposes, it is relatively accurate. For more in-depth information, please see the Wikipedia article related to DNS, zones and records.
I will assume for this exercise that you have your domain (zone) already set up and that we are simply adding a new sub-domain (record) to it. In the SliceManager, go to DNS -> Domains and click the Records link next to your domain. Once you’re in the records list for your zone, click on the New Record link. We’re going to set things similar to the image below, though substitute the Name field with your sub-domain and the data with your home IP address. You can use the dyndns checkip service to easily get your current ip address.

Hit update and you should be able to go to home.yourdomain.com and see your own server (assuming you have a web server set up). Otherwise, if you have a terminal available, run: ping -c 1 home.yourdomain.com and you should see your home ip address appear in parenthesis.
While we’re in the DNS records, you should see your new subdomain “home” listed. Hover over the edit link and copy the url there. It should be something like this:
https://manage.slicehost.com/zones/1234/records/123456/edit
The first number is your zone id and the second is the record id. Write down the record id (in this example, 123456) as we’ll need that in a little bit.
Using the SliceHost API to do some periodic updates for us.
First if you haven’t done so, let’s enable the API and/or get the API key. Go to the Account -> API Access in your SliceManager and click Enable API Access (if applicable). Now you should see you API password displayed. Copy and paste it into a document, write it down or whatever; we’ll need this in a bit as well.
For this next bit I am going to assume you are running some sort of *nix server and have 2 things installed, Python and Subversion. If you do not have them installed, please do so now. Both are very common packages and the installation should be very well documented for your distribution of Linux.
The script makes use of a great package called PyActiveResource. This is essentially a port of the ActiveResource module for Ruby, but in Python. At this moment, it seems the best way to get this package installed is via subversion, so let’s do that.
svn checkout http://pyactiveresource.googlecode.com/svn/trunk/ pyactiveresource-read-only
cd pyactiveresource-read-only
sudo python setup.py install
Assuming there were no errors, this should be installed as a python module now.
Next let’s setup a script to run. I’m gonna assume you are running Vim like I am, but please feel free to substitute that as appropriate.
sudo vim /home/drew/slicehost_dyndns.py
/home/drew/slicehost_dyndns.py
import urllib, re, sys, os
from pyactiveresource.activeresource import ActiveResource
api_key = 'xxxxxx'
api_url = 'https://%s@api.slicehost.com/' % api_key
record_id = '123456'
class Record(ActiveResource):
_site = api_url
results = Record.find(id=record_id)
if len(results) != 1:
print "Can't find Record %s via SliceHost API." % record_id
sys.exit(1)
salon = results[0]
found_ip = (re.findall('[0-9.]+', urllib.urlopen('http://checkip.dyndns.org/').read())[-1])
if salon.data != found_ip:
salon.data = found_ip
salon.save()
print "IP Updated: " + found_ip
else:
print "IP Unchanged: " + salon.data
Update the api_key and record_id with the information we wrote down before and save it. Be sure to maintain the spacing on lines as best as possible. Python is very strict about the use of white-space. The script from the forum post was actually not spaced properly and therefore didn’t run. That is really the only change I made. Otherwise it worked perfectly.
From the command prompt, you should now be able to run
sudo /home/drew/slicehost_dyndns.py
And will see either IP Updated or IP Unchanged. I suggest testing it at least once before moving forward.
crontab -e
Add this to have the script run every 10-minutes. As the script only does an actual update if the ip-address changes, it is fairly innocuous.
*/10 * * * * python /home/drew/slicehost_dyndns.py
You now have a working dynamic dns setup for your home server. I’ll be working on some updates to the script to do things like log when it actually changes the dns entry. I figure it’ll be helpful to monitor who often the script really needs to run and if there are any patterns in when the IP address expires. Might help in customizing the cron’s timing and thus decreasing load on the Slicehost servers. As it stands now though, this should be a very low impact solution, so I doubt they’ll notice.
Error handling in PHP, or how we won the war.
One of my biggest gripes about PHP in comparison to other modern object-oriented languages is the relatively poor state of error handling. The mix and match style of triggered errors and exceptions is unbefitting of a modern language, especially one that is ready for the enterprise. Most modern languages deal with errors strictly through the throwing of exceptions, because it allows for a more robust method of triggering a response from the system. This is because a thrown exception, can be caught. In PHP though, a triggered error cannot.
At least not technically.
Lucky for us there is a way to make PHP behave a little more civilly than it does by default. Through some clever uses of the error and exception handlers, we can make PHP just a little more robust.
Our Exception
First we start with our exception object. Personally, I’m a fan of explicit exception naming, so that I can more easily catch very particular exceptions. For this example we will use one that I will cleverly name PhpException.
class PhpException extends Exception {}
In this case we will not be modifying what the Exception object does, only giving it a new name to reference by. If you’re new to exceptions, then trust me, this will make your life easier down the road when you’re catching an exception from a method that that can fail in a couple different ways. Being able to single out one exception type that you know how to better handle is much preferred than catching everything and maybe covering up a real problem.
The error handler
In short what the error handler is, is a function or class method that you define as the recipient of any php error that is triggered in the system. This is by far the most common way for php native function to trigger their errors. The error handler is the first thing to happen after the triggered error. It is given a list of the message, the error code, the file it occurred in and on what line. From there, as long as you do not return a boolean false, it will not follow the normal error pattern. In our case, what we want to do is provide some new options. So, we will wrap this error in our new PhpException class.
function handleError( $code, $message, $file, $line, $context )
{
// Silencing a statement with @ causes this to be 0.
// Let's be respectful and not throw errors if silenced.
if ( ini_get( 'error_reporting' ) == 0 ) {
return true;
}
$msgTpl = 'PHP Error (%u): %s in: %s:%u'
$message = sprintf( $msgTpl, $code, $message, $file, $line );
throw new PhpException( $message );
}
set_error_handler( 'handleError' );
At it’s core, this is a very simple method of converting a triggered error into an exception. It first checks to see if error reporting is off, due to a silence. If that is the case, it exits early, since we can ignore that error. Otherwise, it just turns the error to an exception object and throws it. With this done, we can now catch the uncatchable.
try {
trigger_error( "You can't catch me!", E_USER_ERROR );
} catch ( PhpException $e ) {
// Oh yes I can!
}
In the most basic of ways, we have made PHP errors catchable exceptions. This is an extremely desirable behavior. The only errors that will not be caught by this are compiler and parse errors. We’ll get to parse errors later though.
Speaking of error types, what is wrong with the above example?
We are catching too much. Some of the error types that are thrown are warnings and we do not want them stopping a code run like an exception will do. This is easy to fix though. What we’ll do is create a list of the two error type and check what kind we have then act accordingly. For this, I am going to turn our error handler function into and EventHandler class, as it will give us some more flexibility later in the examples.
class EventHandler
{
const MSG_TPL = 'PHP Error (%u): %s in: %s:%u';
protected $warningLevels = array (
E_WARNING,
E_NOTICE,
E_CORE_WARNING,
E_COMPILE_WARNING,
E_USER_WARNING,
E_USER_NOTICE,
E_STRICT,
);
protected $fatalLevels = array (
E_ERROR,
E_PARSE,
E_CORE_ERROR,
E_COMPILE_ERROR,
E_USER_ERROR,
E_RECOVERABLE_ERROR,
);
public static function start()
{
$handler = new EventHandler;
set_error_handler( array ( $handler, 'handleError' ) );
}
public function handleError( $code, $message, $file, $line, $context )
{
// Silencing a statement with @ causes this to be 0.
// Let's be respectful.
if ( ini_get( 'error_reporting' ) == 0 ) {
return true;
}
$message = sprintf( self::MSG_TPL, $code, $message, $file, $line );
if ( in_array( $code, $this->warningLevels ) ) {
error_log( $message );
if ( ini_get( 'display_errors' ) ) {
echo '<p>Warning! ' . $message . '</p>';
}
return true;
}
if ( in_array( $code, $this->fatalLevels ) ) {
throw new PhpException( $message );
}
return true;
}
}
EventHandler::start();
What you’ll notice here is that we now have an object to encapsulate our logic and we now distinguish between warning and fatal level errors. In this case, a warning will still write to the PHP error log as the default behavior would do and if display errors is on (like it should NOT be in your production environment) then it will also print that error to the screen. Aside from that, it works identically. Error is caused, if it is fatal, it becomes an exception.
What to do with those exceptions.
Well, we’ve turned all of our fatal errors into catchable exceptions, but what do we do with those now? In some cases we will know what to do and will catch them. In others, we may not anticipate them or we may not know how to handle them and will need to cause a 500 Internal Server error to occur. This is where the exception handler comes in.
Like its brother the error handler, the exception handler is designed to catch exceptions for you automatically. The difference between the two is that errors are caught immediately, an exception will travel back up the call stack until it reaches the point you started you code in. At that point the exception handler will kick in and go all last action hero for you. The idea is, this will handle any failure logic you need, which can be as simple as logging the message in a certain way, as complex as showing a super pretty 500 error page or perhaps a little of both.
Let’s add a couple methods to our EventHandler class.
public function handleException( $exception )
{
header( 'Status: 500 Internal Server Error' );
$this->sendNotice( $exception->getMessage() );
$this->printError( $exception->getMessage() );
exit;
}
protected function sendNotice( $message )
{
$to = 'me@myemail.com';
$subject = 'An error occurred on your website.';
$from = 'errors@mywebsite.com';
$message = wordwrap( $message, 70 );
$headers = "From: %s\r\nReply-To: %s\r\nX-Mailer: PHP/%s";
$headers = sprintf( $headers, $from, $from, PHP_VERSION );
mail( $to, $subject, $message, $headers );
}
protected function printError( $message )
{
// Clear the output buffer. Yes you should have started this
// at some point to avoid fragments of unwanted
// data on your page. See ob_start().
ob_clean();
// Load a template.
echo "<h3>An error has occurred.!!</h3>";
echo "<p>Thank you, come again.</p>";
if ( ini_get( 'display_errors' ) ) {
echo "<p>" . $message . "</p>";
}
}
In this case I have chosen to e-mail out that an error occurred to myself and print a message to the user indicating that there was a problem. For the printError() part I usually pull in a full page template, in the site’s style, but for this example I am keeping it as brief as possible.
To enable this code, just add this line to the bottom of the start() method of the EventHandler class we’ve been making.
set_exception_handler( array ( $handler, 'handleException' ) );
Depending on how complicated your code gets for handling the exception, you may also want to consider wrapping the logic in that method in a try catch block that catches all exceptions. The reason being is that if you are generating a template and a secondary exception is thrown, you will see the dreaded “exception thrown without a stack trace on line 0” error. By wrapping that logic in a try-catch block you can catch it and have a simpler fail over available. Usually mine is just log it, and die(). As in that case I want to be as simple and brief as possible.
A brief example:
public function handleException( $exception )
{
header( 'Status: 500 Internal Server Error' );
try {
$this->sendNotice( $exception->getMessage() );
$this->printError( $exception->getMessage() );
} catch ( Exception $e ) {
$tpl = 'Something really bad happened. An exception was thrown '
. 'in the exception handler. Message: %s, File: %s, Line %u';
error_log( sprintf( $tpl,
$e->getMessage(),
$e->getFile(),
$e->getLine() ) );
}
exit;
}
You can of course do much more than that. Hopefully this was enough to get you thinking about some new possibilities though.
Parse Errors
There is one last type of error that you can catch, that is otherwise harder to see. That is the parse error. This happens when a file you include has a syntax error in it. Usually you’ll see a generic message and a message in your error log. This means you may miss it if some bad code hits your production site.
Luckily PHP has a way to add a shutdown function that will run even when the PHP is crashing. Add this to your EventHandler class.
public function start()
{
....
register_shutdown_function( array ( $handler, 'handleShutdown' ) );
}
public function handleShutdown()
{
if ( !$error = error_get_last() ) {
return;
}
$message = 'Uncaught ' . sprintf( SELF::MSG_TPL,
$error['type'],
$error['message'],
$error['file'],
$error['line'] );
$this->sendNotice( $exception->getMessage() );
$this->printError( $exception->getMessage() );
}
Now we can add a custom message and even a pretty 500 page if we so choose. No more white screens of death for your visitors, just because you forgot something. It happens, we all make mistakes. The point is to have events like these set up to make it less apparent or at least less horrible to our visitors.
Conclusion
The most important feature your can add to any application or framework is a robust way to handle when something goes wrong. Something always will and the better the experience is for your visitors, the more likely it is that they will come back. On the same note, the better your error reporting, the easier time you will have finding and fixing those errors when they occur.
There are all sorts of ways that you can extend what I’ve shown you. The error handling routines I have written for the framework I maintain provide all sorts of other useful information for my company and my team. Of course that goes outside of the scope of this article, I just hope that this gets you thinking on ways to make your own systems the best they can be.
How do you handle errors in your projects?