Zend Framework1.9 启动过程跟踪解析

 
Zend Framework1.9 启动过程跟踪解析
ZF升级太快,已经很久没有阅读新的源码了,今天遇到了点问题,决定冷静下来跟踪源码全面分析一下zf的启动过程。貌似在启动过程中,1.9与1.8并没有什么变化,但我阅读的类库源码是1.9的,所以标题也就写了1.9,倒没有什么哗众取宠的意思。
一、.htaccess
SetEnv APPLICATION_ENV testing

RewriteEngine On

RewriteCond  %{ REQUEST_FILENAME } - [OR]

RewriteCond  %{ REQUEST_FILENAME } - [OR]

RewriteCond  %{ REQUEST_FILENAME } - d

RewriteRule 
^.*$ - [ NC , L ]

RewriteRule  ^.*$  index . php  [ NC , L ]

这段代码由ZEND STUDIO 7.0构建项目时自动生成。它的作用有两个,一是指定当前程序运行环境为testing,二是对客户端输入的url地址进行rewrite转址。如果输入的文件地址存在,就执行该文件,如果输入的地址不存在,就自动转到index.php上。
二、index.php
// Define path to application directory

defined('APPLICATION_PATH')

    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment

defined('APPLICATION_ENV')

    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path

set_include_path(implode(PATH_SEPARATOR, array(

    realpath(APPLICATION_PATH . '/../library'),

    get_include_path(),

)));

/** Zend_Application */

require_once 'Zend/Application.php';  

// Create application, bootstrap, and run

$application = new Zend_Application(

    APPLICATION_ENV, 

    APPLICATION_PATH . '/configs/application.ini'

);

$application->bootstrap()

            ->run();


index.php定义了程序根文件夹APPLICATION_PATH、程序运行环境APPLICATION_ENV,并把ZEND FRAMEWORD库文件位置加入到include_path中,以便调用。最后,实例化了Zend_Application类,并执行了它的bootstrap()和run方法。
三、Zend\Application.php
Application.php是Zend类库的组成部分,位于zend文件夹下。源码较长,就不复制过来了。
实例化Zend_Application时,只是根据输入的ENV和option参数,确定系统的环境变量。如上文所述,通常我们是这样实例化的:
$application  = new  Zend_Application (

    
APPLICATION_ENV

    
APPLICATION_PATH  '/configs/application.ini'

);

用这种方法,我们依据ENV来取application.ini中设定的参数。
以下是一个简短的INI设置:
[ production ]

phpSettings . display_startup_errors  0

phpSettings
. display_errors  0

; includePaths . library  APPLICATION_PATH  "/../library"

bootstrap . path  APPLICATION_PATH  "/Bootstrap.php"

bootstrap .class =  "Bootstrap"

resources . frontController . controllerDirectory  APPLICATION_PATH  "/controllers"

[ staging  production ]

[
testing  production ]

phpSettings . display_startup_errors  1

phpSettings
. display_errors  1

[ development  production ]

phpSettings . display_startup_errors  1

phpSettings
. display_errors  1

实例化zend_application时,将把production段载入$options数组,由于我们设定的APPLCATION_ENV是testing,所以接下来会载入testing段。而straging和development段会被忽略。日后程序正式上架运行时,我们只要在服务器上把环境配置为staging,然后在staging段中配置本地参数如数据库等,就可以简单地区分运行环境,避免维护困扰。比如本地和服务器的数据库一般不会是同一个,就可以分别在staging和testing中设置各自的数据库配置信息。
Zend_Application中的第二个参数options也可以是一个符合Zend_Config结构的对象,也就是说,可以先用Zend_Config类获取config数据,然后传递过来。这个功能我目前没有用到,也想不出什么情况下适合采用这种方式调用,哪位达人想到的,还请交流一下。
实例化后的数组,可以用getOptions()获取,也可以用getOption($key)获取其中的特定参数,还可以用mergeOptions()进行合并,本文结构是以程序流程组织的,目前还没有用到这些功能,就先不阐述了。
在以下代码中,$application->bootstrap()实例化了Zend_Application_Bootstrap_Bootstrap,$application->bootstrap()调用了Zend_Application_Bootstrap_Bootstrap的run方法:
public function getBootstrap()

    {

        if (null === $this->_bootstrap) {

            $this->_bootstrap = new Zend_Application_Bootstrap_Bootstrap($this);

        }

        return $this->_bootstrap;

    }

    public function bootstrap($resource = null)

    {

        $this->getBootstrap()->bootstrap($resource);

        return $this;

    }

    public function run()

    {

        $this->getBootstrap()->run();

    }


注意其中的这一行代码:
$this -> _bootstrap  = new  Zend_Application_Bootstrap_Bootstrap ( $this );

application把自己的实例作为参数,传递给了bootstrap.也就是说bootstrap将不但可以得到具体参数,还可以继续执行application中包含的方法。

 

四、Zend\Application\Bootstrap\Bootstrap.php
Bootstrap.php基于Zend_Application_Bootstrap_BootstrapAbstract派生。它本身只改动了类的两个方法:

public function __construct($application)

    {

        parent::__construct($application);

        if (!$this->hasPluginResource('FrontController')) {

            $this->registerPluginResource('FrontController');

        }

    }


这段代码在实例化时检查有没有前端控制器,如果没有就注册前端控制器。调用了父类中的hasPluginResource()和registerPluginResource()方法。

public function run()

    {

        $front   = $this->getResource('FrontController');

        $default = $front->getDefaultModule();

        if (null === $front->getControllerDirectory($default)) {

            throw new Zend_Application_Bootstrap_Exception(

                'No default controller directory registered with front controller'

            );

        }

        $front->setParam('bootstrap', $this);

        $front->dispatch();

    }



这段代码调用程序默认模块执行代码显示页面。程序执行到此,运行宣告结束。但该段代码中调用的内容不少,如父类中的getResource()、getDefaultModule()、getControllerDirectory()方法, $front->setParam和$front->dispatch方法,明显来源于前端控制器zend_controller。所以,接下来还有大量代码需要跟踪。

五、Zend\Application\Bootstrap\Zend_Application_Bootstrap_BootstrapAbstract

实例化本类的派生类时,__construct代码如下:

public function __construct($application)

    {

        $this->setApplication($application);

        $options = $application->getOptions();

        $this->setOptions($options);

    }


这段代码通过setApplication()方法处理$application类,将其定义为一个内部变量$this->_application,然后通过setOptions()方法处理$设置的options选项。

setApplication()方法非常简单,就不提了。setOptions()方法相对复杂了一些:

public function setOptions(array $options)

    {

        $this->_options = $this->mergeOptions($this->_options, $options);//把所有options选项合并成一个数组



        $options = array_change_key_case($options, CASE_LOWER);

        $this->_optionKeys = array_merge($this->_optionKeys, array_keys($options));



        $methods = get_class_methods($this);

        foreach ($methods as $key => $method) {

            $methods[$key] = strtolower($method);

        }



        if (array_key_exists('pluginpaths', $options)) {

            $pluginLoader = $this->getPluginLoader();

            

            foreach ($options['pluginpaths'] as $prefix => $path) {

                $pluginLoader->addPrefixPath($prefix, $path);

            }

            unset($options['pluginpaths']);

        }



        foreach ($options as $key => $value) {

            $method = 'set' . strtolower($key);



            if (in_array($method, $methods)) {

                $this->$method($value);

            } elseif ('resources' == $key) {

                foreach ($value as $resource => $resourceOptions) {

                    $this->registerPluginResource($resource, $resourceOptions);

                }

            }

        }

        return $this;

    }



这段代码中,先把所有options选项合并成一个数组,然后把所有key值转化为小写,获取本类中的所有方法并同样把它们的key值转化为小写。

接着,判断是否$options中是否配置了插件,如果有,就实例化Zend_Loader_PluginLoader($options),并加载对应插件路径。

然后,寻找所有资源是否在本类中有对应的set方法,如果有就执行。

最后,通过registerPluginResource()方法注册options中设定的所有resources资源。

然后,我们回到index.ini中的最后一行:$application->run(),我们已经知道,它调用的是Bootstrap.php中的run方法:

public function run()

    {

        $front   = $this->getResource('FrontController');

        $default = $front->getDefaultModule();

        if (null === $front->getControllerDirectory($default)) {

            throw new Zend_Application_Bootstrap_Exception(

                'No default controller directory registered with front controller'

            );

        }

        $front->setParam('bootstrap', $this);

        $front->dispatch();

    }


这其中的getResource('FrontController')方法,事实上是调用zend_registry类来获取预存的FrontController前端控制器。

$default = $front->getDefaultModule();获取默认的controller

$front->setParam('bootstrap', $this);把bootstrap作为参数传递给前端控制器

$front->dispatch();执行和输出

你可能感兴趣的:(function,application,Path,Zend,methods,testing)