理解Flight框架核心
看到了這篇分析flight的文章還不錯(cuò),就轉(zhuǎn)過來了,地址:https://blog.csdn.net/sky_zhe/article/details/38906689
?
Flight框架(官網(wǎng))是一個(gè)微型的PHP框架,它簡單,快速,可擴(kuò)展。借助Flight可以快捷而輕松的創(chuàng)建你的RESTFul web應(yīng)用。
雖然是一個(gè)微型的框架,而且代碼量確實(shí)也較少,但我在閱讀Flight代碼的過程中,感到了它設(shè)計(jì)和構(gòu)思獨(dú)特而精妙的地方,覺得有學(xué)習(xí)的價(jià)值,便決定做一下整理分享出來。
如果你對框架還不熟悉,可以先去官網(wǎng)看下文檔,如果需要中文文檔的話可以可以點(diǎn)這里。
如果你已經(jīng)對Flight有一定了解了,接下來就來看看Flight是怎么工作的吧。
<?php class Flight { /** * Framework engine. * @var object */ private static $engine; // Don't allow object instantiation private function __construct() {} private function __destruct() {} private function __clone() {} /** * 之前已經(jīng)看到了,框架內(nèi)所有函數(shù)都是以Flight類的靜態(tài)函數(shù)形式調(diào)用的 * __callStatic()這個(gè)魔術(shù)方法能處理所有的靜態(tài)函數(shù) * @param string $name Method name * @param array $params Method parameters * @return mixed Callback results */ public static function __callStatic($name, $params) { static $initialized = false; if (!$initialized) { //這里定義框架的自動(dòng)加載機(jī)制,實(shí)際上是依據(jù)PSR-0標(biāo)準(zhǔn)來做的 require_once __DIR__.'/autoload.php'; //Engine類是框架的引擎所在 self::$engine = new \flight\Engine(); $initialized = true; } //在這里,Flight對Engine包裝了一層而已。對Flight類靜態(tài)函數(shù)的調(diào)用,實(shí)質(zhì)上是對Engine類的相應(yīng)函數(shù)的調(diào)用 return \flight\core\Dispatcher::invokeMethod(array(self::$engine, $name), $params); } } //那么就直接就來看看Dispatcher::invokeMethod函數(shù)吧 namespace flight\core; class Dispatcher { /** * 調(diào)用一個(gè)方法 * @param mixed $func Class method * @param array $params Class method parameters * @return mixed Function results */ public static function invokeMethod($func, array &$params = array()) { list($class, $method) = $func; $instance = is_object($class); switch (count($params)) { case 0: return ($instance) ? $class->$method() : $class::$method(); case 1: return ($instance) ? $class->$method($params[0]) : $class::$method($params[0]); case 2: return ($instance) ? $class->$method($params[0], $params[1]) : $class::$method($params[0], $params[1]); case 3: return ($instance) ? $class->$method($params[0], $params[1], $params[2]) : $class::$method($params[0], $params[1], $params[2]); case 4: return ($instance) ? $class->$method($params[0], $params[1], $params[2], $params[3]) : $class::$method($params[0], $params[1], $params[2], $params[3]); case 5: return ($instance) ? $class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) : $class::$method($params[0], $params[1], $params[2], $params[3], $params[4]); default: return call_user_func_array($func, $params); } } }?
上面注釋里提到了,自動(dòng)加載和PSR-0,我之前寫過一篇關(guān)于這部分內(nèi)容的文章。Flight框架的自動(dòng)加載就是基于namespace和psr-0標(biāo)準(zhǔn)的:
?
//只列出有關(guān)自動(dòng)加載部分的主要代碼 namespace flight\core; class Loader { /** * Starts/stops autoloader. * * @param bool $enabled Enable/disable autoloading * @param mixed $dirs Autoload directories */ public static function autoload($enabled = true, $dirs = array()) { if ($enabled) { spl_autoload_register(array(__CLASS__, 'loadClass')); } else { spl_autoload_unregister(array(__CLASS__, 'loadClass')); } if (!empty($dirs)) { self::addDirectory($dirs); } } /** * Autoloads classes. * * @param string $class Class name */ public static function loadClass($class) { $class_file = str_replace(array('\\', '_'), '/', $class).'.php'; foreach (self::$dirs as $dir) { $file = $dir.'/'.$class_file; if (file_exists($file)) { require $file; return; } } } }?
?
再繼續(xù)往下看之前,我們不妨先對Flight內(nèi)主要的類和函數(shù)進(jìn)行一下梳理,以下是Flight框架的內(nèi)置類:
?
- Engine類:包含了這個(gè)框架的核心功能。它的責(zé)任是加載HTTP請求,運(yùn)行已注冊的服務(wù),并生成最后的HTTP響應(yīng)。
- Loader類:它負(fù)責(zé)框架內(nèi)對象的加載。用自定義的初始化參數(shù)來生成新的類實(shí)例,并且維護(hù)可復(fù)用的類實(shí)例的列表。它還處理剛才提到過的類的自動(dòng)加載。
- Dispatcher類:它負(fù)責(zé)框架內(nèi)事件的分發(fā)處理。事件即是對類方法或函數(shù)的簡單的稱呼(別名)。它還允許你在事件上的掛鉤點(diǎn)掛載別的函數(shù),能夠改變函數(shù)的輸入或者輸出。
- Router類:它負(fù)責(zé)將一個(gè)HTTP講求發(fā)送到指定的函數(shù)進(jìn)行處理。它視圖將請求的URL和一系列用戶定義的URL范式進(jìn)行匹配。
- Route類:它負(fù)責(zé)路由的具體實(shí)現(xiàn)。Router相當(dāng)于對Route的包裝。
- Request類:它代表了一個(gè)HTTP請求。所有來自$_GET,$_POST,$_COOKIE,$_FILES中的數(shù)據(jù)都要通過Request類獲取和訪問。默認(rèn)的Request屬性就包括url,base,method,user_agent等。
- Response類:對應(yīng)于Request,它代表了一個(gè)HTTP響應(yīng)。這個(gè)對象包括了返回頭,HTTP狀態(tài)碼和返回體。
- View類:視圖類負(fù)責(zé)將輸出展示。它提供了在渲染時(shí)管理視圖數(shù)據(jù)和將數(shù)據(jù)插入視圖模板的函數(shù)。
- Collection類:它允許你既可以以使用數(shù)組的方式,也能以使用對象的方式來訪問數(shù)據(jù)。
?
Flight框架的函數(shù)分兩部分,一部分是核心函數(shù):
?
Flight::map($name, $callback) // Creates a custom framework method. Flight::register($name, $class, [$params], [$callback]) // Registers a class to a framework method. Flight::before($name, $callback) // Adds a filter before a framework method. Flight::after($name, $callback) // Adds a filter after a framework method. Flight::path($path) // Adds a path for autoloading classes. Flight::get($key) // Gets a variable. Flight::set($key, $value) // Sets a variable. Flight::has($key) // Checks if a variable is set. Flight::clear([$key]) // Clears a variable.?
另一部分是擴(kuò)展函數(shù):
?
Flight::start() // Starts the framework. Flight::stop() // Stops the framework and sends a response. Flight::halt([$code], [$message]) // Stop the framework with an optional status code and message. Flight::route($pattern, $callback) // Maps a URL pattern to a callback. Flight::redirect($url, [$code]) // Redirects to another URL. Flight::render($file, [$data], [$key]) // Renders a template file. Flight::error($exception) // Sends an HTTP 500 response. Flight::notFound() // Sends an HTTP 404 response. Flight::etag($id, [$type]) // Performs ETag HTTP caching. Flight::lastModified($time) // Performs last modified HTTP caching. Flight::json($data, [$code], [$encode]) // Sends a JSON response. Flight::jsonp($data, [$param], [$code], [$encode]) // Sends a JSONP response.?
?
Flight框架的使用方式就是對Flight類靜態(tài)函數(shù)調(diào)用(Flight::func()),我們在上面提到過,其實(shí)質(zhì)是對Engine對象中函數(shù)的調(diào)用($engineObj->func())。
?
而Engine類的函數(shù)有兩類,一類是核心函數(shù),是直接進(jìn)行調(diào)用(相對于動(dòng)態(tài)調(diào)用)的,另外的擴(kuò)展函數(shù),則是進(jìn)行動(dòng)態(tài)調(diào)用的。
?
此外,在Flight中加載類和資源,獲得某個(gè)類的實(shí)例,直接調(diào)用Flight::className()即可,等同于$engineObj->className()。這個(gè)也是采用動(dòng)態(tài)調(diào)用的形式。也就是說,除了Engine類的核心函數(shù),其他函數(shù)(類)都是動(dòng)態(tài)調(diào)用的。這樣,框架就可以為此提供一個(gè)統(tǒng)一的入口了。
?
namespace flight; class Engine { //.... public function __construct() { $this->vars = array(); //上面提到過,Flight中,Dispatcher負(fù)責(zé)處理函數(shù),Loader負(fù)責(zé)對象的加載 $this->loader = new Loader(); $this->dispatcher = new Dispatcher(); $this->init(); } /** * __call是一個(gè)魔術(shù)方法,當(dāng)調(diào)用一個(gè)不存在的函數(shù)時(shí),會調(diào)用到該函數(shù) * 剛才講的動(dòng)態(tài)調(diào)用就是通過這個(gè)函數(shù)進(jìn)行的 * @param string $name Method name * @param array $params Method parameters * @return mixed Callback results */ public function __call($name, $params) { //先判斷是類還是可直接調(diào)用的函數(shù) $callback = $this->dispatcher->get($name); //如果是函數(shù),通過dispatcher處理 if (is_callable($callback)) { return $this->dispatcher->run($name, $params); } //是否是共享實(shí)例 $shared = (!empty($params)) ? (bool)$params[0] : true; //通過loader加載該類的對象 return $this->loader->load($name, $shared); } /** * 框架初始化 */ public function init() { static $initialized = false; $self = $this; if ($initialized) { $this->vars = array(); $this->loader->reset(); $this->dispatcher->reset(); } // Flight中,類會通過loader的register函數(shù)進(jìn)行注冊 // 注冊默認(rèn)組件 $this->loader->register('request', '\flight\net\Request'); $this->loader->register('response', '\flight\net\Response'); $this->loader->register('router', '\flight\net\Router'); $this->loader->register('view', '\flight\template\View', array(), function($view) use ($self) { $view->path = $self->get('flight.views.path'); }); // 注冊框架方法 $methods = array( 'start','stop','route','halt','error','notFound', 'render','redirect','etag','lastModified','json','jsonp' ); // Flight中,method會通過dispatcher的set函數(shù)將對應(yīng)的回調(diào)函數(shù)綁定到一個(gè)事件中 // 為了可以進(jìn)行動(dòng)態(tài)調(diào)用,Enginge的擴(kuò)展函數(shù)全部是通過 _method 的名字定義的 foreach ($methods as $name) { $this->dispatcher->set($name, array($this, '_'.$name)); } // 默認(rèn)的配置 $this->set('flight.base_url', null); $this->set('flight.handle_errors', true); $this->set('flight.log_errors', false); $this->set('flight.views.path', './views'); $initialized = true; } /** * 將一個(gè)類注冊到框架方法中,我們就是通過這個(gè)函數(shù)注冊我們自定義的類的 * * @param string $name Method name * @param string $class Class name * @param array $params Class initialization parameters * @param callback $callback Function to call after object instantiation * @throws \Exception If trying to map over a framework method */ public function register($name, $class, array $params = array(), $callback = null) { if (method_exists($this, $name)) { throw new \Exception('Cannot override an existing framework method.'); } //通過loader的register函數(shù)進(jìn)行注冊 $this->loader->register($name, $class, $params, $callback); } /** * 將一個(gè)回調(diào)函數(shù)映射到框架方式中,我們就是通過這個(gè)函數(shù)映射我們自定義函數(shù)的 * * @param string $name Method name * @param callback $callback Callback function * @throws \Exception If trying to map over a framework method */ public function map($name, $callback) { if (method_exists($this, $name)) { throw new \Exception('Cannot override an existing framework method.'); } //會通過dispatcher的set函數(shù)將對應(yīng)的回調(diào)函數(shù)綁定到一個(gè)事件中 $this->dispatcher->set($name, $callback); } //... }?
Flight中的兩個(gè)核心函數(shù),map是映射自定義的函數(shù),最后是通過dispathcer的set函數(shù)實(shí)現(xiàn)的,register是注冊自定義的類,最后是通過loader的register函數(shù)實(shí)現(xiàn)的。而框架自己的核心組件和擴(kuò)展函數(shù),Engine在初始化過程幫我們完成了這兩個(gè)過程。接著Flight提供了一個(gè)統(tǒng)一的入口,可以動(dòng)態(tài)調(diào)用所有非核心的函數(shù),類。這就是Flight的核心加載機(jī)制了。
?
可能你還有疑問,為什么Flight要使用動(dòng)態(tài)調(diào)用的形式去訪問這些函數(shù)或?qū)ο?#xff1f;尤其是對于Engine的擴(kuò)展函數(shù),為什么不直接進(jìn)行調(diào)用呢?因?yàn)镕light可以對它們進(jìn)行過濾或重寫。過濾和重寫是Flight框架進(jìn)行擴(kuò)展的重要功能。框架實(shí)現(xiàn)了統(tǒng)一的資源操作方式后,就可以方便的進(jìn)行重寫或者過濾的處理了。需要注意的是,核心函數(shù)諸如map和register是不能夠進(jìn)行過濾或重寫的,相信你已經(jīng)清楚為什么了。
?
框架的重寫功能還是使用的map和register這兩個(gè)函數(shù)。這個(gè)功能因?yàn)榭蚣艿脑O(shè)計(jì)方式,很輕易的完成了。在Dispatcher和Loader中都動(dòng)態(tài)維護(hù)了一個(gè)映射表,Dispatcher里是回調(diào)到事件的映射,Loader中是class到實(shí)例和構(gòu)造函數(shù)等的映射。這樣,注冊自定義函數(shù)或類時(shí),遇到一樣名字就會覆蓋掉之前的,而使用時(shí)只返回最新的。下面是Loader類的部分代碼:
?
namespace flight\core; class Loader { //.... /** * 注冊一個(gè)類 * * @param string $name Registry name * @param string|callable $class Class name or function to instantiate class * @param array $params Class initialization parameters * @param callback $callback Function to call after object instantiation */ public function register($name, $class, array $params = array(), $callback = null) { unset($this->instances[$name]); $this->classes[$name] = array($class, $params, $callback); } /** * 加載一個(gè)已注冊的類 * * @param string $name Method name * @param bool $shared Shared instance * @return object Class instance */ public function load($name, $shared = true) { $obj = null; //$this->classes是注冊過的類 //$this->instances是加載過的實(shí)例 if (isset($this->classes[$name])) { list($class, $params, $callback) = $this->classes[$name]; $exists = isset($this->instances[$name]); //是不是共享實(shí)例 if ($shared) { $obj = ($exists) ? $this->getInstance($name) : $this->newInstance($class, $params); if (!$exists) { $this->instances[$name] = $obj; } } else { $obj = $this->newInstance($class, $params); } if ($callback && (!$shared || !$exists)) { $ref = array(&$obj); call_user_func_array($callback, $ref); } } return $obj; } /** * 得到一個(gè)類的單一實(shí)例 * * @param string $name Instance name * @return object Class instance */ public function getInstance($name) { return isset($this->instances[$name]) ? $this->instances[$name] : null; } /** * 得到一個(gè)類的新的實(shí)例 * * @param string|callable $class Class name or callback function to instantiate class * @param array $params Class initialization parameters * @return object Class instance */ public function newInstance($class, array $params = array()) { if (is_callable($class)) { return call_user_func_array($class, $params); } switch (count($params)) { case 0: return new $class(); case 1: return new $class($params[0]); case 2: return new $class($params[0], $params[1]); case 3: return new $class($params[0], $params[1], $params[2]); case 4: return new $class($params[0], $params[1], $params[2], $params[3]); case 5: return new $class($params[0], $params[1], $params[2], $params[3], $params[4]); default: $refClass = new \ReflectionClass($class); return $refClass->newInstanceArgs($params); } } //.... }?
跟過濾器功能有關(guān)的函數(shù)是before和after,分別是在被過濾函數(shù)處理之前或之后進(jìn)行操作。最終是在Dispatcher類中實(shí)現(xiàn)的。
?
?
namespace flight; class Engine { /** * Adds a pre-filter to a method. * * @param string $name Method name * @param callback $callback Callback function */ public function before($name, $callback) { $this->dispatcher->hook($name, 'before', $callback); } /** * Adds a post-filter to a method. * * @param string $name Method name * @param callback $callback Callback function */ public function after($name, $callback) { $this->dispatcher->hook($name, 'after', $callback); } } namespace flight\core; class Dispatcher { /** * 將回調(diào)注冊到一個(gè)事件之中 * * @param string $name Event name * @param callback $callback Callback function */ public function set($name, $callback) { $this->events[$name] = $callback; } /** * 得到事件關(guān)聯(lián)的回調(diào) * * @param string $name Event name * @return callback $callback Callback function */ public function get($name) { return isset($this->events[$name]) ? $this->events[$name] : null; } /** * 在事件上掛一個(gè)回調(diào)函數(shù) * * @param string $name Event name * @param string $type Filter type * @param callback $callback Callback function */ public function hook($name, $type, $callback) { $this->filters[$name][$type][] = $callback; } /** * 對事件進(jìn)行分發(fā)處理 * * @param string $name Event name * @param array $params Callback parameters * @return string Output of callback */ public function run($name, array $params = array()) { $output = ''; // 運(yùn)行前置過濾器 if (!empty($this->filters[$name]['before'])) { $this->filter($this->filters[$name]['before'], $params, $output); } // 運(yùn)行所請求的方法 $output = $this->execute($this->get($name), $params); // 運(yùn)行后置過濾器 if (!empty($this->filters[$name]['after'])) { $this->filter($this->filters[$name]['after'], $params, $output); } return $output; } }?
下面,還差最后一步,運(yùn)行這個(gè)框架時(shí)處理流程是怎樣的呢?
?
namespace flight; class Engine { /** * 啟動(dòng)這個(gè)框架 */ public function _start() { $dispatched = false; $self = $this; $request = $this->request(); $response = $this->response(); $router = $this->router(); // 沖刷掉已經(jīng)存在的輸出 if (ob_get_length() > 0) { $response->write(ob_get_clean()); } // 啟動(dòng)輸出緩沖 ob_start(); // 開啟錯(cuò)誤處理 $this->handleErrors($this->get('flight.handle_errors')); // 對AJAX請求關(guān)閉緩存 if ($request->ajax) { $response->cache(false); } // 允許后置過濾器的運(yùn)行 $this->after('start', function() use ($self) { //start完成之后會調(diào)用stop()函數(shù) $self->stop(); }); // 對該請求進(jìn)行路由 while ($route = $router->route($request)) { $params = array_values($route->params); //是否讓路由鏈繼續(xù)下去 $continue = $this->dispatcher->execute( $route->callback, $params ); $dispatched = true; if (!$continue) break; $router->next(); } //路由沒找匹配到 if (!$dispatched) { $this->notFound(); } } /** * 停止這個(gè)框架并且輸出當(dāng)前的響應(yīng)內(nèi)容 * * @param int $code HTTP status code */ public function _stop($code = 200) { $this->response() ->status($code) ->write(ob_get_clean()) ->send(); } }
至此,應(yīng)該對Flight核心的設(shè)計(jì),功能以及處理流程有所認(rèn)識了吧。至于其他的路由,請求已經(jīng)響應(yīng)等內(nèi)容,就留給讀者自行學(xué)習(xí)吧。
轉(zhuǎn)載于:https://www.cnblogs.com/jiujuan/p/8874626.html
總結(jié)
以上是生活随笔為你收集整理的理解Flight框架核心的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xcode7 项目转 Xcode6 时
- 下一篇: libtool: Version mis