rfc:splclassloader

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:splclassloader [2011/11/08 13:30]
guilhermeblanco Added better examples
rfc:splclassloader [2017/09/22 13:28] (current)
Line 3: Line 3:
   * Date: 2010-03-22   * Date: 2010-03-22
   * Author: Guilherme Blanco <guilhermeblanco@hotmail.com>   * Author: Guilherme Blanco <guilhermeblanco@hotmail.com>
-  * Status: Voting at https://wiki.php.net/rfc/splclassloader/vote +  * Status: Declined 
 +  * Voting results at https://wiki.php.net/rfc/splclassloader/vote 
   * First Published at: http://wiki.php.net/rfc/splclassloader   * First Published at: http://wiki.php.net/rfc/splclassloader
  
Line 77: Line 78:
          
     /**     /**
-     Creates a new Autoloader instance.+     Define the autoloader work mode.
      *      *
      * @param integer $mode Autoloader work mode.      * @param integer $mode Autoloader work mode.
      */      */
-    public function __construct($mode = \SplAutoloader::MODE_NORMAL);+    public function setMode($mode);
          
     /**     /**
Line 145: Line 146:
  
 <code php> <code php>
-$classLoader = new \SplClassLoader(\SplClassLoader::MODE_SILENT);+$classLoader = new \SplClassLoader(); 
 +$classLoader->setMode(\SplClassLoader::MODE_SILENT);
 $classLoader->setIncludePathLookup(true); $classLoader->setIncludePathLookup(true);
 $classLoader->add('PEAR'); $classLoader->add('PEAR');
 +$classLoader->register();
 +</code>
 +
 +Autoloading in debug mode:
 +
 +<code php>
 +$classLoader = new \SplClassLoader();
 +$classLoader->setMode(\SplClassLoader::MODE_NORMAL | \SplClassLoader::MODE_DEBUG);
 +$classLoader->add('Symfony', '/path/to/symfony');
 +$classLoader->add('Zend', '/path/to/zf');
 $classLoader->register(); $classLoader->register();
 </code> </code>
Line 177: Line 189:
  
 The following class is a sample SplClassLoader implementation that can load your classes if you follow the autoloader interoperability standards proposed above. It is the current recommended way to load PHP 5.3 classes that follow these standards. The following class is a sample SplClassLoader implementation that can load your classes if you follow the autoloader interoperability standards proposed above. It is the current recommended way to load PHP 5.3 classes that follow these standards.
- 
-> NOTE: This implementation is not the proposed final. It requires two updates: 
->   * Multiple paths per namespace 
->   * Silent mode 
  
 <code php> <code php>
-<?php 
- 
 /** /**
  * SplClassLoader implementation that implements the technical interoperability  * SplClassLoader implementation that implements the technical interoperability
  * standards for PHP 5.3 namespaces and class names.  * standards for PHP 5.3 namespaces and class names.
  *  *
- http://groups.google.com/group/php-standards/web/final-proposal+ https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
  *  *
- // Example which loads classes for the Doctrine Common package in the + * Example usage:
- * // Doctrine\Common namespace. +
- * $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine'); +
- * $classLoader->register();+
  *  *
 +     $classLoader = new \SplClassLoader();
 + *
 +     // Configure the SplClassLoader to act normally or silently
 +     $classLoader->setMode(\SplClassLoader::MODE_NORMAL);
 + *
 +     // Add a namespace of classes
 +     $classLoader->add('Doctrine', array(
 +         '/path/to/doctrine-common', '/path/to/doctrine-dbal', '/path/to/doctrine-orm'
 +     ));
 + *
 +     // Add a prefix
 +     $classLoader->add('Swift', '/path/to/swift');
 + *
 +     // Add a prefix through PEAR1 convention, requiring include_path lookup
 +     $classLoader->add('PEAR');
 + *
 +     // Allow to PHP use the include_path for file path lookup
 +     $classLoader->setIncludePathLookup(true);
 + *
 +     // Possibility to change the default php file extension
 +     $classLoader->setFileExtension('.php');
 + *
 +     // Register the autoloader, prepending it in the stack
 +     $classLoader->register(true);
 + *
 + * @author Guilherme Blanco <guilhermeblanco@php.net>
  * @author Jonathan H. Wage <jonwage@gmail.com>  * @author Jonathan H. Wage <jonwage@gmail.com>
  * @author Roman S. Borschel <roman@code-factory.org>  * @author Roman S. Borschel <roman@code-factory.org>
Line 202: Line 231:
  * @author Fabien Potencier <fabien.potencier@symfony-project.org>  * @author Fabien Potencier <fabien.potencier@symfony-project.org>
  */  */
-class SplClassLoader+class SplClassLoader implements SplAutoloader
 { {
-    private $_fileExtension = '.php'; +    /** 
-    private $_namespace+     * @var string 
-    private $_includePath+     */ 
-    private $_namespaceSeparator '\\';+    private $fileExtension = '.php'; 
 +     
 +    /** 
 +     * @var boolean 
 +     */ 
 +    private $includePathLookup = false; 
 +     
 +    /** 
 +     * @var array 
 +     */ 
 +    private $resources = array(); 
 +     
 +    /** 
 +     * @var integer 
 +     */ 
 +    private $mode self::MODE_NORMAL;
  
     /**     /**
-     Creates a new <tt>SplClassLoader</tt> that loads classes of the +     {@inheritdoc}
-     * specified namespace. +
-     * +
-     @param string $ns The namespace to use.+
      */      */
-    public function __construct($ns = null, $includePath = null)+    public function setMode($mode)
     {     {
-        $this->_namespace = $ns+    if ($mode & self::MODE_SILENT && $mode & self::MODE_NORMAL) { 
-        $this->_includePath = $includePath;+        throw new \InvalidArgumentException( 
 +            sprintf('Cannot have %s working normally and silently at the same time!', __CLASS__) 
 +        ); 
 +    } 
 +     
 +        $this->mode = $mode;
     }     }
 + 
     /**     /**
-     Sets the namespace separator used by classes in the namespace of this class loader.+     Define the file extension of resource files in the path of this class loader.
      *      *
-     * @param string $sep The separator to use.+     * @param string $fileExtension
      */      */
-    public function setNamespaceSeparator($sep)+    public function setFileExtension($fileExtension)
     {     {
-        $this->_namespaceSeparator = $sep;+        $this->fileExtension = $fileExtension;
     }     }
 + 
     /**     /**
-     Gets the namespace seperator used by classes in the namespace of this class loader.+     Retrieve the file extension of resource files in the path of this class loader.
      *      *
-     * @return void+     * @return string
      */      */
-    public function getNamespaceSeparator()+    public function getFileExtension()
     {     {
-        return $this->_namespaceSeparator;+        return $this->fileExtension;
     }     }
 + 
     /**     /**
-     Sets the base include path for all class files in the namespace of this class loader.+     Turns on searching the include for class files. Allows easy loading installed PEAR packages.
      *      *
-     * @param string $includePath+     * @param boolean $includePathLookup
      */      */
-    public function setIncludePath($includePath)+    public function setIncludePathLookup($includePathLookup)
     {     {
-        $this->_includePath = $includePath;+        $this->includePathLookup = $includePathLookup;
     }     }
 + 
     /**     /**
      * Gets the base include path for all class files in the namespace of this class loader.      * Gets the base include path for all class files in the namespace of this class loader.
      *      *
-     * @return string $includePath+     * @return boolean
      */      */
-    public function getIncludePath()+    public function getIncludePathLookup()
     {     {
-        return $this->_includePath;+        return $this->includePathLookup;
     }     }
 + 
     /**     /**
-     Sets the file extension of class files in the namespace of this class loader. +     {@inheritdoc}
-     * +
-     @param string $fileExtension+
      */      */
-    public function setFileExtension($fileExtension)+    public function register($prepend = false)
     {     {
-        $this->_fileExtension = $fileExtension;+        spl_autoload_register(array($this, 'load'), true, $prepend);
     }     }
 + 
     /**     /**
-     Gets the file extension of class files in the namespace of this class loader. +     {@inheritdoc}
-     * +
-     @return string $fileExtension+
      */      */
-    public function getFileExtension()+    public function unregister()
     {     {
-        return $this->_fileExtension;+        spl_autoload_unregister(array($this, 'load'));
     }     }
 +    
     /**     /**
-     Installs this class loader on the SPL autoload stack.+     {@inheritdoc}
      */      */
-    public function register()+    public function add($resource, $resourcePath = null)
     {     {
-        spl_autoload_register(array($this, 'loadClass'));+        $this->resources[$resource] = (array$resourcePath;
     }     }
 +    
     /**     /**
-     Uninstalls this class loader from the SPL autoloader stack.+     {@inheritdoc}
      */      */
-    public function unregister()+    public function load($resourceName)
     {     {
-        spl_autoload_unregister(array($this'loadClass'));+        $resourceAbsolutePath = $this->getResourceAbsolutePath($resourceName); 
 +         
 +        switch (true) { 
 +            case ($this->mode & self::MODE_SILENT): 
 +                if ($resourceAbsolutePath !== false) { 
 +                    require $resourceAbsolutePath; 
 +                } 
 +                break; 
 +         
 +            case ($this->mode & self::MODE_NORMAL): 
 +            default: 
 +                require $resourceAbsolutePath; 
 +                break; 
 +        } 
 +         
 +        if ($this->mode & self::MODE_DEBUG && ! $this->isResourceDeclared($resourceName)) { 
 +            throw new \RuntimeException( 
 +                sprintf('Autoloader expected resource "%s" to be declared in file "%s".', $resourceName, $resourceAbsolutePath) 
 +            ); 
 +        }
     }     }
 +    
     /**     /**
-     Loads the given class or interface.+     Transform resource name into its absolute resource path representation.
      *      *
-     * @param string $className The name of the class to load. +     * @params string $resourceName 
-     * @return void+     * 
 +     * @return string Resource absolute path.
      */      */
-    public function loadClass($className)+    private function getResourceAbsolutePath($resourceName)
     {     {
-        if (null === $this->_namespace || $this->_namespace . $this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace . $this->_namespaceSeparator))) { +        $resourceRelativePath = $this->getResourceRelativePath($resourceName); 
-            $fileName = ''; +         
-            $namespace = ''; +        foreach ($this->resources as $resource =$resourcesPath) { 
-            if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { +            if (strpos($resourceName, $resource!== 0) { 
-                $namespace substr($className, 0, $lastNsPos); +                continue;
-                $className = substr($className, $lastNsPos + 1); +
-                $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;+
             }             }
-            $fileName .str_replace('_', DIRECTORY_SEPARATOR$className$this->_fileExtension+             
- +            foreach ($resourcesPath as $resourcePath) { 
-            require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : ''$fileName;+                $resourceAbsolutePath $resourcePath . DIRECTORY_SEPARATOR $resourceRelativePath; 
 +                 
 +                if (is_file($resourceAbsolutePath)) { 
 +                    return $resourceAbsolutePath
 +                } 
 +            
 +        } 
 +         
 +        if ($this->includePathLookup && ($resourceAbsolutePath = stream_resolve_include_path($resourceRelativePath)) !== false
 +            return $resourceAbsolutePath;
         }         }
 +        
 +        return false;
 +    }
 +    
 +    /**
 +     * Transform resource name into its relative resource path representation.
 +     *
 +     * @params string $resourceName
 +     *
 +     * @return string Resource relative path.
 +     */
 +    private function getResourceRelativePath($resourceName)
 +    {
 +        // We always work with FQCN in this context
 +        $resourceName = ltrim($resourceName, '\\');
 +        $resourcePath = '';
 +        
 +        if (($lastNamespacePosition = strrpos($resourceName, '\\')) !== false) {
 +            // Namespaced resource name
 +            $resourceNamespace = substr($resourceName, 0, $lastNamespacePosition);
 +            $resourceName      = substr($resourceName, $lastNamespacePosition + 1);
 +            $resourcePath      =  str_replace('\\', DIRECTORY_SEPARATOR, $resourceNamespace) . DIRECTORY_SEPARATOR;
 +        }
 +        
 +        return $resourcePath . str_replace('_', DIRECTORY_SEPARATOR . $resourceName) . $this->fileExtension;
 +    }
 +    
 +    /**
 +     * Check if resource is declared in user space.
 +     *
 +     * @params string $resourceName
 +     *
 +     * @return boolean
 +     */
 +    private function isResourceDeclared($resourceName)
 +    {
 +    return class_exists($resourceName, false) 
 +        || interface_exists($resourceName, false) 
 +        || (function_exists('trait_exists') && trait_exists($resourceName, false));
     }     }
 } }
 </code> </code>
  
-===== Example usage ===== +If any interested wants to customize the public methodslike caching through APC to reduce I/Oit should be possible to extend SplClassLoader and overwrite the public methods.
- +
-<code php> +
-$classLoader = new SplClassLoader(\SplClassLoader::MODE_SILENT); +
-$classLoader->setFileExtension('.php'); +
-$classLoader->registerNamespace( +
-    'Doctrine\Common',  +
-    array('/path/to/doctrine''/path/to/another/folder/with/doctrine'+
-); // Namespace style +
-$classLoader->registerPrefix('PEAR_''/path/to/pear'); // PEAR style +
-$classLoader->register(); +
-</code>+
  
 ===== Proposal and Patch ===== ===== Proposal and Patch =====
Line 344: Line 441:
 Main purpose of this proposal is to support both PEAR style directory organization and also Namespace directory organization. Main purpose of this proposal is to support both PEAR style directory organization and also Namespace directory organization.
  
-===== Implementation extension =====+===== Changelog =====
  
-According to new threads in php-standards list, it seems all derived implementations have included these extensions to original support+  * 2011-11-09 Christian KapsUpdate examples to use the new interface(setMode) 
-  * Multiple paths per namespace +  * 2011-11-08 Guilherme Blanco: Removed constructor prototype and created setMode. 
-  * Silent mode as a flag +  * 2011-11-08 Guilherme Blanco: Updated SplClassLoader implementation.
- +
-This turns the RFC specification incompatible with current patchPatch is going to be updated as soon as voting ends. +
- +
-===== Changelog =====+
   * 2011-11-07 Guilherme Blanco: Expanded extended rules.   * 2011-11-07 Guilherme Blanco: Expanded extended rules.
   * 2011-10-25 David Coallier: Added the new SPL patch information and feature request link.   * 2011-10-25 David Coallier: Added the new SPL patch information and feature request link.
Line 358: Line 451:
   * 2010-03-22 Guilherme Blanco: Initial RFC creation.   * 2010-03-22 Guilherme Blanco: Initial RFC creation.
  
 +===== Comments =====
 +  * laruence : I have already got a similar loader implemented in Yaf, called Yaf_Loader, you can found the source here: [[http://svn.php.net/viewvc/pecl/yaf/trunk/yaf_loader.c?view=markup]]
rfc/splclassloader.1320759036.txt.gz · Last modified: 2017/09/22 13:28 (external edit)