Login   Register  
PHP Classes
elePHPant
Icontem

File: autoloader.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Richard Keizer  >  Namespace-aware autoloader  >  autoloader.php  >  Download  
File: autoloader.php
Role: Class source
Content type: text/plain
Description: the class
Class: Namespace-aware autoloader
Load classes automatically supporting namespaces
Author: By
Last change: reindented at last
Date: 3 years ago
Size: 6,690 bytes
 

Contents

Class file image Download
<?php
  
  
/**------------------------------------------------------------------------------
   * File:         autoloader.php
   * Description:  parsing, cacheing and namespace-aware autoloader
   * Version:      1.0
   * Author:       Richard Keizer
   * Email:        ra dot keizer at gmail dot com
   * ------------------------------------------------------------------------------
   * COPYRIGHT (c) 2011 Richard Keizer
   *
   * The source code included in this package is free software; you can
   * redistribute it and/or modify it under the terms of the GNU General Public
   * License as published by the Free Software Foundation. This license can be
   * read at:
   *
   * http://www.opensource.org/licenses/gpl-license.php
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT 
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 
   * ------------------------------------------------------------------------------
   *
   *
   * Usage (see bottom of this file):
   *
   *  Autoloader::create($cachefilename, $excludelist);
   *  e.g:
   *  Autoloader::create('classes.cache', array('3rdparty', '\.svn', '^images$'));
   *
   *
   *  Most autoloaders do nothing more than loading files with names similar to the
   *  classnames. This one does more:
   *  As soon as a class can't be found this autoloader will parse all .php files
   *  in all folders recursively. It extracts a cache containing class/filename pairs.
   *  This is done with respect to the namespace the class is in.
   *  These pairs are then used to include the correct files.
   *
   *
   *
   */  
  
  
  
class ClassCache {
    
    protected 
$items = array();
    
    public function 
__construct($filename) {
      
$this->filename $filename;
      
$this->loadFromFile($filename);
    }
    
    public function 
reset() {
      
$this->items = array();
    }
    
    public function 
isEmpty() {
      return empty(
$this->items);
    }
    
    public function 
addClassFilename($classname$filename) {
      
$classname strtolower($classname);
      
$this->items[$classname] = $filename;               //detect collisions here!
    
}
    
    public function 
getClassFilename($classname) {
      
$classname strtolower($classname);
      return isset(
$this->items[$classname]) ? $this->items[$classname] : null;
    }
    
    public function 
loadFromFile() {
      
$this->items unserialize(@file_get_contents($this->filename));
    }
    
    public function 
saveToFile() {
      
file_put_contents($this->filenameserialize($this->items));
    }
  }
  
  
  
  
  class 
Autoloader {
    protected 
$cache;
    protected 
$excludelist = array();
    protected static 
$instance;
    
    protected function 
__construct($cachefilename$excludelist) {
      
$this->excludelist $excludelist;
      
$this->cache = new ClassCache($cachefilename);
      if (
$this->cache->isEmpty()) $this->rebuildCache();
      
$this->register();
    }
    
    public static function 
create($cachefilename, array $excludelist = array()) {
      if (!isset(
self::$instance)) {
        
$className __CLASS__;
        
self::$instance = new $className($cachefilename$excludelist);
      }
      return 
self::$instance;
    }
        
    protected function 
register() {
      
spl_autoload_register(array($this'autoloadHandler'));
    }
        
    protected function 
autoloadHandler($classname) {
      if (!
$this->includeClass($classname)) {  //on fail...
        
$this->rebuildCache();                        //rebuild cache...
        
$this->includeClass($classname);       //and try again...
      
}
    }
        
    protected function 
includeClass($classname) {
      if (
$filename $this->cache->getClassFilename($classname)) {
        include 
$filename;
        return 
true;
      }
      return 
false;
    }
    
    protected function 
shouldFollow($fsnode) {
      if (!
is_dir($fsnode) || in_array(basename($fsnode), array('.''..'))) return false;
      foreach(
$this->excludelist as $item) if (preg_match("/{$item}/i"basename($fsnode))) return false;
      return 
true;
    }
        
    protected function 
rebuildCache() {
      
$this->cache->reset();
      
      
$stack = array('./');        //push current folder
      
while (!empty($stack)) {
        
        
$folder array_shift($stack);
        
        if (
$h opendir($folder)) {
          while((
$child readdir($h)) !== FALSE) {
            
            if (
$this->shouldFollow($folder.$child)) {
              
array_push($stack$folder.$child.'/');
            } elseif (
is_file($folder.$child) && preg_match("/\.php$/i"$folder.$child)) {
              
$tokens token_get_all(php_strip_whitespace($folder.$child));
              
$namespace '';         //default namespace
              
$status null;
              for(
$i 0$i count($tokens); $i++) {
                switch (
$tokens[$i][0]) {
                  case 
T_NAMESPACE: {
                    
$namespace '';
                    
$status T_NAMESPACE;
                    break;
                  }
                  case 
T_CLASS: {
                    
$classname '';
                    
$status T_CLASS;
                    break;
                  }
                  default: {
                    switch (
$status) {
                      case 
T_NAMESPACE: {
                        if (isset(
$tokens[$i][0]) && $tokens[$i][0] == T_STRING) {
                          
$namespace .= $tokens[$i][1].'\\';
                        } elseif (!
is_array($tokens[$i]) && in_array($tokens[$i], array(';''{'))) {
                          
$status null;
                        }
                        break;
                      }
                        
                      case 
T_CLASS: {
                        if (
is_array($tokens[$i]) && $tokens[$i][0] == T_STRING) {
                          
$this->cache->addClassFilename($namespace.$tokens[$i][1], $folder.$child);
                          
$status null;
                        }
                        break;                    
                      }
                    } 
//switch status
                  
//case token default
                
//switch token
              
//end for
            
}
          }
        }
      }
      
$this->cache->saveToFile();
    }
  }
  
  
  
Autoloader::create('classes.cache', array('3rdparty''\.svn''^images$'));