YAF框架入門教程

YAF框架試用

YAF框架入門教程 - http://www.reibang.com/p/1460d2296f19
下載地址 - http://pecl.php.net/package/yaf
Yaf - Manual - http://php.net/manual/en/book.yaf.php
Yaf(Yet Another Framework)用戶手冊 - http://www.laruence.com/manual/index.html
yaf程序模板生成工具 https://github.com/laruence/yaf/tree/master/tools/cg
PHP-Yaf執(zhí)行流程-源碼分析 - http://www.reibang.com/p/130389235abc
數(shù)據(jù)庫外擴 Medoo Guidebook - https://medoo.in/api/new
yaf-example | Larave - https://laravel-china.org/articles/4308/yaf-example
Medoo 1.6.1 API 手冊(HTML單文件版本示例代碼包內(nèi)包含) - https://www.aiirobo.com/yaf/install/medoo_1.6.1.html
YAF手冊CHM文件下載(示例代碼包內(nèi)含) - https://www.aiirobo.com/yaf/install/yaf_manual.chm

本文示例代碼下載:
鏈接:https://pan.baidu.com/s/1i_PEwCxsWqxblmIqRASSlA

如果你夠接近堅果梳杏,DEMO例子也許還在服務(wù)器上玄糟,可以直接戳戳這個鏈接體驗:https://www.aiirobo.com/yaf/

介紹與安裝

Yaf框架是一個c語言編寫的PHP框架酬荞,是一個以PHP擴展形式提供的PHP開發(fā)框架,相比于一般的PHP框架甘磨, 它更快,更輕便帚稠,內(nèi)存占用率更低孵运,就是本著對性能的追求,Yaf把框架中不易變的部分抽象出來惠呼,類如路由佩番、自動加載、bootstrap罢杉、分發(fā)等趟畏,采用PHP擴展去實現(xiàn),以此來保證性能滩租。

Yaf優(yōu)點

用c語言開發(fā)的PHP框架赋秀,相比原生的PHP利朵,幾乎不會帶來額外的性能開銷。
所有的框架類猎莲,不需要編譯绍弟,在PHP啟動的時候加載,并常駐內(nèi)存著洼。
更快的執(zhí)行速度樟遣,更少的內(nèi)存占用。
靈巧的自動加載身笤。 支持全局和局部兩種加載規(guī)則, 方便類庫共享豹悬。

yaf缺點

維護成本高,要維護PHP擴展液荸,需要熟練C開發(fā)和Zend Api瞻佛。
目標(biāo)用戶群小,現(xiàn)在國內(nèi)很多中小型站都是使用虛擬主機娇钱,并不能隨意的給PHP添加擴展伤柄。
不像其他框架一樣提供各種豐富功能的類庫和各種優(yōu)雅的寫法,它只提供一個MVC的基本骨架文搂。

linux 平臺下安裝上海步驟如适刀,首先使用 wget 下載yaf源代碼,再使用 phpize 去安裝擴展煤蹭。假如你的服務(wù)器上安裝了多個版本php蔗彤,那么需要告訴phpize要建立基于哪個版本的擴展。通過使用--with-php-config=指定你使用哪個php版本疯兼,whereis php-config 可以提供定位信息然遏,安裝擴展后根據(jù)extension_dir配置一下php.ini以加載yaf.so。php 7.0建議使用 yaf 3.0吧彪。

wget http://pecl.php.net/get/yaf-2.2.9.tgz
tar -zxf yaf-2.2.9.tgz
apt install php7.0-dev
phpize
./configure --with-php-config=/usr/bin/php-config
make
make install

使用 php 5.6 可以下載 yaf 2.3.3待侵,windows平臺有DLL庫,下載后解壓到php擴展目錄下姨裸,并相應(yīng)設(shè)置 php.ini 的 extension秧倾。

基本程序結(jié)構(gòu)

基本程序目錄結(jié)構(gòu)如下,yaf框架是精簡的傀缩,本身不含數(shù)據(jù)訪問模塊那先,只能自行編寫數(shù)據(jù)庫訪問模塊或使用現(xiàn)成的如輕量級PHP數(shù)據(jù)庫框架Medoo,WHERE 語法非常精練赡艰,輕量級的yaf搭輕量級的Medoo是絕配售淡,如果還需要視圖模板可以考慮使用 Smarty,當(dāng)然我更喜歡直接使用vue前端框架來實現(xiàn)視圖。

+ public
  |- index.php //入口文件
  |- .htaccess //重寫規(guī)則    
  |+ css
  |+ img
  |+ js
+ conf
  |- application.ini //配置文件   
+ application
  |+ controllers
     |- Index.php //默認(rèn)控制器
  |+ views    
     |+ index    //控制器
        |- index.phtml //默認(rèn)視圖
  |+ modules //其他模塊
  |+ library //本地類庫
  |+ models  //model目錄
  |+ plugins //插件目錄

如果在application.ini中定義了模塊功能揖闸,則需要在程序目錄下modules目錄下建立模塊目錄揍堕,再建controllers目錄,測試中沒有發(fā)現(xiàn)有大小寫要求汤纸,但盡量保持目錄字母大寫一到致衩茸。一個簡單的 index.php 入口程序可以實例化一個Yaf程序類,執(zhí)行run()方法贮泞,bootstrap() 是可選的楞慈,它指示Yaf_Application去尋找 Bootstrap.php, 而這個文件中, 必須定義一個Bootstrap類, 而這個類也必須繼承自Yaf_Bootstrap_Abstract。Bootstrap, 也叫做引導(dǎo)程序啃擦。它是Yaf提供的一個全局配置的入口, 在Bootstrap中, 你可以做很多全局自定義的工作囊蓝。所有在Bootstrap類中定義的, 以_init開頭的方法, 都會被依次調(diào)用, 而這些方法都可以接受一個Yaf_Dispatcher實例作為參數(shù)。方法在Bootstrap類中的定義出現(xiàn)順序, 決定了它們的被調(diào)用順序议惰。

define("APP_PATH",  dirname(__FILE__));
$app  = new Yaf_Application(APP_PATH . "/yaf.ini");
$app->bootstrap()->run();

Bootstrap 類樣例:

class Bootstrap extends Yaf_Bootstrap_Abstract 
{ 

    public function _initRoute(Yaf_Dispatcher $dispatcher) {
        $router = Yaf_Dispatcher::getInstance()->getRouter();
        $router->addConfig(Yaf_Registry::get("config")->routes);
    }
    
    public function _initSession(Yaf_Dispatcher $dispatcher) 
    { 
        $session = new Vendor\Session(); 
        $session->start(); 
    } 

    public function _initDatabase(Yaf_Dispatcher $dispatcher) 
    { 
        $config = Yaf_Application::app()->getConfig()->application->database; 
        Yaf_Registry::set('db', Vendor\Database($config)); 
    } 
}

配置文件可以這樣寫

[common]
application.directory=APP_PATH "/application/"

[product : common]

可以添加其它配置組內(nèi)容,通過 application.modules 添加了兩個模塊乡恕,多模塊配置用逗號分隔言询,使用模塊后目錄結(jié),如app這個模塊的控制器及視圖就要存放到 application/modules/controllers 目錄下:

[modules]
application.ext=php
application.modules="app,index"

[redis]
;用作緩存的redis服務(wù)器
redis.cache.host = 192.168.254.128
redis.cache.port = 6379
redis.cache.dbIndex = 1

;用作存儲用戶信息的redis服務(wù)器
redis.user.host = 192.168.254.128
redis.user.port = 6379
redis.user.dbIndex = 12

;別忘了在這里加上你要讀取的配置組名
[product : common : redis : modules]

配置好后需要一個控制器傲宜,保存到程序目錄的 controllers 子目錄下运杭,控制器的默認(rèn)動作方法是 indexAction。

class IndexController extends Yaf_Controller_Abstract {
    public function indexAction() {
        $this->getView()->assign("content", "Hello World");
        //讀取配置文件
        $config = Yaf_Application::app()->getConfig();
        //打印配置信息
        echo '<pre>';
        print_r($config);
        echo '</pre>';
    }
}

另外還需要一個視圖文件函卒,getView()->assign() 是給視圖傳遞數(shù)據(jù)辆憔,視圖文件保存到程序目錄的 views 目錄下建立的和控制同名的子目錄內(nèi),默認(rèn)為 phtml 擴展名报嵌。

<html>
    <head>
        <title>Hello World</title>
    </head>
    <body>
        <?php echo $content;?>
    </body>
</html>

如果是Ajax請求, 可以關(guān)閉HTML視圖輸出虱咧,disableView() 會關(guān)閉視圖模塊,而 autoRender(false) 則只關(guān)閉視圖輸出:

 if ($this->getRequest()->isXmlHttpRequest()) {
     Yaf_Dispatcher::getInstance()->disableView();
     Yaf_Dispatcher::getInstance()->autoRender(false);
 }

路由使用與CLI執(zhí)行

Yaf摒棄了0.1版本中的自定義路由器方式, 而采用了更為靈活的路由器和路由協(xié)議分離的模式锚国。路由協(xié)議事實上主要負(fù)責(zé)匹配我們預(yù)先定義好的路由協(xié)議, 意思就是我們只有一個路由器, 但我們可以有許多路由協(xié)議腕巡。 路由器主要負(fù)責(zé)管理和運行路由鏈,它根據(jù)路由協(xié)議棧倒序依次調(diào)用各個路由協(xié)議, 一直到某一個路由協(xié)議返回成功以后, 就匹配成功。路由注冊的順序很重要, 最后注冊的路由協(xié)議, 最先嘗試路由, 這就有個陷阱血筑。 請注意绘沉。路由的過程發(fā)生派遣過程的最開始,并且路由解析僅僅發(fā)生一次。路由過程在何控制器動作(Controller, Action)被派遣之前被執(zhí)行, 一旦路由成功, 路由器將會把解析出得到的信息傳遞給請求對象(Yaf_Request_Abstract object), 這些信息包括moduel豺总、controller车伞、action、用戶params等喻喳。 然后派遣器(Yaf_Dispatcher)就會按照這些信息派遣正確的控制器動作另玖。 路由器也有插件鉤子, 就是routerStartup和routerShutdown, 他們在路由解析前后分別被調(diào)用。

默認(rèn)的路由協(xié)議Yaf_Route_Static, 就是分析請求中的request_uri, 在去除掉base_uri以后, 獲取到真正的負(fù)載路由信息的request_uri片段, 具體的策略是, 根據(jù)"/"對request_uri分段, 依次得到Module,Controller,Action, 在得到Module以后, 還需要根據(jù)Yaf_Application::$modules來判斷Module是否是合法的Module, 如果不是, 則認(rèn)為Module并沒有體現(xiàn)在request_uri中, 而把原Module當(dāng)做Controller, 原Controller當(dāng)做Action。

Yaf_Route_Simple是基于請求中的query string來做路由的, 在初始化一個Yaf_Route_Simple路由協(xié)議的時候, 我們需要給出3個參數(shù), m/c/a這3個參數(shù)分別代表在query string中Module, Controller, Action的變量名日矫。路由設(shè)置可以在 Bootstrap 中進行赂弓,也可以在 Yaf_Application 實例化之后 run() 之前進行。

$router = Yaf_Dispatcher::getInstance()->getRouter();
$route = new Yaf_Route_Simple("m", "c", "a");
$router->addRoute("name", $route);
// $router->addConfig(Yaf_Registry::get("config")->routes);

Yaf_Route_Supervar和Yaf_Route_Simple相似, 都是在query string中獲取路由信息, 不同的是, 它獲取的是一個類似包含整個路由信息的request_uri哪轿。Yaf_Route_Map議是一種簡單的路由協(xié)議, 它將REQUEST_URI中以'/'分割的節(jié), 組合在一起, 形成一個分層的控制器或者動作的路由結(jié)果.

Yaf_Route_Map的構(gòu)造函數(shù)接受倆個參數(shù), 第一個參數(shù)表示路由結(jié)果是作為動作的路由結(jié)果盈魁,還是控制器的路由結(jié)果。默認(rèn)的是動作路由結(jié)果窃诉,第二個參數(shù)是一個字符串, 表示一個分隔符, 如果設(shè)置了這個分隔符, 那么在REQUEST_URI中, 分隔符之前的作為路由信息載體, 而之后的作為請求參數(shù)杨耙。

Yaf_Route_Rewrite是一個強大的路由協(xié)議, 它能滿足我們絕大部分的路由需求。如果這些還不能滿足飘痛,那就用復(fù)雜點的 Yaf_Route_Regex珊膜,這是一個正則匹配路由。

http://domain.com/index.php/index/test      Yaf_Route_Static
http://domain.com/index.php?c=index&a=test  Yaf_Route_Simple
http://domain.com/index.php?r=/m/index/test Yaf_Route_Supervar

使用命令行(Cli模式)運行宣脉,為了更好的與web區(qū)分重新創(chuàng)建一個入口文件是比較好的做法车柠。 Yaf_Request_Simple 特別的被用于測試,例如:CLI模式下模擬一些特殊的要求塑猖。

$app = new YafApplication(APP_PATH . "/conf/application.ini");
$app->getDispatcher()->dispatch(new Yaf_Request_Simple());

這樣入口文件就完成了竹祷。接下來,你需要學(xué)會yaf命令行的調(diào)用方法羊苟。來一個示例:

php index.php request_uri="/daemon/start"

Yaf_Request_Simple的構(gòu)造函數(shù)可以不接受任何參數(shù), 在這種情況下, Yaf_Request_Simple會在命令行參數(shù)中, 尋找一個字符串參數(shù), 如果找到, 則會把請求的request_uri置為這個字符串塑陵。CLI參數(shù)中指定的路徑便是 Controller 的路由路徑。在例子里指向/Controller/Daemon.php 中的 startAction()方法蜡励。

要使得yaf在命令行模式下運行, 有倆種方式, 第一種方式專門為用Yaf開發(fā)Contab等任務(wù)腳本設(shè)計的方式, 這種方式下, 對Yaf的唯一要求就是能自動加載所需要的Model或者類庫, 所以可以簡單的通過Yaf_Application::execute來實現(xiàn)令花。它的第一參數(shù)需要定義一個回調(diào)函數(shù),也可以是一個類中的某個函數(shù)凉倚。

$application->execute("main", $argc, $argv);
$application->execute(array("Class","Method"), $argc, $argv);

后面的參數(shù)為一個可變列表兼都,值為你希望傳入的參數(shù)。綜上所述稽寒,我們的另外一種入口文件可以寫成:

$app = new YafApplication(APP_PATH . "/conf/application.ini");
$app->execute('callback', $avg1, $avg2 , ...);

如果需要通過bootstrap去初始化俯抖。只需要和web一樣改為:

$app->bootstrap()->execute('callback', $avg1, $avg2 , ...);

異常處理

Yaf實現(xiàn)了一套錯誤和異常捕獲機制, 主要是對常見的錯誤處理和異常捕獲方法做了一個簡單抽象, 方便應(yīng)用組織自己的錯誤統(tǒng)一處理邏輯。 Yaf自身出錯時候, 根據(jù)配置可以分別采用拋異惩咛ィ或者觸發(fā)錯誤的方式來通知錯誤恩掷。在配置文件 appliation.dispatcher.throwException 打開的情況下, Yaf會拋異常, 或者通過Yaf_Dispatcher::throwException(true) 亦可以姑裂,否則觸發(fā)錯誤捻悯。

那么對應(yīng)的, 就有倆套錯誤處理方式可供應(yīng)用選用蘸拔。 在配置文件開啟 application.dispatcher.catchException 或者可通過Yaf_Dispatcher::catchException(true)時,當(dāng)Yaf遇到未捕獲異常的時候, 就會把運行權(quán)限, 交給當(dāng)前模塊的Error Controller的Error Action動作, 而異掣河螅或作為請求的一個參數(shù), 傳遞給Error Action漫蛔。

在Error Action中可以通過$request->getRequest()->getParam("exception")獲取當(dāng)前發(fā)生的異常嗜愈。

從Yaf1.0.0.12開始, 也可以通過request->getException()來獲取當(dāng)前發(fā)生的異常, 而如果Error Action定義了一個名為exception的參數(shù)的話, 也可以直接通過這個參數(shù)獲取當(dāng)前發(fā)生的異常。

有了這樣的最終異常處理邏輯, 應(yīng)用就可以在出錯的時候直接拋出異常, 在統(tǒng)一異常處理邏輯中, 根據(jù)各種不同的異常邏輯, 處理錯誤, 記錄日志莽龟,Error Action實現(xiàn)參考如下:

class ErrorController extends Yaf_Controller_Abstract {
    public function errorAction($exception) {
        assert($exception === $exception->getCode());
        $this->getView()->assign("code", $exception->getCode());
        $this->getView()->assign("message", $exception->getMessage());
    }
}

class ErrorController extends Yaf_Controller_Abstract {
    public function errorAction($exception) {
        switch($exception->getCode()) {
            case YAF_ERR_LOADFAILD:
            case YAF_ERR_LOADFAILD_MODULE:
            case YAF_ERR_LOADFAILD_CONTROLLER:
            case YAF_ERR_LOADFAILD_ACTION:
            //404
            header("Not Found");
            break;

            case CUSTOM_ERROR_CODE:
            //自定義的異常
            break;
        }
    }
}

class ErrorController extends Yaf_Controller_Abstract {
    public function errorAction() {
        $exception = $this->getRequest()->getException();
        try {
            throw $exception;
        } catch (Yaf_Exception_LoadFailed $e) {
            //加載失敗
        } catch (Yaf_Exception $e) {
            //其他錯誤
        }
    }
}

定義插件

插件類是用戶編寫的, 但是它需要繼承自Yaf_Plugin_Abstract蠕嫁,通過編寫插件可以在yaf運行過程中實現(xiàn)自己的邏輯。對于插件來說毯盈,主要還是借助yaf提供的6個Hook來實現(xiàn)剃毒,只需要在插件類中定義和hook事件同名的方法,那么這個方法就會在該事件觸發(fā)的時候被調(diào)用搂赋,這就是yaf插件的原理赘阀。 插件方法可以接受倆個參數(shù), Yaf_Request_Abstract實例和Yaf_Response_Abstract實例。

class UserPlugin extends Yaf_Plugin_Abstract {
    public function routerStartup(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) { .... }
    public function routerShutdown(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) { .... }
}

Yaf定義了6個Hook, 它們分別是:

觸發(fā)順序   名稱                  觸發(fā)時機說明
1         routerStartup         在路由之前觸發(fā), 這個是6個事件中, 最早的一個. 但是一些全局自定的工作, 還是應(yīng)該放在Bootstrap中去完成脑奠。
2         routerShutdown        路由結(jié)束之后觸發(fā), 此時路由一定正確完成, 否則這個事件不會觸發(fā)基公。
3         dispatchLoopStartup   分發(fā)循環(huán)開始之前被觸發(fā)。
4         preDispatch           分發(fā)之前觸發(fā), 如果在一個請求處理過程中, 發(fā)生了forward, 則這個事件會被觸發(fā)多次宋欺。
5         postDispatch          分發(fā)結(jié)束之后觸發(fā), 此時動作已經(jīng)執(zhí)行結(jié)束, 視圖也已經(jīng)渲染完成, 和preDispatch類似, 此事件也可能觸發(fā)多次轰豆。
6         dispatchLoopShutdown  分發(fā)循環(huán)結(jié)束之后觸發(fā), 此時表示所有的業(yè)務(wù)邏輯都已經(jīng)運行完成, 但是響應(yīng)還沒有發(fā)送。

插件要生效, 還需要向Yaf_Dispatcher注冊, 那么一般的插件的注冊都會放在 Bootstra中 進行齿诞。

class Bootstrap extends Yaf_Bootstrap_Abstract{
    public function _initPlugin(Yaf_Dispatcher $dispatcher) {
        $user = new UserPlugin();
        $dispatcher->registerPlugin($user);
    }
}

一般的, 插件應(yīng)該放置在APPLICATION_PATH下的plugins目錄, 這樣在自動加載的時候, 加載器通過類名, 發(fā)現(xiàn)這是個插件類, 就會在這個目錄下查找酸休。 當(dāng)然, 插件也可以放在任何你想防止的地方, 只要你能把這個類加載進來就可以。

yaf自動加載器與Medoo模塊

Medoo 是采用了ORM (Object Relational Mapping) 設(shè)計模式掌挚,基于PDO數(shù)據(jù)對象(PHP Data Object)封裝雨席,輕量單文件實現(xiàn)易于使用菩咨,適用于所有PHP框架吠式,如Laravel,Codeigniter抽米,Yii特占,Slim和支持單例擴展或編寫器的框架,支持各種常見和復(fù)雜的SQL查詢云茸,數(shù)據(jù)映射以及防止SQL注入是目。訪問mysql數(shù)據(jù)庫參考如下,只需要按配置實例化即可以使用标捺。實例化后懊纳,可以通過使用pdo成員直接訪問PDO對象$db->pdo。

require_once "Medoo.php";
use Medoo\Medoo;

class DBCONFIG {
    const CONF = [
        // required
        'database_type' => 'mysql',
        'database_name' => 'dbname',
        'server' => '192.168.0.242',
        'username' => 'root',
        'password' => 'xxx',

        // [optional]
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_general_ci',
        'port' => 3306,
     
        // [optional] Table prefix
        // 'prefix' => '',
     
        // [optional] Enable logging (Logging is disabled by default for better performance)
        // 'logging' => true,
     
        // [optional] MySQL socket (shouldn't be used with server and port)
        // 'socket' => '/tmp/mysql.sock',
     
        // [optional] driver_option for connection, read more from http://www.php.net/manual/en/pdo.setattribute.php
        'option' => [
            PDO::ATTR_CASE => PDO::CASE_NATURAL
        ],
     
        // [optional] Medoo will execute those commands after connected to the database for initialization
        'command' => [
            'SET SQL_MODE=ANSI_QUOTES'
        ]
    ];
}
$db = new Medoo(DBCONFIG::CONF);
$db = new Medoo\Medoo(DBCONFIG::CONF);
$rows = $db->select("users", "name");
$rows = $db->select("users", ["name", "role", "timestamp"]);

如果使用 SQLite 配置更簡單亡容,還可以使用 In-memory database(IMDB) 即內(nèi)存數(shù)據(jù)庫嗤疯,SQLite 數(shù)據(jù)庫通常是存儲在磁盤文件中的。然而在有些情況下闺兢,我們可以讓數(shù)據(jù)庫始終駐留在內(nèi)存中茂缚。最常用的一種方式是在調(diào)用sqlite3_open()的時候,數(shù)據(jù)庫文件名參數(shù)傳遞":memory:"。相比傳統(tǒng)的基于磁盤的數(shù)據(jù)庫管理系統(tǒng)脚囊,IMDB速度快得多龟糕。

$database = new medoo([
    'database_type' => 'sqlite',
    'database_file' => 'my/database/path/database.db'
]);

$database = new Medoo([
    'database_type' => 'sqlite',
    'database_file' => ':memory:'
]);

配置信息也可以寫到程序配置文件上

[sqlite]
database.database_type='sqlite'
database.database_file='e:\coding\Yeen\ci\yaf\database.db'

[memdb]
database.database_type='sqlite'
database.database_file=':memory:'

[mysql]
database.database_type='mysql'
database.database_name='dbname'
database.server='192.168.0.242'
database.username='user'
database.password='password'

;[optional]
database.charset='utf8mb4'
database.collation='utf8mb4_general_ci'
database.port=3306

;[optional] Table prefix
database.prefix='tbl_'

;[optional] Enable logging (Logging is disabled by default for better performance)
database.logging='true'

;[optional] MySQL socket (shouldn't be used with server and port)
;database.socket='/tmp/mysql.sock'

;[optional] driver_option for connection, read more from http://www.php.net/manual/en/pdo.setattribute.php
;database.option[] = PDO::ATTR_CASE "=" PDO::CASE_NATURAL

;[optional] Medoo will execute those commands after connected to the database for initialization
database.command[] = 'SET SQL_MODE=ANSI_QUOTES'

讀取配置方法示例有兩種,一是直接使用Yaf程序已經(jīng)加載的配置悔耘,另一種是直接實例化一個 Yaf_Config_Ini 對象來讀入配置文件讲岁。使用 Yaf_Config_Ini 可以指定讀取的配置節(jié)點,如 [sqlite] 配置節(jié)點的內(nèi)容淮逊。

$config = Yaf_Application::app()->getConfig()->toArray();
$db = new Medoo\Medoo($config['database']);
$rows = $db->select("users", ["name", "role", "timestamp"]);

// $conf = (new Yaf_Config_Ini('yaf.ini', "sqlite"))->toArray();
$conf = (new Yaf_Config_Ini('yaf.ini'))->toArray();
$database = $conf['product']['database'];
$db = new Medoo\Medoo($database);

在Yaf中使用Medoo催首,只需要將下載到Medoo.php放到程序目錄下的library目錄下即可,Yaf 會在實例化 Medoo 時自動加載它泄鹏。注意 Medoo 使用了命令空間郎任,自PHP 5.3開始支付命令空間,在使用時要使用use引入Medoo類或者在實例化時將命令空間寫上 new Medoo\Medoo($config)备籽。注意 use 引入的命令空間不被include引入的文件識別舶治,所以即使在入口 index.php 引入了 Medoo 命令空間,想要在模型或控制器中使用 Medoo 則還是需要重新引入命令空間的车猬。

composer就是用來解決自動加載的工具霉猛,有了自動加載基本就拋棄了require和include函數(shù)。一個項目中珠闰,這兩個函數(shù)只可能出現(xiàn)一次惜浅,那就是require '../vendor/autoload.php'。這個工具根據(jù)配置文件 composer.json 的依賴項 require 和加載項 autoload 來完成自動加載任務(wù)伏嗜。autoload 中又包含主要的兩個選項 files 和 psr-4坛悉。files 就是需要 composer 自動加載的函數(shù)庫,不含類承绸。只要在 files 這個數(shù)組中將函數(shù)庫的文件路徑填寫好即可裸影。PSR-4 是PHP Standards Recommendation的簡稱,是FIG-PHP工作組推出的自動加載技術(shù)規(guī)范军熏,它能夠滿足面向package的自動加載轩猩,它規(guī)范了如何從文件路徑自動加載類,同時規(guī)范了自動加載文件的位置荡澎。psr-4 顧名思義均践,是一個基PSR-4自動加載規(guī)則的類庫信息,只要在其后的配置項以 {"命名空間": "類實現(xiàn)文件路徑"} 的方式寫入類庫信息即可摩幔。

php中對應(yīng)的spl_autoload_register函數(shù)用來實現(xiàn)自動加載彤委,如下實現(xiàn)的一個autoload.php,在實例化時热鞍,PHP遇到?jīng)]有定義的類就會執(zhí)行 spl_autoload_register 注冊的自動加載函數(shù)葫慎,函數(shù)接收到類命令空間信息后再引入指定目錄下的類文件衔彻。

function classLoader($class)
{
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
    $file = __DIR__ . '/src/' . $path . '.php';

    if (file_exists($file)) {
        require_once $file;
    }
}
spl_autoload_register('classLoader');

Yaf為了方便在一臺服務(wù)器上部署的不同產(chǎn)品之間共享公司級別的共享庫, 支持全局類和本地類兩種加載方式。 全局類是指, 所有產(chǎn)品之間共享的類, 這些類庫的路徑是在 php.ini 配置項 ap.library 設(shè)置的偷办。當(dāng)然艰额,如果PHP在編譯的時候, 支持了 with-config-file-scan-dir 那么也可以寫在單獨的 ap.ini 中設(shè)置。而本地類是指, 產(chǎn)品自身的類庫, 這些類庫的路徑是通過在產(chǎn)品的配置文件中, 通過ap.library配置的椒涯。 在Yaf中, 通過調(diào)用 Yaf_Loader的registerLocalNamespace方法來申明哪些類前綴是本地類即可柄沮。

Yaf運行時配置項參考,其中 yaf.library yaf.use_namespace yaf.use_spl_autoload 這三個配置是和類自動加載相關(guān)的废岂。

選項名稱              默認(rèn)值  可修改范圍       更新記錄
yaf.environ          product PHP_INI_ALL     環(huán)境名稱, 當(dāng)用INI作為Yaf的配置文件時, 這個指明了Yaf將要在INI配置中讀取的節(jié)的名字
yaf.cache_config     0       PHP_INI_SYSTEM  是否緩存配置文件只針對INI配置文件生效, 打開此選項可在復(fù)雜配置的情況下提高性能
yaf.name_suffix      1       PHP_INI_ALL     在處理Controller, Action, Plugin, Model的時候, 類名中關(guān)鍵信息是否是后綴式, 比如UserModel, 而在前綴模式下則是ModelUser
yaf.name_separator   ""      PHP_INI_ALL     在處理Controller, Action, Plugin, Model的時候, 前綴和名字之間的分隔符, 默認(rèn)為空, 也就是UserPlugin, 加入設(shè)置為下劃線_, 則判斷的依據(jù)就會變成:"User_Plugin", 這個主要是為了兼容ST已有的命名規(guī)范
yaf.forward_limit    5       PHP_INI_ALL     forward最大嵌套深度
yaf.library          NULL    PHP_INI_ALL     全局類庫的目錄路徑
yaf.use_namespace    0       PHP_INI_SYSTEM  開啟的情況下, Yaf將會使用命名空間方式注冊自己的類, 比如Yaf_Application將會變成Yaf\Application
yaf.use_spl_autoload 0       PHP_INI_ALL     開啟的情況下, Yaf在加載不成功的情況下, 會繼續(xù)讓PHP的自動加載函數(shù)加載, 從性能考慮, 除非特殊情況, 否則保持這個選項關(guān)閉

Yaf應(yīng)用配置中關(guān)于自動加載的配置項有三個

application.library               本地類庫目錄路徑
application.library.directory     本地類庫目錄路徑
application.library.namespace     以逗號分隔的本地庫命名空間前綴

在配置項 yaf.use_spl_autoload 關(guān)閉的情況下, Yaf Autoloader在一次找不到的情況下, 會立即返回, 而剝奪其后的自動加載器的執(zhí)行機會祖搓。

Yaf類的自動加載規(guī)則, 都是一樣的: Yaf規(guī)定類名中必須包含路徑信息, 也就是以下劃線 _ 分割的目錄信息。Yaf將依照類名中的目錄信息, 完成自動加載湖苞,例如, 在沒有申明本地類的情況下拯欧,Yaf將在類庫目錄中尋找類定義文件,類庫路徑在 php.ini 的配置項 ap.library 中指定财骨,默認(rèn)路徑是程序目錄下的 library 子目錄镐作。如 Foo_Dummy_Bar 這樣的類對應(yīng)了 library/Foo/Dummy/Bar.php。

如果通過 registerLocalNamespace 方式注冊注冊本地類隆箩,下面申明凡是以Foo和Local開頭的類, 都是本地類该贾,

 $loader = Yaf_Loader::getIgnstance();
 $loader->registerLocalNamespace(array("Foo", "Local"));

那么對于剛才的例子, 將會在程序配置文件中指定的類庫路徑,即 application.ini 中指定的 ap.library 目錄下尋找Foo_Dummy_Bar捌臊。

分析源代碼可以得到Y(jié)af_Loader自動加載策略有以下幾個要點

1)yaf.library和application.library勻未配置時杨蛋,Yaf_Loader::$_library及Yaf_Loader::$_global_library都將設(shè)置為[application.directory]/library;故不管是否配置application.library.namespace或者Yaf/Loader::registerLocalNamespace()是否注冊本地命名空間前綴理澎,加載類文件時逞力,自動到[application.directory]/library目錄查找類并加載。
2)如果配置了application.library時矾端,但未配置application.library掏击。namespace時或者未通過Yaf/Loader::registerLocalNamespace()注冊本地命名空間前綴卵皂,不管yaf.library是否配置都到y(tǒng)af.library中加載相應(yīng)類文件秩铆。
3)如果配置了application.library和application.library.namespace,且類名中包含配置的命名空間前綴灯变,則到application.library加載相應(yīng)的類文件殴玛,否則到y(tǒng)af.library中加載相應(yīng)類文件。
4)Yaf內(nèi)部中加載文件時添祸,類名中有”_”會轉(zhuǎn)換為目錄分隔符滚粟。

Yaf在自啟動的時候, 會通過SPL注冊一個自己的Autoloader, 出于性能的考慮, 對于框架相關(guān)的MVC類, Yaf Autoloader只以目錄映射的方式嘗試一次。但是要注意的一點是, 從2.1.18開始, Yaf支持在PHP腳本中觸發(fā)對Controller的自動加載, 但是因為Controller的定位需要根據(jù)Module路由結(jié)果來判斷, 這就造成了 在Bootstrap或者RouteStarrup之前, 無法確定. 所以, 對于Controller的加載, Yaf將只會嘗試去加載默認(rèn)Module的Controller, 也就是只在"{項目路徑}/controllers" 目錄下尋找刃泌。

具體的目錄映射規(guī)則如下凡壤,后綴或者前綴可以通過php.ini中ap.name_suffix來切換:

類型      后綴或者前綴   映射路徑
控制器    Controller    默認(rèn)模塊下為{項目路徑}/controllers/, 否則為{項目路徑}/modules/{模塊名}/controllers/
數(shù)據(jù)模型  Model         {項目路徑}/models/
插件      Plugin        {項目路徑}/plugins/

而對于非框架MVC相關(guān)的類, Yaf支持全局類和自身類的兩種加載方式, 并且Yaf支持大小寫敏感和不敏感兩種方式來處理文件路徑署尤。

應(yīng)用SQLite數(shù)據(jù)與前端框架結(jié)合

SQLite 教程 - http://www.runoob.com/sqlite/sqlite-tutorial.html
SQLite Download Page - https://www.sqlite.org/download.html

SQLite 是一個軟件庫,實現(xiàn)了自給自足的亚侠、無服務(wù)器的曹体、零配置的、事務(wù)性的 SQL 數(shù)據(jù)庫引擎硝烂。SQLite 是在世界上最廣泛部署的 SQL 數(shù)據(jù)庫引擎箕别,特別是在小型設(shè)置上。SQLite 源代碼不受版權(quán)限制滞谢。在windows平臺上php一般自帶了 sqlite.dll 擴展串稀,配置文件中打開就好,在Ubuntu系統(tǒng)上可以執(zhí)行以下命令安裝狮杨,視php版本選擇母截。一般安裝后會自動配置,可以查看 Additional .ini 是否有 pdo_sqlite.ini橄教,有則表明已經(jīng)自動加載了微酬。

sudo apt-get install php7.0-sqlite
sudo apt-get install php5.6-sqlite

SQLite 數(shù)據(jù)庫的數(shù)據(jù)類型簡單:

存儲類 描述
NULL    值是一個 NULL 值。
INTEGER 值是一個帶符號的整數(shù)颤陶,根據(jù)值的大小存儲在 1颗管、2、3滓走、4垦江、6 或 8 字節(jié)中。
REAL    值是一個浮點值搅方,存儲為 8 字節(jié)的 IEEE 浮點數(shù)字比吭。
TEXT    值是一個文本字符串,使用數(shù)據(jù)庫編碼(UTF-8姨涡、UTF-16BE 或 UTF-16LE)存儲衩藤。
BLOB    值是一個 blob 數(shù)據(jù),完全根據(jù)它的輸入存儲涛漂。

SQLite 的存儲類稍微比數(shù)據(jù)類型更普遍赏表。INTEGER 存儲類,例如匈仗,包含 6 種不同的不同長度的整數(shù)數(shù)據(jù)類型瓢剿。如果對 SQLite 系統(tǒng)不熟悉,可以考慮使用一些數(shù)據(jù)庫管理工具如 Navicat 之類悠轩。SQLite 官方也提供了命令工具间狂,可以用來做查詢調(diào)試。

為了方便開發(fā)火架,可以使用 php 內(nèi)置的 Server鉴象,執(zhí)行以下命令即可在本地運行一個服務(wù)器 通過localhost即可以訪問忙菠,如果要在局域網(wǎng)其它主機上訪問,可以在80端口前指定IP地纺弊,localhost 或IP最好選一個只搁,避免服務(wù)器接收不到請求,在 Windows 平臺還可以使用 start 命令打開頁面俭尖。注意修改php配置氢惋,如果Medoo使用了PDO方式訪問mysql數(shù)據(jù)庫,請確保打開配置文件中的 extension=php_pdo_mysql.dll稽犁。在 Linux 服務(wù)器上要確保目錄讀寫權(quán)限打開焰望。

php -S localhost:80 -t e:\coding\Yeen\ci\yaf
start http://localhost && php -S localhost:80 -t e:\coding\Yeen\ci\yaf

將 Medoo 類文件拷貝到 library 目錄后,就可以開始正式寫程序已亥,實現(xiàn)自己的控制器和視圖了熊赖,那么這里就結(jié)合 vue + bootstrap 等寫個例子程序,這個例子代碼可以直接替換默認(rèn)的 Index.php 控制器虑椎。

// use Medoo\Medoo;

class IndexController extends Yaf_Controller_Abstract {
    public function indexAction() {
        $db = new Medoo\Medoo(DBCONFIG::CONF);
        $rows = $db->select("users", ["name", "role", "timestamp"]);
        $this->getView()->assign("content", "Hello World");
        $this->getView()->assign("rows", $rows);
    }
}

視圖展示了VUE的表單處理能力震鹉,代碼稍有點長

<html>
    <head>
        <meta charset="utf-8">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet"  >
        <link rel="stylesheet"  >
        <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
    </head>
    <body>
        <div id="app" class="container">
            <div class="jumbotron">
            <h1><?php echo $content;?></h1>
            <div class="table-responsive"><table class="table">
                <tr class="row" v-for="item in rows">
                    <td class="col-xs-1"><label class="btn btn-primary">{{item.name}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-danger"  v-if="item.role=='administrator'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-danger"  v-if="item.role=='advanced'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-danger"  v-if="item.role=='operator'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-success" v-if="item.role=='agent'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-warning" v-if="item.role=='member'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-info"    v-if="item.role=='guest'">{{item.role}}</label></td>
                    <td class="col-xs-1"><label class="btn btn-info">{{item.timestamp}}</label></td>
                </tr>
            </table></div>
            </div>
            <div class="panel">

                <div class="well">
                    <p>單選框:</p>
                    <input type="radio" value="ABC" name="group" v-model="radio">
                    <input type="radio" value="XYZ" name="group" v-model="radio">
                    <label for="checkbox">{{ radio }}</label>
                </div>

                <div class="well">
                    <p>單個復(fù)選框:</p>
                    <input type="checkbox" value="true" id="checkbox1" v-model="listA">
                    <label for="checkbox1">{{ listA }}</label>
                    <input type="checkbox" value="false" id="checkbox2" v-model="listB">
                    <label for="checkbox2">{{ listB }}</label>
                </div>

                <div class="well">
                    <p>多個復(fù)選框:</p>
                    <input type="checkbox" id="vue" value="Vue" v-model="checkedNames">
                    <label for="vue">vue</label>
                    <input type="checkbox" id="bootstrap" value="Bootstrap" v-model="checkedNames">
                    <label for="bootstrap">Bootstrap</label>
                    <p>選擇的值為: {{ checkedNames }}</p>
                </div>

                <div class="well">
                    <select v-model="selected" name="strawberry">
                    <option value="">單選選擇</option>
                    <option value="vue">vue</option>
                    <option value="bootstrap">bootstrap</option>
                    </select>
                    <p>選擇的是: {{selected}}</p>
                </div>

                <div class="well">
                    <select multiple v-model="multiple" name="strawberry">
                    <option value="">多選列表</option>
                    <option value="vue">vue</option>
                    <option value="bootstrap">bootstrap</option>
                    </select>
                    <p>選擇的是: {{multiple}}</p>
                </div>

            </div>
        </div>
        <script>
        new Vue({
            el: '#app',
            data: {
                rows:<?php echo json_encode($rows,JSON_UNESCAPED_UNICODE);?>,
                radio : "XYZ",
                listA : "true",
                listB : "true",
                selected : '',
                multiple : ['vue','bootstrap'],
                checkedNames: []
            }
        })
        </script>
    </body>
</html>
AF + VUE + Bootstrap demo.jpg

PDO方法參考

PDO::beginTransaction — 啟動一個事務(wù)
PDO::commit — 提交一個事務(wù)
PDO::__construct — 創(chuàng)建一個表示數(shù)據(jù)庫連接的 PDO 實例
PDO::errorCode — 獲取跟數(shù)據(jù)庫句柄上一次操作相關(guān)的 SQLSTATE
PDO::errorInfo — 獲取錯誤信息
PDO::exec — 執(zhí)行一條 SQL 語句,并返回受影響的行數(shù)
PDO::getAttribute — 取回一個數(shù)據(jù)庫連接的屬性
PDO::getAvailableDrivers — 返回一個可用驅(qū)動的數(shù)組(了解即可)
PDO::inTransaction — 檢查是否在一個事務(wù)內(nèi)(了解即可)
PDO::lastInsertId — 返回最后插入行的ID或序列值
PDO::prepare — 創(chuàng)建SQL的預(yù)處理,返回PDOStatement對象
PDO::query — 用于執(zhí)行查詢SQL語句,返回PDOStatement對象
PDO::quote — 為sql字串添加單引號
PDO::rollBack — 回滾一個事務(wù)
PDO::setAttribute — 設(shè)置屬性

$stmt = $pdo->query('select * from user limit 2');
$row = $stmt->fetch();
$rows = $stmt->fetchAll();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捆姜,隨后出現(xiàn)的幾起案子传趾,更是在濱河造成了極大的恐慌,老刑警劉巖泥技,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浆兰,死亡現(xiàn)場離奇詭異,居然都是意外死亡珊豹,警方通過查閱死者的電腦和手機簸呈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來店茶,“玉大人蜕便,你說我怎么就攤上這事》坊茫” “怎么了轿腺?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長段直。 經(jīng)常有香客問我吃溅,道長溶诞,這世上最難降的妖魔是什么鸯檬? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮螺垢,結(jié)果婚禮上喧务,老公的妹妹穿的比我還像新娘赖歌。我一直安慰自己,他們只是感情好功茴,可當(dāng)我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布庐冯。 她就那樣靜靜地躺著,像睡著了一般坎穿。 火紅的嫁衣襯著肌膚如雪展父。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天玲昧,我揣著相機與錄音栖茉,去河邊找鬼。 笑死孵延,一個胖子當(dāng)著我的面吹牛吕漂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尘应,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼惶凝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了犬钢?” 一聲冷哼從身側(cè)響起苍鲜,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玷犹,沒想到半個月后坡贺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡箱舞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年遍坟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晴股。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡愿伴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出电湘,到底是詐尸還是另有隱情隔节,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布寂呛,位于F島的核電站怎诫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贷痪。R本人自食惡果不足惜幻妓,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劫拢。 院中可真熱鬧肉津,春花似錦强胰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至距糖,卻和暖如春玄窝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悍引。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工哆料, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吗铐。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓东亦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唬渗。 傳聞我的和親對象是個殘疾皇子典阵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 快速開始 典型的目錄結(jié)構(gòu) 入口文件 所有請求的入口, 一般都借助于rewrite規(guī)則, 把所有的請求都重定向到這個...
    零一間閱讀 3,782評論 0 8
  • 介紹 Yaf框架是一個c語言編寫的PHP框架,是一個以PHP擴展形式提供的PHP開發(fā)框架镊逝,相比于一般的PHP框架壮啊,...
    簡單方式閱讀 18,664評論 1 44
  • 在上一節(jié)講到y(tǒng)af框架的安裝以及相關(guān)的配置 ,這節(jié)主要分享yaf框架的入口文件以及相關(guān)的配置撑蒜。 一歹啼、yaf框架入口...
    淹死的魚_hz閱讀 3,206評論 0 1
  • 先說幾句廢話,調(diào)和氣氛座菠。事情的起由來自客戶需求頻繁變更狸眼,偉大的師傅決定橫刀立馬的改革使用新的框架(created ...
    wsdadan閱讀 3,047評論 0 12
  • Laravel 學(xué)習(xí)交流 QQ 群:375462817 本文檔前言Laravel 文檔寫的很好,只是新手看起來會有...
    Leonzai閱讀 7,841評論 2 12