Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

application

  • Loading branch information...
commit 50e804e3a5dc185d5d502993f9d21b6f249ef483 1 parent b5cc548
sunshine1988 authored February 27, 2013

Showing 27 changed files with 3,797 additions and 0 deletions. Show diff stats Hide diff stats

  1. 212  Application/Bootstrap/Bootstrap.php
  2. 848  Application/Bootstrap/BootstrapAbstract.php
  3. 94  Application/Bootstrap/Bootstrapper.php
  4. 38  Application/Bootstrap/Exception.php
  5. 95  Application/Bootstrap/ResourceBootstrapper.php
  6. 38  Application/Exception.php
  7. 94  Application/Module/Autoloader.php
  8. 128  Application/Module/Bootstrap.php
  9. 73  Application/Resource/Cachemanager.php
  10. 193  Application/Resource/Db.php
  11. 76  Application/Resource/Dojo.php
  12. 40  Application/Resource/Exception.php
  13. 222  Application/Resource/Frontcontroller.php
  14. 70  Application/Resource/Layout.php
  15. 117  Application/Resource/Locale.php
  16. 78  Application/Resource/Log.php
  17. 147  Application/Resource/Mail.php
  18. 155  Application/Resource/Modules.php
  19. 210  Application/Resource/Multidb.php
  20. 128  Application/Resource/Navigation.php
  21. 80  Application/Resource/Resource.php
  22. 164  Application/Resource/ResourceAbstract.php
  23. 87  Application/Resource/Router.php
  24. 118  Application/Resource/Session.php
  25. 134  Application/Resource/Translate.php
  26. 72  Application/Resource/Useragent.php
  27. 86  Application/Resource/View.php
212  Application/Bootstrap/Bootstrap.php
... ...
@@ -0,0 +1,212 @@
  1
+<?php
  2
+/**
  3
+ * Zend Framework
  4
+ *
  5
+ * LICENSE
  6
+ *
  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.
  14
+ *
  15
+ * @category   Zend
  16
+ * @package    Zend_Application
  17
+ * @subpackage Bootstrap
  18
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
  20
+ * @version    $Id: Bootstrap.php 23775 2011-03-01 17:25:24Z ralph $
  21
+ */
  22
+
  23
+/**
  24
+ * Concrete base class for bootstrap classes
  25
+ *
  26
+ * Registers and utilizes Zend_Controller_Front by default.
  27
+ *
  28
+ * @uses       Zend_Application_Bootstrap_Bootstrap
  29
+ * @category   Zend
  30
+ * @package    Zend_Application
  31
+ * @subpackage Bootstrap
  32
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  33
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
  34
+ */
  35
+class Zend_Application_Bootstrap_Bootstrap
  36
+    extends Zend_Application_Bootstrap_BootstrapAbstract
  37
+{
  38
+    /**
  39
+     * Application resource namespace
  40
+     * @var false|string
  41
+     */
  42
+    protected $_appNamespace = false;
  43
+
  44
+    /**
  45
+     * Application resource autoloader
  46
+     * @var Zend_Loader_Autoloader_Resource
  47
+     */
  48
+    protected $_resourceLoader;
  49
+
  50
+    /**
  51
+     * Constructor
  52
+     * ###
  53
+     * 此类一般作为自定义bootstrap类的父类使用
  54
+     * 但是这个父类的构造函数里面的代码如下面的分析所示,是有垃圾代码需要删除掉的,可以又要调用此类的父类构造函数,因此你在自定义bootstrap类中,是无法回避此构造函数的。
  55
+     * 如果想修改的话,拷贝此类的代码到一个新类,修改构造函数行为,然后自定义的bootstrap类继承新类即可
  56
+     * ###
  57
+     *
  58
+     * Ensure FrontController resource is registered
  59
+     *
  60
+     * @param  Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
  61
+     * @return void
  62
+     */
  63
+    public function __construct($application)
  64
+    {
  65
+        parent::__construct($application);
  66
+
  67
+        /**
  68
+         * ###
  69
+         * 下面这段代码是完全无用的
  70
+         * 因为parent::__construct($application);方法中,有一种自动调用bootstrap类中set{$optionKey}()方法的机制(Zend_Application_Bootstrap_BootstrapAbstract Line138)
  71
+         * 那里的$optionKey就是从传过去的$application中取到的,这里再次判断$application中有无resourceloader有的话又去调用setOptions()方法,而setOptions()方法又要去自动调用
  72
+         * set{$optionKey}()方法,所以就重复了!!因此无论如何,下面这段代码(if(){})是毫无意义的!!!可删除
  73
+         * 
  74
+         * 值得提到的另一点是,$application中的$options一般是解析自配置文件,配置文件中只能提供字符串形式的配置,且$application中没有特殊处理resourceloader字段的配置来实例化相应的resourceLoader实例
  75
+         * 保存到resourceloader字段,但是可但是,这个类中setResourceLoader(Zend_Loader_Autoloader_Resource $loader)方法却又偏偏限制了参数的类型必须是一个类,这样配置中一旦含有resourceloader
  76
+         * 字段,那么就会自动调用setResourceLoader方法,然后检测到参数为字符串类型不符合,那么程序就直接运行不下去了...那么按现在这种情况,application.ini中就不能配置resourceloader字段咯
  77
+         * 
  78
+         * 如果想修改默认的resourceloader,可以在bootstrap类中重载getResourceLoader()方法,去设置默认的自定义resourceLoader,可以在application.ini中设置自定义的配置,然后重载的get方法通过
  79
+         * getOption($key)方法获取相应自定义配置($key不能是resourceloader),然后自定义默认resourceloader。
  80
+         * 
  81
+         * resourceloader一般是用来自加载application目录下(或者配置到其他目录下)的model、viewHelper、filter这些东西的,用不到的话,可以application.ini中不设置appnamespace字段,因为
  82
+         * 这里$this->getResourceLoader();的调用,里面是先判断有无设置appnamespace
  83
+         * ###
  84
+         */
  85
+//         if ($application->hasOption('resourceloader')) {
  86
+//             $this->setOptions(array(
  87
+//                 'resourceloader' => $application->getOption('resourceloader')
  88
+//             ));
  89
+//         }
  90
+        $this->getResourceLoader();
  91
+
  92
+        /**
  93
+         * 下面这段if代码,本意是要保证FrontController资源启动,但是它并没有能保证,因为registerPluginResource只是创建资源对象,但是没有执行$this->_executeResource($resource)
  94
+         * 然后就没有运行pluginResource类中的init()方法,然后就不会将frontController资源返回来存入Container中,但是可但是,问题来了,boot以后,开始run时,获取资源都是直接从
  95
+         * Container中获取的,获取不到就算没有,也不再去查找此资源有无注册,所以下面run()方法,如果没有bootstrap frontController资源的话,$front   = $this->getResource('FrontController');
  96
+         * 就获取$front = null了。。。就悲了个催了! 
  97
+         * 换成下面的代码解决这个问题!不过最好不让代码去做这种“检测不到一个必须的东东然后自己可怜巴巴滴去实现它”的事情,会增加开销的,因为代码会很不高兴,后果。。。!!
  98
+         * PS:这样看来,这个构造函数的源码中,貌似只有两行代码有点用。。。我了个擦!!
  99
+         * 
  100
+         * ###
  101
+         * 事情还没有结束,关键的灵感忽然来了~~
  102
+         * 
  103
+         * 在改进了按需启动后,这段if代码,便可以保证没有即使配置frontController时,FrontController资源的正常使用了!!!
  104
+         * ###
  105
+         */
  106
+//         if (!$this->hasPluginResource('FrontController')) {
  107
+//             $this->registerPluginResource('FrontController');
  108
+//         }
  109
+
  110
+        /**
  111
+         * 在改进资源按需启动后,完全可以去掉这个“保证frontController资源启动的代码了”
  112
+         */
  113
+// 		if(!$this->hasResource('FrontController')){
  114
+// 	        if (!$this->hasPluginResource('FrontController')) {
  115
+// 	            $this->registerPluginResource('FrontController');
  116
+// 	        }
  117
+// 	        $this->bootstrap('FrontController');
  118
+// 		}
  119
+    }
  120
+
  121
+    /**
  122
+     * Run the application
  123
+     *
  124
+     * Checks to see that we have a default controller directory. If not, an
  125
+     * exception is thrown.
  126
+     *
  127
+     * If so, it registers the bootstrap with the 'bootstrap' parameter of
  128
+     * the front controller, and dispatches the front controller.
  129
+     *
  130
+     * @return mixed
  131
+     * @throws Zend_Application_Bootstrap_Exception
  132
+     */
  133
+    public function run()
  134
+    {
  135
+        $front   = $this->getResource('FrontController');
  136
+        $default = $front->getDefaultModule();
  137
+        if (null === $front->getControllerDirectory($default)) {
  138
+            throw new Zend_Application_Bootstrap_Exception(
  139
+                'No default controller directory registered with front controller'
  140
+            );
  141
+        }
  142
+
  143
+        $front->setParam('bootstrap', $this);
  144
+        $response = $front->dispatch();
  145
+        if ($front->returnResponse()) {
  146
+            return $response;
  147
+        }
  148
+    }
  149
+
  150
+    /**
  151
+     * Set module resource loader
  152
+     * 因为这里限制了参数必须是Zend_Loader_Autoloader_Resource类型的,所以显然不可以通过在application.ini中的resourceloader字段来配置一个类名来
  153
+     * 设置(会自动调用相应的setXXX()方法,见Zend_Loader_Autoloader_Resource::setOptions Line138)
  154
+     * 如果想设置默认的resourceLoader,可以在bootstrap类中重载getResourceLoader()方法,在里面重新设置自己想要设置的默认值,见下面的getResourceLoader()方法
  155
+     * 
  156
+     * 当然,也可以在bootstrap类的构造函数中,先调用setResourceLoader()方法设置一个默认的实例,但是这样做不优雅
  157
+     * 由于bootstrap的$options是从已经解析好的Zend_Application类的$options中来的,所以你也可以重载Zend_Application的相应方法,处理来自application.ini中有关
  158
+     * resourceloader的配置,然后将resourceloader这个字段的值保存成Zend_Loader_Autoloader_Resource的实例,这样当bootstrap解析此字段的时候,就会触发调用setXXX()方法
  159
+     * 来设置。。。显然,这个思路就更跑偏了~~~ 但是,可以说明zf是多么的可灵活定制!!!
  160
+     *
  161
+     * @param  Zend_Loader_Autoloader_Resource $loader
  162
+     * @return Zend_Application_Module_Bootstrap
  163
+     */
  164
+    public function setResourceLoader(Zend_Loader_Autoloader_Resource $loader)
  165
+    {
  166
+        $this->_resourceLoader = $loader;
  167
+        return $this;
  168
+    }
  169
+
  170
+    /**
  171
+     * Retrieve module resource loader
  172
+     * 
  173
+     *
  174
+     * @return Zend_Loader_Autoloader_Resource
  175
+     */
  176
+    public function getResourceLoader()
  177
+    {
  178
+        if ((null === $this->_resourceLoader)
  179
+            && (false !== ($namespace = $this->getAppNamespace()))
  180
+        ) {
  181
+            $r    = new ReflectionClass($this);
  182
+            $path = $r->getFileName();
  183
+            $this->setResourceLoader(new Zend_Application_Module_Autoloader(array(
  184
+                'namespace' => $namespace,
  185
+                'basePath'  => dirname($path),
  186
+            )));
  187
+        }
  188
+        return $this->_resourceLoader;
  189
+    }
  190
+
  191
+    /**
  192
+     * Get application namespace (used for module autoloading)
  193
+     *
  194
+     * @return string
  195
+     */
  196
+    public function getAppNamespace()
  197
+    {
  198
+        return $this->_appNamespace;
  199
+    }
  200
+
  201
+    /**
  202
+     * Set application namespace (for module autoloading)
  203
+     *
  204
+     * @param  string
  205
+     * @return Zend_Application_Bootstrap_Bootstrap
  206
+     */
  207
+    public function setAppNamespace($value)
  208
+    {
  209
+        $this->_appNamespace = (string) $value;
  210
+        return $this;
  211
+    }
  212
+}
848  Application/Bootstrap/BootstrapAbstract.php
... ...
@@ -0,0 +1,848 @@
  1
+<?php
  2
+/**
  3
+ * Zend Framework
  4
+ *
  5
+ * LICENSE
  6
+ *
  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.
  14
+ *
  15
+ * @category   Zend
  16
+ * @package    Zend_Application
  17
+ * @subpackage Bootstrap
  18
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
  20
+ * @version    $Id: BootstrapAbstract.php 24394 2011-08-21 13:57:08Z padraic $
  21
+ */
  22
+
  23
+/**
  24
+ * Abstract base class for bootstrap classes
  25
+ * 实现两个接口
  26
+ * Zend_Application_Bootstrap_Bootstrapper, 定义classResource相关方法
  27
+ * Zend_Application_Bootstrap_ResourceBootstrapper, 定义pluginResource相关方法
  28
+ *
  29
+ * @uses       Zend_Application_Bootstrap_Bootstrapper
  30
+ * @uses       Zend_Application_Bootstrap_ResourceBootstrapper
  31
+ * @category   Zend
  32
+ * @package    Zend_Application
  33
+ * @subpackage Bootstrap
  34
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  35
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
  36
+ */
  37
+abstract class Zend_Application_Bootstrap_BootstrapAbstract
  38
+    implements Zend_Application_Bootstrap_Bootstrapper,
  39
+               Zend_Application_Bootstrap_ResourceBootstrapper
  40
+{
  41
+    /**
  42
+     * @var Zend_Application|Zend_Application_Bootstrap_Bootstrapper
  43
+     */
  44
+    protected $_application;
  45
+
  46
+    /**
  47
+     * @var array Internal resource methods (resource/method pairs)
  48
+     */
  49
+    protected $_classResources;
  50
+
  51
+    /**
  52
+     * @var object Resource container
  53
+     */
  54
+    protected $_container;
  55
+
  56
+    /**
  57
+     * @var string
  58
+     */
  59
+    protected $_environment;
  60
+
  61
+    /**
  62
+     * Flattened (lowercase) option keys used for lookups
  63
+     *
  64
+     * @var array
  65
+     */
  66
+    protected $_optionKeys = array();
  67
+
  68
+    /**
  69
+     * @var array
  70
+     */
  71
+    protected $_options = array();
  72
+
  73
+    /**
  74
+     * @var Zend_Loader_PluginLoader_Interface
  75
+     */
  76
+    protected $_pluginLoader;
  77
+
  78
+    /**
  79
+     * @var array Class-based resource plugins
  80
+     */
  81
+    protected $_pluginResources = array();
  82
+
  83
+    /**
  84
+     * @var array Initializers that have been run
  85
+     */
  86
+    protected $_run = array();
  87
+
  88
+    /**
  89
+     * @var array Initializers that have been started but not yet completed (circular dependency detection)
  90
+     */
  91
+    protected $_started = array();
  92
+
  93
+    /**
  94
+     * Constructor
  95
+     *
  96
+     * Sets application object, initializes options, and prepares list of
  97
+     * initializer methods.
  98
+     *
  99
+     * @param  Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
  100
+     * @return void
  101
+     * @throws Zend_Application_Bootstrap_Exception When invalid application is provided
  102
+     */
  103
+    public function __construct($application)
  104
+    {
  105
+        $this->setApplication($application);
  106
+        $options = $application->getOptions();
  107
+        $this->setOptions($options);
  108
+    }
  109
+
  110
+    /**
  111
+     * Set class state
  112
+     *
  113
+     * @param  array $options
  114
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  115
+     */
  116
+    public function setOptions(array $options)
  117
+    {
  118
+        $this->_options = $this->mergeOptions($this->_options, $options);
  119
+
  120
+        $options = array_change_key_case($options, CASE_LOWER);
  121
+        $this->_optionKeys = array_merge($this->_optionKeys, array_keys($options));
  122
+
  123
+        $methods = get_class_methods($this);
  124
+        foreach ($methods as $key => $method) {
  125
+            $methods[$key] = strtolower($method);
  126
+        }
  127
+
  128
+        /**
  129
+         * pluginpaths字段,配置自定义的pluginResources类的自动加载路径和前缀
  130
+         * pluginpath.Hitg_Application_Resource = "Hitg/Application/Resource" 或者
  131
+         * pluginpath.Hitg_App_Res = "Hitg/Application/Resource" 类名和路径不必一一对应 只要配置好了就成
  132
+         */
  133
+        if (array_key_exists('pluginpaths', $options)) {
  134
+            $pluginLoader = $this->getPluginLoader();
  135
+
  136
+            foreach ($options['pluginpaths'] as $prefix => $path) {
  137
+                $pluginLoader->addPrefixPath($prefix, $path);
  138
+            }
  139
+            unset($options['pluginpaths']);
  140
+        }
  141
+
  142
+        /**
  143
+         * 自动调用setXXX方法,设置相应的$option字段
  144
+         * 基于这一点,可以在启动类文件中定义相应的set{$optionKey}()方法,来使用application.ini中自定义的配置做一些自己想做的事情,很灵活
  145
+         * 
  146
+         * 并且如果设置了resources字段,则注册配置的pluginResources(db、cache、log这些...)
  147
+         */
  148
+        foreach ($options as $key => $value) {
  149
+            $method = 'set' . strtolower($key);
  150
+
  151
+            if (in_array($method, $methods)) {
  152
+                $this->$method($value);
  153
+            } elseif ('resources' == $key) {
  154
+                foreach ($value as $resource => $resourceOptions) {
  155
+                    $this->registerPluginResource($resource, $resourceOptions);
  156
+                }
  157
+            }
  158
+        }
  159
+        return $this;
  160
+    }
  161
+
  162
+    /**
  163
+     * Get current options from bootstrap
  164
+     *
  165
+     * @return array
  166
+     */
  167
+    public function getOptions()
  168
+    {
  169
+        return $this->_options;
  170
+    }
  171
+
  172
+    /**
  173
+     * Is an option present?
  174
+     *
  175
+     * @param  string $key
  176
+     * @return bool
  177
+     */
  178
+    public function hasOption($key)
  179
+    {
  180
+        return in_array(strtolower($key), $this->_optionKeys);
  181
+    }
  182
+
  183
+    /**
  184
+     * Retrieve a single option
  185
+     *
  186
+     * @param  string $key
  187
+     * @return mixed
  188
+     */
  189
+    public function getOption($key)
  190
+    {
  191
+        if ($this->hasOption($key)) {
  192
+            $options = $this->getOptions();
  193
+            $options = array_change_key_case($options, CASE_LOWER);
  194
+            return $options[strtolower($key)];
  195
+        }
  196
+        return null;
  197
+    }
  198
+
  199
+    /**
  200
+     * Merge options recursively
  201
+     *
  202
+     * @param  array $array1
  203
+     * @param  mixed $array2
  204
+     * @return array
  205
+     */
  206
+    public function mergeOptions(array $array1, $array2 = null)
  207
+    {
  208
+        if (is_array($array2)) {
  209
+            foreach ($array2 as $key => $val) {
  210
+                if (is_array($array2[$key])) {
  211
+                    $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
  212
+                                  ? $this->mergeOptions($array1[$key], $array2[$key])
  213
+                                  : $array2[$key];
  214
+                } else {
  215
+                    $array1[$key] = $val;
  216
+                }
  217
+            }
  218
+        }
  219
+        return $array1;
  220
+    }
  221
+
  222
+    /**
  223
+     * Get class resources (as resource/method pairs)
  224
+     * 
  225
+     * classResources,即在bootstrap类中方法名以"_init"开头的方法
  226
+     * 这里将他们收集到$this->_classResources数组中缓存起来(因为运行时,bootstrap类方法是不会改变的),下标是去掉"_init"前缀的部分作为classResources资源名,
  227
+     * 值就直接是方法名
  228
+     *
  229
+     * Uses get_class_methods() by default, reflection on prior to 5.2.6,
  230
+     * as a bug prevents the usage of get_class_methods() there.
  231
+     *
  232
+     * @return array
  233
+     */
  234
+    public function getClassResources()
  235
+    {
  236
+        if (null === $this->_classResources) {
  237
+            if (version_compare(PHP_VERSION, '5.2.6') === -1) {
  238
+                $class        = new ReflectionObject($this);
  239
+                $classMethods = $class->getMethods();
  240
+                $methodNames  = array();
  241
+
  242
+                foreach ($classMethods as $method) {
  243
+                    $methodNames[] = $method->getName();
  244
+                }
  245
+            } else {
  246
+                $methodNames = get_class_methods($this);
  247
+            }
  248
+
  249
+            $this->_classResources = array();
  250
+            foreach ($methodNames as $method) {
  251
+                if (5 < strlen($method) && '_init' === substr($method, 0, 5)) {
  252
+                    $this->_classResources[strtolower(substr($method, 5))] = $method;
  253
+                }
  254
+            }
  255
+        }
  256
+
  257
+        return $this->_classResources;
  258
+    }
  259
+
  260
+    /**
  261
+     * Get class resource names
  262
+     *
  263
+     * @return array
  264
+     */
  265
+    public function getClassResourceNames()
  266
+    {
  267
+        $resources = $this->getClassResources();
  268
+        return array_keys($resources);
  269
+    }
  270
+
  271
+    /**
  272
+     * Register a new resource plugin
  273
+     * 注册资源插件,存入$this->_pluginResources数组中,下标是资源名称,值为资源实例或者资源配置选项
  274
+     * 
  275
+     * 如果参数$resource是资源的实例对象,则会调用$this->_resolvePluginResourceName方法,来获取该资源名称作为数组下标。获取的规则是依次检测1、是否设置了$resource->_explicitType
  276
+     * 2、获取资源类全名(get_class()),然后检查其前缀是否在$this->_pluginLoader中注册,若是则去掉前缀只保留短资源类名,若否则使用类全名。此时,数组值即为该资源实例。
  277
+     * ###
  278
+     * 但是这种情况会忽略第二个参数$options,我想应该也支持此参数,调用一下$resource->setOptions($options),支持直接注册资源实例的同时,还可以传入一些特殊配置选项。可以在bootstrap类
  279
+     * 中重装此方法。
  280
+     * ###
  281
+     * 
  282
+     * 如果参数$resource是一个字符串,则将之看做资源名看待作为数组下标,然后把$options参数作为相应的字段值,保存,暂不实例化该资源
  283
+     *
  284
+     * @param  string|Zend_Application_Resource_Resource $resource
  285
+     * @param  mixed  $options
  286
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  287
+     * @throws Zend_Application_Bootstrap_Exception When invalid resource is provided
  288
+     */
  289
+    public function registerPluginResource($resource, $options = null)
  290
+    {
  291
+        if ($resource instanceof Zend_Application_Resource_Resource) {
  292
+            $resource->setBootstrap($this);
  293
+            $pluginName = $this->_resolvePluginResourceName($resource);
  294
+            $this->_pluginResources[$pluginName] = $resource;
  295
+            return $this;
  296
+        }
  297
+
  298
+        if (!is_string($resource)) {
  299
+            throw new Zend_Application_Bootstrap_Exception('Invalid resource provided to ' . __METHOD__);
  300
+        }
  301
+
  302
+        $this->_pluginResources[$resource] = $options;
  303
+        return $this;
  304
+    }
  305
+
  306
+    /**
  307
+     * Unregister a resource from the bootstrap
  308
+     *
  309
+     * @param  string|Zend_Application_Resource_Resource $resource
  310
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  311
+     * @throws Zend_Application_Bootstrap_Exception When unknown resource type is provided
  312
+     */
  313
+    public function unregisterPluginResource($resource)
  314
+    {
  315
+        if ($resource instanceof Zend_Application_Resource_Resource) {
  316
+            if ($index = array_search($resource, $this->_pluginResources, true)) {
  317
+                unset($this->_pluginResources[$index]);
  318
+            }
  319
+            return $this;
  320
+        }
  321
+
  322
+        if (!is_string($resource)) {
  323
+            throw new Zend_Application_Bootstrap_Exception('Unknown resource type provided to ' . __METHOD__);
  324
+        }
  325
+
  326
+        $resource = strtolower($resource);
  327
+        if (array_key_exists($resource, $this->_pluginResources)) {
  328
+            unset($this->_pluginResources[$resource]);
  329
+        }
  330
+
  331
+        return $this;
  332
+    }
  333
+
  334
+    /**
  335
+     * Is the requested plugin resource registered?
  336
+     *
  337
+     * @param  string $resource
  338
+     * @return bool
  339
+     */
  340
+    public function hasPluginResource($resource)
  341
+    {
  342
+        return (null !== $this->getPluginResource($resource));
  343
+    }
  344
+
  345
+    /**
  346
+     * Get a registered plugin resource
  347
+     * 
  348
+     * 太乱了这个方法
  349
+     * 
  350
+     *
  351
+     * @param  string $resourceName
  352
+     * @return Zend_Application_Resource_Resource
  353
+     */
  354
+    public function getPluginResource($resource)
  355
+    {
  356
+        if (array_key_exists(strtolower($resource), $this->_pluginResources)) {
  357
+            $resource = strtolower($resource);
  358
+            if (!$this->_pluginResources[$resource] instanceof Zend_Application_Resource_Resource) {
  359
+                $resourceName = $this->_loadPluginResource($resource, $this->_pluginResources[$resource]);
  360
+                if (!$resourceName) {
  361
+                    throw new Zend_Application_Bootstrap_Exception(sprintf('Unable to resolve plugin "%s"; no corresponding plugin with that name', $resource));
  362
+                }
  363
+                $resource = $resourceName;
  364
+            }
  365
+            return $this->_pluginResources[$resource];
  366
+        }
  367
+
  368
+        /**
  369
+         * 下面这段代码,未免有点过度设计了
  370
+         * 这一坨代码,用处就是当要获取的pluginResource未注册在$this->_pluginResources时,检查一下是否$resource是否等于$this->_resolvePluginResourceName($pluginResource)
  371
+         * 因为,pluginResourceName有三种可能,优先使用顺序是:1、$pluginResourceName->_explicitType值,即在pluginResource类中加入这么一个公共成员变量,用以明确指定其名字,而非通过
  372
+         * $this->registerPluginResource注册的名字!!(这特么不没事儿找事儿吗?);2、pluginResource的short name,即除了前缀以外的名字,一般就是现在使用的注册的名字了~(最好这样一致起来);
  373
+         * 3、类全名!!    下面这一坨代码,就是处理1、3这两种情况的,允许通过$pluginResourceName->_explicitType和$pluginResource的类名获取pluginResource,但是代价却是要遍历真整个注册的
  374
+         * plugin数组并且实例化,直到找到为止,让按需实例化plugin成为不可能! 因此,慎用这种方式~
  375
+         * 
  376
+         * 还有一种情况,看上面的条件语句array_key_exists(strtolower($resource), $this->_pluginResources),就是要获取的$resourceName按小写来(其实就是忽略大小写),与name相关的
  377
+         * 方法如resolvePluginResourceName、 unRegsiterPluginResource都对pluginName做了strtolower处理,但是唯独registerPluginResource没有做这个处理,这样注册到plugin数组
  378
+         * 后,如果大小写不一样,同样会进入下面的代码! 如前端控制器, 资源类是Zend_Application_Resource_Frontcontroller,ini文件配置的是frontController,就出现了这种情况
  379
+         * 
  380
+         * Zend_Loader_Pluginloader::load方法加载plugin类时,对类名是做了ucfirst(strtolower($name))处理,即不管资源名由几个单词组成,只有首字母大写(相应类名也是这样的规范),
  381
+         * 因此重载一下registerPluginResource方法吧!
  382
+         */
  383
+        foreach ($this->_pluginResources as $plugin => $spec) {
  384
+            if ($spec instanceof Zend_Application_Resource_Resource) {
  385
+                $pluginName = $this->_resolvePluginResourceName($spec);
  386
+                if (0 === strcasecmp($resource, $pluginName)) {
  387
+                    unset($this->_pluginResources[$plugin]);
  388
+                    $this->_pluginResources[$pluginName] = $spec;
  389
+                    return $spec;
  390
+                }
  391
+                continue;
  392
+            }
  393
+
  394
+            if (false !== $pluginName = $this->_loadPluginResource($plugin, $spec)) {
  395
+                if (0 === strcasecmp($resource, $pluginName)) {
  396
+                    return $this->_pluginResources[$pluginName];
  397
+                }
  398
+                continue;
  399
+            }
  400
+
  401
+            if (class_exists($plugin)
  402
+            && is_subclass_of($plugin, 'Zend_Application_Resource_Resource')
  403
+            ) { //@SEE ZF-7550
  404
+                $spec = (array) $spec;
  405
+                $spec['bootstrap'] = $this;
  406
+                $instance = new $plugin($spec);
  407
+                $pluginName = $this->_resolvePluginResourceName($instance);
  408
+                unset($this->_pluginResources[$plugin]);
  409
+                $this->_pluginResources[$pluginName] = $instance;
  410
+
  411
+                if (0 === strcasecmp($resource, $pluginName)) {
  412
+                    return $instance;
  413
+                }
  414
+            }
  415
+        }
  416
+
  417
+        return null;
  418
+    }
  419
+
  420
+    /**
  421
+     * Retrieve all plugin resources
  422
+     *
  423
+     * @return array
  424
+     */
  425
+    public function getPluginResources()
  426
+    {
  427
+        foreach (array_keys($this->_pluginResources) as $resource) {
  428
+            $this->getPluginResource($resource);
  429
+        }
  430
+        return $this->_pluginResources;
  431
+    }
  432
+
  433
+    /**
  434
+     * Retrieve plugin resource names
  435
+     *
  436
+     * @return array
  437
+     */
  438
+    public function getPluginResourceNames()
  439
+    {
  440
+        $this->getPluginResources();
  441
+        return array_keys($this->_pluginResources);
  442
+    }
  443
+
  444
+    /**
  445
+     * Set plugin loader for loading resources
  446
+     *
  447
+     * @param  Zend_Loader_PluginLoader_Interface $loader
  448
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  449
+     */
  450
+    public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
  451
+    {
  452
+        $this->_pluginLoader = $loader;
  453
+        return $this;
  454
+    }
  455
+
  456
+    /**
  457
+     * Get the plugin loader for resources
  458
+     * bootstrap里的pluginLoader其实为了获取pluginResources用的
  459
+     * 可以在bootstrap类中重载此方法,以改变默认使用的pluginLoader
  460
+     * 
  461
+     * 源码中,这个get方法没有调用上面相应的set方法,其实是不规范的~~
  462
+     *
  463
+     * @return Zend_Loader_PluginLoader_Interface
  464
+     */
  465
+    public function getPluginLoader()
  466
+    {
  467
+        if ($this->_pluginLoader === null) {
  468
+            $options = array(
  469
+                'Zend_Application_Resource'  => 'Zend/Application/Resource',
  470
+                'ZendX_Application_Resource' => 'ZendX/Application/Resource'
  471
+            );
  472
+
  473
+            $this->_pluginLoader = new Zend_Loader_PluginLoader($options);
  474
+        }
  475
+
  476
+        return $this->_pluginLoader;
  477
+    }
  478
+
  479
+    /**
  480
+     * Set application/parent bootstrap
  481
+     *
  482
+     * @param  Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
  483
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  484
+     */
  485
+    public function setApplication($application)
  486
+    {
  487
+        if (($application instanceof Zend_Application)
  488
+            || ($application instanceof Zend_Application_Bootstrap_Bootstrapper)
  489
+        ) {
  490
+            if ($application === $this) {
  491
+                throw new Zend_Application_Bootstrap_Exception('Cannot set application to same object; creates recursion');
  492
+            }
  493
+            $this->_application = $application;
  494
+        } else {
  495
+            throw new Zend_Application_Bootstrap_Exception('Invalid application provided to bootstrap constructor (received "' . get_class($application) . '" instance)');
  496
+        }
  497
+        return $this;
  498
+    }
  499
+
  500
+    /**
  501
+     * Retrieve parent application instance
  502
+     *
  503
+     * @return Zend_Application|Zend_Application_Bootstrap_Bootstrapper
  504
+     */
  505
+    public function getApplication()
  506
+    {
  507
+        return $this->_application;
  508
+    }
  509
+
  510
+    /**
  511
+     * Retrieve application environment
  512
+     *
  513
+     * @return string
  514
+     */
  515
+    public function getEnvironment()
  516
+    {
  517
+        if (null === $this->_environment) {
  518
+            $this->_environment = $this->getApplication()->getEnvironment();
  519
+        }
  520
+        return $this->_environment;
  521
+    }
  522
+
  523
+    /**
  524
+     * Set resource container
  525
+     *
  526
+     * By default, if a resource callback has a non-null return value, this
  527
+     * value will be stored in a container using the resource name as the
  528
+     * key.
  529
+     *
  530
+     * Containers must be objects, and must allow setting public properties.
  531
+     *
  532
+     * @param  object $container
  533
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  534
+     */
  535
+    public function setContainer($container)
  536
+    {
  537
+        if (!is_object($container)) {
  538
+            throw new Zend_Application_Bootstrap_Exception('Resource containers must be objects');
  539
+        }
  540
+        $this->_container = $container;
  541
+        return $this;
  542
+    }
  543
+
  544
+    /**
  545
+     * Retrieve resource container
  546
+     * 
  547
+     * ###
  548
+     * 这里默认的(资源)container,使用的是Zend_registry实例,但不是单例(注意new Zend_Registry(),而不是Zend_Registry::getInstance())
  549
+     * 因此,如果不改变这个方法,在程序其他地方取得解析资源配置后然后存储在这里的资源,只能通过$bootstrap类的getResource()方法进而通过getContainer()然后获取。
  550
+     * 也就是说,程序其他地方想取得配置好的某个资源,但是却无法获取bootstrap实例或者获取很麻烦,就没有啥子办法了~~~
  551
+     * 这样做的原因,或许是防止程序其他地方使用Zend_Registry时意外覆盖已存储的资源吧
  552
+     * 
  553
+     * 想改变这个默认行为,可以在bootstrap类中重载此方法,修改相应代码为setContainer(Zend_Registry::getInstance()),世界一下简单多了,无论何时,不论何地,
  554
+     * Zend_Registry::getInstance()->get($resourceName);搞定
  555
+     * ###
  556
+     * 
  557
+     * @return object
  558
+     */
  559
+    public function getContainer()
  560
+    {
  561
+        if (null === $this->_container) {
  562
+            $this->setContainer(new Zend_Registry());
  563
+        }
  564
+        return $this->_container;
  565
+    }
  566
+
  567
+    /**
  568
+     * Determine if a resource has been stored in the container
  569
+     *
  570
+     * During bootstrap resource initialization, you may return a value. If
  571
+     * you do, it will be stored in the {@link setContainer() container}.
  572
+     * You can use this method to determine if a value was stored.
  573
+     *
  574
+     * @param  string $name
  575
+     * @return bool
  576
+     */
  577
+    public function hasResource($name)
  578
+    {
  579
+        $resource  = strtolower($name);
  580
+        $container = $this->getContainer();
  581
+        return isset($container->{$resource});
  582
+    }
  583
+
  584
+    /**
  585
+     * Retrieve a resource from the container
  586
+     *
  587
+     * During bootstrap resource initialization, you may return a value. If
  588
+     * you do, it will be stored in the {@link setContainer() container}.
  589
+     * You can use this method to retrieve that value.
  590
+     *
  591
+     * If no value was returned, this will return a null value.
  592
+     *
  593
+     * @param  string $name
  594
+     * @return null|mixed
  595
+     */
  596
+    public function getResource($name)
  597
+    {
  598
+        $resource  = strtolower($name);
  599
+        $container = $this->getContainer();
  600
+        if ($this->hasResource($resource)) {
  601
+            return $container->{$resource};
  602
+        }
  603
+        return null;
  604
+    }
  605
+
  606
+    /**
  607
+     * Implement PHP's magic to retrieve a ressource
  608
+     * in the bootstrap
  609
+     *
  610
+     * @param string $prop
  611
+     * @return null|mixed
  612
+     */
  613
+    public function __get($prop)
  614
+    {
  615
+        return $this->getResource($prop);
  616
+    }
  617
+
  618
+    /**
  619
+     * Implement PHP's magic to ask for the
  620
+     * existence of a ressource in the bootstrap
  621
+     *
  622
+     * @param string $prop
  623
+     * @return bool
  624
+     */
  625
+    public function __isset($prop)
  626
+    {
  627
+        return $this->hasResource($prop);
  628
+    }
  629
+
  630
+    /**
  631
+     * Bootstrap individual, all, or multiple resources
  632
+     *
  633
+     * Marked as final to prevent issues when subclassing and naming the
  634
+     * child class 'Bootstrap' (in which case, overriding this method
  635
+     * would result in it being treated as a constructor).
  636
+     *
  637
+     * If you need to override this functionality, override the
  638
+     * {@link _bootstrap()} method.
  639
+     *
  640
+     * @param  null|string|array $resource
  641
+     * @return Zend_Application_Bootstrap_BootstrapAbstract
  642
+     * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
  643
+     */
  644
+    final public function bootstrap($resource = null)
  645
+    {
  646
+        $this->_bootstrap($resource);
  647
+        return $this;
  648
+    }
  649
+
  650
+    /**
  651
+     * Overloading: intercept calls to bootstrap<resourcename>() methods
  652
+     *
  653
+     * @param  string $method
  654
+     * @param  array  $args
  655
+     * @return void
  656
+     * @throws Zend_Application_Bootstrap_Exception On invalid method name
  657
+     */
  658
+    public function __call($method, $args)
  659
+    {
  660
+        if (9 < strlen($method) && 'bootstrap' === substr($method, 0, 9)) {
  661
+            $resource = substr($method, 9);
  662
+            return $this->bootstrap($resource);
  663
+        }
  664
+
  665
+        throw new Zend_Application_Bootstrap_Exception('Invalid method "' . $method . '"');
  666
+    }
  667
+
  668
+    /**
  669
+     * Bootstrap implementation
  670
+     *
  671
+     * This method may be overridden to provide custom bootstrapping logic.
  672
+     * It is the sole method called by {@link bootstrap()}.
  673
+     *
  674
+     * @param  null|string|array $resource
  675
+     * @return void
  676
+     * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
  677
+     */
  678
+    protected function _bootstrap($resource = null)
  679
+    {
  680
+        if (null === $resource) {
  681
+            foreach ($this->getClassResourceNames() as $resource) {
  682
+                $this->_executeResource($resource);
  683
+            }
  684
+
  685
+            foreach ($this->getPluginResourceNames() as $resource) {
  686
+                $this->_executeResource($resource);
  687
+            }
  688
+        } elseif (is_string($resource)) {
  689
+            $this->_executeResource($resource);
  690
+        } elseif (is_array($resource)) {
  691
+            foreach ($resource as $r) {
  692
+                $this->_executeResource($r);
  693
+            }
  694
+        } else {
  695
+            throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__);
  696
+        }
  697
+    }
  698
+
  699
+    /**
  700
+     * Execute a resource
  701
+     *
  702
+     * Checks to see if the resource has already been run. If not, it searches
  703
+     * first to see if a local method matches the resource, and executes that.
  704
+     * If not, it checks to see if a plugin resource matches, and executes that
  705
+     * if found.
  706
+     *
  707
+     * Finally, if not found, it throws an exception.
  708
+     * 
  709
+     * 这个方法还是蛮巧妙的!
  710
+     * 设置了两个标志位数组:$this_run存储已经运行过的资源,首先检查要执行的资源是否已在$this->_run数组中,在的话直接返回,防止意外重复执行
  711
+     * $this->_started存储当前正在执行但是尚未执行完毕的资源。因为某个classResource的执行体内部,可能会需要先启动其他的classResource或者pluginResource(依赖),如果
  712
+     * classResource1依赖classResource2,而classResource2又意外的依赖classResource1,这样就出现了执行classResource1时要调用classResource2,classResource2执行又去
  713
+     * 调用classResource1的情况,即无限死循环,这个标志位数组就是为了检测这种异常情况的,一个资源执行时,将其在$this->_started数组中相应标志位置true,执行完再注销掉
  714
+     * 
  715
+     * ###
  716
+     * 虽然classResources和pluginResources虽然是存储在两个数组中的,但是运行完后却都存在了$this->_run数组中,下标就是各自的资源名。这里,因为_bootstrap方法中是首先执行classResources的,
  717
+     * 所以,两种resource同名时(说白了就是bootstrap类中_initXXX的XXX也是application.ini所配置的一个资源名),classResource会覆盖掉pluginResource
  718
+     * ###
  719
+     * 
  720
+     * 两种resource执行完时,若有返回值返回,则会被存在Container中(关于Container的OOXX,详见getContainer()方法注释)
  721
+     *
  722
+     * @param  string $resource
  723
+     * @return void
  724
+     * @throws Zend_Application_Bootstrap_Exception When resource not found
  725
+     */
  726
+    protected function _executeResource($resource)
  727
+    {
  728
+        $resourceName = strtolower($resource);
  729
+
  730
+        if (in_array($resourceName, $this->_run)) {
  731
+            return;
  732
+        }
  733
+
  734
+        if (isset($this->_started[$resourceName]) && $this->_started[$resourceName]) {
  735
+            throw new Zend_Application_Bootstrap_Exception('Circular resource dependency detected');
  736
+        }
  737
+
  738
+        $classResources = $this->getClassResources();
  739
+        if (array_key_exists($resourceName, $classResources)) {
  740
+            $this->_started[$resourceName] = true;
  741
+            $method = $classResources[$resourceName];
  742
+            $return = $this->$method();
  743
+            unset($this->_started[$resourceName]);
  744
+            $this->_markRun($resourceName);
  745
+
  746
+            if (null !== $return) {
  747
+                $this->getContainer()->{$resourceName} = $return;
  748
+            }
  749
+
  750
+            return;
  751
+        }
  752
+
  753
+        if ($this->hasPluginResource($resource)) {
  754
+            $this->_started[$resourceName] = true;
  755
+            $plugin = $this->getPluginResource($resource);
  756
+            $return = $plugin->init();
  757
+            unset($this->_started[$resourceName]);
  758
+            $this->_markRun($resourceName);
  759
+
  760
+            if (null !== $return) {
  761
+                $this->getContainer()->{$resourceName} = $return;
  762
+            }
  763
+
  764
+            return;
  765
+        }
  766
+
  767
+        throw new Zend_Application_Bootstrap_Exception('Resource matching "' . $resource . '" not found');
  768
+    }
  769
+
  770
+    /**
  771
+     * Load a plugin resource
  772
+     * 
  773
+     * 加载pluginResource的优先顺序是,后注册的prefix优先,后注册的path优先
  774
+     * 具体细节见 @link Zend_Loader_PluginLoader::load方法(有两处关键的array_reverse)
  775
+     *
  776
+     * @param  string $resource
  777
+     * @param  array|object|null $options
  778
+     * @return string|false
  779
+     */
  780
+    protected function _loadPluginResource($resource, $options)
  781
+    {
  782
+        $options   = (array) $options;
  783
+        $options['bootstrap'] = $this;
  784
+        $className = $this->getPluginLoader()->load(strtolower($resource), false);
  785
+
  786
+        if (!$className) {
  787
+            return false;
  788
+        }
  789
+
  790
+        $instance = new $className($options);
  791
+
  792
+        unset($this->_pluginResources[$resource]);
  793
+
  794
+        if (isset($instance->_explicitType)) {
  795
+            $resource = $instance->_explicitType;
  796
+        }
  797
+        $resource = strtolower($resource);
  798
+        $this->_pluginResources[$resource] = $instance;
  799
+
  800
+        return $resource;
  801
+    }
  802
+
  803
+    /**
  804
+     * Mark a resource as having run
  805
+     *
  806
+     * @param  string $resource
  807
+     * @return void
  808
+     */
  809
+    protected function _markRun($resource)
  810
+    {
  811
+        if (!in_array($resource, $this->_run)) {
  812
+            $this->_run[] = $resource;
  813
+        }
  814
+    }
  815
+
  816
+    /**
  817
+     * Resolve a plugin resource name
  818
+     *
  819
+     * Uses, in order of preference
  820
+     * - $_explicitType property of resource
  821
+     * - Short name of resource (if a matching prefix path is found)
  822
+     * - class name (if none of the above are true)
  823
+     *
  824
+     * The name is then cast to lowercase.
  825
+     *
  826
+     * @param  Zend_Application_Resource_Resource $resource
  827
+     * @return string
  828
+     */
  829
+    protected function _resolvePluginResourceName($resource)
  830
+    {
  831
+        if (isset($resource->_explicitType)) {
  832
+            $pluginName = $resource->_explicitType;
  833
+        } else  {
  834
+            $className  = get_class($resource);
  835
+            $pluginName = $className;
  836
+            $loader     = $this->getPluginLoader();
  837
+            foreach ($loader->getPaths() as $prefix => $paths) {
  838
+                if (0 === strpos($className, $prefix)) {
  839
+                    $pluginName = substr($className, strlen($prefix));
  840
+                    $pluginName = trim($pluginName, '_');
  841
+                    break;
  842
+                }
  843
+            }
  844
+        }
  845
+        $pluginName = strtolower($pluginName);
  846
+        return $pluginName;
  847
+    }
  848
+}
94  Application/Bootstrap/Bootstrapper.php
... ...
@@ -0,0 +1,94 @@
  1
+<?php
  2
+/**
  3
+ * Zend Framework
  4
+ *
  5
+ * LICENSE
  6
+ *
  7
+ * This source file is subject to the new BSD license that is bundled
  8
+ * with this package in the file LICENSE.txt.