PSR-4-autoloader-examples
다음 예는 PSR-4에 호환하는 코드입니다.
역자주: 사실상 오토로더는 컴포저로 대동단결 되었기 때문에 직접 구현하시기 보다는 컴포저를 사용하시는 것을 추천합니다

Closure 예제

1
<?php
2
/**
3
* An example of a project-specific implementation.
4
*
5
* After registering this autoload function with SPL, the following line
6
* would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
7
* from /path/to/project/src/Baz/Qux.php:
8
*
9
* new \Foo\Bar\Baz\Qux;
10
*
11
* @param string $class The fully-qualified class name.
12
* @return void
13
*/
14
spl_autoload_register(function ($class) {
15
16
// project-specific namespace prefix
17
$prefix = 'Foo\\Bar\\';
18
19
// base directory for the namespace prefix
20
$base_dir = __DIR__ . '/src/';
21
22
// does the class use the namespace prefix?
23
$len = strlen($prefix);
24
if (strncmp($prefix, $class, $len) !== 0) {
25
// no, move to the next registered autoloader
26
return;
27
}
28
29
// get the relative class name
30
$relative_class = substr($class, $len);
31
32
// replace the namespace prefix with the base directory, replace namespace
33
// separators with directory separators in the relative class name, append
34
// with .php
35
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
36
37
// if the file exists, require it
38
if (file_exists($file)) {
39
require $file;
40
}
41
});
Copied!

Class 예제

다음은 네임스페이스를 활용하여 여러 클래스를 처리하기위한 클래스 구현의 예제입니다.
1
<?php
2
namespace Example;
3
4
/**
5
* An example of a general-purpose implementation that includes the optional
6
* functionality of allowing multiple base directories for a single namespace
7
* prefix.
8
*
9
* Given a foo-bar package of classes in the file system at the following
10
* paths ...
11
*
12
* /path/to/packages/foo-bar/
13
* src/
14
* Baz.php # Foo\Bar\Baz
15
* Qux/
16
* Quux.php # Foo\Bar\Qux\Quux
17
* tests/
18
* BazTest.php # Foo\Bar\BazTest
19
* Qux/
20
* QuuxTest.php # Foo\Bar\Qux\QuuxTest
21
*
22
* ... add the path to the class files for the \Foo\Bar\ namespace prefix
23
* as follows:
24
*
25
* <?php
26
* // instantiate the loader
27
* $loader = new \Example\Psr4AutoloaderClass;
28
*
29
* // register the autoloader
30
* $loader->register();
31
*
32
* // register the base directories for the namespace prefix
33
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
34
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
35
*
36
* The following line would cause the autoloader to attempt to load the
37
* \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
38
*
39
* <?php
40
* new \Foo\Bar\Qux\Quux;
41
*
42
* The following line would cause the autoloader to attempt to load the
43
* \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:
44
*
45
* <?php
46
* new \Foo\Bar\Qux\QuuxTest;
47
*/
48
class Psr4AutoloaderClass
49
{
50
/**
51
* An associative array where the key is a namespace prefix and the value
52
* is an array of base directories for classes in that namespace.
53
*
54
* @var array
55
*/
56
protected $prefixes = array();
57
58
/**
59
* Register loader with SPL autoloader stack.
60
*
61
* @return void
62
*/
63
public function register()
64
{
65
spl_autoload_register(array($this, 'loadClass'));
66
}
67
68
/**
69
* Adds a base directory for a namespace prefix.
70
*
71
* @param string $prefix The namespace prefix.
72
* @param string $base_dir A base directory for class files in the
73
* namespace.
74
* @param bool $prepend If true, prepend the base directory to the stack
75
* instead of appending it; this causes it to be searched first rather
76
* than last.
77
* @return void
78
*/
79
public function addNamespace($prefix, $base_dir, $prepend = false)
80
{
81
// normalize namespace prefix
82
$prefix = trim($prefix, '\\') . '\\';
83
84
// normalize the base directory with a trailing separator
85
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
86
87
// initialize the namespace prefix array
88
if (isset($this->prefixes[$prefix]) === false) {
89
$this->prefixes[$prefix] = array();
90
}
91
92
// retain the base directory for the namespace prefix
93
if ($prepend) {
94
array_unshift($this->prefixes[$prefix], $base_dir);
95
} else {
96
array_push($this->prefixes[$prefix], $base_dir);
97
}
98
}
99
100
/**
101
* Loads the class file for a given class name.
102
*
103
* @param string $class The fully-qualified class name.
104
* @return mixed The mapped file name on success, or boolean false on
105
* failure.
106
*/
107
public function loadClass($class)
108
{
109
// the current namespace prefix
110
$prefix = $class;
111
112
// work backwards through the namespace names of the fully-qualified
113
// class name to find a mapped file name
114
while (false !== $pos = strrpos($prefix, '\\')) {
115
116
// retain the trailing namespace separator in the prefix
117
$prefix = substr($class, 0, $pos + 1);
118
119
// the rest is the relative class name
120
$relative_class = substr($class, $pos + 1);
121
122
// try to load a mapped file for the prefix and relative class
123
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
124
if ($mapped_file) {
125
return $mapped_file;
126
}
127
128
// remove the trailing namespace separator for the next iteration
129
// of strrpos()
130
$prefix = rtrim($prefix, '\\');
131
}
132
133
// never found a mapped file
134
return false;
135
}
136
137
/**
138
* Load the mapped file for a namespace prefix and relative class.
139
*
140
* @param string $prefix The namespace prefix.
141
* @param string $relative_class The relative class name.
142
* @return mixed Boolean false if no mapped file can be loaded, or the
143
* name of the mapped file that was loaded.
144
*/
145
protected function loadMappedFile($prefix, $relative_class)
146
{
147
// are there any base directories for this namespace prefix?
148
if (isset($this->prefixes[$prefix]) === false) {
149
return false;
150
}
151
152
// look through base directories for this namespace prefix
153
foreach ($this->prefixes[$prefix] as $base_dir) {
154
155
// replace the namespace prefix with the base directory,
156
// replace namespace separators with directory separators
157
// in the relative class name, append with .php
158
$file = $base_dir
159
. str_replace('\\', '/', $relative_class)
160
. '.php';
161
162
// if the mapped file exists, require it
163
if ($this->requireFile($file)) {
164
// yes, we're done
165
return $file;
166
}
167
}
168
169
// never found it
170
return false;
171
}
172
173
/**
174
* If a file exists, require it from the file system.
175
*
176
* @param string $file The file to require.
177
* @return bool True if the file exists, false if not.
178
*/
179
protected function requireFile($file)
180
{
181
if (file_exists($file)) {
182
require $file;
183
return true;
184
}
185
return false;
186
}
187
}
Copied!

Unit Tests

다음 예제는 위의 클래스 로더를 단위 테스트하는 방법의 한 가지입니다.
1
<?php
2
namespace Example\Tests;
3
4
class MockPsr4AutoloaderClass extends Psr4AutoloaderClass
5
{
6
protected $files = array();
7
8
public function setFiles(array $files)
9
{
10
$this->files = $files;
11
}
12
13
protected function requireFile($file)
14
{
15
return in_array($file, $this->files);
16
}
17
}
18
19
class Psr4AutoloaderClassTest extends \PHPUnit_Framework_TestCase
20
{
21
protected $loader;
22
23
protected function setUp()
24
{
25
$this->loader = new MockPsr4AutoloaderClass;
26
27
$this->loader->setFiles(array(
28
'/vendor/foo.bar/src/ClassName.php',
29
'/vendor/foo.bar/src/DoomClassName.php',
30
'/vendor/foo.bar/tests/ClassNameTest.php',
31
'/vendor/foo.bardoom/src/ClassName.php',
32
'/vendor/foo.bar.baz.dib/src/ClassName.php',
33
'/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php',
34
));
35
36
$this->loader->addNamespace(
37
'Foo\Bar',
38
'/vendor/foo.bar/src'
39
);
40
41
$this->loader->addNamespace(
42
'Foo\Bar',
43
'/vendor/foo.bar/tests'
44
);
45
46
$this->loader->addNamespace(
47
'Foo\BarDoom',
48
'/vendor/foo.bardoom/src'
49
);
50
51
$this->loader->addNamespace(
52
'Foo\Bar\Baz\Dib',
53
'/vendor/foo.bar.baz.dib/src'
54
);
55
56
$this->loader->addNamespace(
57
'Foo\Bar\Baz\Dib\Zim\Gir',
58
'/vendor/foo.bar.baz.dib.zim.gir/src'
59
);
60
}
61
62
public function testExistingFile()
63
{
64
$actual = $this->loader->loadClass('Foo\Bar\ClassName');
65
$expect = '/vendor/foo.bar/src/ClassName.php';
66
$this->assertSame($expect, $actual);
67
68
$actual = $this->loader->loadClass('Foo\Bar\ClassNameTest');
69
$expect = '/vendor/foo.bar/tests/ClassNameTest.php';
70
$this->assertSame($expect, $actual);
71
}
72
73
public function testMissingFile()
74
{
75
$actual = $this->loader->loadClass('No_Vendor\No_Package\NoClass');
76
$this->assertFalse($actual);
77
}
78
79
public function testDeepFile()
80
{
81
$actual = $this->loader->loadClass('Foo\Bar\Baz\Dib\Zim\Gir\ClassName');
82
$expect = '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php';
83
$this->assertSame($expect, $actual);
84
}
85
86
public function testConfusion()
87
{
88
$actual = $this->loader->loadClass('Foo\Bar\DoomClassName');
89
$expect = '/vendor/foo.bar/src/DoomClassName.php';
90
$this->assertSame($expect, $actual);
91
92
$actual = $this->loader->loadClass('Foo\BarDoom\ClassName');
93
$expect = '/vendor/foo.bardoom/src/ClassName.php';
94
$this->assertSame($expect, $actual);
95
}
96
}
Copied!
Last modified 2yr ago