File indexing completed on 2024-06-16 05:29:52

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 }