diff --git a/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php b/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php new file mode 100644 index 0000000000000..a705a7f7bf391 --- /dev/null +++ b/src/Symfony/Component/ClassLoader/Psr4ClassLoader.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +/** + * A PSR-4 compatible class loader. + * + * See http://www.php-fig.org/psr/psr-4/ + * + * @author Alexander M. Turek + */ +class Psr4ClassLoader +{ + /** + * @var array + */ + private $prefixes = array(); + + /** + * @param string $prefix + * @param string $baseDir + */ + public function addPrefix($prefix, $baseDir) + { + $prefix = trim($prefix, '\\').'\\'; + $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + $this->prefixes[] = array($prefix, $baseDir); + } + + /** + * @param string $class + * + * @return string|null + */ + public function findFile($class) + { + $class = ltrim($class, '\\'); + + foreach ($this->prefixes as $current) { + list($currentPrefix, $currentBaseDir) = $current; + if (0 === strpos($class, $currentPrefix)) { + $classWithoutPrefix = substr($class, strlen($currentPrefix)); + $file = $currentBaseDir . str_replace('\\', DIRECTORY_SEPARATOR, $classWithoutPrefix) . '.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + /** + * @param string $class + * + * @return Boolean + */ + public function loadClass($class) + { + $file = $this->findFile($class); + if (null !== $file) { + require $file; + + return true; + } + + return false; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Removes this instance from the registered autoloaders. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } +} diff --git a/src/Symfony/Component/ClassLoader/Tests/Fixtures/psr-4/Class_With_Underscores.php b/src/Symfony/Component/ClassLoader/Tests/Fixtures/psr-4/Class_With_Underscores.php new file mode 100644 index 0000000000000..ce9b8ea323fd7 --- /dev/null +++ b/src/Symfony/Component/ClassLoader/Tests/Fixtures/psr-4/Class_With_Underscores.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use Symfony\Component\ClassLoader\Psr4ClassLoader; + +class Psr4ClassLoaderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param string $className + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4' + ); + $loader->loadClass($className); + $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); + } + + /** + * @return array + */ + public function getLoadClassTests() + { + return array( + array('Acme\\DemoLib\\Foo'), + array('Acme\\DemoLib\\Class_With_Underscores'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores') + ); + } + + /** + * @param string $className + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'psr-4' + ); + $loader->loadClass($className); + $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); + } + + /** + * @return array + */ + public function getLoadNonexistentClassTests() + { + return array( + array('Acme\\DemoLib\\I_Do_Not_Exist'), + array('UnknownVendor\\SomeLib\\I_Do_Not_Exist') + ); + } +}