|
1 <?php |
|
2 /* |
|
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
14 * |
|
15 * This software consists of voluntary contributions made by many individuals |
|
16 * and is licensed under the LGPL. For more information, see |
|
17 * <http://www.doctrine-project.org>. |
|
18 */ |
|
19 |
|
20 namespace Doctrine\Common; |
|
21 |
|
22 /** |
|
23 * A <tt>ClassLoader</tt> is an autoloader for class files that can be |
|
24 * installed on the SPL autoload stack. It is a class loader that either loads only classes |
|
25 * of a specific namespace or all namespaces and it is suitable for working together |
|
26 * with other autoloaders in the SPL autoload stack. |
|
27 * |
|
28 * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader |
|
29 * relies on the PHP <code>include_path</code>. |
|
30 * |
|
31 * @author Roman Borschel <roman@code-factory.org> |
|
32 * @since 2.0 |
|
33 */ |
|
34 class ClassLoader |
|
35 { |
|
36 private $fileExtension = '.php'; |
|
37 private $namespace; |
|
38 private $includePath; |
|
39 private $namespaceSeparator = '\\'; |
|
40 |
|
41 /** |
|
42 * Creates a new <tt>ClassLoader</tt> that loads classes of the |
|
43 * specified namespace from the specified include path. |
|
44 * |
|
45 * If no include path is given, the ClassLoader relies on the PHP include_path. |
|
46 * If neither a namespace nor an include path is given, the ClassLoader will |
|
47 * be responsible for loading all classes, thereby relying on the PHP include_path. |
|
48 * |
|
49 * @param string $ns The namespace of the classes to load. |
|
50 * @param string $includePath The base include path to use. |
|
51 */ |
|
52 public function __construct($ns = null, $includePath = null) |
|
53 { |
|
54 $this->namespace = $ns; |
|
55 $this->includePath = $includePath; |
|
56 } |
|
57 |
|
58 /** |
|
59 * Sets the namespace separator used by classes in the namespace of this ClassLoader. |
|
60 * |
|
61 * @param string $sep The separator to use. |
|
62 */ |
|
63 public function setNamespaceSeparator($sep) |
|
64 { |
|
65 $this->namespaceSeparator = $sep; |
|
66 } |
|
67 |
|
68 /** |
|
69 * Gets the namespace separator used by classes in the namespace of this ClassLoader. |
|
70 * |
|
71 * @return string |
|
72 */ |
|
73 public function getNamespaceSeparator() |
|
74 { |
|
75 return $this->namespaceSeparator; |
|
76 } |
|
77 |
|
78 /** |
|
79 * Sets the base include path for all class files in the namespace of this ClassLoader. |
|
80 * |
|
81 * @param string $includePath |
|
82 */ |
|
83 public function setIncludePath($includePath) |
|
84 { |
|
85 $this->includePath = $includePath; |
|
86 } |
|
87 |
|
88 /** |
|
89 * Gets the base include path for all class files in the namespace of this ClassLoader. |
|
90 * |
|
91 * @return string |
|
92 */ |
|
93 public function getIncludePath() |
|
94 { |
|
95 return $this->includePath; |
|
96 } |
|
97 |
|
98 /** |
|
99 * Sets the file extension of class files in the namespace of this ClassLoader. |
|
100 * |
|
101 * @param string $fileExtension |
|
102 */ |
|
103 public function setFileExtension($fileExtension) |
|
104 { |
|
105 $this->fileExtension = $fileExtension; |
|
106 } |
|
107 |
|
108 /** |
|
109 * Gets the file extension of class files in the namespace of this ClassLoader. |
|
110 * |
|
111 * @return string |
|
112 */ |
|
113 public function getFileExtension() |
|
114 { |
|
115 return $this->fileExtension; |
|
116 } |
|
117 |
|
118 /** |
|
119 * Registers this ClassLoader on the SPL autoload stack. |
|
120 */ |
|
121 public function register() |
|
122 { |
|
123 spl_autoload_register(array($this, 'loadClass')); |
|
124 } |
|
125 |
|
126 /** |
|
127 * Removes this ClassLoader from the SPL autoload stack. |
|
128 */ |
|
129 public function unregister() |
|
130 { |
|
131 spl_autoload_unregister(array($this, 'loadClass')); |
|
132 } |
|
133 |
|
134 /** |
|
135 * Loads the given class or interface. |
|
136 * |
|
137 * @param string $classname The name of the class to load. |
|
138 * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. |
|
139 */ |
|
140 public function loadClass($className) |
|
141 { |
|
142 if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { |
|
143 return false; |
|
144 } |
|
145 |
|
146 require ($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') |
|
147 . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) |
|
148 . $this->fileExtension; |
|
149 |
|
150 return true; |
|
151 } |
|
152 |
|
153 /** |
|
154 * Asks this ClassLoader whether it can potentially load the class (file) with |
|
155 * the given name. |
|
156 * |
|
157 * @param string $className The fully-qualified name of the class. |
|
158 * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. |
|
159 */ |
|
160 public function canLoadClass($className) |
|
161 { |
|
162 if ($this->namespace !== null && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0) { |
|
163 return false; |
|
164 } |
|
165 |
|
166 $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; |
|
167 |
|
168 if ($this->includePath !== null) { |
|
169 return file_exists($this->includePath . DIRECTORY_SEPARATOR . $file); |
|
170 } |
|
171 |
|
172 return self::fileExistsInIncludePath($file); |
|
173 } |
|
174 |
|
175 /** |
|
176 * Checks whether a class with a given name exists. A class "exists" if it is either |
|
177 * already defined in the current request or if there is an autoloader on the SPL |
|
178 * autoload stack that is a) responsible for the class in question and b) is able to |
|
179 * load a class file in which the class definition resides. |
|
180 * |
|
181 * If the class is not already defined, each autoloader in the SPL autoload stack |
|
182 * is asked whether it is able to tell if the class exists. If the autoloader is |
|
183 * a <tt>ClassLoader</tt>, {@link canLoadClass} is used, otherwise the autoload |
|
184 * function of the autoloader is invoked and expected to return a value that |
|
185 * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports |
|
186 * that the class exists, TRUE is returned. |
|
187 * |
|
188 * Note that, depending on what kinds of autoloaders are installed on the SPL |
|
189 * autoload stack, the class (file) might already be loaded as a result of checking |
|
190 * for its existence. This is not the case with a <tt>ClassLoader</tt>, who separates |
|
191 * these responsibilities. |
|
192 * |
|
193 * @param string $className The fully-qualified name of the class. |
|
194 * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. |
|
195 */ |
|
196 public static function classExists($className) |
|
197 { |
|
198 if (class_exists($className, false)) { |
|
199 return true; |
|
200 } |
|
201 |
|
202 foreach (spl_autoload_functions() as $loader) { |
|
203 if (is_array($loader)) { // array(???, ???) |
|
204 if (is_object($loader[0])) { |
|
205 if ($loader[0] instanceof ClassLoader) { // array($obj, 'methodName') |
|
206 if ($loader[0]->canLoadClass($className)) { |
|
207 return true; |
|
208 } |
|
209 } else if ($loader[0]->{$loader[1]}($className)) { |
|
210 return true; |
|
211 } |
|
212 } else if ($loader[0]::$loader[1]($className)) { // array('ClassName', 'methodName') |
|
213 return true; |
|
214 } |
|
215 } else if ($loader instanceof \Closure) { // function($className) {..} |
|
216 if ($loader($className)) { |
|
217 return true; |
|
218 } |
|
219 } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" |
|
220 return true; |
|
221 } |
|
222 } |
|
223 |
|
224 return false; |
|
225 } |
|
226 |
|
227 /** |
|
228 * Gets the <tt>ClassLoader</tt> from the SPL autoload stack that is responsible |
|
229 * for (and is able to load) the class with the given name. |
|
230 * |
|
231 * @param string $className The name of the class. |
|
232 * @return The <tt>ClassLoader</tt> for the class or NULL if no such <tt>ClassLoader</tt> exists. |
|
233 */ |
|
234 public static function getClassLoader($className) |
|
235 { |
|
236 foreach (spl_autoload_functions() as $loader) { |
|
237 if (is_array($loader) |
|
238 && $loader[0] instanceof ClassLoader |
|
239 && $loader[0]->canLoadClass($className) |
|
240 ) { |
|
241 return $loader[0]; |
|
242 } |
|
243 } |
|
244 |
|
245 return null; |
|
246 } |
|
247 |
|
248 /** |
|
249 * @param string $file The file relative path. |
|
250 * @return boolean Whether file exists in include_path. |
|
251 */ |
|
252 public static function fileExistsInIncludePath($file) |
|
253 { |
|
254 foreach (explode(PATH_SEPARATOR, get_include_path()) as $dir) { |
|
255 if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) { |
|
256 return true; |
|
257 } |
|
258 } |
|
259 return false; |
|
260 } |
|
261 } |