<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@zend.com so we can send you a copy immediately. * * @category Zend * @package Zend_Loader * @subpackage Autoloader * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @version $Id$ * @license http://framework.zend.com/license/new-bsd New BSD License */ /** Zend_Loader_Autoloader_Interface */ require_once 'Zend/Loader/Autoloader/Interface.php'; /** * Resource loader * * @uses Zend_Loader_Autoloader_Interface * @package Zend_Loader * @subpackage Autoloader * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Loader_Autoloader_Resource implements Zend_Loader_Autoloader_Interface { /** * @var string Base path to resource classes */ protected $_basePath; /** * @var array Components handled within this resource */ protected $_components = array(); /** * @var string Default resource/component to use when using object registry */ protected $_defaultResourceType; /** * @var string Namespace of classes within this resource */ protected $_namespace; /** * @var array Available resource types handled by this resource autoloader */ protected $_resourceTypes = array(); /** * Constructor * * @param array|Zend_Config $options Configuration options for resource autoloader * @return void */ public function __construct($options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (!is_array($options)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('Options must be passed to resource loader constructor'); } $this->setOptions($options); $namespace = $this->getNamespace(); if ((null === $namespace) || (null === $this->getBasePath()) ) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('Resource loader requires both a namespace and a base path for initialization'); } if (!empty($namespace)) { $namespace .= '_'; } require_once 'Zend/Loader/Autoloader.php'; Zend_Loader_Autoloader::getInstance()->unshiftAutoloader($this, $namespace); } /** * Overloading: methods * * Allow retrieving concrete resource object instances using 'get<Resourcename>()' * syntax. Example: * <code> * $loader = new Zend_Loader_Autoloader_Resource(array( * 'namespace' => 'Stuff_', * 'basePath' => '/path/to/some/stuff', * )) * $loader->addResourceType('Model', 'models', 'Model'); * * $foo = $loader->getModel('Foo'); // get instance of Stuff_Model_Foo class * </code> * * @param string $method * @param array $args * @return mixed * @throws Zend_Loader_Exception if method not beginning with 'get' or not matching a valid resource type is called */ public function __call($method, $args) { if ('get' == substr($method, 0, 3)) { $type = strtolower(substr($method, 3)); if (!$this->hasResourceType($type)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception("Invalid resource type $type; cannot load resource"); } if (empty($args)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception("Cannot load resources; no resource specified"); } $resource = array_shift($args); return $this->load($resource, $type); } require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception("Method '$method' is not supported"); } /** * Helper method to calculate the correct class path * * @param string $class * @return False if not matched other wise the correct path */ public function getClassPath($class) { $segments = explode('_', $class); $namespaceTopLevel = $this->getNamespace(); $namespace = ''; if (!empty($namespaceTopLevel)) { $namespace = array(); $topLevelSegments = count(explode('_', $namespaceTopLevel)); for ($i = 0; $i < $topLevelSegments; $i++) { $namespace[] = array_shift($segments); } $namespace = implode('_', $namespace); if ($namespace != $namespaceTopLevel) { // wrong prefix? we're done return false; } } if (count($segments) < 2) { // assumes all resources have a component and class name, minimum return false; } $final = array_pop($segments); $component = $namespace; $lastMatch = false; do { $segment = array_shift($segments); $component .= empty($component) ? $segment : '_' . $segment; if (isset($this->_components[$component])) { $lastMatch = $component; } } while (count($segments)); if (!$lastMatch) { return false; } $final = substr($class, strlen($lastMatch) + 1); $path = $this->_components[$lastMatch]; $classPath = $path . '/' . str_replace('_', '/', $final) . '.php'; if (Zend_Loader::isReadable($classPath)) { return $classPath; } return false; } /** * Attempt to autoload a class * * @param string $class * @return mixed False if not matched, otherwise result if include operation */ public function autoload($class) { $classPath = $this->getClassPath($class); if (false !== $classPath) { return include $classPath; } return false; } /** * Set class state from options * * @param array $options * @return Zend_Loader_Autoloader_Resource */ public function setOptions(array $options) { // Set namespace first, see ZF-10836 if (isset($options['namespace'])) { $this->setNamespace($options['namespace']); unset($options['namespace']); } $methods = get_class_methods($this); foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (in_array($method, $methods)) { $this->$method($value); } } return $this; } /** * Set namespace that this autoloader handles * * @param string $namespace * @return Zend_Loader_Autoloader_Resource */ public function setNamespace($namespace) { $this->_namespace = rtrim((string) $namespace, '_'); return $this; } /** * Get namespace this autoloader handles * * @return string */ public function getNamespace() { return $this->_namespace; } /** * Set base path for this set of resources * * @param string $path * @return Zend_Loader_Autoloader_Resource */ public function setBasePath($path) { $this->_basePath = (string) $path; return $this; } /** * Get base path to this set of resources * * @return string */ public function getBasePath() { return $this->_basePath; } /** * Add resource type * * @param string $type identifier for the resource type being loaded * @param string $path path relative to resource base path containing the resource types * @param null|string $namespace sub-component namespace to append to base namespace that qualifies this resource type * @return Zend_Loader_Autoloader_Resource */ public function addResourceType($type, $path, $namespace = null) { $type = strtolower($type); if (!isset($this->_resourceTypes[$type])) { if (null === $namespace) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('Initial definition of a resource type must include a namespace'); } $namespaceTopLevel = $this->getNamespace(); $namespace = ucfirst(trim($namespace, '_')); $this->_resourceTypes[$type] = array( 'namespace' => empty($namespaceTopLevel) ? $namespace : $namespaceTopLevel . '_' . $namespace, ); } if (!is_string($path)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('Invalid path specification provided; must be string'); } $this->_resourceTypes[$type]['path'] = $this->getBasePath() . '/' . rtrim($path, '\/'); $component = $this->_resourceTypes[$type]['namespace']; $this->_components[$component] = $this->_resourceTypes[$type]['path']; return $this; } /** * Add multiple resources at once * * $types should be an associative array of resource type => specification * pairs. Each specification should be an associative array containing * minimally the 'path' key (specifying the path relative to the resource * base path) and optionally the 'namespace' key (indicating the subcomponent * namespace to append to the resource namespace). * * As an example: * <code> * $loader->addResourceTypes(array( * 'model' => array( * 'path' => 'models', * 'namespace' => 'Model', * ), * 'form' => array( * 'path' => 'forms', * 'namespace' => 'Form', * ), * )); * </code> * * @param array $types * @return Zend_Loader_Autoloader_Resource */ public function addResourceTypes(array $types) { foreach ($types as $type => $spec) { if (!is_array($spec)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('addResourceTypes() expects an array of arrays'); } if (!isset($spec['path'])) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('addResourceTypes() expects each array to include a paths element'); } $paths = $spec['path']; $namespace = null; if (isset($spec['namespace'])) { $namespace = $spec['namespace']; } $this->addResourceType($type, $paths, $namespace); } return $this; } /** * Overwrite existing and set multiple resource types at once * * @see Zend_Loader_Autoloader_Resource::addResourceTypes() * @param array $types * @return Zend_Loader_Autoloader_Resource */ public function setResourceTypes(array $types) { $this->clearResourceTypes(); return $this->addResourceTypes($types); } /** * Retrieve resource type mappings * * @return array */ public function getResourceTypes() { return $this->_resourceTypes; } /** * Is the requested resource type defined? * * @param string $type * @return bool */ public function hasResourceType($type) { return isset($this->_resourceTypes[$type]); } /** * Remove the requested resource type * * @param string $type * @return Zend_Loader_Autoloader_Resource */ public function removeResourceType($type) { if ($this->hasResourceType($type)) { $namespace = $this->_resourceTypes[$type]['namespace']; unset($this->_components[$namespace]); unset($this->_resourceTypes[$type]); } return $this; } /** * Clear all resource types * * @return Zend_Loader_Autoloader_Resource */ public function clearResourceTypes() { $this->_resourceTypes = array(); $this->_components = array(); return $this; } /** * Set default resource type to use when calling load() * * @param string $type * @return Zend_Loader_Autoloader_Resource */ public function setDefaultResourceType($type) { if ($this->hasResourceType($type)) { $this->_defaultResourceType = $type; } return $this; } /** * Get default resource type to use when calling load() * * @return string|null */ public function getDefaultResourceType() { return $this->_defaultResourceType; } /** * Object registry and factory * * Loads the requested resource of type $type (or uses the default resource * type if none provided). If the resource has been loaded previously, * returns the previous instance; otherwise, instantiates it. * * @param string $resource * @param string $type * @return object * @throws Zend_Loader_Exception if resource type not specified or invalid */ public function load($resource, $type = null) { if (null === $type) { $type = $this->getDefaultResourceType(); if (empty($type)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('No resource type specified'); } } if (!$this->hasResourceType($type)) { require_once 'Zend/Loader/Exception.php'; throw new Zend_Loader_Exception('Invalid resource type specified'); } $namespace = $this->_resourceTypes[$type]['namespace']; $class = $namespace . '_' . ucfirst($resource); if (!isset($this->_resources[$class])) { $this->_resources[$class] = new $class; } return $this->_resources[$class]; } }