Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Merge branch 'master' of git://git.zendframework.com/zf
Browse files Browse the repository at this point in the history
  • Loading branch information
DASPRiD committed Oct 3, 2011
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 0 deletions.
189 changes: 189 additions & 0 deletions src/ModuleAutoloader.php
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;
}
}
149 changes: 149 additions & 0 deletions test/ModuleAutoloaderTest.php
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);
}
}
6 changes: 6 additions & 0 deletions test/TestAsset/FooModule/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace FooModule;

class Module
{
}
7 changes: 7 additions & 0 deletions test/TestAsset/FooModule/SubModule/Module.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace FooModule\SubModule;

class Module
{
}
Binary file added test/TestAsset/PharModule.phar
Binary file not shown.
Binary file added test/TestAsset/PharModuleBz2.phar.bz2
Binary file not shown.
2 changes: 2 additions & 0 deletions test/TestAsset/PharModuleFake.tar/readme
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 added test/TestAsset/PharModuleFake.zip
Binary file not shown.
Binary file added test/TestAsset/PharModuleGz.phar.gz
Binary file not shown.
Binary file added test/TestAsset/PharModulePharTar.phar.tar
Binary file not shown.
Binary file added test/TestAsset/PharModulePharTarBz2.phar.tar.bz2
Binary file not shown.
Binary file added test/TestAsset/PharModulePharTarGz.phar.tar.gz
Binary file not shown.
Binary file added test/TestAsset/PharModulePharZip.phar.zip
Binary file not shown.
Binary file added test/TestAsset/PharModuleTar.tar
Binary file not shown.
Binary file added test/TestAsset/PharModuleTarBz2.tar.bz2
Binary file not shown.
Binary file added test/TestAsset/PharModuleTarGz.tar.gz
Binary file not shown.
Binary file added test/TestAsset/PharModuleZip.zip
Binary file not shown.
Loading

0 comments on commit e6d7081

Please sign in to comment.