The definitive guide of Symfony 1.1

17.3. Integrating with Other Framework's Components

If you need capabilities provided by a third-party class, and if you don't want to copy this class in one of the symfony lib/ dirs, you will probably install it outside of the usual places where symfony looks for files. In that case, using this class will imply a manual require in your code, unless you use the symfony spl autoload integration to take advantage of the autoloading.

Symfony doesn't (yet) provide tools for everything. If you need a PDF generator, an API to Google Maps, or a PHP implementation of the Lucene search engine, you will probably need a few libraries from the Zend Framework. If you want to manipulate images directly in PHP, connect to a POP3 account to read e-mails, or design a console interface, you might choose the libraries from eZcomponents. Fortunately, if you define the right settings, the components from both these libraries will work out of the box in symfony.

First, you need to declare (unless you installed the third-party libraries via PEAR) the path to the root directory of the libraries in the application's app.yml:

all:
  zend_lib_dir:   /usr/local/zend/library/
  ez_lib_dir:     /usr/local/ezcomponents/
  swift_lib_dir:  /usr/local/swiftmailer/

Then, extend the PHP autoload system by specifying which library to consider when the autoloading fails with symfony. You can do this by registering the autoload classes in the application configuration class (see Chapter 19 for more information), as in Listing 17-9.

Listing 17-9 - Extending the Autoloading System To Enable Third Party Components, in apps/frontend/config/ApplicationConfiguration.class.php

class frontendConfiguration extends sfApplicationConfiguration
{
  public function initialize()
  {
    parent::initialize(); // load symfony autoloading first

    // Integrate Zend Framework
    if ($sf_zend_lib_dir = sfConfig::get('app_zend_lib_dir'))
    {
      set_include_path($sf_zend_lib_dir.PATH_SEPARATOR.get_include_path());
      require_once($sf_zend_lib_dir.'/Zend/Loader.php');
      spl_autoload_register(array('Zend_Loader', 'autoload'));
    }

    // Integrate eZ Components
    if ($sf_ez_lib_dir = sfConfig::get('app_ez_lib_dir'))
    {
      set_include_path($sf_ez_lib_dir.PATH_SEPARATOR.get_include_path());
      require_once($sf_ez_lib_dir.'/Base/base.php');
      spl_autoload_register(array('ezcBase', 'autoload'));
    }

    // Integrate Swift Mailer
    if ($sf_swift_lib_dir = sfConfig::get('app_swift_lib_dir'))
    {
      set_include_path($sf_swift_lib_dir.PATH_SEPARATOR.get_include_path());
      require_once($sf_swift_lib_dir.'/Swift/ClassLoader.php');
      spl_autoload_register(array('Swift_ClassLoader', 'load'));
    }
  }
}

What happens when you create a new object of an unloaded class is simple:

  1. The symfony autoloading function first looks for a class in the paths declared in the autoload.yml file.
  2. If no class path is found, the callback methods registered by spl_autoload_register() calls will be called one after the other, until one of them returns true. So each of Zend_Loader::autoload(), ezcBase::autoload(), and Swift_ClassLoader::load() are called until one finds the class.
  3. If these also return false, PHP will generate an error.

This means that the other framework components benefit from the autoload mechanism, and you can use them even more easily than within their own environment. For instance, if you want to use the Zend_Search component in the Zend Framework to implement an equivalent of the Lucene search engine in PHP, you usually need a require statement:

require_once 'Zend/Search/Lucene.php';
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
// ...

With symfony and spl autoloading, it is simpler. You can omit the require statement and stop worrying about include paths and class locations:

$doc = new Zend_Search_Lucene_Document(); // The class is autoloaded
$doc->addField(Zend_Search_Lucene_Field::Text('url', $docUrl));
// ...