Created
August 5, 2011 06:42
-
-
Save k-holy/1127033 to your computer and use it in GitHub Desktop.
PSR-0対応クラスローダ試案
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
<?php | |
// PSR-0 の対応実験 こんなふうに書きたい | |
namespace Holy\Example; | |
$lib_path = realpath(__DIR__ . '/../lib'); | |
$test_path = realpath(__DIR__ . '/../tests'); | |
set_include_path($lib_path . PATH_SEPARATOR . get_include_path()); | |
require_once realpath($lib_path . '/vendor/Holy/Loader.php'); | |
\Holy\Loader::getInstance() | |
->set('Holy' , realpath($lib_path . '/vendor')) | |
->set('Smorty' , realpath($lib_path . '/vendor/Smorty/libs'), '.class.php') | |
->enableAutoload(); | |
// 現在の名前空間以下のクラス | |
$foo = new Foo(); // /path/to/lib/vendor/Holy/Example/Foo.php | |
$bar = new Foo\Bar(); // /path/to/lib/vendor/Holy/Example/Foo/Bar.php | |
$baz = new Foo\Bar\Baz(); // /path/to/lib/vendor/Holy/Example/Foo/Bar/Baz.php | |
$sub_package_klass_file = new sub_package\Klass\File(); // /path/to/lib/vendor/Holy/sub_package/Klass/File.php | |
// singletonなので設定し直しても前の設定は残ります | |
\Holy\Loader::getInstance() | |
->set('AutoloadSample\Foo' , realpath($lib_path . '/vendor')) | |
->set('AutoloadSample\Test', $test_path) | |
->enableAutoload(); | |
// すまてぃ形式のクラスファイルもOK | |
$smorty = new \Smorty(); // /path/to/lib/vendor/Smorty/libs/Smorty.class.php | |
// PEAR形式 (include_path以下) Holy/Example.php | |
$example = new \Holy_Example(); | |
// 別名前空間のクラス | |
$sample = new \AutoloadSample\Foo\Bar(); // /path/to/lib/vendor/AutoloadSample/Foo/Bar.php | |
// サブ名前空間がTestの場合のみ別ディレクトリから読み込む | |
$test = new \AutoloadSample\Test\FooTest(); // /path/to/tests/AutoloadSample/Test/FooTest.php | |
//$example = new \Holy_Example_Foo(); // include_path内のトップディレクトリ名とLoader::set()で指定したベンダー名が同じ場合はベンダーの方がよばれて二重定義エラーになってしまう… |
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
<?php | |
namespace Holy; | |
/** | |
* Loader | |
* | |
* @author [email protected] | |
*/ | |
class Loader | |
{ | |
protected static $instance = null; | |
protected $autoloadEnabled = false; | |
protected $vendorInfo = array(); | |
protected $fileExtension = '.php'; | |
protected $namespaceSeparator = '\\'; | |
protected function __construct() | |
{ | |
$this->vendorInfo = array(); | |
$this->autoloadEnabled = false; | |
} | |
/** | |
* シングルトンインスタンスを返します。 | |
* @return object Holy\Loader | |
*/ | |
public static function getInstance() | |
{ | |
if (!isset(self::$instance)) { | |
self::$instance = new self(); | |
} | |
return self::$instance; | |
} | |
/** | |
* シングルトンインスタンスをクリアします。 | |
*/ | |
public static function resetInstance() | |
{ | |
self::$instance = null; | |
} | |
/** | |
* ベンダー名および設置パス、拡張子を設定します。 | |
* @param string ベンダー名(namespace) | |
* @param string 設置パス | |
* @param string 拡張子 | |
*/ | |
public function set($name, $includeDir, $fileExtension=null) | |
{ | |
$includeDir = rtrim($includeDir, '\\/'); | |
if ('\\' === DIRECTORY_SEPARATOR) { | |
$includeDir = str_replace('/', '\\', $includeDir); | |
} | |
$this->vendorInfo[$name] = array($includeDir, $fileExtension); | |
return $this; | |
} | |
/** | |
* オートローダーによるクラスローディングを有効にします。 | |
* | |
* @param bool SPLオートローダースタックの先頭に追加するかどうか | |
*/ | |
public function enableAutoload($prepend = false) | |
{ | |
if ($this->autoloadEnabled) { | |
spl_autoload_unregister(array($this, 'load')); | |
} | |
spl_autoload_register(array($this, 'load'), true, $prepend); | |
$this->autoloadEnabled = true; | |
return $this; | |
} | |
/** | |
* オートローダーによるクラスローディングを無効にします。 | |
*/ | |
public function disableAutoload() | |
{ | |
if ($this->autoloadEnabled) { | |
spl_autoload_unregister(array($this, 'load')); | |
} | |
return $this; | |
} | |
/** | |
* 指定されたクラスのファイルを読み込みます。 | |
* @param string クラス名 | |
*/ | |
public function load($className) | |
{ | |
$filePath = $this->findFile($className); | |
if (false === $filePath) { | |
return false; | |
} | |
include $filePath; | |
return true; | |
} | |
protected function findFile($className) | |
{ | |
$className = ltrim($className, $this->namespaceSeparator); // 先頭の名前空間セパレータは無条件で除去 | |
$useNamespace = false; | |
if (false !== ($pos = strrpos($className, $this->namespaceSeparator))) { // 名前空間セパレータが使われてるかどうか | |
$useNamespace = true; | |
$namespace = substr($className, 0, $pos); | |
$className = substr($className, $pos + 1); | |
$fileName = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) // 名前空間に含まれるアンダースコアを DIRECTORY_SEPARATOR に | |
. DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $className); // クラス名に含まれるアンダースコアを DIRECTORY_SEPARATOR に | |
} else { | |
$fileName = str_replace('_', DIRECTORY_SEPARATOR, $className); // クラス名に含まれるアンダースコアを DIRECTORY_SEPARATOR に | |
} | |
$requirePath = null; | |
foreach ($this->vendorInfo as $vendorName => $info) { // ベンダー毎に指定されたディレクトリと拡張子でファイルを検索 | |
$includeDir = $info[0]; | |
$fileExtension = (isset($info[1])) ? $info[1] : $this->fileExtension; | |
if (0 === strpos(($useNamespace) ? $namespace : $className, $vendorName)) { | |
$path = $includeDir . DIRECTORY_SEPARATOR . $fileName . $fileExtension; | |
if (file_exists($path)) { | |
$requirePath = $path; | |
break; | |
} | |
} | |
} | |
if (is_null($requirePath)) { | |
$requirePath = stream_resolve_include_path($fileName . $this->fileExtension); // include_path からファイルを検索 | |
} | |
return $requirePath; | |
} | |
} |
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
<?php | |
namespace Holy\Tests; | |
use Holy\Loader; | |
/** | |
* LoaderTest | |
* | |
* @author [email protected] | |
*/ | |
class LoaderTest extends \PHPUnit_Framework_TestCase | |
{ | |
protected $defaultLoaders = array(); | |
protected $defaultIncludePath = null; | |
public function setUp() | |
{ | |
$this->defaultLoaders = spl_autoload_functions(); | |
if (!is_array($this->defaultLoaders)) { | |
$this->defaultLoaders = array(); | |
} | |
$this->defaultIncludePath = get_include_path(); | |
Loader::resetInstance(); | |
} | |
public function tearDown() | |
{ | |
$loaders = spl_autoload_functions(); | |
if (is_array($loaders)) { | |
foreach ($loaders as $loader) { | |
spl_autoload_unregister($loader); | |
} | |
} | |
if (is_array($this->defaultLoaders)) { | |
foreach ($this->defaultLoaders as $loader) { | |
spl_autoload_register($loader); | |
} | |
} | |
set_include_path($this->defaultIncludePath); | |
Loader::resetInstance(); | |
} | |
public function testSingleton() | |
{ | |
$this->assertInstanceOf('\Holy\Loader', Loader::getInstance()); | |
$this->assertSame( | |
Loader::getInstance(), | |
Loader::getInstance()); | |
} | |
public function testLoadingNamespacedClass() | |
{ | |
Loader::getInstance() | |
->set('LoadSample', realpath(__DIR__ . '/LoaderTest/lib/vendor')) | |
->load('\LoadSample\Foo'); | |
$this->assertInstanceOf('\LoadSample\Foo', new \LoadSample\Foo()); | |
Loader::getInstance() | |
->load('\LoadSample\Foo\Bar'); | |
$this->assertInstanceOf('\LoadSample\Foo\Bar', new \LoadSample\Foo\Bar()); | |
Loader::getInstance() | |
->load('\LoadSample\Foo\Bar\Baz'); | |
$this->assertInstanceOf('\LoadSample\Foo\Bar\Baz', new \LoadSample\Foo\Bar\Baz()); | |
} | |
public function testAutoloadingNamespacedClass() | |
{ | |
Loader::getInstance() | |
->set('AutoloadSample', realpath(__DIR__ . '/LoaderTest/lib/vendor')) | |
->enableAutoload(true); | |
$this->assertInstanceOf('\AutoloadSample\Foo', new \AutoloadSample\Foo()); | |
$this->assertInstanceOf('\AutoloadSample\Foo\Bar', new \AutoloadSample\Foo\Bar()); | |
$this->assertInstanceOf('\AutoloadSample\Foo\Bar\Baz', new \AutoloadSample\Foo\Bar\Baz()); | |
} | |
public function testAutoloadingLegacyClassWithDirectoryAndExtension() | |
{ | |
Loader::getInstance() | |
->set('Smorty', realpath(__DIR__ . '/LoaderTest/lib/vendor/Smorty/libs'), '.class.php') | |
->enableAutoload(); | |
$this->assertInstanceOf('\Smorty', new \Smorty()); | |
} | |
public function testAutoloadingLegacyClassInIncludePath() | |
{ | |
set_include_path(realpath(__DIR__ . '/LoaderTest/include_path')); | |
Loader::getInstance() | |
->enableAutoload(); | |
$this->assertInstanceOf('\PearStyleClass_Example', new \PearStyleClass_Example()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
こっちが最新版
https://gist.github.com/1707998
singletonやめて、__invoke()を実装して、spl_autoload_register()をユーザ側に任せただけですが…。