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.
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?