-
-
Save jwage/221634 to your computer and use it in GitHub Desktop.
<?php | |
/* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
* This software consists of voluntary contributions made by many individuals | |
* and is licensed under the MIT license. For more information, see | |
* <http://www.doctrine-project.org>. | |
*/ | |
/** | |
* SplClassLoader implementation that implements the technical interoperability | |
* standards for PHP 5.3 namespaces and class names. | |
* | |
* http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1 | |
* | |
* // Example which loads classes for the Doctrine Common package in the | |
* // Doctrine\Common namespace. | |
* $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine'); | |
* $classLoader->register(); | |
* | |
* @license http://www.opensource.org/licenses/mit-license.html MIT License | |
* @author Jonathan H. Wage <[email protected]> | |
* @author Roman S. Borschel <[email protected]> | |
* @author Matthew Weier O'Phinney <[email protected]> | |
* @author Kris Wallsmith <[email protected]> | |
* @author Fabien Potencier <[email protected]> | |
*/ | |
class SplClassLoader | |
{ | |
private $_fileExtension = '.php'; | |
private $_namespace; | |
private $_includePath; | |
private $_namespaceSeparator = '\\'; | |
/** | |
* Creates a new <tt>SplClassLoader</tt> that loads classes of the | |
* specified namespace. | |
* | |
* @param string $ns The namespace to use. | |
*/ | |
public function __construct($ns = null, $includePath = null) | |
{ | |
$this->_namespace = $ns; | |
$this->_includePath = $includePath; | |
} | |
/** | |
* Sets the namespace separator used by classes in the namespace of this class loader. | |
* | |
* @param string $sep The separator to use. | |
*/ | |
public function setNamespaceSeparator($sep) | |
{ | |
$this->_namespaceSeparator = $sep; | |
} | |
/** | |
* Gets the namespace seperator used by classes in the namespace of this class loader. | |
* | |
* @return void | |
*/ | |
public function getNamespaceSeparator() | |
{ | |
return $this->_namespaceSeparator; | |
} | |
/** | |
* Sets the base include path for all class files in the namespace of this class loader. | |
* | |
* @param string $includePath | |
*/ | |
public function setIncludePath($includePath) | |
{ | |
$this->_includePath = $includePath; | |
} | |
/** | |
* Gets the base include path for all class files in the namespace of this class loader. | |
* | |
* @return string $includePath | |
*/ | |
public function getIncludePath() | |
{ | |
return $this->_includePath; | |
} | |
/** | |
* Sets the file extension of class files in the namespace of this class loader. | |
* | |
* @param string $fileExtension | |
*/ | |
public function setFileExtension($fileExtension) | |
{ | |
$this->_fileExtension = $fileExtension; | |
} | |
/** | |
* Gets the file extension of class files in the namespace of this class loader. | |
* | |
* @return string $fileExtension | |
*/ | |
public function getFileExtension() | |
{ | |
return $this->_fileExtension; | |
} | |
/** | |
* Installs this class loader on the SPL autoload stack. | |
*/ | |
public function register() | |
{ | |
spl_autoload_register(array($this, 'loadClass')); | |
} | |
/** | |
* Uninstalls this class loader from the SPL autoloader stack. | |
*/ | |
public function unregister() | |
{ | |
spl_autoload_unregister(array($this, 'loadClass')); | |
} | |
/** | |
* Loads the given class or interface. | |
* | |
* @param string $className The name of the class to load. | |
* @return void | |
*/ | |
public function loadClass($className) | |
{ | |
if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) { | |
$fileName = ''; | |
$namespace = ''; | |
if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { | |
$namespace = substr($className, 0, $lastNsPos); | |
$className = substr($className, $lastNsPos + 1); | |
$fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; | |
} | |
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; | |
require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; | |
} | |
} | |
} |
This is the way I do it. It only require
s the file if its on the include path, so you get your error messages in the right place and multiple autoloaders can be used, like in @adriengibrat's comment.
<?php
spl_autoload_register(function($class) {
$parts = explode('\\', $class);
# Support for non-namespaced classes.
$parts[] = str_replace('_', DIRECTORY_SEPARATOR, array_pop($parts));
$path = implode(DIRECTORY_SEPARATOR, $parts);
$file = stream_resolve_include_path($path.'.php');
if($file !== false) {
require $file;
}
});
Current implementation implicitly shows that there is only one include path possible for one Vendor Name
But let`s imagine I have the following structure of the code
folder1/Vendor Name 1/Namespace 1/...
folder2/Vendor Name 1/Namespace 1/..
folder2/Vendor Name 1/Namespace 2/...
There is no one rule from PSR-0 has conflict with this case:
- A fully-qualified namespace and class must have the following structure ()*
- Each namespace must have a top-level namespace ("Vendor Name")
- Each namespace can have as many sub-namespaces as it wishes.
- etc...
You cannot have the same namespace in two folders, like your Vendor1. You might be lucky and have Subnamespaces below that Namespace1, and can then use this loader.
Otherwise, you'd have to solve the question of "how do you know where the file for a class is", and if the answer is "I start looking in folder1, and if nothing is found, continue with folder2", that might work, but is is not really PSR-0 compliant.
And it isn't feasible as well. What if the same class exists in both folders?
What do you think is it make sense to add this rule item into PSR-0 standards?
P.S. I guess here is an answer https://github.com/php-fig/fig-standards/blob/f4d92e6b4003f42c20403e078e9d5bbde2e984cc/proposed/autoloader.md
I think "include" is more logical, after all the method class loader utilises to load a class should be encapsulated. No one needs to know about that, so using "file_exists" is the best.
If the class is not found with your class load the php will keep searching until it reaches the last class loader, you don't need to worry about boolean return either. A fatal error will be generated if PHP not found the class anyway. Don't forget that some libraries use their own class loaders and using "require" will break them.
pls add a licence ( LGPL / MIT / BSD / GPL / AGPL )
Am I missing something here (I'm a namespace noob)?
Using a namespaced class such as Zend\XmlRpc\Server.php (use Zend\XmlRpc) and stepping through the code, the function :
tryLoadClassByPath($className, $unresolvedFilePath)
always returns false for namespaced classes because the $className parameter is the bare
class name without its namespace prefix so fails the test.
$isFound = class_exists($className, false);
If I modify the calling code to use the namespaced class like this:
public function loadClass($className)
{
$originalClassName = $className;
... and use this later ...
$isFound = $this->tryLoadClassByPath($originalClassName, $unresolvedFilePath);
then it returns true as expected. The class file is successfully loaded in any case as the file path is correctly setup so you don't actually see much happen even if the function does return false but I am fairly certain it should return true.
There was a problem with constants I'd defined. My BASE_PATH CONST was defined like this
define('BASE_PATH', dirname(realpath(__FILE__)) . '/');
So when I instantiated your class like this
$classLoader = new SplClassLoader('App\Controllers\Content', BASE_PATH);
The trailing Slash from my BASE_PATH Const was the causefor an error.
To modify this class by adding a rtrim() to the __construct method worked for me:
public function __construct($ns = null, $includePath = null) { $this->_namespace = $ns; $this->_includePath = rtrim($includePath, '/'); }
Perhaps something for the next Version.....no one needs trailing slashes :)
As ClemensSahs mentioned I would also like to know the license for this code.
Please remove the http://groups.google.com/group/php-standards/web/final-proposal link in the file, it leads to a 404
page.
Zeokat very usefull piece of code. I need to know the license because i thinking into using it.
Thanks.
Hi All,
Why declared methods setIncludePath / getIncludePath and not declared methods getNamespace and setNamespace in this SplClassLoader ??
Regards
Download file of this gist don't have white line in the end file. See PSR2
This link
https://gist.github.com/r2pq/aebc8131955ee819086f
Is a fork to this file with the next PHPDoc fixes:
- The constructor has two parameters, this was not reflected before.
- The PHPDoc related to getNamespaceSeparator(), had a typo. It said 'seperator' instead of 'separator'.
- The PHPDoc related to getNamespaceSeparator(), now has a return string instead of return void.
I think, there is a problem with this implementation or the PSR-0 itself. When - theoretically - using classnames that begin or end with and underscore "", e.g. \Vendor\Namespace_Class_Extension.php
This would end up in something like this:
Vendor/Namespace//Class/Extension/.php
and will definitely fail to load.
There is no omission or MUST NOT for leading or trailing underscores in the definition of classnames.
To avoid this, just replace the line:
<?php
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension;
with
<?php
$fileName .= preg_replace("#([a-z0-9]+)_([a-z0-9]+)#i", "$1" . DIRECTORY_SEPARATOR . "$2", $className) . $this->_fileExtension;
and the example above will end up in:
Vendor/Namespace/_Class/Extension_.php
Maybe useful:
https://github.com/keradus/SplClassLoader - taken from PSR and little fixed like docs etc.
If need anything to change just PR.
Btw, consider using PSR4 autoloader:
https://github.com/keradus/Psr4Autoloader
Here is the file loading solution I use within my autoloader:
REM** $class can be a Class OR an Interface
include $filename;
// Check to see if the include declared the class
if(!class_exists( $class, FALSE )) {
// Check to see if the include declared the interface
if(!interface_exists( $class, FALSE )){
// We do not have the class nor interface : file did not load or did not exist
// Do something : Log error; check other file resources or die gracefully.
}
}
Why I use this:
Loading files is normally the slowest part of any php application. Using file_exists(), even though it does not load the file still takes 'almost' as much time as loading the file itself (assuming a small file which class files tend to be >10Kb). So if you use:
file_exists(fileName){
require fileName;
}
From a application run time perspective its like loading the file twice !!! In addition require will kill your app if it fails where include only throws a warning.
Testing if the class or interface exists after an include will give you options to try something else before YOU gracefully exit your application.
Hope this helps
Got it.
Maybe replace 'require' with 'require_once' in loadClass(...) method ?
http://php.net/manual/en/function.require-once.php
Hi guys, I have trouble.
Using SplClassLoader loosing displaying of parse errors and it is really pain the ass to debug the code manually. I can only catch then with register_shutdown_function(). I have set error_reporting to E_ALL, I did almost everything to display them and nothing.
If I require the classes manually everything works as expected (any errors are displayed). Any suggestions?
get class in to on?
SplClassLoader line 133 is throwing error. File not found. But the file is present in another folder, how to fix this
It is just a test https://gist.github.com/Romanzo/2cd7b5cc382f5f419670
Hello there people! New important info forgotten here..
The method loadClass on line 141 must return a boolean value! Recomended:
return class_exists($className, false) || trait_exists($className, false) || interface_exists($className, false);
This is imperative! PHP SPL autoload function suports bubbling effect.. You can register as many autoload functions as necessary, so, you must return a boolean value to indicate if the function loaded the resource or not. If it returns true
, the SPL stops casting autoload functions.. if it returns false
it keeps goin on the rest of the functions registered through spl_autoload_register.
The second parameter in every *_exists
methods refer to autoloading and it must be false
inside the autoload function scope to avoid infinite loop (the autoload function casting itself). If you try to load an interface, class_exists
will drop false
, because it's not a class, but interface_exists
will drop true
, then the autoload stops trying to load that resource.
Also, it's recommended to use include_once
than any of other options. For instance, if you try to load My\Personal\Class\Name
and My_Personal_Class_Name
as the code is it will drop a fatal error trying to duplicate the resource (class, trait or interface). The fix proposed here will solve this problem.
See ya!
I am trying this code from last 5 hours and it dint work.
Can someone remove the spam comments from 2017 (since first-reviews' comment)? I don't see any possibility to flag them from my side..
Very useful~