* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Autoload; use Composer\Installer\InstallationManager; use Composer\Json\JsonFile; use Composer\Package\Loader\JsonLoader; use Composer\Package\PackageInterface; use Composer\Repository\RepositoryInterface; /** * @author Igor Wiedler * @author Jordi Boggiano */ class AutoloadGenerator { private $localRepo; private $package; private $installationManager; public function __construct(RepositoryInterface $localRepo, PackageInterface $package, InstallationManager $installationManager) { $this->localRepo = $localRepo; $this->package = $package; $this->installationManager = $installationManager; } public function dump($targetFilename) { $autoloads = $this->parseAutoloads(); $file = <<<'EOF' $path) { $exportedNamespace = var_export($namespace, true); $exportedPath = var_export(($def['path'] ? '/'.$def['path'] : '').'/'.$path, true); $file .= <<registerNamespace($exportedNamespace, dirname(__DIR__).$exportedPath); EOF; } } } if (isset($autoloads['pear'])) { foreach ($autoloads['pear'] as $def) { foreach ($def['mapping'] as $prefix => $path) { $exportedPrefix = var_export($prefix, true); $exportedPath = var_export(($def['path'] ? '/'.$def['path'] : '').'/'.$path, true); $file .= <<registerPrefix($exportedPrefix, dirname(__DIR__).$exportedPath); EOF; } } } $file .= <<<'EOF' $loader->register(); /* * This class was taken from Symfony, the following license applies: * * Copyright (c) 2004-2011 Fabien Potencier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * UniversalClassLoader implements a "universal" autoloader for PHP 5.3. * * It is able to load classes that use either: * * * The technical interoperability standards for PHP 5.3 namespaces and * class names (http://groups.google.com/group/php-standards/web/psr-0-final-proposal); * * * The PEAR naming convention for classes (http://pear.php.net/). * * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be * looked for in a list of locations to ease the vendoring of a sub-set of * classes for large projects. * * Example usage: * * $loader = new UniversalClassLoader(); * * // register classes with namespaces * $loader->registerNamespaces(array( * 'Symfony\Component' => __DIR__.'/component', * 'Symfony' => __DIR__.'/framework', * 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'), * )); * * // register a library using the PEAR naming convention * $loader->registerPrefixes(array( * 'Swift_' => __DIR__.'/Swift', * )); * * // activate the autoloader * $loader->register(); * * In this example, if you try to use a class in the Symfony\Component * namespace or one of its children (Symfony\Component\Console for instance), * the autoloader will first look for the class under the component/ * directory, and it will then fallback to the framework/ directory if not * found before giving up. * * @author Fabien Potencier */ class UniversalClassLoader { private $namespaces = array(); private $prefixes = array(); private $namespaceFallbacks = array(); private $prefixFallbacks = array(); /** * Gets the configured namespaces. * * @return array A hash with namespaces as keys and directories as values */ public function getNamespaces() { return $this->namespaces; } /** * Gets the configured class prefixes. * * @return array A hash with class prefixes as keys and directories as values */ public function getPrefixes() { return $this->prefixes; } /** * Gets the directory(ies) to use as a fallback for namespaces. * * @return array An array of directories */ public function getNamespaceFallbacks() { return $this->namespaceFallbacks; } /** * Gets the directory(ies) to use as a fallback for class prefixes. * * @return array An array of directories */ public function getPrefixFallbacks() { return $this->prefixFallbacks; } /** * Registers the directory to use as a fallback for namespaces. * * @param array $dirs An array of directories * * @api */ public function registerNamespaceFallbacks(array $dirs) { $this->namespaceFallbacks = $dirs; } /** * Registers the directory to use as a fallback for class prefixes. * * @param array $dirs An array of directories * * @api */ public function registerPrefixFallbacks(array $dirs) { $this->prefixFallbacks = $dirs; } /** * Registers an array of namespaces * * @param array $namespaces An array of namespaces (namespaces as keys and locations as values) * * @api */ public function registerNamespaces(array $namespaces) { foreach ($namespaces as $namespace => $locations) { $this->namespaces[$namespace] = (array) $locations; } } /** * Registers a namespace. * * @param string $namespace The namespace * @param array|string $paths The location(s) of the namespace * * @api */ public function registerNamespace($namespace, $paths) { $this->namespaces[$namespace] = (array) $paths; } /** * Registers an array of classes using the PEAR naming convention. * * @param array $classes An array of classes (prefixes as keys and locations as values) * * @api */ public function registerPrefixes(array $classes) { foreach ($classes as $prefix => $locations) { $this->prefixes[$prefix] = (array) $locations; } } /** * Registers a set of classes using the PEAR naming convention. * * @param string $prefix The classes prefix * @param array|string $paths The location(s) of the classes * * @api */ public function registerPrefix($prefix, $paths) { $this->prefixes[$prefix] = (array) $paths; } /** * Registers this instance as an autoloader. * * @param Boolean $prepend Whether to prepend the autoloader or not * * @api */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); } /** * Loads the given class or interface. * * @param string $class The name of the class */ public function loadClass($class) { if ($file = $this->findFile($class)) { require $file; } } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|null The path, if found */ public function findFile($class) { if ('\\' == $class[0]) { $class = substr($class, 1); } if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $namespace = substr($class, 0, $pos); foreach ($this->namespaces as $ns => $dirs) { foreach ($dirs as $dir) { if (0 === strpos($namespace, $ns)) { $className = substr($class, $pos + 1); $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; if (file_exists($file)) { return $file; } } } } foreach ($this->namespaceFallbacks as $dir) { $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'; if (file_exists($file)) { return $file; } } } else { // PEAR-like class name foreach ($this->prefixes as $prefix => $dirs) { foreach ($dirs as $dir) { if (0 === strpos($class, $prefix)) { $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; if (file_exists($file)) { return $file; } } } } foreach ($this->prefixFallbacks as $dir) { $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; if (file_exists($file)) { return $file; } } } } } EOF; file_put_contents($targetFilename, $file); } private function parseAutoloads() { $installPaths = array(); foreach ($this->localRepo->getPackages() as $package) { $installPaths[] = array( $package, $this->installationManager->getInstallPath($package) ); } $installPaths[] = array($this->package, ''); $autoloads = array(); foreach ($installPaths as $item) { list($package, $installPath) = $item; if (null !== $package->getTargetDir()) { $installPath = substr($installPath, 0, -strlen('/'.$package->getTargetDir())); } foreach ($package->getAutoload() as $type => $mapping) { $autoloads[$type][] = array( 'mapping' => $mapping, 'path' => $installPath, ); } } return $autoloads; } }