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' into feature/view-layer
Browse files Browse the repository at this point in the history
  • Loading branch information
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 69 deletions.
154 changes: 94 additions & 60 deletions src/ModuleAutoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// Grab SplAutoloader interface
require_once __DIR__ . '/SplAutoloader.php';

use SplFileInfo,
use GlobIterator,
SplFileInfo,
Traversable;

class ModuleAutoloader implements SplAutoloader
Expand All @@ -16,26 +17,14 @@ class ModuleAutoloader implements SplAutoloader
protected $paths = array();

/**
* @var array An array of modulename => path
* @var array An array of modulename => path
*/
protected $explicitPaths = array();

/**
* @var array An array of supported phar formats
* @var array An array of supported phar extensions (filled on constructor)
*/
protected $pharExtensions = array(
'phar',
'phar.gz',
'phar.bz2',
'phar.tar',
'phar.tar.gz',
'phar.tar.bz2',
'phar.zip',
'tar',
'tar.gz',
'tar.bz2',
'zip',
);
protected $pharExtensions = array();

/**
* @var array An array of module classes to their containing files
Expand All @@ -46,12 +35,37 @@ class ModuleAutoloader implements SplAutoloader
* Constructor
*
* Allow configuration of the autoloader via the constructor.
*
* @param null|array|Traversable $options
*
* @param null|array|Traversable $options
* @return void
*/
public function __construct($options = null)
{
if (extension_loaded('phar')) {
$this->pharExtensions = array(
'phar',
'phar.tar',
'tar',
);

// ext/zlib enabled -> phar can read gzip & zip compressed files
if (extension_loaded('zlib')) {
$this->pharExtensions[] = 'phar.gz';
$this->pharExtensions[] = 'phar.tar.gz';
$this->pharExtensions[] = 'tar.gz';

$this->pharExtensions[] = 'phar.zip';
$this->pharExtensions[] = 'zip';
}

// ext/bzip2 enabled -> phar can read bz2 compressed files
if (extension_loaded('bzip2')) {
$this->pharExtensions[] = 'phar.bz2';
$this->pharExtensions[] = 'phar.tar.bz2';
$this->pharExtensions[] = 'tar.bz2';
}
}

if (null !== $options) {
$this->setOptions($options);
}
Expand All @@ -60,10 +74,10 @@ public function __construct($options = null)
/**
* Configure the autoloader
*
* In most cases, $options should be either an associative array or
* In most cases, $options should be either an associative array or
* Traversable object.
*
* @param array|Traversable $options
*
* @param array|Traversable $options
* @return SplAutoloader
*/
public function setOptions($options)
Expand All @@ -86,6 +100,7 @@ public function autoload($class)
if (substr($class, -7) !== '\Module') {
return false;
}

$moduleName = substr($class, 0, -7);
if (isset($this->explicitPaths[$moduleName])) {
if ($classLoaded = $this->loadModuleFromDir($this->explicitPaths[$moduleName], $class)) {
Expand All @@ -95,32 +110,45 @@ public function autoload($class)
}
}

$moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $moduleName);
$moduleClassPath = str_replace('\\', DIRECTORY_SEPARATOR, $moduleName);

$pharSuffixPattern = null;
if ($this->pharExtensions) {
$pharSuffixPattern = '(' . implode('|', array_map('preg_quote', $this->pharExtensions)) . ')';
}

foreach ($this->paths as $path) {
$path = $path . $moduleClassPath;
if ($classLoaded = $this->loadModuleFromDir($path, $class)) {
return $classLoaded;
}
}

// No directory with Module.php, searching for phars
//$moduleName = substr($class, 0, strpos($class, '\\'));
if ($pharSuffixPattern) {
foreach (new GlobIterator($path . '.*') as $entry) {
if ($entry->isDir()) {
continue;
}

// Find executable phars
$matches = glob($path . '.{' . implode($this->pharExtensions, ',') . '}', GLOB_BRACE);
foreach ($matches as $phar) {
if ($classLoaded = $this->loadModuleFromPhar($phar, $class)) {
return $classLoaded;
if (!preg_match('#.+\.' . $pharSuffixPattern . '$#', $entry->getPathname())) {
continue;
}

if ($classLoaded = $this->loadModuleFromPhar($entry->getPathname(), $class)) {
return $classLoaded;
}
}
}
}

return false;
}

/**
* loadModuleFromDir
*
* @param string $dirPath
* @param string $class
* loadModuleFromDir
*
* @param string $dirPath
* @param string $class
* @return mixed
* False [if unable to load $class]
* get_class($class) [if $class is successfully loaded]
Expand All @@ -140,10 +168,10 @@ protected function loadModuleFromDir($dirPath, $class)
}

/**
* loadModuleFromPhar
*
* @param string $pharPath
* @param string $class
* loadModuleFromPhar
*
* @param string $pharPath
* @param string $class
* @return mixed
* False [if unable to load $class]
* get_class($class) [if $class is successfully loaded]
Expand All @@ -155,17 +183,21 @@ protected function loadModuleFromPhar($pharPath, $class)
if (!$file->isReadable() || !$file->isFile()) {
return false;
}

$fileRealPath = $file->getRealPath();

// Phase 0: Check for executable phar with Module class in stub
if (strpos($file->getRealPath(), '.phar') !== false) {
if (strpos($fileRealPath, '.phar') !== false) {
// First see if the stub makes the Module class available
require_once $file->getRealPath();
require_once $fileRealPath;
if (class_exists($class)) {
$this->moduleClassMap[$class] = $file->getRealPath();
$this->moduleClassMap[$class] = $fileRealPath;
return $class;
}
}

// Phase 1: Not executable phar, no stub, or stub did not provide Module class; try Module.php directly
$moduleClassFile = 'phar://' . $file->getRealPath() . '/Module.php';
$moduleClassFile = 'phar://' . $fileRealPath . '/Module.php';
$moduleFile = new SplFileInfo($moduleClassFile);
if ($moduleFile->isReadable() && $moduleFile->isFile()) {
require_once $moduleClassFile;
Expand All @@ -174,11 +206,12 @@ protected function loadModuleFromPhar($pharPath, $class)
return $class;
}
}

// Phase 2: Check for nested module directory within archive
// Checks for /path/to/MyModule.tar/MyModule/Module.php
// Checks for /path/to/MyModule.tar/MyModule/Module.php
// (shell-integrated zip/tar utilities wrap directories like this)
$pharBaseName = $this->pharFileToModuleName($file->getRealPath());
$moduleClassFile = 'phar://' . $file->getRealPath() . '/' . $pharBaseName . '/Module.php';
$pharBaseName = $this->pharFileToModuleName($fileRealPath);
$moduleClassFile = 'phar://' . $fileRealPath . '/' . $pharBaseName . '/Module.php';
$moduleFile = new SplFileInfo($moduleClassFile);
if ($moduleFile->isReadable() && $moduleFile->isFile()) {
require_once $moduleClassFile;
Expand All @@ -187,12 +220,13 @@ protected function loadModuleFromPhar($pharPath, $class)
return $class;
}
}

return false;
}

/**
* Register the autoloader with spl_autoload registry
*
*
* @return void
*/
public function register()
Expand All @@ -202,7 +236,7 @@ public function register()

/**
* Unregister the autoloader with spl_autoload registry
*
*
* @return void
*/
public function unregister()
Expand All @@ -211,9 +245,9 @@ public function unregister()
}

/**
* registerPaths
*
* @param array|Traversable $paths
* registerPaths
*
* @param array|Traversable $paths
* @return ModuleLoader
*/
public function registerPaths($paths)
Expand All @@ -225,7 +259,7 @@ public function registerPaths($paths)
} else {
$this->registerPath($path);
}
}
}
} else {
throw new \InvalidArgumentException(
'Parameter to \\Zend\\Loader\\ModuleAutoloader\'s '
Expand All @@ -237,10 +271,10 @@ public function registerPaths($paths)
}

/**
* registerPath
*
* @param string $path
* @param string $moduleName
* registerPath
*
* @param string $path
* @param string $moduleName
* @return ModuleLoader
*/
public function registerPath($path, $moduleName = false)
Expand All @@ -260,10 +294,10 @@ public function registerPath($path, $moduleName = false)
}

/**
* getPaths
* getPaths
*
* This is primarily for unit testing, but could have other uses.
*
*
* @return array
*/
public function getPaths()
Expand All @@ -273,8 +307,8 @@ public function getPaths()

/**
* Returns the base module name from the path to a phar
*
* @param string $pharPath
*
* @param string $pharPath
* @return string
*/
protected function pharFileToModuleName($pharPath)
Expand All @@ -288,8 +322,8 @@ protected function pharFileToModuleName($pharPath)

/**
* Normalize a path for insertion in the stack
*
* @param string $path
*
* @param string $path
* @return string
*/
public static function normalizePath($path, $trailingSlash = true)
Expand Down
42 changes: 33 additions & 9 deletions test/ModuleAutoloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,42 @@ public function testCanAutoloadPharModules()
$loader = new ModuleAutoloader;
$loader->registerPath(__DIR__ . '/_files/');
$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'));
$this->assertTrue(class_exists('PharModulePharTar\Module'));
$this->assertTrue(class_exists('PharModuleNested\Module'));

// gzip / zip
if (extension_loaded('zlib')) {
// gzip
$this->assertTrue(class_exists('PharModuleGz\Module'));
$this->assertTrue(class_exists('PharModulePharTarGz\Module'));
$this->assertTrue(class_exists('PharModuleTarGz\Module'));

// zip
$this->assertTrue(class_exists('PharModulePharZip\Module'));
$this->assertTrue(class_exists('PharModuleZip\Module'));
} else {
$this->assertFalse(class_exists('PharModuleGz\Module'));
$this->assertFalse(class_exists('PharModulePharTarGz\Module'));
$this->assertFalse(class_exists('PharModuleTarGz\Module'));

$this->assertFalse(class_exists('PharModulePharZip\Module'));
$this->assertFalse(class_exists('PharModuleZip\Module'));
}

// bzip2
if (extension_loaded('bzip2')) {
$this->assertTrue(class_exists('PharModuleBz2\Module'));
$this->assertTrue(class_exists('PharModulePharTarBz2\Module'));
$this->assertTrue(class_exists('PharModuleTarBz2\Module'));
} else {
$this->assertFalse(class_exists('PharModuleBz2\Module'));
$this->assertFalse(class_exists('PharModulePharTarBz2\Module'));
$this->assertFalse(class_exists('PharModuleTarBz2\Module'));
}

$loader->unregister();
}

Expand Down

0 comments on commit 6c89c61

Please sign in to comment.