File indexing completed on 2024-12-22 05:36:49

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Loader
0017  * @subpackage Autoloader
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @version    $Id$
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  */
0022 
0023 /** Zend_Loader_Autoloader_Interface */
0024 // require_once 'Zend/Loader/Autoloader/Interface.php';
0025 
0026 /**
0027  * Resource loader
0028  *
0029  * @uses       Zend_Loader_Autoloader_Interface
0030  * @package    Zend_Loader
0031  * @subpackage Autoloader
0032  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0033  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0034  */
0035 class Zend_Loader_Autoloader_Resource implements Zend_Loader_Autoloader_Interface
0036 {
0037     /**
0038      * @var string Base path to resource classes
0039      */
0040     protected $_basePath;
0041 
0042     /**
0043      * @var array Components handled within this resource
0044      */
0045     protected $_components = array();
0046 
0047     /**
0048      * @var string Default resource/component to use when using object registry
0049      */
0050     protected $_defaultResourceType;
0051 
0052     /**
0053      * @var string Namespace of classes within this resource
0054      */
0055     protected $_namespace;
0056 
0057     /**
0058      * @var array Available resource types handled by this resource autoloader
0059      */
0060     protected $_resourceTypes = array();
0061 
0062     /**
0063      * Constructor
0064      *
0065      * @param  array|Zend_Config $options Configuration options for resource autoloader
0066      * @return void
0067      */
0068     public function __construct($options)
0069     {
0070         if ($options instanceof Zend_Config) {
0071             $options = $options->toArray();
0072         }
0073         if (!is_array($options)) {
0074             // require_once 'Zend/Loader/Exception.php';
0075             throw new Zend_Loader_Exception('Options must be passed to resource loader constructor');
0076         }
0077 
0078         $this->setOptions($options);
0079 
0080         $namespace = $this->getNamespace();
0081         if ((null === $namespace)
0082             || (null === $this->getBasePath())
0083         ) {
0084             // require_once 'Zend/Loader/Exception.php';
0085             throw new Zend_Loader_Exception('Resource loader requires both a namespace and a base path for initialization');
0086         }
0087 
0088         if (!empty($namespace)) {
0089             $namespace .= '_';
0090         }
0091         // require_once 'Zend/Loader/Autoloader.php';
0092         Zend_Loader_Autoloader::getInstance()->unshiftAutoloader($this, $namespace);
0093     }
0094 
0095     /**
0096      * Overloading: methods
0097      *
0098      * Allow retrieving concrete resource object instances using 'get<Resourcename>()'
0099      * syntax. Example:
0100      * <code>
0101      * $loader = new Zend_Loader_Autoloader_Resource(array(
0102      *     'namespace' => 'Stuff_',
0103      *     'basePath'  => '/path/to/some/stuff',
0104      * ))
0105      * $loader->addResourceType('Model', 'models', 'Model');
0106      *
0107      * $foo = $loader->getModel('Foo'); // get instance of Stuff_Model_Foo class
0108      * </code>
0109      *
0110      * @param  string $method
0111      * @param  array $args
0112      * @return mixed
0113      * @throws Zend_Loader_Exception if method not beginning with 'get' or not matching a valid resource type is called
0114      */
0115     public function __call($method, $args)
0116     {
0117         if ('get' == substr($method, 0, 3)) {
0118             $type  = strtolower(substr($method, 3));
0119             if (!$this->hasResourceType($type)) {
0120                 // require_once 'Zend/Loader/Exception.php';
0121                 throw new Zend_Loader_Exception("Invalid resource type $type; cannot load resource");
0122             }
0123             if (empty($args)) {
0124                 // require_once 'Zend/Loader/Exception.php';
0125                 throw new Zend_Loader_Exception("Cannot load resources; no resource specified");
0126             }
0127             $resource = array_shift($args);
0128             return $this->load($resource, $type);
0129         }
0130 
0131         // require_once 'Zend/Loader/Exception.php';
0132         throw new Zend_Loader_Exception("Method '$method' is not supported");
0133     }
0134 
0135     /**
0136      * Helper method to calculate the correct class path
0137      *
0138      * @param string $class
0139      * @return False if not matched other wise the correct path
0140      */
0141     public function getClassPath($class)
0142     {
0143         $segments          = explode('_', $class);
0144         $namespaceTopLevel = $this->getNamespace();
0145         $namespace         = '';
0146 
0147         if (!empty($namespaceTopLevel)) {
0148             $namespace = array();
0149             $topLevelSegments = count(explode('_', $namespaceTopLevel));
0150             for ($i = 0; $i < $topLevelSegments; $i++) {
0151                 $namespace[] = array_shift($segments);
0152             }
0153             $namespace = implode('_', $namespace);
0154             if ($namespace != $namespaceTopLevel) {
0155                 // wrong prefix? we're done
0156                 return false;
0157             }
0158         }
0159 
0160         if (count($segments) < 2) {
0161             // assumes all resources have a component and class name, minimum
0162             return false;
0163         }
0164 
0165         $final     = array_pop($segments);
0166         $component = $namespace;
0167         $lastMatch = false;
0168         do {
0169             $segment    = array_shift($segments);
0170             $component .= empty($component) ? $segment : '_' . $segment;
0171             if (isset($this->_components[$component])) {
0172                 $lastMatch = $component;
0173             }
0174         } while (count($segments));
0175 
0176         if (!$lastMatch) {
0177             return false;
0178         }
0179 
0180         $final = substr($class, strlen($lastMatch) + 1);
0181         $path = $this->_components[$lastMatch];
0182         $classPath = $path . '/' . str_replace('_', '/', $final) . '.php';
0183 
0184         if (Zend_Loader::isReadable($classPath)) {
0185             return $classPath;
0186         }
0187 
0188         return false;
0189     }
0190 
0191     /**
0192      * Attempt to autoload a class
0193      *
0194      * @param  string $class
0195      * @return mixed False if not matched, otherwise result if include operation
0196      */
0197     public function autoload($class)
0198     {
0199         $classPath = $this->getClassPath($class);
0200         if (false !== $classPath) {
0201             return include $classPath;
0202         }
0203         return false;
0204     }
0205 
0206     /**
0207      * Set class state from options
0208      *
0209      * @param  array $options
0210      * @return Zend_Loader_Autoloader_Resource
0211      */
0212     public function setOptions(array $options)
0213     {
0214         // Set namespace first, see ZF-10836
0215         if (isset($options['namespace'])) {
0216             $this->setNamespace($options['namespace']);
0217             unset($options['namespace']);
0218         }
0219 
0220         $methods = get_class_methods($this);
0221         foreach ($options as $key => $value) {
0222             $method = 'set' . ucfirst($key);
0223             if (in_array($method, $methods)) {
0224                 $this->$method($value);
0225             }
0226         }
0227         return $this;
0228     }
0229 
0230     /**
0231      * Set namespace that this autoloader handles
0232      *
0233      * @param  string $namespace
0234      * @return Zend_Loader_Autoloader_Resource
0235      */
0236     public function setNamespace($namespace)
0237     {
0238         $this->_namespace = rtrim((string) $namespace, '_');
0239         return $this;
0240     }
0241 
0242     /**
0243      * Get namespace this autoloader handles
0244      *
0245      * @return string
0246      */
0247     public function getNamespace()
0248     {
0249         return $this->_namespace;
0250     }
0251 
0252     /**
0253      * Set base path for this set of resources
0254      *
0255      * @param  string $path
0256      * @return Zend_Loader_Autoloader_Resource
0257      */
0258     public function setBasePath($path)
0259     {
0260         $this->_basePath = (string) $path;
0261         return $this;
0262     }
0263 
0264     /**
0265      * Get base path to this set of resources
0266      *
0267      * @return string
0268      */
0269     public function getBasePath()
0270     {
0271         return $this->_basePath;
0272     }
0273 
0274     /**
0275      * Add resource type
0276      *
0277      * @param  string $type identifier for the resource type being loaded
0278      * @param  string $path path relative to resource base path containing the resource types
0279      * @param  null|string $namespace sub-component namespace to append to base namespace that qualifies this resource type
0280      * @return Zend_Loader_Autoloader_Resource
0281      */
0282     public function addResourceType($type, $path, $namespace = null)
0283     {
0284         $type = strtolower($type);
0285         if (!isset($this->_resourceTypes[$type])) {
0286             if (null === $namespace) {
0287                 // require_once 'Zend/Loader/Exception.php';
0288                 throw new Zend_Loader_Exception('Initial definition of a resource type must include a namespace');
0289             }
0290             $namespaceTopLevel = $this->getNamespace();
0291             $namespace = ucfirst(trim($namespace, '_'));
0292             $this->_resourceTypes[$type] = array(
0293                 'namespace' => empty($namespaceTopLevel) ? $namespace : $namespaceTopLevel . '_' . $namespace,
0294             );
0295         }
0296         if (!is_string($path)) {
0297             // require_once 'Zend/Loader/Exception.php';
0298             throw new Zend_Loader_Exception('Invalid path specification provided; must be string');
0299         }
0300         $this->_resourceTypes[$type]['path'] = $this->getBasePath() . '/' . rtrim($path, '\/');
0301 
0302         $component = $this->_resourceTypes[$type]['namespace'];
0303         $this->_components[$component] = $this->_resourceTypes[$type]['path'];
0304         return $this;
0305     }
0306 
0307     /**
0308      * Add multiple resources at once
0309      *
0310      * $types should be an associative array of resource type => specification
0311      * pairs. Each specification should be an associative array containing
0312      * minimally the 'path' key (specifying the path relative to the resource
0313      * base path) and optionally the 'namespace' key (indicating the subcomponent
0314      * namespace to append to the resource namespace).
0315      *
0316      * As an example:
0317      * <code>
0318      * $loader->addResourceTypes(array(
0319      *     'model' => array(
0320      *         'path'      => 'models',
0321      *         'namespace' => 'Model',
0322      *     ),
0323      *     'form' => array(
0324      *         'path'      => 'forms',
0325      *         'namespace' => 'Form',
0326      *     ),
0327      * ));
0328      * </code>
0329      *
0330      * @param  array $types
0331      * @return Zend_Loader_Autoloader_Resource
0332      */
0333     public function addResourceTypes(array $types)
0334     {
0335         foreach ($types as $type => $spec) {
0336             if (!is_array($spec)) {
0337                 // require_once 'Zend/Loader/Exception.php';
0338                 throw new Zend_Loader_Exception('addResourceTypes() expects an array of arrays');
0339             }
0340             if (!isset($spec['path'])) {
0341                 // require_once 'Zend/Loader/Exception.php';
0342                 throw new Zend_Loader_Exception('addResourceTypes() expects each array to include a paths element');
0343             }
0344             $paths  = $spec['path'];
0345             $namespace = null;
0346             if (isset($spec['namespace'])) {
0347                 $namespace = $spec['namespace'];
0348             }
0349             $this->addResourceType($type, $paths, $namespace);
0350         }
0351         return $this;
0352     }
0353 
0354     /**
0355      * Overwrite existing and set multiple resource types at once
0356      *
0357      * @see    Zend_Loader_Autoloader_Resource::addResourceTypes()
0358      * @param  array $types
0359      * @return Zend_Loader_Autoloader_Resource
0360      */
0361     public function setResourceTypes(array $types)
0362     {
0363         $this->clearResourceTypes();
0364         return $this->addResourceTypes($types);
0365     }
0366 
0367     /**
0368      * Retrieve resource type mappings
0369      *
0370      * @return array
0371      */
0372     public function getResourceTypes()
0373     {
0374         return $this->_resourceTypes;
0375     }
0376 
0377     /**
0378      * Is the requested resource type defined?
0379      *
0380      * @param  string $type
0381      * @return bool
0382      */
0383     public function hasResourceType($type)
0384     {
0385         return isset($this->_resourceTypes[$type]);
0386     }
0387 
0388     /**
0389      * Remove the requested resource type
0390      *
0391      * @param  string $type
0392      * @return Zend_Loader_Autoloader_Resource
0393      */
0394     public function removeResourceType($type)
0395     {
0396         if ($this->hasResourceType($type)) {
0397             $namespace = $this->_resourceTypes[$type]['namespace'];
0398             unset($this->_components[$namespace]);
0399             unset($this->_resourceTypes[$type]);
0400         }
0401         return $this;
0402     }
0403 
0404     /**
0405      * Clear all resource types
0406      *
0407      * @return Zend_Loader_Autoloader_Resource
0408      */
0409     public function clearResourceTypes()
0410     {
0411         $this->_resourceTypes = array();
0412         $this->_components    = array();
0413         return $this;
0414     }
0415 
0416     /**
0417      * Set default resource type to use when calling load()
0418      *
0419      * @param  string $type
0420      * @return Zend_Loader_Autoloader_Resource
0421      */
0422     public function setDefaultResourceType($type)
0423     {
0424         if ($this->hasResourceType($type)) {
0425             $this->_defaultResourceType = $type;
0426         }
0427         return $this;
0428     }
0429 
0430     /**
0431      * Get default resource type to use when calling load()
0432      *
0433      * @return string|null
0434      */
0435     public function getDefaultResourceType()
0436     {
0437         return $this->_defaultResourceType;
0438     }
0439 
0440     /**
0441      * Object registry and factory
0442      *
0443      * Loads the requested resource of type $type (or uses the default resource
0444      * type if none provided). If the resource has been loaded previously,
0445      * returns the previous instance; otherwise, instantiates it.
0446      *
0447      * @param  string $resource
0448      * @param  string $type
0449      * @return object
0450      * @throws Zend_Loader_Exception if resource type not specified or invalid
0451      */
0452     public function load($resource, $type = null)
0453     {
0454         if (null === $type) {
0455             $type = $this->getDefaultResourceType();
0456             if (empty($type)) {
0457                 // require_once 'Zend/Loader/Exception.php';
0458                 throw new Zend_Loader_Exception('No resource type specified');
0459             }
0460         }
0461         if (!$this->hasResourceType($type)) {
0462             // require_once 'Zend/Loader/Exception.php';
0463             throw new Zend_Loader_Exception('Invalid resource type specified');
0464         }
0465         $namespace = $this->_resourceTypes[$type]['namespace'];
0466         $class     = $namespace . '_' . ucfirst($resource);
0467         if (!isset($this->_resources[$class])) {
0468             $this->_resources[$class] = new $class;
0469         }
0470         return $this->_resources[$class];
0471     }
0472 }