Skip to content

Commit afd5d47

Browse files
committed
Added a basic PSR4 and classmap autoloader.
1 parent 0d92381 commit afd5d47

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

system/Autoloader/Autoloader.php

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<?php namespace CodeIgniter\Autoloader;
2+
3+
/**
4+
* Class Autoloader
5+
*
6+
* An autoloader that uses both PSR4 autoloading, and traditional classmaps.
7+
*
8+
* Given a foo-bar package of classes in the file system at the following paths:
9+
*
10+
* /path/to/packages/foo-bar/
11+
* /src
12+
* Baz.php # Foo\Bar\Baz
13+
* Qux/
14+
* Quux.php # Foo\Bar\Qux\Quux
15+
*
16+
* you can add the path to the configuration array that is passed in the constructor.
17+
* The config array consists of 2 primary keys, both of which are associative arrays:
18+
* 'psr4', and 'classmap'.
19+
*
20+
* $config = [
21+
* 'psr4' => [
22+
* 'Foo\Bar' => '/path/to/packages/foo-bar'
23+
* ],
24+
* 'classmap' => [
25+
* 'MyClass' => '/path/to/class/file.php'
26+
* ]
27+
* ];
28+
*
29+
* Example:
30+
*
31+
* <?php
32+
* // our configuration array
33+
* $config = [ ... ];
34+
* $loader = new \CodeIgniter\Autoloader\Autoloader($config);
35+
*
36+
* // register the autoloader
37+
* $loader->register();
38+
*
39+
* @package CodeIgniter\Autoloader
40+
*/
41+
class Autoloader
42+
{
43+
44+
/**
45+
* Stores namespaces as key, and path as values.
46+
*
47+
* @var array
48+
*/
49+
protected $prefixes = [];
50+
51+
/**
52+
* Stores class name as key, and path as values.
53+
*
54+
* @var array
55+
*/
56+
protected $classmap = [];
57+
58+
//--------------------------------------------------------------------
59+
60+
/**
61+
* Reads in the configuration array (described above) and stores
62+
* the valid parts that we'll need.
63+
*
64+
* @param $config
65+
*/
66+
public function __construct($config)
67+
{
68+
// We have to have one or the other, though we don't enforce the need
69+
// to have both present in order to work.
70+
if (empty($config['psr4']) && empty($config['classmap']))
71+
{
72+
throw new \InvalidArgumentException('Config array must contain either the \'psr4\' key or the \'classmap\' key.');
73+
}
74+
75+
if (isset($config['psr4']))
76+
{
77+
$this->prefixes = $config['psr4'];
78+
}
79+
80+
if (isset($config['classmap']))
81+
{
82+
$this->classmap = $config['classmap'];
83+
}
84+
85+
unset($config);
86+
}
87+
88+
//--------------------------------------------------------------------
89+
90+
/**
91+
* Register the loader with the SPL autoloader stack.
92+
*/
93+
public function register()
94+
{
95+
spl_autoload_register([$this, 'loadClass']);
96+
}
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+
*
105+
* @return mixed The mapped file on success, or boolean false
106+
* on failure.
107+
*/
108+
public function loadClass($class)
109+
{
110+
// Try loading through class map
111+
$mapped_file = $this->loadFromClassmap($class);
112+
113+
// Nothing? Then try PSR4.
114+
if ( ! $mapped_file)
115+
{
116+
$mapped_file = $this->loadInNamespace($class);
117+
}
118+
119+
return $mapped_file;
120+
}
121+
122+
//--------------------------------------------------------------------
123+
124+
/**
125+
* Attempts to locate the file as one in our classmap and loads it
126+
* if possible.
127+
*
128+
* @param string $class The fully-qualified class name
129+
*
130+
* @return mixed The mapped file name on success, or boolean false on failure
131+
*/
132+
protected function loadFromClassmap($class)
133+
{
134+
if ( ! array_key_exists($class, $this->classmap))
135+
{
136+
return false;
137+
}
138+
139+
if ( ! file_exists($this->classmap[$class]))
140+
{
141+
return false;
142+
}
143+
144+
require $this->classmap[$class];
145+
146+
return $this->classmap[$class];
147+
}
148+
149+
//--------------------------------------------------------------------
150+
151+
/**
152+
* Loads the class file for a given class name.
153+
*
154+
* @param string $class The fully-qualified class name
155+
*
156+
* @return mixed The mapped file name on success, or boolean false on fail
157+
*/
158+
protected function loadInNamespace($class)
159+
{
160+
// the current namespace prefix
161+
$prefix = $class;
162+
163+
// work backwards through the namespace names of the fully-qualified
164+
// class name to find a mapped file name.
165+
while (false !== $pos = strrpos($prefix, '\\'))
166+
{
167+
// retain trailing namespace separator in the prefix
168+
$prefix = substr($class, 0, $pos + 1);
169+
170+
// the rest is the relative class name
171+
$relative_class = substr($class, $pos + 1);
172+
173+
// try to load the mapped file for prefix and relative class
174+
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
175+
176+
if ($mapped_file)
177+
{
178+
return $mapped_file;
179+
}
180+
181+
// remove the trailing namespace separator for the next iteration
182+
// of strpos()
183+
$prefix = rtrim($prefix, '\\');
184+
}
185+
186+
// never found a mapped file
187+
return false;
188+
}
189+
190+
//--------------------------------------------------------------------
191+
192+
/**
193+
* Loads the mapped file for a namespace prefix and relative class.
194+
*
195+
* @param string $prefix The namespace prefix
196+
* @param string $relative_class The relative class name
197+
*
198+
* @return mixed Boolean false if no mapped file can be loaded,
199+
* or the name of the mapped file that was loaded.
200+
*/
201+
protected function loadMappedFile($prefix, $relative_class)
202+
{
203+
// are there any base directories for this namespace prefix?
204+
if ( ! isset($this->prefixes[$prefix]))
205+
{
206+
return false;
207+
}
208+
209+
// look through base directories for this namespace prefix
210+
$file = $this->prefixes[$prefix].str_replace('\\', '/', $relative_class).'.php';
211+
212+
// if the mapped file exists, grab it
213+
if (file_exists($file))
214+
{
215+
require $file;
216+
217+
return $file;
218+
}
219+
220+
return false;
221+
}
222+
223+
//--------------------------------------------------------------------
224+
225+
}

0 commit comments

Comments
 (0)