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 }