PSR-4-新鮮なPHP仕様
FIGが制定したPHP規範は、PSRと略称され、PHP開発の事実基準である.
PSRにはもともと4つの規範があり、それぞれ: PSR-0自動ロード PSR-1基本コード仕様 PSR-2コード様式 PSR-3ログインタフェース 2013年末、第5の規範であるPSR-4が新たに発表された.
PSR-4では、クラス定義を自動的にロードするためにファイルパスを指定する方法と、ファイルを自動的にロードする場所を指定します.これは一見PSR-0と重複しているが、実際には機能的に重複している.違いはPSR-4の仕様が比較的清潔で、PHP 5.3以前のバージョンと互換性がある内容を除いて、PSR-0のアップグレード版の感じがします.もちろん、PSR-4もPSR-0に完全に代わるのではなく、必要なときにPSR-0を補充します.もちろん、もしあなたが望むなら、PSR-4もPSR-0に代わることができます.PSR−4は、PSR−0を含む他の自動ローディング機構と共に使用することができる.
PSR−4とPSR−0の最大の違いはアンダースコアの定義の違いである.PSR-4ではクラス名に下線を使用しても特に意味はありません.PSR−0は、クラス名の下線
コードサンプル
以下のコードは、複数のネーミングスペースを処理するPSR-4に従うクラス定義を示しています.
対応するユニットテストコード
Composer
PHPのパケット管理システムComposerはPSR-4をサポートしているとともに、
ComposerはPSR-0スタイルを使用
ComposerはPSR-4スタイルを使用
以上の2つの構造を比較すると、PSR-4がより簡潔なファイル構造をもたらすことが明らかになった.
SegmentFaultを書く
PSRにはもともと4つの規範があり、それぞれ:
PSR-4では、クラス定義を自動的にロードするためにファイルパスを指定する方法と、ファイルを自動的にロードする場所を指定します.これは一見PSR-0と重複しているが、実際には機能的に重複している.違いはPSR-4の仕様が比較的清潔で、PHP 5.3以前のバージョンと互換性がある内容を除いて、PSR-0のアップグレード版の感じがします.もちろん、PSR-4もPSR-0に完全に代わるのではなく、必要なときにPSR-0を補充します.もちろん、もしあなたが望むなら、PSR-4もPSR-0に代わることができます.PSR−4は、PSR−0を含む他の自動ローディング機構と共に使用することができる.
PSR−4とPSR−0の最大の違いはアンダースコアの定義の違いである.PSR-4ではクラス名に下線を使用しても特に意味はありません.PSR−0は、クラス名の下線
_
がディレクトリ区切り記号に変換されることを規定している.コードサンプル
以下のコードは、複数のネーミングスペースを処理するPSR-4に従うクラス定義を示しています.
register();
*
* // register the base directories for the namespace prefix
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
*
* The following line would cause the autoloader to attempt to load the
* \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
*
* prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = array();
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $base_dir);
} else {
array_push($this->prefixes[$prefix], $base_dir);
}
}
/**
* Loads the class file for a given class name.
*
* @param string $class The fully-qualified class name.
* @return mixed The mapped file name on success, or boolean false on
* failure.
*/
public function loadClass($class)
{
// the current namespace prefix
$prefix = $class;
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
while (false !== $pos = strrpos($prefix, '\\')) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
$relative_class = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
// remove the trailing namespace separator for the next iteration
// of strrpos()
$prefix = rtrim($prefix, '\\');
}
// never found a mapped file
return false;
}
/**
* Load the mapped file for a namespace prefix and relative class.
*
* @param string $prefix The namespace prefix.
* @param string $relative_class The relative class name.
* @return mixed Boolean false if no mapped file can be loaded, or the
* name of the mapped file that was loaded.
*/
protected function loadMappedFile($prefix, $relative_class)
{
// are there any base directories for this namespace prefix?
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
// look through base directories for this namespace prefix
foreach ($this->prefixes[$prefix] as $base_dir) {
// replace the namespace prefix with the base directory,
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $base_dir
. str_replace('\\', DIRECTORY_SEPARATOR, $relative_class)
. '.php';
$file = $base_dir
. str_replace('\\', '/', $relative_class)
. '.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
// yes, we're done
return $file;
}
}
// never found it
return false;
}
/**
* If a file exists, require it from the file system.
*
* @param string $file The file to require.
* @return bool True if the file exists, false if not.
*/
protected function requireFile($file)
{
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}
対応するユニットテストコード
files = $files;
}
protected function requireFile($file)
{
return in_array($file, $this->files);
}
}
class Psr4AutoloaderClassTest extends \PHPUnit_Framework_TestCase
{
protected $loader;
protected function setUp()
{
$this->loader = new MockPsr4AutoloaderClass;
$this->loader->setFiles(array(
'/vendor/foo.bar/src/ClassName.php',
'/vendor/foo.bar/src/DoomClassName.php',
'/vendor/foo.bar/tests/ClassNameTest.php',
'/vendor/foo.bardoom/src/ClassName.php',
'/vendor/foo.bar.baz.dib/src/ClassName.php',
'/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php',
));
$this->loader->addNamespace(
'Foo\Bar',
'/vendor/foo.bar/src'
);
$this->loader->addNamespace(
'Foo\Bar',
'/vendor/foo.bar/tests'
);
$this->loader->addNamespace(
'Foo\BarDoom',
'/vendor/foo.bardoom/src'
);
$this->loader->addNamespace(
'Foo\Bar\Baz\Dib',
'/vendor/foo.bar.baz.dib/src'
);
$this->loader->addNamespace(
'Foo\Bar\Baz\Dib\Zim\Gir',
'/vendor/foo.bar.baz.dib.zim.gir/src'
);
}
public function testExistingFile()
{
$actual = $this->loader->loadClass('Foo\Bar\ClassName');
$expect = '/vendor/foo.bar/src/ClassName.php';
$this->assertSame($expect, $actual);
$actual = $this->loader->loadClass('Foo\Bar\ClassNameTest');
$expect = '/vendor/foo.bar/tests/ClassNameTest.php';
$this->assertSame($expect, $actual);
}
public function testMissingFile()
{
$actual = $this->loader->loadClass('No_Vendor\No_Package\NoClass');
$this->assertFalse($actual);
}
public function testDeepFile()
{
$actual = $this->loader->loadClass('Foo\Bar\Baz\Dib\Zim\Gir\ClassName');
$expect = '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php';
$this->assertSame($expect, $actual);
}
public function testConfusion()
{
$actual = $this->loader->loadClass('Foo\Bar\DoomClassName');
$expect = '/vendor/foo.bar/src/DoomClassName.php';
$this->assertSame($expect, $actual);
$actual = $this->loader->loadClass('Foo\BarDoom\ClassName');
$expect = '/vendor/foo.bardoom/src/ClassName.php';
$this->assertSame($expect, $actual);
}
}
Composer
PHPのパケット管理システムComposerはPSR-4をサポートしているとともに、
composer.json
で異なるprefixを定義して異なる自動ロードメカニズムを使用することも可能である.ComposerはPSR-0スタイルを使用
vendor/
vendor_name/
package_name/
src/
Vendor_Name/
Package_Name/
ClassName.php # Vendor_Name\Package_Name\ClassName
tests/
Vendor_Name/
Package_Name/
ClassNameTest.php # Vendor_Name\Package_Name\ClassName
ComposerはPSR-4スタイルを使用
vendor/
vendor_name/
package_name/
src/
ClassName.php # Vendor_Name\Package_Name\ClassName
tests/
ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest
以上の2つの構造を比較すると、PSR-4がより簡潔なファイル構造をもたらすことが明らかになった.
SegmentFaultを書く