May 23, 2013

Classes
Tags blog

Zend Framework 1.x...Without Inhaling

<p><em>Not sure how to integrate all Zend Framework 1.x has to offer into your existing code? Do you want to know how yo

Not sure how to integrate all Zend Framework 1.x has to offer into your existing code? Do you want to know how you can use Zend Framework 1.x without having to fully adopt the MVC architecture? What if you want to begin using MVC later? This article will present ideas and examples on how you can integrate Zend Framework 1.x alongside your existing codebase with minimal need for future re-factoring.

Since I began using Zend Framework 1.x I have had the opportunity to deploy it as a full-stack MVC solution for new not-yet-started projects, use just components of it to compliment existing solutions, and everything in-between. It is the 'everything in-between' that has inspired me to write this article. Perhaps you have reviewed all Zend Framework 1.x has to offer but are not sure how to integrate it with your existing code. How can you take advantage of Zend Framework 1.x's capabilities without having to fully adopt the MVC architecture? Can you easily switch to using MVC at a later point without having to do a lot of refactoring? If you follow the advice I offer in this article then the answer to all of these questions is "Yes!"

In this article I am going to present 4 tips for integrating Zend Framework 1.x into your existing codebase, all of which presume that your existing codebase is not a full-stack MVC application. The first tip is going to explain how you can sprinkle individual Zend Framework 1.x components into your existing codebase. The second tip will expand on this and examine how you can get Zend Framework 1.x and any existing autoloaders to play nicely together. The third tip changes focus and will talk about how to best approach the addition of completely new functionality to an existing codebase. The fourth and final tip will present an approach that can be used to easily convert an existing codebase to a full MVC application.

Sprinkle In Individual Components

Zend Framework 1.x is a lightweight, loosely-coupled component library with minimal interdependencies. It is this very design that allows you to very easily begin using components of Zend Framework 1.x in your existing codebase without having to convert it to a MVC application. Take for example the code below:

Listing 1




Display Currency Amount Formatted by Locale

 
echo $_SERVER['PHP_SELF']; ?>"> if ( ! empty( $_REQUEST['amount'] ) ) { switch( $_REQUEST['locale'] ) { case 'en-US': $formattedCurrency = '$ ' . number_format( $_REQUEST['amount'], 2 ); break;   case 'en_GB': $formattedCurrency = '£ ' . number_format( $_REQUEST['amount'], 2, ',', ' ' ); break; }   echo '

Formatted: ', $formattedCurrency, '

'
; } ?>   Amount:
Currency:
 

While very simple in nature, and not a shining example of "perfectly-written" code, it is a decent representation of the type of code you will likely run across in an existing PHP project that does not make use of one of the popular frameworks. This code, again simple in its nature for the sake of this example, will display a form to the user into which they can enter a monetary value and then select which currency they would like to have the amount formatted as. Upon submission of the form the switch statement on lines 10-19 will determine which currency to format the monetary value as.

Coding style, the need for input validation and any personal preferences aside, there is nothing wrong with this code. For any of you that have been coding PHP since version 3 or even the earlier years of 4 this is probably similar to code you have written yourself. But as the needs placed on your application grow you quickly find yourself in a position where you are having to make continuous additions of case statements to handle additional currencies. Along with having to support these new currencies you also have to research how monetary values are formatted in each currency you are supporting, which symbol to use when displaying the currency, where the symbol should be placed in regards to the monetary value...well, you get the idea. It is at this point that you will likely go looking for an already-written library that handles currencies that you can use to address all of the issues we just listed.

Since Zend Framework 1.x is a loosely-coupled component library with minimal interdependencies it is perfectly suited for our task at hand. After reviewing the list of components that are available in Zend Framework 1.x at http://framework.zend.com/manual/en/reference.html we can decide on using Zend_Currency. The code below contains the same form we were using before, but now you will notice that lines 12-22 are different:

Listing 2




Display Currency Amount Formatted by Locale

 
echo $_SERVER['PHP_SELF']; ?>">   if ( ! empty( $_REQUEST['amount'] ) ) { $zfPath = '/usr/local/share/ZendFramework-1.11.10/library';   set_include_path( $zfPath . DIRECTORY_SEPARATOR . get_include_path() );   require_once( $zfPath . '/Zend/Loader/Autoloader.php' );   Zend_Loader_Autoloader::getInstance();   $currency = new Zend_Currency( $_REQUEST['locale'] );   echo '

Formatted: ', $currency->toCurrency($_REQUEST['amount']), '

'
; } ?>   Amount:
Currency:
 

Many of Zend Framework 1.x's components, when used, require the inclusion of either additional classes contained within the same component or sometimes even other components, depending on how you are using a component. Because of this the easiest way to make use of a Zend Framework 1.x component is to first set up the auto loading, which can be done with the Zend_Loader component. Line 12 contains the path to where we have saved the Zend Framework 1.x library on our filesystem. Line 14 adds this path to our already-existing include path. Line 16 includes the auto loading code for use and line 18 registers the 'Zend_' namespace with spl_autoload. On line 20 we instantiate the Zend_Currency object with the locale that the user selected from the form. Now that we have an instantiated Zend_Currency object we merely only need to call the toCurrency() method as shown in line 22, passing it the monetary value the user provided in the form. We successfully replaced our ever-growing switch statement with a currency library which is what we set out to originally do. Almost all of the components in Zend Framework 1.x can be used this same way.

Autoloaders Just Want To Get Along

In the previous section we talked about how you could integrate a stand-alone component of Zend Framework 1.x into some existing code. The example we used though is likely not an entirely accurate representation of the type of codebase you will encounter while attempting to utilize Zend Framework 1.x without adopting the full MVC architecture. More likely you have a library of your own that has been written and maintained over the life of the application which may consist of only functions but most likely also contains classes. Regardless of which, it more likely than not contains its own autoloading capability and now you need to know how you can get the Zend_Loader component to work alongside it. The code in Listing 3 below represents existing code that relies on the use of an already-existent autoloader and Listing 4 is said autoloader.

Listing 3




 

 
// Require Autoloader
require_once 'classloader.php';
 
// Instantiate 'User' class
// Loaded automatically via the Autoloader loaded on line 8
$User = new User();
 
// Set properties
$User->setFirstName('Jeremy');
$User->setLastName('Brown');
 
?>
 
Full name from the 'User' class:  echo $User->getFullName(); ?>
 

Listing 4


 
function __autoload( $className )
{
    require_once '/path/to/library/' . $className . '.php';
}

So line 8 of Listing 3 includes the code in Listing 4. Using the newly included autoloader, line 12 of Listing 3 then instantiates a new User object, the code for which is in Listing 5 but is not really relevant for what I am trying to demonstrate but has been included so that all of this code will work correctly if you are trying it on your own computer. The newly instantiated User object can then be successfully used on lines 15, 16 and 20 of Listing 3. All of this code we have just looked at represents code that already exists in your application's codebase.

Listing 5


 
class User
{
    /**
     * First name
     *
     * @var string
     */
    protected $_firstName;
 
    /**
     * Last name
     *
     * @var string
     */
    protected $_lastName;
 
    /**
     * Set first name
     *
     * @param  string $firstName
     * @return object User
     */
    public function setFirstName( $firstName )
    {
        $this->_firstName = $firstName;
 
        return $this;
    }
 
    /**
     * Set last name
     *
     * @param  string $lastName
     * @return object User
     */
    public function setLastName( $lastName )
    {
        $this->_lastName = $lastName;
 
        return $this;
    }
 
    /**
     * Get full name
     *
     * @return string
     */
    public function getFullName()
    {
        return (string) $this->_firstName . ' ' . $this->_lastName;
    }
}

To this code we now want to add the functionality of the Zend_Currency component we previously looked at. This is how the code now looks:

Listing 6




 

 
// Require Autoloader
require_once 'classloader.php';
 
// Instantiate 'User' class
// Loaded automatically via the Autoloader loaded line 8
$User = new User();
 
// Set properties
$User->setFirstName('Jeremy');
$User->setLastName('Brown');
 
?>
 
Full name from the 'User' class:  echo $User->getFullName(); ?>
 

    $currency = new Zend_Currency( array( 'locale' => 'en_US', 'value' => 5, ));   ?>   Currency amount: echo $currency ?>  

Lines 26-29 instantiate and configure the Zend_Currency object a little differently than we saw in Listings 1 and 2, as we are no longer using a form to set the values and are passing the configuration options into the object's constructor, but that is nothing to get hung up on. What is important to recognize is that we are now utilizing the Zend_Currency component in our existing code. When we run the code in Listing 6 though we will receive several errors regarding PHP's inability to include the Zend_Currency class because our already-existant autoloader from Listing 4 has attempted to autoload the Zend_Currency class for us but does not know how to correctly load Zend Framework 1.x components.

We can remedy this by making the changes shown below:

Listing 7


 
function __autoload( $className )
{
    if ( file_exists( '/path/to/library/' . $className . '.php' ) )
    {
        require_once '/path/to/library/' . $className . '.php';
    }
    else
    {
        $zfPath = '/usr/local/share/ZendFramework-1.11.10/library';
 
        set_include_path( $zfPath . DIRECTORY_SEPARATOR . get_include_path() );
 
        require_once( $zfPath . '/Zend/Loader/Autoloader.php' );
 
        Zend_Loader_Autoloader::getInstance();
 
        Zend_Loader::loadClass( $className );
    }
}

Notice that line 7 still exists as it did on line 5 in Listing 4, but we have wrapped its execution with code that first checks that the file in question exists. What we have done is directed our autoloader to first look in our own library path for the class we want to autoload. If it exists we load it and if it does not then it is assumed to be a Zend Framework 1.x component and the necessary code to autoload a Zend Framework 1.x component is executed. This is what lines 11-17 do. If they look familiar, it is because they should. They are the same as lines 12-18 in Listing 2. The only difference is that Listing 7 contains one extra call on line 19. Because we are wanting to load a specific class, which we know the name of, we instruct Zend_Loader to directly load the class for us.

You will likely need to make a few tweaks to the specific code in Listing 7 to suit you specific needs, but it has now been successfully demonstrated how you can load individual Zend Framework 1.x components into our existing codebase alongside an existing autoloader.

Always Remember Where You Are Trying To Go

So far the approaches I have presented have revolved around using Zend Framework 1.x components as libraries in your existing codebase. But what if your ultimate end-goal is to fully convert your existing codebase into a full MVC application? The fourth and final tip I will offer in this article really gets us moving in that direction, but before we get there there is another tip that is worth sharing.

Let's assume that you have just been asked to develop some new functionality for your application that supports the ability for visitors to your website to be able to add comments, and for the comments left by everyone to be displayed. We want to take care to develop this new functionality in such a way as to minimize the amount of refactoring that will be required later once the MVC architecture is fully utilized. But how do we do this? To begin, let's discuss for a moment what the MVC architecture is. MVC stands for Model View Controller and is a software architecture, that among other things, promotes the concept of separation of concerns in your code. Additional information can be found at wikipedia. Most of the code examples we have looked at so far in this article have had no separation of concerns, with the PHP code being contained and displayed inline with the HTML code. The only exception to this is the User object from Listing 5. It is self-contained code that can be very readily re-used and re-purposed. This code represents the 'M' in MVC as it encapsulates data and/or business logic. So knowing that objects encapsulate data and/or business logic and that they are also the Models in an MVC application, let's take a look at how we can implement the requested commenting functionality in our existing application.

Listing 8


 
class Comments
{
    /**
     * Reference to database adapter
     *
     * @var Zend_Db_Adapter_Abstract
     */
    protected $_db;
 
    /**
     * Setup database connection
     *
     * @return void
     */
    public function __construct()
    {
        $zfPath = '/usr/local/share/ZendFramework-1.11.10/library';
 
        set_include_path( $zfPath . DIRECTORY_SEPARATOR . get_include_path() );
 
        require_once( $zfPath . '/Zend/Loader/Autoloader.php' );
 
        Zend_Loader_Autoloader::getInstance();
 
        $this->_db = new Zend_Db_Adapter_Pdo_Mysql(array(
            'host'     => 'localhost',
            'username' => 'secretUsername',
            'password' => 'secretPassword',
            'dbname'   => 'myDatabase'
        ));
 
        $this->_db->setFetchMode(Zend_Db::FETCH_OBJ);
    }
 
    /**
     * Add comment
     * 
     * @param  string $title
     * @param  string $description
     * @return object Comments
     */
    public function addComment( $title, $description )
    {
        $data = array(
            'title'            => $title,
            'description'  => $description,
        );
 
        $this->_db->insert( 'comments', $data );
 
        return $this;
    }
 
    /**
     * Retrieve all comments
     * 
     * @return object 
     */
    public function getComments()
    {
        $select = $this->_db->select()
            ->from(
                'comments',
                array(
                    'title',
                    'description'
                )
            );
 
        return $select->query()->fetchAll();
    }
}

Listing 8 contains our new Comments class. It provides the functionality of being able to add a new comment as well as retrieve all existing comments. You will notice that both of these methods are using either the insert() or select() methods of the Zend_Db_Adapter_Pdo_Mysql object. Since the ultimate goal you have for your codebase is to fully convert it to a full Zend Framework 1.x application it only makes sense to have all new code utilize as many Zend Framework 1.x components and coding practices as possible. This will lead to a lot less refactoring in the future. The constructor of our object is where the Zend_Db_Adapter_Pdo_Mysql object gets instantiated. Once again, because it is a Zend Framework 1.x component, and we are not yet using a full MVC implementation of Zend Framework 1.x, we must register the Zend_Loader_Autoloader component as we have seen in previous examples. This is what lines 19-25 do. Lines 27-32 configure and instantiate the Zend_Db_Adapter_Pdo_Mysql object for use and line 34 sets the fetch mode of the adapter.

At the point in the future when you have completely converted your codebase to a full-stack Zend Framework 1.x MVC application you will have capabilities available to you that had not previously existed in your code. One such set of capabilities are those provided by Zend_Application. By using Zend_Application you can replace lines 19-34 in Listing 8 with the following:

$this->_db = Zend_Controller_Front::getInstance()
    ->getParam('bootstrap')
    ->getPluginResource('db')
    ->getDbAdapter();

So while in the original example of Listing 8's code you were having to instantiate the Zend_Db_Adapter_Pdo_Mysql object yourself in order to use it you can now grab the default database adapter off of the resource called 'db' that was created during the application's bootstrap process. Have you noticed what did not change in all of this though? Any code in any of the methods in the Comments class. Because you knew where you ultimately wanted to go with your codebase you were able to confine any code that needed to be changed later to your constructor so that the refactoring you had to do in the future was greatly minimized.

You Never Have To Stop Developing

You have utilized all of the tips provided so far and have been able to make improvements to your application. But you have now reached a point where you are finding that you need a full MVC stack and once again are not sure how to proceed. The biggest question you are likely asking yourself is a several-step question. How can you begin converting all of the code that is not object-based (remember, these are the Models in MVC) into View and Controllers, the V and C in MVC? It isn't so much how you convert the code but rather how can you do it while allowing your existing application to remain available for use? If you decide to approach the problem by doing the conversion in an offline version of your codebase, how do you best manage changes that are required to be made to your application? You will need to roll any changes made to your existing application into your offline version as well so that you do not miss picking up any bug fixes, feature releases, etc. Thankfully there is a way to achieve this conversion in a very manageable way.

The traditional setup for a website is that the files for the main pages of the site are located in the document root of the site's installation. Alongside these files will likely be a filed named index.php and your web server is configured to serve this file by default if no other file is specified in the URL. The traditional setup for a Zend Framework 1.x application is that there will be several folders at what is traditionally the document root of the site's installation. The folders include, but are not limited to:

  • application
  • library
  • public

The document root of your Zend Framework 1.x application is actually the public folder and with the use of Apache mod_rewrite rules, or IIS equivalents, hereafter referred to as just rewrite rules, all requests for your application get routed through the public/index.php file. It is the use of these rewrite rules that allows you great flexibility in configuring your application and being able to have a Zend Framework 1.x application sit alongside your existing website.

The default rewrite rules of a Zend Framework 1.x application actually route any requests for non-existent folders or files through the public/index.php file. For files that exist the web server serves them directly and your Zend Framework 1.x application never gets involved. So how does this help you? Think for a moment about your existing website that you want to convert over to fully utilizing Zend Framework 1.x. The first thing you will need to do is to create, at minimum, the three folders listed previously. If you already have folders in your website with these names then you are either going to have to first refactor your existing codebase to change the names of these folders or give these folders different names in your Zend Framework 1.x application. Giving them different names will not cause any problems with any of the code that comprises the Zend Framework 1.x library, as it does not have any hard-coded references to these folder names, but you will need to be constantly vigilant of the fact that you have made such modifications because the Zend Framework 1.x manual assumes the use of these standard naming conventions and you will have to keep the name changes straight as you are reading it. For the sake of this article we are going to refer to these folders following the standard naming convention.

Once you have created these folders you should move every file and folder that should be accessible from your website into the new /public folder. You have succeeded in moving your website's document root from its previous location to the new /public folder location and if you change your web server's configuration to reflect this your website should still continue to operate as before (if you had any hard-coded paths in your website's codebase they would need to be modified to reflect this new path).

I mentioned earlier that any request for a file or folder that does not exist in a Zend Framework 1.x application will get routed through the index.php file. This means that you are going to need to convert your existing index.php file and its contents into a Zend Framework Controller and associated View Script so that we can repurpose the index.php file. Assuming your existing index.php file contains pretty simplistic logic this will be as easy as extracting the PHP code and logic and placing it in the indexAction method of the /application/modules/default/controllers/IndexController.php file and placing the HTML to be rendered in the /application/modules/default/views/scripts/index/index.phtml file, which is the standard configuration for a Zend Framework 1.x application. You are now free to replace the contents of the index.php file with that of the guidance of the Zend Framework 1.x manual.

Your existing website is still accessible as it was before all of these changes, with the difference being that it is now configured with the structure of a Zend Framework 1.x application. You have converted your original index.php file over to a Zend Framework Controller and View Script and the rest of your site's pages and resources can remain configured exactly as they are right now. The changes you have introduced give you two options moving forward. The first option is that any new pages or sections you want to add to your application can be added in a 100% MVC fashion. You only need to create a new Controller and any corresponding View Scripts. The second option is that you can now work on converting existing pages in your website at whichever pace best suits your development timeline. You can make modifications or improvements to the existing pages without having to worry about them getting out of sync with another copy of your code and you can convert them to an MVC implementation with ease. In this way both your old and new code can be maintained and exist side-by-side simultaneously.

Conclusion

I hope that these 4 tips I have presented have shown you the range of ways you can begin taking advantage of all Zend Framework 1.x has to offer. Whether as a component library or a full-stack MVC application, Zend Framework 1.x can be used very easily in almost any situation.

-Jeremy


Feedback?

If this article contains any error, or leaves any of your questions unanswered, please help us out by opening up a github issue.
Open an issue