ZF-7224, ZF-6658, ZF-6672: fix loader warnings during app bootstrapping
[zend/radio.git] / library / Zend / Application / Bootstrap / BootstrapAbstract.php
blobe3c9f0f4073c36b3c209e5d56408eb9a267b2b2b
1 <?php
2 /**
3 * Zend Framework
5 * LICENSE
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.
15 * @category Zend
16 * @package Zend_Application
17 * @subpackage Bootstrap
18 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
19 * @license http://framework.zend.com/license/new-bsd New BSD License
20 * @version $Id$
23 /**
24 * Abstract base class for bootstrap classes
26 * @uses Zend_Application_Bootstrap_Bootstrapper
27 * @uses Zend_Application_Bootstrap_ResourceBootstrapper
28 * @category Zend
29 * @package Zend_Application
30 * @subpackage Bootstrap
31 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
32 * @license http://framework.zend.com/license/new-bsd New BSD License
34 abstract class Zend_Application_Bootstrap_BootstrapAbstract
35 implements Zend_Application_Bootstrap_Bootstrapper,
36 Zend_Application_Bootstrap_ResourceBootstrapper
38 /**
39 * @var Zend_Application|Zend_Application_Bootstrap_Bootstrapper
41 protected $_application;
43 /**
44 * @var array Internal resource methods (resource/method pairs)
46 protected $_classResources;
48 /**
49 * @var object Resource container
51 protected $_container;
53 /**
54 * @var string
56 protected $_environment;
58 /**
59 * @var array
61 protected $_options = array();
63 /**
64 * @var Zend_Loader_PluginLoader_Interface
66 protected $_pluginLoader;
68 /**
69 * @var array Class-based resource plugins
71 protected $_pluginResources = array();
73 /**
74 * @var array Initializers that have been run
76 protected $_run = array();
78 /**
79 * @var array Initializers that have been started but not yet completed (circular dependency detection)
81 protected $_started = array();
83 /**
84 * Constructor
86 * Sets application object, initializes options, and prepares list of
87 * initializer methods.
89 * @param Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
90 * @return void
91 * @throws Zend_Application_Bootstrap_Exception When invalid applicaiton is provided
93 public function __construct($application)
95 $this->setApplication($application);
96 $options = $application->getOptions();
97 $this->setOptions($options);
101 * Set class state
103 * @param array $options
104 * @return Zend_Application_Bootstrap_BootstrapAbstract
106 public function setOptions(array $options)
108 $options = array_change_key_case($options, CASE_LOWER);
109 $methods = get_class_methods($this);
110 foreach ($methods as $key => $method) {
111 $methods[$key] = strtolower($method);
114 if (array_key_exists('pluginpaths', $options)) {
115 $pluginLoader = $this->getPluginLoader();
117 foreach ($options['pluginpaths'] as $prefix => $path) {
118 $pluginLoader->addPrefixPath($prefix, $path);
121 unset($options['pluginpaths']);
124 foreach ($options as $key => $value) {
125 $method = 'set' . strtolower($key);
127 if (in_array($method, $methods)) {
128 $this->$method($value);
129 } elseif ('resources' == $key) {
130 foreach ($value as $resource => $resourceOptions) {
131 $this->registerPluginResource($resource, $resourceOptions);
135 $this->_options = $this->mergeOptions($this->_options, $options);
136 return $this;
140 * Get current options from bootstrap
142 * @return array
144 public function getOptions()
146 return $this->_options;
150 * Is an option present?
152 * @param string $key
153 * @return bool
155 public function hasOption($key)
157 return array_key_exists($key, $this->_options);
161 * Retrieve a single option
163 * @param string $key
164 * @return mixed
166 public function getOption($key)
168 if ($this->hasOption($key)) {
169 return $this->_options[$key];
171 return null;
175 * Merge options recursively
177 * @param array $array1
178 * @param mixed $array2
179 * @return array
181 public function mergeOptions(array $array1, $array2 = null)
183 if (is_array($array2)) {
184 foreach ($array2 as $key => $val) {
185 if (is_array($array2[$key])) {
186 $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
187 ? $this->mergeOptions($array1[$key], $array2[$key])
188 : $array2[$key];
189 } else {
190 $array1[$key] = $val;
194 return $array1;
198 * Get class resources (as resource/method pairs)
200 * Uses get_class_methods() by default, reflection on prior to 5.2.6,
201 * as a bug prevents the usage of get_class_methods() there.
203 * @return array
205 public function getClassResources()
207 if (null === $this->_classResources) {
208 if (version_compare(PHP_VERSION, '5.2.6') === -1) {
209 $class = new ReflectionObject($this);
210 $classMethods = $class->getMethods();
211 $methodNames = array();
213 foreach ($classMethods as $method) {
214 $methodNames[] = $method->getName();
216 } else {
217 $methodNames = get_class_methods($this);
220 $this->_classResources = array();
221 foreach ($methodNames as $method) {
222 if (5 < strlen($method) && '_init' === substr($method, 0, 5)) {
223 $this->_classResources[strtolower(substr($method, 5))] = $method;
228 return $this->_classResources;
232 * Get class resource names
234 * @return array
236 public function getClassResourceNames()
238 $resources = $this->getClassResources();
239 return array_keys($resources);
243 * Register a new resource plugin
245 * @param string|Zend_Application_Resource_Resource $resource
246 * @param mixed $options
247 * @return Zend_Application_Bootstrap_BootstrapAbstract
248 * @throws Zend_Application_Bootstrap_Exception When invalid resource is provided
250 public function registerPluginResource($resource, $options = null)
253 if (is_string($resource) && class_exists($resource)) {
254 $options = (array) $options;
255 $options['bootstrap'] = $this;
256 $resource = new $resource($options);
259 if ($resource instanceof Zend_Application_Resource_Resource) {
260 $resource->setBootstrap($this);
261 $pluginName = $this->_resolvePluginResourceName($resource);
262 $this->_pluginResources[$pluginName] = $resource;
263 return $this;
266 if (!is_string($resource)) {
267 throw new Zend_Application_Bootstrap_Exception('Invalid resource provided to ' . __METHOD__);
270 // $resource = strtolower($resource);
271 $this->_pluginResources[$resource] = $options;
272 return $this;
276 * Unregister a resource from the bootstrap
278 * @param string|Zend_Application_Resource_Resource $resource
279 * @return Zend_Application_Bootstrap_BootstrapAbstract
280 * @throws Zend_Application_Bootstrap_Exception When unknown resource type is provided
282 public function unregisterPluginResource($resource)
284 if ($resource instanceof Zend_Application_Resource_Resource) {
285 if ($index = array_search($resource, $this->_pluginResources, true)) {
286 unset($this->_pluginResources[$index]);
288 return $this;
291 if (!is_string($resource)) {
292 throw new Zend_Application_Bootstrap_Exception('Unknown resource type provided to ' . __METHOD__);
295 $resource = strtolower($resource);
296 if (array_key_exists($resource, $this->_pluginResources)) {
297 unset($this->_pluginResources[$resource]);
300 return $this;
304 * Is the requested plugin resource registered?
306 * @param string $resource
307 * @return bool
309 public function hasPluginResource($resource)
311 return (null !== $this->getPluginResource($resource));
315 * Get a registered plugin resource
317 * @param string $resourceName
318 * @return Zend_Application_Resource_Resource
320 public function getPluginResource($resource)
322 if (array_key_exists(strtolower($resource), $this->_pluginResources)) {
323 $resource = strtolower($resource);
324 if (!$this->_pluginResources[$resource] instanceof Zend_Application_Resource_Resource) {
325 $resourceName = $this->_loadPluginResource($resource, $this->_pluginResources[$resource]);
326 if (!$resourceName) {
327 throw new Zend_Application_Bootstrap_Exception(sprintf('Unable to resolve plugin "%s"; no corresponding plugin with that name', $resource));
329 $resource = $resourceName;
331 return $this->_pluginResources[$resource];
334 foreach ($this->_pluginResources as $plugin => $spec) {
335 if ($spec instanceof Zend_Application_Resource_Resource) {
336 $pluginName = $this->_resolvePluginResourceName($spec);
337 if (0 === strcasecmp($resource, $pluginName)) {
338 unset($this->_pluginResources[$plugin]);
339 $this->_pluginResources[$pluginName] = $spec;
340 return $spec;
342 continue;
346 if (false !== $pluginName = $this->_loadPluginResource($plugin, $spec)) {
347 if (0 === strcasecmp($resource, $pluginName)) {
348 return $this->_pluginResources[$pluginName];
352 if (class_exists($plugin, false)) {
353 $spec = (array) $spec;
354 $spec['bootstrap'] = $this;
355 $instance = new $plugin($spec);
356 $pluginName = $this->_resolvePluginResourceName($instance);
357 unset($this->_pluginResources[$plugin]);
358 $this->_pluginResources[$pluginName] = $instance;
360 if (0 === strcasecmp($resource, $pluginName)) {
361 return $instance;
366 return null;
370 * Retrieve all plugin resources
372 * @return array
374 public function getPluginResources()
376 foreach (array_keys($this->_pluginResources) as $resource) {
377 $this->getPluginResource($resource);
379 return $this->_pluginResources;
383 * Retrieve plugin resource names
385 * @return array
387 public function getPluginResourceNames()
389 $this->getPluginResources();
390 return array_keys($this->_pluginResources);
394 * Set plugin loader for loading resources
396 * @param Zend_Loader_PluginLoader_Interface $loader
397 * @return Zend_Application_Bootstrap_BootstrapAbstract
399 public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
401 $this->_pluginLoader = $loader;
402 return $this;
406 * Get the plugin loader for resources
408 * @return Zend_Loader_PluginLoader_Interface
410 public function getPluginLoader()
412 if ($this->_pluginLoader === null) {
413 $options = array(
414 'Zend_Application_Resource' => 'Zend/Application/Resource'
417 $this->_pluginLoader = new Zend_Loader_PluginLoader($options);
420 return $this->_pluginLoader;
424 * Set application/parent bootstrap
426 * @param Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
427 * @return Zend_Application_Bootstrap_BootstrapAbstract
429 public function setApplication($application)
431 if (($application instanceof Zend_Application)
432 || ($application instanceof Zend_Application_Bootstrap_Bootstrapper)
434 $this->_application = $application;
435 } else {
436 throw new Zend_Application_Bootstrap_Exception('Invalid application provided to bootstrap constructor (received "' . get_class($application) . '" instance)');
438 return $this;
442 * Retrieve parent application instance
444 * @return Zend_Application|Zend_Application_Bootstrap_Bootstrapper
446 public function getApplication()
448 return $this->_application;
452 * Retrieve application environment
454 * @return string
456 public function getEnvironment()
458 if (null === $this->_environment) {
459 $this->_environment = $this->getApplication()->getEnvironment();
461 return $this->_environment;
465 * Set resource container
467 * By default, if a resource callback has a non-null return value, this
468 * value will be stored in a container using the resource name as the
469 * key.
471 * Containers must be objects, and must allow setting public properties.
473 * @param object $container
474 * @return Zend_Application_Bootstrap_BootstrapAbstract
476 public function setContainer($container)
478 if (!is_object($container)) {
479 throw new Zend_Application_Bootstrap_Exception('Resource containers must be objects');
481 $this->_container = $container;
482 return $this;
486 * Retrieve resource container
488 * @return object
490 public function getContainer()
492 if (null === $this->_container) {
493 $this->setContainer(new Zend_Registry());
495 return $this->_container;
499 * Determine if a resource has been stored in the container
501 * During bootstrap resource initialization, you may return a value. If
502 * you do, it will be stored in the {@link setContainer() container}.
503 * You can use this method to determine if a value was stored.
505 * @param string $name
506 * @return bool
508 public function hasResource($name)
510 $resource = strtolower($name);
511 $container = $this->getContainer();
512 return isset($container->{$resource});
516 * Retrieve a resource from the container
518 * During bootstrap resource initialization, you may return a value. If
519 * you do, it will be stored in the {@link setContainer() container}.
520 * You can use this method to retrieve that value.
522 * If no value was returned, this will return a null value.
524 * @param string $name
525 * @return null|mixed
527 public function getResource($name)
529 $resource = strtolower($name);
530 $container = $this->getContainer();
531 if ($this->hasResource($resource)) {
532 return $container->{$resource};
534 return null;
538 * Implement PHP's magic to retrieve a ressource
539 * in the bootstrap
541 * @param string $prop
542 * @return null|mixed
544 public function __get($prop)
546 return $this->getResource($prop);
550 * Implement PHP's magic to ask for the
551 * existence of a ressource in the bootstrap
553 * @param string $prop
554 * @return bool
556 public function __isset($prop)
558 return $this->hasResource($prop);
562 * Bootstrap individual, all, or multiple resources
564 * Marked as final to prevent issues when subclassing and naming the
565 * child class 'Bootstrap' (in which case, overriding this method
566 * would result in it being treated as a constructor).
568 * If you need to override this functionality, override the
569 * {@link _bootstrap()} method.
571 * @param null|string|array $resource
572 * @return Zend_Application_Bootstrap_BootstrapAbstract
573 * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
575 final public function bootstrap($resource = null)
577 $this->_bootstrap($resource);
578 return $this;
582 * Overloading: intercept calls to bootstrap<resourcename>() methods
584 * @param string $method
585 * @param array $args
586 * @return void
587 * @throws Zend_Application_Bootstrap_Exception On invalid method name
589 public function __call($method, $args)
591 if (9 < strlen($method) && 'bootstrap' === substr($method, 0, 9)) {
592 $resource = substr($method, 9);
593 return $this->bootstrap($resource);
596 throw new Zend_Application_Bootstrap_Exception('Invalid method "' . $method . '"');
600 * Bootstrap implementation
602 * This method may be overridden to provide custom bootstrapping logic.
603 * It is the sole method called by {@link bootstrap()}.
605 * @param null|string|array $resource
606 * @return void
607 * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
609 protected function _bootstrap($resource = null)
611 if (null === $resource) {
612 foreach ($this->getClassResourceNames() as $resource) {
613 $this->_executeResource($resource);
616 foreach ($this->getPluginResourceNames() as $resource) {
617 $this->_executeResource($resource);
619 } elseif (is_string($resource)) {
620 $this->_executeResource($resource);
621 } elseif (is_array($resource)) {
622 foreach ($resource as $r) {
623 $this->_executeResource($r);
625 } else {
626 throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__);
631 * Execute a resource
633 * Checks to see if the resource has already been run. If not, it searches
634 * first to see if a local method matches the resource, and executes that.
635 * If not, it checks to see if a plugin resource matches, and executes that
636 * if found.
638 * Finally, if not found, it throws an exception.
640 * @param string $resource
641 * @return void
642 * @throws Zend_Application_Bootstrap_Exception When resource not found
644 protected function _executeResource($resource)
646 $resource = strtolower($resource);
648 if (in_array($resource, $this->_run)) {
649 return;
652 if (isset($this->_started[$resource]) && $this->_started[$resource]) {
653 throw new Zend_Application_Bootstrap_Exception('Circular resource dependency detected');
656 $classResources = $this->getClassResources();
657 if (array_key_exists($resource, $classResources)) {
658 $this->_started[$resource] = true;
659 $method = $classResources[$resource];
660 $return = $this->$method();
661 unset($this->_started[$resource]);
662 $this->_markRun($resource);
664 if (null !== $return) {
665 $this->getContainer()->{$resource} = $return;
668 return;
671 if ($this->hasPluginResource($resource)) {
672 $this->_started[$resource] = true;
673 $plugin = $this->getPluginResource($resource);
674 $return = $plugin->init();
675 unset($this->_started[$resource]);
676 $this->_markRun($resource);
678 if (null !== $return) {
679 $this->getContainer()->{$resource} = $return;
682 return;
685 throw new Zend_Application_Bootstrap_Exception('Resource matching "' . $resource . '" not found');
689 * Load a plugin resource
691 * @param string $resource
692 * @param array|object|null $options
693 * @return string|false
695 protected function _loadPluginResource($resource, $options)
697 $options = (array) $options;
698 $options['bootstrap'] = $this;
699 $className = $this->getPluginLoader()->load(strtolower($resource), false);
701 if (!$className) {
702 return false;
705 $instance = new $className($options);
707 unset($this->_pluginResources[$resource]);
709 if (isset($instance->_explicitType)) {
710 $resource = $instance->_explicitType;
712 $resource = strtolower($resource);
713 $this->_pluginResources[$resource] = $instance;
715 return $resource;
719 * Mark a resource as having run
721 * @param string $resource
722 * @return void
724 protected function _markRun($resource)
726 if (!in_array($resource, $this->_run)) {
727 $this->_run[] = $resource;
732 * Resolve a plugin resource name
734 * Uses, in order of preference
735 * - $_explicitType property of resource
736 * - Short name of resource (if a matching prefix path is found)
737 * - class name (if none of the above are true)
739 * The name is then cast to lowercase.
741 * @param Zend_Application_Resource_Resource $resource
742 * @return string
744 protected function _resolvePluginResourceName($resource)
746 if (isset($resource->_explicitType)) {
747 $pluginName = $resource->_explicitType;
748 } else {
749 $className = get_class($resource);
750 $pluginName = $className;
751 $loader = $this->getPluginLoader();
752 foreach ($loader->getPaths() as $prefix => $paths) {
753 if (0 === strpos($className, $prefix)) {
754 $pluginName = substr($className, strlen($prefix));
755 $pluginName = trim($pluginName, '_');
759 $pluginName = strtolower($pluginName);
760 return $pluginName;