All about Magento E-commerce Store.......MagentoForum

Friday, September 16, 2011

PHP Error Handling and Magento Developer Mode

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.

?
1
2
3
4
5
#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

?
1
2
3
4
5
6
7
8
9
10
#
public function doSomeThing($var)
{
    if(...$var is somehow not appropriate)
    {
        throw new Exception("Please stop hitting me");
    }
 
    ...
}

and Programmer B might write something like

?
1
$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

?
1
2
3
4
5
6
7
8
9
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

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

?
1
2
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

?
1
2
3
4
5
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.

?
1
2
3
4
5
6
7
8
9
10
11
//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

?
1
2
3
4
5
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

?
1
2
3
4
#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

?
1
2
3
4
5
6
$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.

?
1
2
3
4
5
6
7
8
9
10
#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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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 knowinghow developer mode achieves this can help you debug your, and your client's, systems

Magento Models and ORM Basics


The implementation of a "Models Tier" is a huge part of any MVC framework. It represents the data of your application, and most applications are useless without data. Magento Models play an even bigger role, as they typically contain the "Business Logic" that's often (right or wrong) relegated to the Controller or Helper methods in other PHP MVC frameworks.
The following is part of a longer series about Magento aimed at developers familiar with PHP MVC development. While each article can be read stand alone, each article does build on concepts covered in previous articles. If you're confused, be sure to catch up on the older stuff first.

Traditional PHP MVC Models

If the definition of MVC is somewhat fuzzy, the definition of a Model is even fuzzier. Prior to the wide adoption of the MVC pattern by PHP developers, data access was usually raw SQL statements and/or a SQL abstraction layer (such as AdoDB). Developers would write queries and not think too much about that objects they were modeling.
In 2009 raw SQL is mostly frowned upon, but many PHP frameworks are still SQL centric. Models will be objects that provide some layer of abstraction, but behind the scenes developers are still writing SQL and/or calling SQL like abstraction methods to read and write-down their data.
Other frameworks eschew SQL and take the Object Relational Mapping (ORM) approach. Here, a developer is dealing strictly with Objects. Properties are set, and when a save method is called on the Object, the data is automatically written to the database. Some ORMs will attempt to divine object properties from the database, others require the user to specify them in some way, (usually in an abstract data language such as YAML). One of the most famous and popular implementations of this approach is ActiveRecord.
This definition of ORM should suffice for now, but like everything Computer Science these days, the strict definition of ORM has blurred over the years. It's beyond the scope of this article to settle that dispute, but suffice it say I'm generalizing a bit and probably asking to be jumped by a couple of Java developers when I leave the house.

Layouts, Blocks and Templates


Update: In researching No Frills Magento Layout it became apparent that this article contains incorrect and misleading information. Specifically, the sections on block and reference tags is incorrect. The article still contains useful context (handles, layout XML, etc.), so I'm letting it stand, but proceed with caution.
Continuing along with Magento, we're going to skip models for the time being, and jump to the right side of our flowchart to look at Layouts and Blocks.
Unlike many popular PHP MVC systems, Magento's Action Controller does not pass a data object to the view or set properties on the view object. Instead, the View component of Model, View, Controller directly references system models to get the information it needs for display.
One consequence of this design decision is that the View has been separated into Blocks and Templates. Blocks are PHP objects, Templates are "raw" PHP files that contain a mix of HTML and PHP (where PHP is used as a template language). Each Block is tied to a single Template file. Inside a phtml file, PHP's $this keyword will contain a reference to the Template's Block object.
A quick example. Take a look a the default product Template at
File: app/design/frontend/base/default/template/catalog/product/list.phtml //.. 
You'll see the following PHP template code.
<?php $_productCollection=$this->getLoadedProductCollection() ?>    <?php if(!$_productCollection->count()): ?> <div class="note-msg">     <?php echo $this->__("There are no products matching the selection.") ?>    </div> <?php else: ?>   
The getLoadedProductCollection method can be found in the Template's Block, Mage_Catalog_Block_Product_List
File: app/code/core/Mage/Catalog/Block/Product/List.php ... public function getLoadedProductCollection() {     return $this->_getProductCollection(); }    ... 
The block's _getProductCollection then instantiates models and reads their data, returning a result to the template.

Nesting Blocks

The real power of Blocks/Templates come with the getChildHtml method. This allows you to include the contents of a secondary Block/Template inside of a primary Block/Template.
Blocks calling Blocks calling Blocks is how the entire HTML layout for your page is created. Take a look at the one column layout Template.
File: app/design/frontend/base/default/template/page/1column.phtml  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>"> <head> <?php echo $this->getChildHtml('head') ?> </head> <body class="page-popup <?php echo $this->getBodyClass()?$this->getBodyClass():'' ?>">     <?php echo $this->getChildHtml('content') ?>     <?php echo $this->getChildHtml('before_body_end') ?>     <?php echo $this->getAbsoluteFooter() ?> </body> </html>  
The template itself is only 11 lines long. However, each call to $this->getChildHtml(...) will include and render another Block. These Blocks will, in turn, use getChildHtml to render other Blocks. It's Blocks all the way down.

Magento Controller Dispatch and Hello World


The Model-View-Controller (MVC) architecture traces its origins back to the Smalltalk Programming language and Xerox Parc. Since then there have been many systems that describe their architecture as MVC. Each system is slightly different, but all have the goal of separating data access, business logic and user-interface code from one another.
The architecture of most PHP MVC frameworks will look something like this.
  1. A URL is intercepted by a single PHP file (usually called a Front Controller)
  2. This PHP file will examine the URL, and derive a controller name and an action name (a process that's often called routing)
  3. The derived controller is instantiated
  4. The method name matching the derived action name is called on the controller
  5. This action method will instantiate and call methods on models, depending on the request variables
  6. The action method will also prepare a data structure of information, which is passed on to the view
  7. The view then renders HTML, using the information in the data structure it has received from the controller
While this pattern was a great leap forward from the "each php file is a page" pattern established early on, for some software engineers, it's still a hack. Common complaints are
  • The Front Controller PHP file still operates in the global namespace
  • Convention over configuration leads to less modularity.
    • URLs routing is often inflexible
    • Controllers are often bound to specific views
    • Even when a system offers a way to override these defaults, the convention leads to applications where it's difficult/impossible to drop in new a new model, view, or controller implementation without massive re-factoring.
As you've probably guessed, the Magento team shares this world view and has created a more abstract MVC pattern that looks somethinglike this.
  1. A URL is intercepted by a single PHP file
  2. This PHP file instantiates a Magento application
  3. The Magento application instantiates a Front Controller object
  4. Front Controller instantiates any number of Router objects (specified in global config)
  5. Routers check the request URL for a "match"
  6. If a match is found, an Action Controller and Action are derived
  7. Action Controller is instantiated and the method name matching the Action Name is called
  8. This action method will instantiate and call methods on models, depending on the request
  9. This Action Controller will then instantiate a Layout Object
  10. This Layout Object will, based on some request variables and system properties (also known as "handles"), create a list of Block objects that are valid for this request
  11. Layout will also call an output method on certain Block objects, which start a nested rendering (Blocks will include other Blocks)
  12. Each Block has a corresponding Template file. Blocks contain PHP logic, templates contain HTML and PHP output code
  13. Blocks refer directly back to the models for their data. In other words, the Action Controller does not pass them a data structure

The Magento Config

The config is the beating heart of the Magento System. It describe, in whole, almost any module/model/class/template/etc than you'll need to access. It's a level of abstraction that most PHP developers aren't used to working with, and while it adds development time in the form of confusion and head scratching, it also allows you an unprecedented amount of flexibility as far as overriding default system behaviors go.
To start with, we're going to create a Magento module that will let us view the system config in our web browser. While this is all wrote copying and pasting, it's worth going through on your own as a way to start getting comfortable with things you'll be doing while working with Magento, as well as learning key terminology.