File indexing completed on 2025-01-26 05:29:16
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_Application 0017 * @subpackage Bootstrap 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * @see Zend_Application_Bootstrap_Bootstrapper 0025 */ 0026 // require_once 'Zend/Application/Bootstrap/Bootstrapper.php'; 0027 0028 /** 0029 * @see Zend_Application_Bootstrap_ResourceBootstrapper 0030 */ 0031 // require_once 'Zend/Application/Bootstrap/ResourceBootstrapper.php'; 0032 0033 /** 0034 * Abstract base class for bootstrap classes 0035 * 0036 * @uses Zend_Application_Bootstrap_Bootstrapper 0037 * @uses Zend_Application_Bootstrap_ResourceBootstrapper 0038 * @category Zend 0039 * @package Zend_Application 0040 * @subpackage Bootstrap 0041 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0042 * @license http://framework.zend.com/license/new-bsd New BSD License 0043 */ 0044 abstract class Zend_Application_Bootstrap_BootstrapAbstract 0045 implements Zend_Application_Bootstrap_Bootstrapper, 0046 Zend_Application_Bootstrap_ResourceBootstrapper 0047 { 0048 /** 0049 * @var Zend_Application|Zend_Application_Bootstrap_Bootstrapper 0050 */ 0051 protected $_application; 0052 0053 /** 0054 * @var array Internal resource methods (resource/method pairs) 0055 */ 0056 protected $_classResources; 0057 0058 /** 0059 * @var object Resource container 0060 */ 0061 protected $_container; 0062 0063 /** 0064 * @var string 0065 */ 0066 protected $_environment; 0067 0068 /** 0069 * Flattened (lowercase) option keys used for lookups 0070 * 0071 * @var array 0072 */ 0073 protected $_optionKeys = array(); 0074 0075 /** 0076 * @var array 0077 */ 0078 protected $_options = array(); 0079 0080 /** 0081 * @var Zend_Loader_PluginLoader_Interface 0082 */ 0083 protected $_pluginLoader; 0084 0085 /** 0086 * @var array Class-based resource plugins 0087 */ 0088 protected $_pluginResources = array(); 0089 0090 /** 0091 * @var array Initializers that have been run 0092 */ 0093 protected $_run = array(); 0094 0095 /** 0096 * @var array Initializers that have been started but not yet completed (circular dependency detection) 0097 */ 0098 protected $_started = array(); 0099 0100 /** 0101 * Constructor 0102 * 0103 * Sets application object, initializes options, and prepares list of 0104 * initializer methods. 0105 * 0106 * @param Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application 0107 * @throws Zend_Application_Bootstrap_Exception When invalid application is provided 0108 */ 0109 public function __construct($application) 0110 { 0111 $this->setApplication($application); 0112 $options = $application->getOptions(); 0113 $this->setOptions($options); 0114 } 0115 0116 /** 0117 * Set class state 0118 * 0119 * @param array $options 0120 * @return Zend_Application_Bootstrap_BootstrapAbstract 0121 */ 0122 public function setOptions(array $options) 0123 { 0124 $this->_options = $this->mergeOptions($this->_options, $options); 0125 0126 $options = array_change_key_case($options, CASE_LOWER); 0127 $this->_optionKeys = array_merge($this->_optionKeys, array_keys($options)); 0128 0129 $methods = get_class_methods($this); 0130 foreach ($methods as $key => $method) { 0131 $methods[$key] = strtolower($method); 0132 } 0133 0134 if (array_key_exists('pluginpaths', $options)) { 0135 $pluginLoader = $this->getPluginLoader(); 0136 0137 foreach ($options['pluginpaths'] as $prefix => $path) { 0138 $pluginLoader->addPrefixPath($prefix, $path); 0139 } 0140 unset($options['pluginpaths']); 0141 } 0142 0143 foreach ($options as $key => $value) { 0144 $method = 'set' . strtolower($key); 0145 0146 if (in_array($method, $methods)) { 0147 $this->$method($value); 0148 } elseif ('resources' == $key) { 0149 foreach ($value as $resource => $resourceOptions) { 0150 $this->registerPluginResource($resource, $resourceOptions); 0151 } 0152 } 0153 } 0154 return $this; 0155 } 0156 0157 /** 0158 * Get current options from bootstrap 0159 * 0160 * @return array 0161 */ 0162 public function getOptions() 0163 { 0164 return $this->_options; 0165 } 0166 0167 /** 0168 * Is an option present? 0169 * 0170 * @param string $key 0171 * @return bool 0172 */ 0173 public function hasOption($key) 0174 { 0175 return in_array(strtolower($key), $this->_optionKeys); 0176 } 0177 0178 /** 0179 * Retrieve a single option 0180 * 0181 * @param string $key 0182 * @return mixed 0183 */ 0184 public function getOption($key) 0185 { 0186 if ($this->hasOption($key)) { 0187 $options = $this->getOptions(); 0188 $options = array_change_key_case($options, CASE_LOWER); 0189 return $options[strtolower($key)]; 0190 } 0191 return null; 0192 } 0193 0194 /** 0195 * Merge options recursively 0196 * 0197 * @param array $array1 0198 * @param mixed $array2 0199 * @return array 0200 */ 0201 public function mergeOptions(array $array1, $array2 = null) 0202 { 0203 if (is_array($array2)) { 0204 foreach ($array2 as $key => $val) { 0205 if (is_array($array2[$key])) { 0206 $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key])) 0207 ? $this->mergeOptions($array1[$key], $array2[$key]) 0208 : $array2[$key]; 0209 } else { 0210 $array1[$key] = $val; 0211 } 0212 } 0213 } 0214 return $array1; 0215 } 0216 0217 /** 0218 * Get class resources (as resource/method pairs) 0219 * 0220 * Uses get_class_methods() by default, reflection on prior to 5.2.6, 0221 * as a bug prevents the usage of get_class_methods() there. 0222 * 0223 * @return array 0224 */ 0225 public function getClassResources() 0226 { 0227 if (null === $this->_classResources) { 0228 if (version_compare(PHP_VERSION, '5.2.6') === -1) { 0229 $class = new ReflectionObject($this); 0230 $classMethods = $class->getMethods(); 0231 $methodNames = array(); 0232 0233 foreach ($classMethods as $method) { 0234 $methodNames[] = $method->getName(); 0235 } 0236 } else { 0237 $methodNames = get_class_methods($this); 0238 } 0239 0240 $this->_classResources = array(); 0241 foreach ($methodNames as $method) { 0242 if (5 < strlen($method) && '_init' === substr($method, 0, 5)) { 0243 $this->_classResources[strtolower(substr($method, 5))] = $method; 0244 } 0245 } 0246 } 0247 0248 return $this->_classResources; 0249 } 0250 0251 /** 0252 * Get class resource names 0253 * 0254 * @return array 0255 */ 0256 public function getClassResourceNames() 0257 { 0258 $resources = $this->getClassResources(); 0259 return array_keys($resources); 0260 } 0261 0262 /** 0263 * Register a new resource plugin 0264 * 0265 * @param string|Zend_Application_Resource_Resource $resource 0266 * @param mixed $options 0267 * @return Zend_Application_Bootstrap_BootstrapAbstract 0268 * @throws Zend_Application_Bootstrap_Exception When invalid resource is provided 0269 */ 0270 public function registerPluginResource($resource, $options = null) 0271 { 0272 if ($resource instanceof Zend_Application_Resource_Resource) { 0273 $resource->setBootstrap($this); 0274 $pluginName = $this->_resolvePluginResourceName($resource); 0275 $this->_pluginResources[$pluginName] = $resource; 0276 return $this; 0277 } 0278 0279 if (!is_string($resource)) { 0280 throw new Zend_Application_Bootstrap_Exception('Invalid resource provided to ' . __METHOD__); 0281 } 0282 0283 $this->_pluginResources[$resource] = $options; 0284 return $this; 0285 } 0286 0287 /** 0288 * Unregister a resource from the bootstrap 0289 * 0290 * @param string|Zend_Application_Resource_Resource $resource 0291 * @return Zend_Application_Bootstrap_BootstrapAbstract 0292 * @throws Zend_Application_Bootstrap_Exception When unknown resource type is provided 0293 */ 0294 public function unregisterPluginResource($resource) 0295 { 0296 if ($resource instanceof Zend_Application_Resource_Resource) { 0297 if ($index = array_search($resource, $this->_pluginResources, true)) { 0298 unset($this->_pluginResources[$index]); 0299 } 0300 return $this; 0301 } 0302 0303 if (!is_string($resource)) { 0304 throw new Zend_Application_Bootstrap_Exception('Unknown resource type provided to ' . __METHOD__); 0305 } 0306 0307 $resource = strtolower($resource); 0308 if (array_key_exists($resource, $this->_pluginResources)) { 0309 unset($this->_pluginResources[$resource]); 0310 } 0311 0312 return $this; 0313 } 0314 0315 /** 0316 * Is the requested plugin resource registered? 0317 * 0318 * @param string $resource 0319 * @return bool 0320 */ 0321 public function hasPluginResource($resource) 0322 { 0323 return (null !== $this->getPluginResource($resource)); 0324 } 0325 0326 /** 0327 * Get a registered plugin resource 0328 * 0329 * @param string $resource 0330 * @return Zend_Application_Resource_Resource 0331 * @throws Zend_Application_Bootstrap_Exception 0332 */ 0333 public function getPluginResource($resource) 0334 { 0335 if (array_key_exists(strtolower($resource), $this->_pluginResources)) { 0336 $resource = strtolower($resource); 0337 if (!$this->_pluginResources[$resource] instanceof Zend_Application_Resource_Resource) { 0338 $resourceName = $this->_loadPluginResource($resource, $this->_pluginResources[$resource]); 0339 if (!$resourceName) { 0340 throw new Zend_Application_Bootstrap_Exception(sprintf('Unable to resolve plugin "%s"; no corresponding plugin with that name', $resource)); 0341 } 0342 $resource = $resourceName; 0343 } 0344 return $this->_pluginResources[$resource]; 0345 } 0346 0347 foreach ($this->_pluginResources as $plugin => $spec) { 0348 if ($spec instanceof Zend_Application_Resource_Resource) { 0349 $pluginName = $this->_resolvePluginResourceName($spec); 0350 if (0 === strcasecmp($resource, $pluginName)) { 0351 unset($this->_pluginResources[$plugin]); 0352 $this->_pluginResources[$pluginName] = $spec; 0353 return $spec; 0354 } 0355 continue; 0356 } 0357 0358 if (false !== $pluginName = $this->_loadPluginResource($plugin, $spec)) { 0359 if (0 === strcasecmp($resource, $pluginName)) { 0360 return $this->_pluginResources[$pluginName]; 0361 } 0362 continue; 0363 } 0364 0365 if (class_exists($plugin) 0366 && is_subclass_of($plugin, 'Zend_Application_Resource_Resource') 0367 ) { //@SEE ZF-7550 0368 $spec = (array) $spec; 0369 $spec['bootstrap'] = $this; 0370 $instance = new $plugin($spec); 0371 $pluginName = $this->_resolvePluginResourceName($instance); 0372 unset($this->_pluginResources[$plugin]); 0373 $this->_pluginResources[$pluginName] = $instance; 0374 0375 if (0 === strcasecmp($resource, $pluginName)) { 0376 return $instance; 0377 } 0378 } 0379 } 0380 0381 return null; 0382 } 0383 0384 /** 0385 * Retrieve all plugin resources 0386 * 0387 * @return array 0388 */ 0389 public function getPluginResources() 0390 { 0391 foreach (array_keys($this->_pluginResources) as $resource) { 0392 $this->getPluginResource($resource); 0393 } 0394 return $this->_pluginResources; 0395 } 0396 0397 /** 0398 * Retrieve plugin resource names 0399 * 0400 * @return array 0401 */ 0402 public function getPluginResourceNames() 0403 { 0404 $this->getPluginResources(); 0405 return array_keys($this->_pluginResources); 0406 } 0407 0408 /** 0409 * Set plugin loader for loading resources 0410 * 0411 * @param Zend_Loader_PluginLoader_Interface $loader 0412 * @return Zend_Application_Bootstrap_BootstrapAbstract 0413 */ 0414 public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader) 0415 { 0416 $this->_pluginLoader = $loader; 0417 return $this; 0418 } 0419 0420 /** 0421 * Get the plugin loader for resources 0422 * 0423 * @return Zend_Loader_PluginLoader_Interface 0424 */ 0425 public function getPluginLoader() 0426 { 0427 if ($this->_pluginLoader === null) { 0428 $options = array( 0429 'Zend_Application_Resource' => 'Zend/Application/Resource', 0430 'ZendX_Application_Resource' => 'ZendX/Application/Resource' 0431 ); 0432 0433 $this->_pluginLoader = new Zend_Loader_PluginLoader($options); 0434 } 0435 0436 return $this->_pluginLoader; 0437 } 0438 0439 /** 0440 * Set application/parent bootstrap 0441 * 0442 * @param Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application 0443 * @return Zend_Application_Bootstrap_BootstrapAbstract 0444 * @throws Zend_Application_Bootstrap_Exception 0445 */ 0446 public function setApplication($application) 0447 { 0448 if (($application instanceof Zend_Application) 0449 || ($application instanceof Zend_Application_Bootstrap_Bootstrapper) 0450 ) { 0451 if ($application === $this) { 0452 throw new Zend_Application_Bootstrap_Exception('Cannot set application to same object; creates recursion'); 0453 } 0454 $this->_application = $application; 0455 } else { 0456 throw new Zend_Application_Bootstrap_Exception('Invalid application provided to bootstrap constructor (received "' . get_class($application) . '" instance)'); 0457 } 0458 return $this; 0459 } 0460 0461 /** 0462 * Retrieve parent application instance 0463 * 0464 * @return Zend_Application|Zend_Application_Bootstrap_Bootstrapper 0465 */ 0466 public function getApplication() 0467 { 0468 return $this->_application; 0469 } 0470 0471 /** 0472 * Retrieve application environment 0473 * 0474 * @return string 0475 */ 0476 public function getEnvironment() 0477 { 0478 if (null === $this->_environment) { 0479 $this->_environment = $this->getApplication()->getEnvironment(); 0480 } 0481 return $this->_environment; 0482 } 0483 0484 /** 0485 * Set resource container 0486 * 0487 * By default, if a resource callback has a non-null return value, this 0488 * value will be stored in a container using the resource name as the 0489 * key. 0490 * 0491 * Containers must be objects, and must allow setting public properties. 0492 * 0493 * @param object $container 0494 * @return Zend_Application_Bootstrap_BootstrapAbstract 0495 * @throws Zend_Application_Bootstrap_Exception 0496 */ 0497 public function setContainer($container) 0498 { 0499 if (!is_object($container)) { 0500 throw new Zend_Application_Bootstrap_Exception('Resource containers must be objects'); 0501 } 0502 $this->_container = $container; 0503 return $this; 0504 } 0505 0506 /** 0507 * Retrieve resource container 0508 * 0509 * @return object 0510 */ 0511 public function getContainer() 0512 { 0513 if (null === $this->_container) { 0514 $this->setContainer(new Zend_Registry()); 0515 } 0516 return $this->_container; 0517 } 0518 0519 /** 0520 * Determine if a resource has been stored in the container 0521 * 0522 * During bootstrap resource initialization, you may return a value. If 0523 * you do, it will be stored in the {@link setContainer() container}. 0524 * You can use this method to determine if a value was stored. 0525 * 0526 * @param string $name 0527 * @return bool 0528 */ 0529 public function hasResource($name) 0530 { 0531 $resource = strtolower($name); 0532 $container = $this->getContainer(); 0533 return isset($container->{$resource}); 0534 } 0535 0536 /** 0537 * Retrieve a resource from the container 0538 * 0539 * During bootstrap resource initialization, you may return a value. If 0540 * you do, it will be stored in the {@link setContainer() container}. 0541 * You can use this method to retrieve that value. 0542 * 0543 * If no value was returned, this will return a null value. 0544 * 0545 * @param string $name 0546 * @return null|mixed 0547 */ 0548 public function getResource($name) 0549 { 0550 $resource = strtolower($name); 0551 $container = $this->getContainer(); 0552 if ($this->hasResource($resource)) { 0553 return $container->{$resource}; 0554 } 0555 return null; 0556 } 0557 0558 /** 0559 * Implement PHP's magic to retrieve a resource 0560 * in the bootstrap 0561 * 0562 * @param string $prop 0563 * @return null|mixed 0564 */ 0565 public function __get($prop) 0566 { 0567 return $this->getResource($prop); 0568 } 0569 0570 /** 0571 * Implement PHP's magic to ask for the 0572 * existence of a resource in the bootstrap 0573 * 0574 * @param string $prop 0575 * @return bool 0576 */ 0577 public function __isset($prop) 0578 { 0579 return $this->hasResource($prop); 0580 } 0581 0582 /** 0583 * Bootstrap individual, all, or multiple resources 0584 * 0585 * Marked as final to prevent issues when subclassing and naming the 0586 * child class 'Bootstrap' (in which case, overriding this method 0587 * would result in it being treated as a constructor). 0588 * 0589 * If you need to override this functionality, override the 0590 * {@link _bootstrap()} method. 0591 * 0592 * @param null|string|array $resource 0593 * @return Zend_Application_Bootstrap_BootstrapAbstract 0594 * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed 0595 */ 0596 final public function bootstrap($resource = null) 0597 { 0598 $this->_bootstrap($resource); 0599 return $this; 0600 } 0601 0602 /** 0603 * Overloading: intercept calls to bootstrap<resourcename>() methods 0604 * 0605 * @param string $method 0606 * @param array $args 0607 * @return Zend_Application_Bootstrap_BootstrapAbstract 0608 * @throws Zend_Application_Bootstrap_Exception On invalid method name 0609 */ 0610 public function __call($method, $args) 0611 { 0612 if (9 < strlen($method) && 'bootstrap' === substr($method, 0, 9)) { 0613 $resource = substr($method, 9); 0614 return $this->bootstrap($resource); 0615 } 0616 0617 throw new Zend_Application_Bootstrap_Exception('Invalid method "' . $method . '"'); 0618 } 0619 0620 /** 0621 * Bootstrap implementation 0622 * 0623 * This method may be overridden to provide custom bootstrapping logic. 0624 * It is the sole method called by {@link bootstrap()}. 0625 * 0626 * @param null|string|array $resource 0627 * @return void 0628 * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed 0629 */ 0630 protected function _bootstrap($resource = null) 0631 { 0632 if (null === $resource) { 0633 foreach ($this->getClassResourceNames() as $resource) { 0634 $this->_executeResource($resource); 0635 } 0636 0637 foreach ($this->getPluginResourceNames() as $resource) { 0638 $this->_executeResource($resource); 0639 } 0640 } elseif (is_string($resource)) { 0641 $this->_executeResource($resource); 0642 } elseif (is_array($resource)) { 0643 foreach ($resource as $r) { 0644 $this->_executeResource($r); 0645 } 0646 } else { 0647 throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__); 0648 } 0649 } 0650 0651 /** 0652 * Execute a resource 0653 * 0654 * Checks to see if the resource has already been run. If not, it searches 0655 * first to see if a local method matches the resource, and executes that. 0656 * If not, it checks to see if a plugin resource matches, and executes that 0657 * if found. 0658 * 0659 * Finally, if not found, it throws an exception. 0660 * 0661 * @param string $resource 0662 * @return void 0663 * @throws Zend_Application_Bootstrap_Exception When resource not found 0664 */ 0665 protected function _executeResource($resource) 0666 { 0667 $resourceName = strtolower($resource); 0668 0669 if (in_array($resourceName, $this->_run)) { 0670 return; 0671 } 0672 0673 if (isset($this->_started[$resourceName]) && $this->_started[$resourceName]) { 0674 throw new Zend_Application_Bootstrap_Exception('Circular resource dependency detected'); 0675 } 0676 0677 $classResources = $this->getClassResources(); 0678 if (array_key_exists($resourceName, $classResources)) { 0679 $this->_started[$resourceName] = true; 0680 $method = $classResources[$resourceName]; 0681 $return = $this->$method(); 0682 unset($this->_started[$resourceName]); 0683 $this->_markRun($resourceName); 0684 0685 if (null !== $return) { 0686 $this->getContainer()->{$resourceName} = $return; 0687 } 0688 0689 return; 0690 } 0691 0692 if ($this->hasPluginResource($resource)) { 0693 $this->_started[$resourceName] = true; 0694 $plugin = $this->getPluginResource($resource); 0695 $return = $plugin->init(); 0696 unset($this->_started[$resourceName]); 0697 $this->_markRun($resourceName); 0698 0699 if (null !== $return) { 0700 $this->getContainer()->{$resourceName} = $return; 0701 } 0702 0703 return; 0704 } 0705 0706 throw new Zend_Application_Bootstrap_Exception('Resource matching "' . $resource . '" not found'); 0707 } 0708 0709 /** 0710 * Load a plugin resource 0711 * 0712 * @param string $resource 0713 * @param array|object|null $options 0714 * @return string|false 0715 */ 0716 protected function _loadPluginResource($resource, $options) 0717 { 0718 $options = (array) $options; 0719 $options['bootstrap'] = $this; 0720 $className = $this->getPluginLoader()->load(strtolower($resource), false); 0721 0722 if (!$className) { 0723 return false; 0724 } 0725 0726 $instance = new $className($options); 0727 0728 unset($this->_pluginResources[$resource]); 0729 0730 if (isset($instance->_explicitType)) { 0731 $resource = $instance->_explicitType; 0732 } 0733 $resource = strtolower($resource); 0734 $this->_pluginResources[$resource] = $instance; 0735 0736 return $resource; 0737 } 0738 0739 /** 0740 * Mark a resource as having run 0741 * 0742 * @param string $resource 0743 * @return void 0744 */ 0745 protected function _markRun($resource) 0746 { 0747 if (!in_array($resource, $this->_run)) { 0748 $this->_run[] = $resource; 0749 } 0750 } 0751 0752 /** 0753 * Resolve a plugin resource name 0754 * 0755 * Uses, in order of preference 0756 * - $_explicitType property of resource 0757 * - Short name of resource (if a matching prefix path is found) 0758 * - class name (if none of the above are true) 0759 * 0760 * The name is then cast to lowercase. 0761 * 0762 * @param Zend_Application_Resource_Resource $resource 0763 * @return string 0764 */ 0765 protected function _resolvePluginResourceName($resource) 0766 { 0767 if (isset($resource->_explicitType)) { 0768 $pluginName = $resource->_explicitType; 0769 } else { 0770 $className = get_class($resource); 0771 $pluginName = $className; 0772 $loader = $this->getPluginLoader(); 0773 foreach ($loader->getPaths() as $prefix => $paths) { 0774 if (0 === strpos($className, $prefix)) { 0775 $pluginName = substr($className, strlen($prefix)); 0776 $pluginName = trim($pluginName, '_'); 0777 break; 0778 } 0779 } 0780 } 0781 $pluginName = strtolower($pluginName); 0782 return $pluginName; 0783 } 0784 }