Autoloading Symfony using Doctrine with Zend Framework
Photo by Dayne Topkin on Unsplash.
On some projects I use the Doctrine Object Relational Mapper, a fantastic library for modeling database objects. To combine it with Zend Framework 1, I use Guilherme Blanco's Bisna application resource. I recently upgraded a project to the latest version of Doctrine and suddenly couldn't autoload the Symphony classes. It took far too long for me to figure out the problem, so I hope I can spare someone else the same trouble.
Doctrine borrows some classes from the Symfony framework, namely for the Doctrine console and YAML parser. These classes retain the Symfony namespace, but reside in /Doctrine/Symfony
. This adds a wrinkle to the autoloading process. If you use the Bisna application resource, you may have something like this in your application INI:
resources.doctrine.classLoader.loaderClass = "Doctrine\Common\ClassLoader"
resources.doctrine.classLoader.loaderFile = "Doctrine/Common/ClassLoader.php"
resources.doctrine.classLoader.loaders.symfony_console.namespace = "Symfony"
resources.doctrine.classLoader.loaders.symfony_console.includePath = "Doctrine"
However you choose to autoload, you need to specify that the root of the Symfony library is in the /Doctrine
directory. Once the Bisna resource initializes during your application's bootstrap phase, any references to Symfony will autoload correctly.
The problem occurs when you use the Doctrine command line tool. This tool resides in Doctrine's /bin
directory and includes the doctrine.php
script. Depending on how you obtained Doctrine (PEAR, Composer, or a downloadable archive), a doctrine.php
script may already be in the /bin
directory. This doctrine.php
script used to properly autoload Symfony, until a recent update. As you can see in the commit changelog, the call to \Doctrine\Common\ClassLoader
was updated and no longer specifies /Doctrine
as the base directory of the Symfony library. This code runs before your cli-config.php
, and once Symfony's path is defined in the autoloading stack it's difficult to change. We need our own version of the doctrine.php
script.
Luckily the Doctrine command line loads the script with a simple include('doctrine.php');
. Because this path is relative, we can override the default with our own doctrine.php
if we place it higher in the include_path
. Our doctrine.php
can mirror the default one, minus the autoloader settings.
<?php
// doctrine.php
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php';
$helperSet = null;
if (file_exists($configFile)) {
if ( ! is_readable($configFile)) {
trigger_error(
'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR
);
}
require $configFile;
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
$helperSet = $helperSetCandidate;
break;
}
}
}
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);
This version will still kick off the cli-config.php
script, which will bootstrap the application and configure the autoloader.
So in the end, the problem was complex but the solution is simple. Copy the /bin/doctrine.php
file from the Doctrine library into your project and remove the \Doctrine\Common\ClassLoader
setup code.