web/lib/Zend/Loader/Autoloader/Resource.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Loader
       
    17  * @subpackage Autoloader
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @version    $Id: Resource.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    20  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    21  */
       
    22 
       
    23 /** Zend_Loader_Autoloader_Interface */
       
    24 require_once 'Zend/Loader/Autoloader/Interface.php';
       
    25 
       
    26 /**
       
    27  * Resource loader
       
    28  *
       
    29  * @uses       Zend_Loader_Autoloader_Interface
       
    30  * @package    Zend_Loader
       
    31  * @subpackage Autoloader
       
    32  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    33  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    34  */
       
    35 class Zend_Loader_Autoloader_Resource implements Zend_Loader_Autoloader_Interface
       
    36 {
       
    37     /**
       
    38      * @var string Base path to resource classes
       
    39      */
       
    40     protected $_basePath;
       
    41 
       
    42     /**
       
    43      * @var array Components handled within this resource
       
    44      */
       
    45     protected $_components = array();
       
    46 
       
    47     /**
       
    48      * @var string Default resource/component to use when using object registry
       
    49      */
       
    50     protected $_defaultResourceType;
       
    51 
       
    52     /**
       
    53      * @var string Namespace of classes within this resource
       
    54      */
       
    55     protected $_namespace;
       
    56 
       
    57     /**
       
    58      * @var array Available resource types handled by this resource autoloader
       
    59      */
       
    60     protected $_resourceTypes = array();
       
    61 
       
    62     /**
       
    63      * Constructor
       
    64      *
       
    65      * @param  array|Zend_Config $options Configuration options for resource autoloader
       
    66      * @return void
       
    67      */
       
    68     public function __construct($options)
       
    69     {
       
    70         if ($options instanceof Zend_Config) {
       
    71             $options = $options->toArray();
       
    72         }
       
    73         if (!is_array($options)) {
       
    74             require_once 'Zend/Loader/Exception.php';
       
    75             throw new Zend_Loader_Exception('Options must be passed to resource loader constructor');
       
    76         }
       
    77 
       
    78         $this->setOptions($options);
       
    79 
       
    80         $namespace = $this->getNamespace();
       
    81         if ((null === $namespace)
       
    82             || (null === $this->getBasePath())
       
    83         ) {
       
    84             require_once 'Zend/Loader/Exception.php';
       
    85             throw new Zend_Loader_Exception('Resource loader requires both a namespace and a base path for initialization');
       
    86         }
       
    87 
       
    88         if (!empty($namespace)) {
       
    89             $namespace .= '_';
       
    90         }
       
    91         Zend_Loader_Autoloader::getInstance()->unshiftAutoloader($this, $namespace);
       
    92     }
       
    93 
       
    94     /**
       
    95      * Overloading: methods
       
    96      *
       
    97      * Allow retrieving concrete resource object instances using 'get<Resourcename>()'
       
    98      * syntax. Example:
       
    99      * <code>
       
   100      * $loader = new Zend_Loader_Autoloader_Resource(array(
       
   101      *     'namespace' => 'Stuff_',
       
   102      *     'basePath'  => '/path/to/some/stuff',
       
   103      * ))
       
   104      * $loader->addResourceType('Model', 'models', 'Model');
       
   105      *
       
   106      * $foo = $loader->getModel('Foo'); // get instance of Stuff_Model_Foo class
       
   107      * </code>
       
   108      *
       
   109      * @param  string $method
       
   110      * @param  array $args
       
   111      * @return mixed
       
   112      * @throws Zend_Loader_Exception if method not beginning with 'get' or not matching a valid resource type is called
       
   113      */
       
   114     public function __call($method, $args)
       
   115     {
       
   116         if ('get' == substr($method, 0, 3)) {
       
   117             $type  = strtolower(substr($method, 3));
       
   118             if (!$this->hasResourceType($type)) {
       
   119                 require_once 'Zend/Loader/Exception.php';
       
   120                 throw new Zend_Loader_Exception("Invalid resource type $type; cannot load resource");
       
   121             }
       
   122             if (empty($args)) {
       
   123                 require_once 'Zend/Loader/Exception.php';
       
   124                 throw new Zend_Loader_Exception("Cannot load resources; no resource specified");
       
   125             }
       
   126             $resource = array_shift($args);
       
   127             return $this->load($resource, $type);
       
   128         }
       
   129 
       
   130         require_once 'Zend/Loader/Exception.php';
       
   131         throw new Zend_Loader_Exception("Method '$method' is not supported");
       
   132     }
       
   133 
       
   134     /**
       
   135      * Helper method to calculate the correct class path
       
   136      *
       
   137      * @param string $class
       
   138      * @return False if not matched other wise the correct path
       
   139      */
       
   140     public function getClassPath($class)
       
   141     {
       
   142         $segments          = explode('_', $class);
       
   143         $namespaceTopLevel = $this->getNamespace();
       
   144         $namespace         = '';
       
   145 
       
   146         if (!empty($namespaceTopLevel)) {
       
   147             $namespace = array_shift($segments);
       
   148             if ($namespace != $namespaceTopLevel) {
       
   149                 // wrong prefix? we're done
       
   150                 return false;
       
   151             }
       
   152         }
       
   153 
       
   154         if (count($segments) < 2) {
       
   155             // assumes all resources have a component and class name, minimum
       
   156             return false;
       
   157         }
       
   158 
       
   159         $final     = array_pop($segments);
       
   160         $component = $namespace;
       
   161         $lastMatch = false;
       
   162         do {
       
   163             $segment    = array_shift($segments);
       
   164             $component .= empty($component) ? $segment : '_' . $segment;
       
   165             if (isset($this->_components[$component])) {
       
   166                 $lastMatch = $component;
       
   167             }
       
   168         } while (count($segments));
       
   169 
       
   170         if (!$lastMatch) {
       
   171             return false;
       
   172         }
       
   173 
       
   174         $final = substr($class, strlen($lastMatch) + 1);
       
   175         $path = $this->_components[$lastMatch];
       
   176         $classPath = $path . '/' . str_replace('_', '/', $final) . '.php';
       
   177 
       
   178         if (Zend_Loader::isReadable($classPath)) {
       
   179             return $classPath;
       
   180         }
       
   181 
       
   182         return false;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Attempt to autoload a class
       
   187      *
       
   188      * @param  string $class
       
   189      * @return mixed False if not matched, otherwise result if include operation
       
   190      */
       
   191     public function autoload($class)
       
   192     {
       
   193         $classPath = $this->getClassPath($class);
       
   194         if (false !== $classPath) {
       
   195             return include $classPath;
       
   196         }
       
   197         return false;
       
   198     }
       
   199 
       
   200     /**
       
   201      * Set class state from options
       
   202      *
       
   203      * @param  array $options
       
   204      * @return Zend_Loader_Autoloader_Resource
       
   205      */
       
   206     public function setOptions(array $options)
       
   207     {
       
   208         $methods = get_class_methods($this);
       
   209         foreach ($options as $key => $value) {
       
   210             $method = 'set' . ucfirst($key);
       
   211             if (in_array($method, $methods)) {
       
   212                 $this->$method($value);
       
   213             }
       
   214         }
       
   215         return $this;
       
   216     }
       
   217 
       
   218     /**
       
   219      * Set namespace that this autoloader handles
       
   220      *
       
   221      * @param  string $namespace
       
   222      * @return Zend_Loader_Autoloader_Resource
       
   223      */
       
   224     public function setNamespace($namespace)
       
   225     {
       
   226         $this->_namespace = rtrim((string) $namespace, '_');
       
   227         return $this;
       
   228     }
       
   229 
       
   230     /**
       
   231      * Get namespace this autoloader handles
       
   232      *
       
   233      * @return string
       
   234      */
       
   235     public function getNamespace()
       
   236     {
       
   237         return $this->_namespace;
       
   238     }
       
   239 
       
   240     /**
       
   241      * Set base path for this set of resources
       
   242      *
       
   243      * @param  string $path
       
   244      * @return Zend_Loader_Autoloader_Resource
       
   245      */
       
   246     public function setBasePath($path)
       
   247     {
       
   248         $this->_basePath = (string) $path;
       
   249         return $this;
       
   250     }
       
   251 
       
   252     /**
       
   253      * Get base path to this set of resources
       
   254      *
       
   255      * @return string
       
   256      */
       
   257     public function getBasePath()
       
   258     {
       
   259         return $this->_basePath;
       
   260     }
       
   261 
       
   262     /**
       
   263      * Add resource type
       
   264      *
       
   265      * @param  string $type identifier for the resource type being loaded
       
   266      * @param  string $path path relative to resource base path containing the resource types
       
   267      * @param  null|string $namespace sub-component namespace to append to base namespace that qualifies this resource type
       
   268      * @return Zend_Loader_Autoloader_Resource
       
   269      */
       
   270     public function addResourceType($type, $path, $namespace = null)
       
   271     {
       
   272         $type = strtolower($type);
       
   273         if (!isset($this->_resourceTypes[$type])) {
       
   274             if (null === $namespace) {
       
   275                 require_once 'Zend/Loader/Exception.php';
       
   276                 throw new Zend_Loader_Exception('Initial definition of a resource type must include a namespace');
       
   277             }
       
   278             $namespaceTopLevel = $this->getNamespace();
       
   279             $namespace = ucfirst(trim($namespace, '_'));
       
   280             $this->_resourceTypes[$type] = array(
       
   281                 'namespace' => empty($namespaceTopLevel) ? $namespace : $namespaceTopLevel . '_' . $namespace,
       
   282             );
       
   283         }
       
   284         if (!is_string($path)) {
       
   285             require_once 'Zend/Loader/Exception.php';
       
   286             throw new Zend_Loader_Exception('Invalid path specification provided; must be string');
       
   287         }
       
   288         $this->_resourceTypes[$type]['path'] = $this->getBasePath() . '/' . rtrim($path, '\/');
       
   289 
       
   290         $component = $this->_resourceTypes[$type]['namespace'];
       
   291         $this->_components[$component] = $this->_resourceTypes[$type]['path'];
       
   292         return $this;
       
   293     }
       
   294 
       
   295     /**
       
   296      * Add multiple resources at once
       
   297      *
       
   298      * $types should be an associative array of resource type => specification
       
   299      * pairs. Each specification should be an associative array containing
       
   300      * minimally the 'path' key (specifying the path relative to the resource
       
   301      * base path) and optionally the 'namespace' key (indicating the subcomponent
       
   302      * namespace to append to the resource namespace).
       
   303      *
       
   304      * As an example:
       
   305      * <code>
       
   306      * $loader->addResourceTypes(array(
       
   307      *     'model' => array(
       
   308      *         'path'      => 'models',
       
   309      *         'namespace' => 'Model',
       
   310      *     ),
       
   311      *     'form' => array(
       
   312      *         'path'      => 'forms',
       
   313      *         'namespace' => 'Form',
       
   314      *     ),
       
   315      * ));
       
   316      * </code>
       
   317      *
       
   318      * @param  array $types
       
   319      * @return Zend_Loader_Autoloader_Resource
       
   320      */
       
   321     public function addResourceTypes(array $types)
       
   322     {
       
   323         foreach ($types as $type => $spec) {
       
   324             if (!is_array($spec)) {
       
   325                 require_once 'Zend/Loader/Exception.php';
       
   326                 throw new Zend_Loader_Exception('addResourceTypes() expects an array of arrays');
       
   327             }
       
   328             if (!isset($spec['path'])) {
       
   329                 require_once 'Zend/Loader/Exception.php';
       
   330                 throw new Zend_Loader_Exception('addResourceTypes() expects each array to include a paths element');
       
   331             }
       
   332             $paths  = $spec['path'];
       
   333             $namespace = null;
       
   334             if (isset($spec['namespace'])) {
       
   335                 $namespace = $spec['namespace'];
       
   336             }
       
   337             $this->addResourceType($type, $paths, $namespace);
       
   338         }
       
   339         return $this;
       
   340     }
       
   341 
       
   342     /**
       
   343      * Overwrite existing and set multiple resource types at once
       
   344      *
       
   345      * @see    Zend_Loader_Autoloader_Resource::addResourceTypes()
       
   346      * @param  array $types
       
   347      * @return Zend_Loader_Autoloader_Resource
       
   348      */
       
   349     public function setResourceTypes(array $types)
       
   350     {
       
   351         $this->clearResourceTypes();
       
   352         return $this->addResourceTypes($types);
       
   353     }
       
   354 
       
   355     /**
       
   356      * Retrieve resource type mappings
       
   357      *
       
   358      * @return array
       
   359      */
       
   360     public function getResourceTypes()
       
   361     {
       
   362         return $this->_resourceTypes;
       
   363     }
       
   364 
       
   365     /**
       
   366      * Is the requested resource type defined?
       
   367      *
       
   368      * @param  string $type
       
   369      * @return bool
       
   370      */
       
   371     public function hasResourceType($type)
       
   372     {
       
   373         return isset($this->_resourceTypes[$type]);
       
   374     }
       
   375 
       
   376     /**
       
   377      * Remove the requested resource type
       
   378      *
       
   379      * @param  string $type
       
   380      * @return Zend_Loader_Autoloader_Resource
       
   381      */
       
   382     public function removeResourceType($type)
       
   383     {
       
   384         if ($this->hasResourceType($type)) {
       
   385             $namespace = $this->_resourceTypes[$type]['namespace'];
       
   386             unset($this->_components[$namespace]);
       
   387             unset($this->_resourceTypes[$type]);
       
   388         }
       
   389         return $this;
       
   390     }
       
   391 
       
   392     /**
       
   393      * Clear all resource types
       
   394      *
       
   395      * @return Zend_Loader_Autoloader_Resource
       
   396      */
       
   397     public function clearResourceTypes()
       
   398     {
       
   399         $this->_resourceTypes = array();
       
   400         $this->_components    = array();
       
   401         return $this;
       
   402     }
       
   403 
       
   404     /**
       
   405      * Set default resource type to use when calling load()
       
   406      *
       
   407      * @param  string $type
       
   408      * @return Zend_Loader_Autoloader_Resource
       
   409      */
       
   410     public function setDefaultResourceType($type)
       
   411     {
       
   412         if ($this->hasResourceType($type)) {
       
   413             $this->_defaultResourceType = $type;
       
   414         }
       
   415         return $this;
       
   416     }
       
   417 
       
   418     /**
       
   419      * Get default resource type to use when calling load()
       
   420      *
       
   421      * @return string|null
       
   422      */
       
   423     public function getDefaultResourceType()
       
   424     {
       
   425         return $this->_defaultResourceType;
       
   426     }
       
   427 
       
   428     /**
       
   429      * Object registry and factory
       
   430      *
       
   431      * Loads the requested resource of type $type (or uses the default resource
       
   432      * type if none provided). If the resource has been loaded previously,
       
   433      * returns the previous instance; otherwise, instantiates it.
       
   434      *
       
   435      * @param  string $resource
       
   436      * @param  string $type
       
   437      * @return object
       
   438      * @throws Zend_Loader_Exception if resource type not specified or invalid
       
   439      */
       
   440     public function load($resource, $type = null)
       
   441     {
       
   442         if (null === $type) {
       
   443             $type = $this->getDefaultResourceType();
       
   444             if (empty($type)) {
       
   445                 require_once 'Zend/Loader/Exception.php';
       
   446                 throw new Zend_Loader_Exception('No resource type specified');
       
   447             }
       
   448         }
       
   449         if (!$this->hasResourceType($type)) {
       
   450             require_once 'Zend/Loader/Exception.php';
       
   451             throw new Zend_Loader_Exception('Invalid resource type specified');
       
   452         }
       
   453         $namespace = $this->_resourceTypes[$type]['namespace'];
       
   454         $class     = $namespace . '_' . ucfirst($resource);
       
   455         if (!isset($this->_resources[$class])) {
       
   456             $this->_resources[$class] = new $class;
       
   457         }
       
   458         return $this->_resources[$class];
       
   459     }
       
   460 }