PHP Error Handling and Magento Developer Mode

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Many people, myself included, recommend that you “Turn on Developer Mode” while working on your Magento store. These days, this can be done in an .htaccess file, or directly in Magento’s index.php bootstrap file.

#File: index.php

if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {
    Mage::setIsDeveloperMode(true);
}

But what does Developer Mode do? That’s what I hope this this article will explain. However before we can do that, we’ll need to jump into the world of PHP Error Handling and Exceptions.

Code examples in this article are specific to Magento Community Edition 1.4.1.1.

Exceptions: A Simple Explanation

Exceptions are a way for a programmer to say

Hey, this unexpected thing happened and I don’t know what to do about it

while at the same time allowing a future programmer to say

Hey, if that unexpected thing happens while I run this code, I don’t want the program to stop. Do this other thing instead,

So, Programmer A might write something like

#
public function doSomeThing($var)
{
    if(...$var is somehow not appropriate)
    {
        throw new Exception("Please stop hitting me");
    }

    ...
}

and Programmer B might write something like

$result = $object->doSometThing();

If the conditions for the Exception were met, above code would halt execution. If Programmer B didn’t want that to happen, they’d write something like

try
{
    $result = $object->doSometThing();
}
catch(Exception $e)
{
    //woah, sorry, I didn't mean to hit you.  Let me do something here instead of 
    //having the program stop
}

If any of the code in the try block results in a throw Exception, execution is handed to the catch block, where the variable $e will contain a reference to the Exception object created by

throw new Exception("Please stop hitting me");

The Exception class is used to instantiate base level Exceptions for a PHP program. Programmers can define their own Exceptions by extending this class. You’d do this if you wanted to pass more information up to the catch block, or have certain things happen automatically whenever an Exception of your new type was thrown. Also, the PHP Standard Library provides a number of predefined custom Exceptions.

Exceptions and PHP

Exceptions are one of those things that people have strong opinions about that (mostly) have to do with how they write code, and how they expect their code to be used. Speaking stereotypically, old time C programmers look down on Exceptions. If something unexpected happens to a program that’s carefully managing memory, it means you have bigger problems. On the other other side, people working with systems that need 100% uptime and require a large team of programmers rely on Exceptions and the level of logging they allow without bringing the entire system down.

How Exceptions are used is going to vary project by project, team by team, language by language. PHP developers have an additional problem to contend with. Exceptions didn’t exist in the language until PHP 5. Before PHP 5, the language relied on an internal error handling system.

With PHP’s non-exception error handling system, if “something bad” happens, PHP issues an error. If the the level of this error is high enough, PHP halts execution. If the error is considered a recoverable error (say, not declaring a variable before using it) PHP will continue execution.

However, before continuing execution, PHP checks the error reporting level. If the issued error level falls within the configured reporting level, an error message will be sent to the error log. The location of the error log is set with a php.ini variable, and if unset PHP will use the web server’s error logs.

After logging the error, PHP checks the value of the display_errors ini setting. If turned on, PHP will send that same error message to the standard error handler. In the case of apache/web based requests, this means the error is printed out to the browser.

When the switch to PHP 5 was made, this error handling system stayed in place, and most native PHP functions continued issuing the same errors instead of throwing Exceptions. This was considered necessary by the PHP team in order to ensure that old code would work. This means PHP has two different ways of dealing with errors. There’s the Exception system, and the traditional error handling system.

Problems with the Error Handling System

Most PHP projects eventually run into a problem with the error handling system. Every developer has a different opinion on what a reasonable level of error reporting is. Some people hold to the view that so long as it runs you have working code. Others feel you shouldn’t be writing PHP code that could raises any warnings or notices, even if they’re only spewing into the log file.

The other problem with PHP errors is that they’re ignorant of try/catch blocks. An error issued in a try block won’t turn control over to the catch block. PHP has a system for setting custom error handlers, but these callbacks will apply system wide, and changing how other people’s code deal with errors often leads to trouble.

Back in the old days the perl programming language had a similar problem. People were creating libraries with different expectations about what was considered “good perl” or “bad perl”. Eventually, the language added a pragma called strict which makes perl complain about code that’s technically legal, but probably not doing what you mean or want it to.

Magento’s Use Strict

Which brings us, finally, to the point. PHP lacks, and likely always will lack, anything similar to the use strict pragma in perl. There’s a core team philosophy that’s focused on the practical, and not too concerned with people using their system to write dodgy code.

One of the big draws for Magento is the Magento core team have used the custom error handling system and a very high error reporting level

error_reporting(E_ALL & ~E_NOTICE);

to create their own version of use strict version for PHP. That’s what Developer Mode is. When on, Magento forces you to write clean, notice free PHP code. For developer of a certain mindset, that’s a godsend.

Consider the following PHP code.

echo $foo;
echo "\nDone\n";

As written, with error reporting turned on and a reasonable error reporting level, the above will produce output something like the following to the browser and error log

PHP Notice:  Undefined variable: foo in /path/to/file.php on line 2
PHP Stack trace:
PHP   1. {main}() /path/to/file.php:0

Done

PHP will issue a Notice about the use of the undefined variable $foo, but then continue on its way. This is true even if you wrap things in a try/catch block.

//will output the same as above
try
{
    echo $foo;
    echo "\nDone\n";
}
catch(Exception $e)
{
    echo "\nPHP hates me, and will never call me, and still lets the users
    see that god awful notice text. *cries* \n";
}

However, if you take the above code and put it in, say, a Magento controller action

public function indexAction()   
{       
    echo $foo;
    echo "\nDone\n";
}

Magento will output either this

Notice: Undefined variable: foo  in /path/to/controllers/IndexController.php on line XX

when Developer Mode is on, or this

Done

when it’s off. Let’s take a look at how this has been implemented.

Magento’s Custom Error Handler

You’ll remember that we mentioned a custom error handler earlier. During the Magento application initialization process, a custom PHP error handler is created. That means when you write some PHP code that issues an error PHP will call a custom function or method (actually, a PHP pseudo-type called a callback) of your choosing. For Magento, it’s the global function named mageCoreErrorHandler

#File: app/code/core/Mage/Core/functions.php
function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){
    ...
}

This function examines the error information and extracts the type of PHP error that occurred. Then, there’s the code at the end of that error handling function

$errorMessage .= ": {$errstr}  in {$errfile} on line {$errline}";
if (Mage::getIsDeveloperMode()) {
    throw new Exception($errorMessage);
} else {
    Mage::log($errorMessage, Zend_Log::ERR);
}

As you can see, if Developer Mode is on an Exception will be thrown. This means, regardless of your PHP system’s error reporting level, if you write something that raises an error or a warning during developer, execution will halt and you’ll be forced to fix it. If Developer Mode is off, the error will be logged, and PHP will attempt continue on as though nothing bad has happened. Developer Mode allows you to treat all programming errors strictly during development, but doesn’t force that same draconian error handling on your production systems.

That’s the primary protection Developer Mode offers you. There are, however, two other things Developer Model does that you’ll want to be aware of.

Observer Protection

Take a look at the following system code.

#File: app/code/core/Mage/Core/Model/App.php
protected function _callObserverMethod($object, $method, $observer)
{
    if (method_exists($object, $method)) {
        $object->$method($observer);
    } elseif (Mage::getIsDeveloperMode()) {
        Mage::throwException('Method "'.$method.'" is not defined in "'.get_class($object).'"');
    }
    return $this;
}

When you setup an event observer in Magento, this is the system code that hands off execution to your observer method. However, before trying to call your method, the system will check to make sure your observer is callable with the method_exists function. Calling an un-callable method is a Fatal error, so this check is important to ensure a production system doesn’t crash due to a simple misconfiguration.

As you can see above, when Developer Mode is on, an Exception will be thrown, halting execution and forcing you to fix the problem. When its off, execution continues as though nothing has happened.

Translation

Magento’s translation feature is (as many are) based on concept similar to GNU gettext. English strings or symbols are passed through a translation method, and used as keys to lookup the actual string that should be displayed.

Magento allows each module to have its own set of translation files. Occasionally, translation files for two separate modules will attempt to translate the same word. If you’re developing a module and have another module installed with similar language, it may lead to gaps as you’re developing your translation file and don’t realize that you need to translate those phrases in the other file. While you could leave you system installed as is and things would probably be fin, not all users are going to have all modules installed.

There’s a bit of code deep in the translation system that will force a helper’s translation method to only use symbols scoped to that module

#File: app/code/core/Mage/Core/Model/Translate.php
protected function _addData($data, $scope, $forceReload=false)
{
    foreach ($data as $key => $value) {
        if ($key === $value) {
            continue;
        }
        $key    = $this->_prepareDataString($key);
        $value  = $this->_prepareDataString($value);
        if ($scope && isset($this->_dataScope[$key]) && !$forceReload ) {
            /**
             * Checking previos value
             */
            $scopeKey = $this->_dataScope[$key] . self::SCOPE_SEPARATOR . $key;
            if (!isset($this->_data[$scopeKey])) {
                if (isset($this->_data[$key])) {
                    $this->_data[$scopeKey] = $this->_data[$key];
                    /**
                     * Not allow use translation not related to module
                     */
                    if (Mage::getIsDeveloperMode()) {
                        unset($this->_data[$key]);
                    }
                }
            }
        ...

Wrap-up

And that, in a nutshell, is Developer Mode. While writing 100% compliant PHP code may seem like a hassle, it’s worth it in the long run. PHP will never offer the full type safety of a compiled language like Java, but having the system force you into cleaner coding conventions in the next best thing. Developer Mode will help you write cleaner code without subjecting your users to awkward PHP errors when something goes wrong, and knowing how developer mode achieves this can help you debug your, and your client’s, systems.

Like this article? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Originally published November 23, 2010
blog comments powered by Disqus