This repository has been archived by the owner on Jan 30, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of git://git.zendframework.com/zf
- Loading branch information
Showing
18 changed files
with
416 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
<?php | ||
|
||
namespace Zend\Loader; | ||
|
||
use SplFileInfo, | ||
Zend\Loader\SplAutoloader, // @TODO: Remove once ported to the _real_ Zend\Loader namespace | ||
Traversable; | ||
|
||
class ModuleAutoloader implements SplAutoloader | ||
{ | ||
/** | ||
* @var array An array of module paths to scan | ||
*/ | ||
protected $paths = array(); | ||
|
||
/** | ||
* Constructor | ||
* | ||
* Allow configuration of the autoloader via the constructor. | ||
* | ||
* @param null|array|Traversable $options | ||
* @return void | ||
*/ | ||
public function __construct($options = null) | ||
{ | ||
if (null !== $options) { | ||
$this->setOptions($options); | ||
} | ||
} | ||
|
||
/** | ||
* Configure the autoloader | ||
* | ||
* In most cases, $options should be either an associative array or | ||
* Traversable object. | ||
* | ||
* @param array|Traversable $options | ||
* @return SplAutoloader | ||
*/ | ||
public function setOptions($options) | ||
{ | ||
$this->registerPaths($options); | ||
return $this; | ||
} | ||
|
||
/** | ||
* Autoload a class | ||
* | ||
* @param $class | ||
* @return mixed | ||
* False [if unable to load $class] | ||
* get_class($class) [if $class is successfully loaded] | ||
*/ | ||
public function autoload($class) | ||
{ | ||
// Limit scope of this autoloader | ||
if (substr($class, -7) !== '\Module') { | ||
return false; | ||
} | ||
$moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; | ||
|
||
foreach ($this->paths as $path) { | ||
$file = new SplFileInfo($path . $moduleClassPath); | ||
if ($file->isReadable()) { | ||
// Found directory with Module.php in it | ||
require_once $file->getRealPath(); | ||
return $class; | ||
} | ||
// No directory with Module.php, searching for phars | ||
$moduleName = substr($class, 0, strpos($class, '\\')); | ||
|
||
// Find executable phars | ||
$matches = glob($path . $moduleName . '.{phar,phar.gz,phar.bz2,phar.tar,phar.tar.gz,phar.tar.bz2,phar.zip}', GLOB_BRACE); | ||
$executable = true; | ||
if (count($matches) == 0) { | ||
$matches = glob($path . $moduleName . '.{tar,tar.gz,tar.bz2,zip}', GLOB_BRACE); | ||
$executable = false; | ||
} | ||
foreach ($matches as $phar) { | ||
$file = new SplFileInfo($phar); | ||
if ($file->isReadable() && $file->isFile()) { | ||
if ($executable) { | ||
// First see if the stub makes the Module class available | ||
require_once $file->getRealPath(); | ||
if (class_exists($class)) { | ||
return $class; | ||
} | ||
} | ||
// No stub, or stub did not provide Module class; try Module.php directly | ||
$moduleClassFile = 'phar://' . $file->getRealPath() . '/Module.php'; | ||
$file = new SplFileInfo($moduleClassFile); | ||
if ($file->isReadable() && $file->isFile()) { | ||
require_once $moduleClassFile; | ||
if (class_exists($class)) { | ||
return $class; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Register the autoloader with spl_autoload registry | ||
* | ||
* @return void | ||
*/ | ||
public function register() | ||
{ | ||
spl_autoload_register(array($this, 'autoload')); | ||
} | ||
|
||
/** | ||
* Unregister the autoloader with spl_autoload registry | ||
* | ||
* @return void | ||
*/ | ||
public function unregister() | ||
{ | ||
$test = spl_autoload_unregister(array($this, 'autoload')); | ||
} | ||
|
||
/** | ||
* registerPaths | ||
* | ||
* @param array|Traversable $paths | ||
* @return ModuleLoader | ||
*/ | ||
public function registerPaths($paths) | ||
{ | ||
if (is_array($paths) || $paths instanceof Traversable) { | ||
foreach ($paths as $path) { | ||
$this->registerPath($path); | ||
} | ||
} else { | ||
throw new \InvalidArgumentException( | ||
'Parameter to \\Zend\\Loader\\ModuleAutoloader\'s ' | ||
. 'registerPaths method must be an array or ' | ||
. 'implement the \\Traversable interface' | ||
); | ||
} | ||
return $this; | ||
} | ||
|
||
/** | ||
* registerPath | ||
* | ||
* @param string $path | ||
* @return ModuleLoader | ||
*/ | ||
public function registerPath($path) | ||
{ | ||
if (!is_string($path)) { | ||
throw new \InvalidArgumentException(sprintf( | ||
'Invalid path provided; must be a string, received %s', | ||
gettype($path) | ||
)); | ||
} | ||
$this->paths[] = static::normalizePath($path); | ||
return $this; | ||
} | ||
|
||
/** | ||
* getPaths | ||
* | ||
* This is primarily for unit testing, but could have other uses. | ||
* | ||
* @return array | ||
*/ | ||
public function getPaths() | ||
{ | ||
return $this->paths; | ||
} | ||
|
||
/** | ||
* Normalize a path for insertion in the stack | ||
* | ||
* @param string $path | ||
* @return string | ||
*/ | ||
public static function normalizePath($path) | ||
{ | ||
$path = rtrim($path, '/'); | ||
$path = rtrim($path, '\\'); | ||
$path .= DIRECTORY_SEPARATOR; | ||
return $path; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<?php | ||
|
||
namespace ZendTest\Loader\ModuleAutoloaderTest; | ||
|
||
use PHPUnit_Framework_TestCase as TestCase, | ||
Zend\Loader\ModuleAutoloader, | ||
InvalidArgumentException; | ||
|
||
class ManagerTest extends TestCase | ||
{ | ||
public function setUp() | ||
{ | ||
// Store original autoloaders | ||
$this->loaders = spl_autoload_functions(); | ||
if (!is_array($this->loaders)) { | ||
// spl_autoload_functions does not return empty array when no | ||
// autoloaders registered... | ||
$this->loaders = array(); | ||
} | ||
|
||
// Store original include_path | ||
$this->includePath = get_include_path(); | ||
} | ||
|
||
public function tearDown() | ||
{ | ||
// Restore original autoloaders | ||
$loaders = spl_autoload_functions(); | ||
if (is_array($loaders)) { | ||
foreach ($loaders as $loader) { | ||
spl_autoload_unregister($loader); | ||
} | ||
} | ||
|
||
foreach ($this->loaders as $loader) { | ||
spl_autoload_register($loader); | ||
} | ||
|
||
// Restore original include_path | ||
set_include_path($this->includePath); | ||
} | ||
|
||
public function testCanRegisterPathsFromConstructor() | ||
{ | ||
$paths = array(__DIR__ . '/TestAsset/'); | ||
$loader = new ModuleAutoloader($paths); | ||
$registeredPaths = $loader->getPaths(); | ||
$this->assertSame($paths, $registeredPaths); | ||
} | ||
|
||
public function testPathsNormalizedWithTrailingSlash() | ||
{ | ||
$paths = array( | ||
__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset', | ||
__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset///', | ||
__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset\\\\', | ||
); | ||
$loader = new ModuleAutoloader($paths); | ||
$registeredPaths = $loader->getPaths(); | ||
$this->assertSame(__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset' . DIRECTORY_SEPARATOR, $registeredPaths[0]); | ||
$this->assertSame(__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset' . DIRECTORY_SEPARATOR, $registeredPaths[1]); | ||
$this->assertSame(__DIR__ . DIRECTORY_SEPARATOR . 'TestAsset' . DIRECTORY_SEPARATOR, $registeredPaths[2]); | ||
} | ||
|
||
public function testCanAutoloadModule() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$moduleClass = $loader->autoload('FooModule\Module'); | ||
$this->assertSame('FooModule\Module', $moduleClass); | ||
$module = new \FooModule\Module; | ||
$this->assertInstanceOf('FooModule\Module', $module); | ||
} | ||
|
||
public function testCanAutoloadSubModule() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$loader->register(); | ||
$subModule = new \FooModule\SubModule\Module; | ||
$this->assertInstanceOf('FooModule\SubModule\Module', $subModule); | ||
$loader->unregister(); | ||
} | ||
|
||
public function testCanAutoloadPharModules() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$loader->register(); | ||
$this->assertTrue(class_exists('PharModule\Module')); | ||
$this->assertTrue(class_exists('PharModuleGz\Module')); | ||
$this->assertTrue(class_exists('PharModuleBz2\Module')); | ||
$this->assertTrue(class_exists('PharModulePharTar\Module')); | ||
$this->assertTrue(class_exists('PharModulePharTarGz\Module')); | ||
$this->assertTrue(class_exists('PharModulePharTarBz2\Module')); | ||
$this->assertTrue(class_exists('PharModulePharZip\Module')); | ||
$this->assertTrue(class_exists('PharModuleTar\Module')); | ||
$this->assertTrue(class_exists('PharModuleTarGz\Module')); | ||
$this->assertTrue(class_exists('PharModuleTarBz2\Module')); | ||
$this->assertTrue(class_exists('PharModuleZip\Module')); | ||
$loader->unregister(); | ||
} | ||
|
||
public function testProvidesFluidInterface() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$this->assertInstanceOf('Zend\Loader\ModuleAutoloader', $loader->setOptions(array('foo'))); | ||
$this->assertInstanceOf('Zend\Loader\ModuleAutoloader', $loader->registerPaths(array('foo'))); | ||
$this->assertInstanceOf('Zend\Loader\ModuleAutoloader', $loader->registerPath('foo')); | ||
} | ||
|
||
public function testReturnsFalseForNonModuleClass() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$moduleClass = $loader->autoload('FooModule\NotModule'); | ||
$this->assertFalse($moduleClass); | ||
} | ||
|
||
public function testReturnsFalseForNonExistantModuleClass() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$moduleClass = $loader->autoload('NonExistantModule\Module'); | ||
$this->assertFalse($moduleClass); | ||
} | ||
|
||
public function testReturnsFalseForNonModulePhar() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$loader->registerPath(__DIR__ . '/TestAsset/'); | ||
$moduleClass = $loader->autoload('PharModuleFake\Module'); | ||
$this->assertFalse($moduleClass); | ||
} | ||
|
||
public function testInvalidPathThrowsException() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$this->setExpectedException('InvalidArgumentException'); | ||
$loader->registerPath(123); | ||
} | ||
|
||
public function testInvalidPathsThrowsException() | ||
{ | ||
$loader = new ModuleAutoloader; | ||
$this->setExpectedException('InvalidArgumentException'); | ||
$loader->registerPaths(123); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?php | ||
namespace FooModule; | ||
|
||
class Module | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
namespace FooModule\SubModule; | ||
|
||
class Module | ||
{ | ||
} |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
This directory is just here to confirm that the autoloader prefers Phar files | ||
over directories that match the glob. |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Oops, something went wrong.