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開始, 也可以通過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>
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();