Yaf framework 相关项目骨架
在 yaf 的源码目录,附带了一个工具 tools 目录, 下面有个 cg/yaf_cg 工具,是通过模版生成 yaf 项目骨架的工具。
Usage:
./yaf_cg ApplicationName [ApplicationPath]
项目目录
├── application
│ ├── Bootstrap.php
│ ├── controllers
│ ├── library
│ ├── models
│ ├── plugins
│ └── views
├── conf
│ └── application.ini
└── index.php
- index.php 程序的入口文件
- application 主要的应用程序代码目录。
2.1 可以通过application.directory
这个配置来修改。
2.2 application 目录下的目录(controllers、models、plugins、views)是默认的代码结构目录,yaf 会根据相关的类名称,去相关目录下加载相关的文件,例如IndexController extends Yaf\Controller_Abstract
这个文件,yaf 就会到controllers
目录下查找 Index.php、类UserModel
就会到 models 目录下查找 User.php(可以定制文件后缀或前缀模式,如 ModelUser 就是前缀模式)。
2.3 除了 library 目录以外,其它目录都是在框架编译的时候确定的,library 目录主要是存放一些本地库文件例如我有个Helper_Html (名称空间模式 Helper\Html)
类,通过注册本地类名称空间Yaf_Loader::registerLocalNamespace("Helper")
来加载,文件的存储结构是library/helper/Html.php
需要注意的就是要开启名称空间,使用namespace
的类名称和不使用namespace
的 classname 有些差异。
2.4 Bootstrap.php 是框架的引导类 (可以没有),继承自Yaf\Bootstrap_Abstract
,在 Application 对象创建的时候可以调用$application->bootstrap()->run()
这样会执行Bootstrap类里所有_init
开头的方法,一般用于初始化一些配置或者数据或者初始化plugin等资源。 - conf 是配置文件目录, 也是可以修改的。
Yaf 完成一次请求的流程图

流程解读
index.php 入口
<?php
define('APPLICATION_PATH', dirname(__FILE__));
$application = new Yaf\Application(APPLICATION_PATH . "/conf/application.ini");
$application->bootstrap() // 执行引导类Bootstrap.php 相关的引导方法
->run();
Bootstrap.php 引导类
<?php
/** * 所有的 _init 开头的的方法,都会被执行 */
class Bootstrap extends Yaf\Bootstrap_Abstract {
/** * 初始化本地类库的名称空间 Biz Ns * 例如本地类库 Biz_Test, Ns\Test 放在library目录下 * library/biz/Test.php * library/ns/Test.php */
public function _initRegisterLocalClass(Yaf\Dispatcher $dispatcher) {
$loader = Yaf\Loader::getInstance();
$loader->registerLocalNamespace(array("Biz", "Ns"));
}
/** * 初始化一些配置信息 */
public function _initConfig() {
$arrConfig = Yaf\Application::app()->getConfig();
Yaf\Registry::set('config', $arrConfig);
}
public function _initPlugin(Yaf\Dispatcher $dispatcher) {
// 初始化一些插件, 插件文件存放在 plugins 目录,
// 类名字规则是 XxxxPlugin 放在 plugins 下的 Xxxx.php 文件
}
public function _initRoute(Yaf\Dispatcher $dispatcher) {
// 增加一些路由规则
// 默认是 Yaf_Route_Static
// 支持以下方式
// Yaf_Route_Simple
// Yaf_Route_Supervar
// Yaf_Route_Static
// Yaf_Route_Map
// Yaf_Route_Rewrite
// Yaf_Route_Regex
}
…… 可以做更多的事情
}
插件 Plugins
创建一个插件 Sample
// Sample.php
class SamplePlugin extends Yaf\Plugin_Abstract {
插件定义了6个 hook
routerStartup
routerShutdown
dispatchLoopStartup
preDispatch
postDispatch
dispatchLoopShutdown
插件的执行顺序是先进先调用。
路由规则 routes
yaf 支持多种路由规则,默认采用了
Yaf_Route_Static
方式,支持以下方式Yaf_Route_Simple
Yaf_Route_Supervar
Yaf_Route_Static
Yaf_Route_Map
Yaf_Route_Rewrite
Yaf_Route_Regex
路由规则可以配置在 application.ini 配置文件内,也可以在程序初始化的时候动态生成增加,用户也可以通过实现
Route_Interface
接口,自定义路由规则。具体各种路由协议的含义和使用方式见路由协议详解。视图 Views
yaf 的视图文件默认放在 views 目录下,默认文件后缀名称
.phtml
,view 文件就是 php 文件。模块 modules 支持
- yaf 默认支持模块的,在默认的路由模式下,一般请求的路径为
/index.php/Module/Controller/Action/p1/v1/p2/v2
(通过 rewrite 规则去除 index.php)就是/Module/Controller/Action/p1/v1/p2/v2
- 如果不指定默认的模块控制器和action,那么这三个值默认为
Index
,请求/index.php
执行的路由规则就是/Index/Index/Index
。 - 增加一个模块 Test, 需要配置
application.modules="Index,Test"
,且在 applications 下增加 modules/Test/controllers 文件夹,这样/test/index/index
就会执行modules/Test/controllers/Index.php
文件的indexAction
方法 - 如果只有一个默认 module ,那么不需要 modules 文件夹。
引入外部库
composer 方式
通过 composer 安装的第三方库都会带有autoload.php 文件,那么根据具体的情况可以在 index.php 入口文件或者 Bootstrap 引导类内,加载这个 autoload.php 文件来使用外部类,例如
<?php
// index.php
define('APPLICATION_PATH',
dirname(__FILE__));
// 引入第三方类库
require(APPLICATION_PATH . '/vendor/autoload.php');
$application = new Yaf\Application(APPLICATION_PATH . "/conf/application.ini");
$application->bootstrap()
->run();
在 Controller 使用第三方库
<?php
use GuzzleHttp\Client;
class IndexController extends Yaf\Controller_Abstract {
public function indexAction() {
$httpClient = new Client();
var_dump($httpClient);
}
全局库 yaf.library
这个配置选项是配置在 php.ini 内的,例如多个项目需要公用一些全局的库文件,那么通过指定该路径,来让不同的项目之间共享同一个库文件。
自定义 class loader
如果你的库文件命名方式等不符合 yaf 的自动加载风格,那么可以自定义 auto 方式,可以在 Bootstrap 引导类加载的时候进行注册。
主要类库代码解析
Yaf_Loader::autoload
这个是 Yaf 自动加载类的函数, 整个加载行为会受到
yaf.use_spl_autoload
这个配置的影响,开启的情况下, Yaf在加载不成功的情况下, 会继续让PHP的自动加载函数加载,否则会触发 E_WARNING 或者 E_STRICT 下面是相关部分源码:if (yaf_internal_autoload(file_name, file_name_len, &directory TSRMLS_CC)) {
char *lc_classname = zend_str_tolower_dup(origin_classname, class_name_len);
if (zend_hash_exists(EG(class_table), lc_classname, class_name_len + 1)) {
……
} else {
efree(lc_classname);
php_error_docref(NULL TSRMLS_CC, E_STRICT, "Could not find class %s in %s", class_name, directory);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed opening script %s: %s", directory, strerror(errno));
}
Yaf_Loader::autoload 流程图

- 将 \ 处理为 _ 主要是为了让类名无论是名称空间模式,还是 _ 模式,都兼容。
- 确定文件目录位置: 主要是根据系统的几个固定目录来判断文件的位置 controllers、models、plugins、library 等。
Yaf_Application::bootstrap
除了 autoload 是一个比较重要的方法以外,bootstrap 也是一个比较重要的方法,因为经常会有很多数据初始化等代码需要在这个阶段来做,
bootstrap()
方法的主要工作就是循环遍历 Bootstrap extends Yaf_Bootstrap
这个类的所有以 _init 开头的方法,相关代码如下methods = &((*ce)->function_table);
for(zend_hash_internal_pointer_reset(methods);
zend_hash_has_more_elements(methods) == SUCCESS;
zend_hash_move_forward(methods)) {
char *func;
uint len;
ulong idx;
zend_hash_get_current_key_ex(methods, &func, &len, &idx, 0, NULL);
/* cann't use ZEND_STRL in strncasecmp, it cause a compile failed in VS2009 */
/* YAF_BOOTSTRAP_INITFUNC_PREFIX 就是 _init */
if (strncasecmp(func, YAF_BOOTSTRAP_INITFUNC_PREFIX, sizeof(YAF_BOOTSTRAP_INITFUNC_PREFIX)-1)) {
continue;
}
zend_call_method(&bootstrap, *ce, NULL, func, len - 1, NULL, 1, dispatcher, NULL TSRMLS_CC);
/** an uncaught exception threw in function call */
if (EG(exception)) {
zval_ptr_dtor(&bootstrap);
RETURN_FALSE;
}
}
Yaf_Dispatcher
Yaf_Dispatcher 是个非常重要的的类,基本贯穿了整个开发过程,包括 plugin 的注册和调用,都和 Yaf_Dispatcher 这个类密切相关。注册类的代码非常简单
PHP_METHOD(yaf_dispatcher, registerPlugin) {
zval *plugin, *plugins;
yaf_dispatcher_t *self = getThis();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &plugin) == FAILURE) {
return;
}
if (Z_TYPE_P(plugin) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(plugin), yaf_plugin_ce TSRMLS_CC)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expect a %s instance", yaf_plugin_ce->name);
RETURN_FALSE;
}
plugins = zend_read_property(yaf_dispatcher_ce, self, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_PLUGINS), 1 TSRMLS_CC);
Z_ADDREF_P(plugin);
add_next_index_zval(plugins, plugin);
RETVAL_ZVAL(self, 1, 0);
}
调用插件以及插件中的 6 个 hook 的代码主要集中在
yaf_response_t * yaf_dispatcher_dispatch(yaf_dispatcher_t *dispatcher TSRMLS_DC)
方法内,主要是 Yaf_Application::run() 的时候,调用该方法。/** 代表了 hook 的 6 个阶段 YAF_PLUGIN_HOOK_ROUTESTARTUP YAF_PLUGIN_HOOK_ROUTESHUTDOWN YAF_PLUGIN_HOOK_LOOPSTARTUP YAF_PLUGIN_HOOK_PREDISPATCH YAF_PLUGIN_HOOK_POSTDISPATCH YAF_PLUGIN_HOOK_LOOPSHUTDOWN */
if (!yaf_request_is_routed(request TSRMLS_CC)) {
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_ROUTESTARTUP, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
if (!yaf_dispatcher_route(dispatcher, request TSRMLS_CC)) {
yaf_trigger_error(YAF_ERR_ROUTE_FAILED TSRMLS_CC, "Routing request failed");
YAF_EXCEPTION_HANDLE_NORET(dispatcher, request, response);
zval_ptr_dtor(&response);
return NULL;
}
yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_ROUTESHUTDOWN, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
(void)yaf_request_set_routed(request, 1 TSRMLS_CC);
} else {
yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
}
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_LOOPSTARTUP, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
view = yaf_dispatcher_init_view(dispatcher, NULL, NULL TSRMLS_CC);
if (!view) {
return NULL;
}
do {
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_PREDISPATCH, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
if (!yaf_dispatcher_handle(dispatcher, request, response, view TSRMLS_CC)) {
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
zval_ptr_dtor(&response);
return NULL;
}
yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_POSTDISPATCH, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
} while (--nesting > 0 && !yaf_request_is_dispatched(request TSRMLS_CC));
YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_LOOPSHUTDOWN, request, response);
YAF_EXCEPTION_HANDLE(dispatcher, request, response);
if (0 == nesting && !yaf_request_is_dispatched(request TSRMLS_CC)) {
yaf_trigger_error(YAF_ERR_DISPATCH_FAILED TSRMLS_CC, "The max dispatch nesting %ld was reached", YAF_G(forward_limit));
YAF_EXCEPTION_HANDLE_NORET(dispatcher, request, response);
zval_ptr_dtor(&response);
return NULL;
}
以上就是几个主要的类和相关代码流程。
评论