為什么搭建一個(gè)框架(搭建一個(gè)怎樣的框架)
- 通過(guò)搭建一個(gè)框架更好的學(xué)習(xí)PHP
- 搭建一個(gè)專門用于構(gòu)建API的微型框架。
微型框架基本上是一個(gè)封裝的路由,用來(lái)轉(zhuǎn)發(fā)HTTP請(qǐng)求至一個(gè)閉包,控制器,或方法等等,盡可能地加快開發(fā)的速度,有時(shí)還會(huì)使用一些類庫(kù)來(lái)幫助開發(fā),例如一個(gè)基本的數(shù)據(jù)庫(kù)封裝等等。
- 為了快速實(shí)現(xiàn),我們盡可能的使用他人造好的輪子,然后進(jìn)行組裝。
概念了解
- 框架,就是遵照一定的規(guī)范(PHP-FIG),借助他人的輪子,實(shí)現(xiàn)快速業(yè)務(wù),安全等附加價(jià)值。
- MVC,一種分工協(xié)作的模式。還有依賴注入、單例等等很多設(shè)計(jì)模式。
- 框架流程大體是:URI--》入口文件--》路由--》控制器--》服務(wù)層--》模型層--》返回結(jié)果(html、json、xml等)
準(zhǔn)備工作
- 了解composer,使用Packagist中國(guó)鏡像
- 安裝LAMP或者LNMP,,windows下可以安裝集成環(huán)境,如wamp server等。
- 最好安裝了git:1.可以在windows下使用Git Bash;2.方便代碼同步到github等。
如果你對(duì)上述3個(gè)問(wèn)題都不熟悉,可以點(diǎn)擊這里
第一步:利用composer創(chuàng)建項(xiàng)目
選擇一個(gè)目錄,如/tmp或者C:\Users\Public\
mkdir bee //給一個(gè)項(xiàng)目命名往往很燒腦,我取名‘小蜜蜂’,。
cd bee
composer init
按照提示輸入即可,如圖。

bee目錄下生成了composer.json,
然后執(zhí)行composer install,
bee目錄下生成了vendor目錄,效果如圖。

恭喜你完成了最艱難的第一步!
composer主要用到了spl_autoload_register等方法,關(guān)于PHP自動(dòng)加載問(wèn)題,請(qǐng)補(bǔ)充相關(guān)知識(shí)。
第二步:路由
選擇一個(gè)路由輪子
github上路由輪子太多了,選擇往往也很糾結(jié)。
目前star最多的FastRoute被用于Slim框架,它遵照最新的PSR7規(guī)范,有些復(fù)雜。
這里選擇一個(gè)簡(jiǎn)單的Macaw
在命令行執(zhí)行composer require noahbuscher/macaw:dev-master。
效果就是在composer.json中多出了
"require": {
"noahbuscher/macaw": "dev-master"
}
新建入口文件index.php(也可以叫前端控制器)
用來(lái)轉(zhuǎn)發(fā)HTTP的請(qǐng)求
touch index.php
依照文檔在index.php內(nèi)容添加:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use \NoahBuscher\Macaw\Macaw;
Macaw::get('/', function() {
echo 'Welcome to Bee!';
});
Macaw::dispatch();
執(zhí)行命令php -S 127.0.0.1:8000,啟動(dòng)PHP自帶的server。
在瀏覽器輸入http://127.0.0.1:8000/,見證奇跡的時(shí)刻:瀏覽器輸出了“Welcome to Bee!”;
恭喜您,一個(gè)框架即將誕生!
第三步:控制器
Macaw路由組件可以HTTP請(qǐng)求轉(zhuǎn)發(fā)給控制器。
增加路由:
<?php
require __DIR__ . '/vendor/autoload.php';
use \NoahBuscher\Macaw\Macaw;
Macaw::get('/', function () {
echo 'Welcome to Bee!';
});
//用戶的增刪改查
Macaw::get('/users', 'Bee\Controllers\Users@index');
Macaw::post('/users', 'Bee\Controllers\Users@store');
Macaw::get('/users/(:num)', 'Bee\Controllers\Users@show');
Macaw::put('/users', 'Bee\Controllers\Users@update');
Macaw::delete('/users/(:num)', 'Bee\Controllers\Users@destroy');
Macaw::dispatch();
新建app目錄,新建controllers目錄,新建Users.php:
<?php
namespace Bee\Controllers;
class Users
{
public function index()
{
var_dump($_GET);
}
public function store()
{
var_dump($_POST);
}
public function show($id)
{
echo $id;
}
public function update()
{
$_PUT = array();
if ('put' == strtolower($_SERVER['REQUEST_METHOD'])) {
parse_str(file_get_contents('php://input'), $_PUT);
}
var_dump($_PUT);
}
public function destroy($id)
{
echo $id;
}
}
且修改vendor\noahbuscher,acaw\Macaw.php的29行代碼:
$uri = dirname($_SERVER['PHP_SELF']).'/'.$params[0];
為
$uri = strpos($params[0], '/') === 0 ? $params[0] : dirname($_SERVER['PHP_SELF']) . '/' . $params[0];
利用chrome的Postman工具查看用戶的5個(gè)請(qǐng)求。
比如:
GET http://127.0.0.1:8000/users?start=10&len=20
得到
array(2) {
["start"]=>
string(2) "10"
["len"]=>
string(2) "20"
}
注意:當(dāng)請(qǐng)求是PUT時(shí),postman中的請(qǐng)求方式選擇‘x-www-form-urlencoded’

這種content-type問(wèn)題,也是Slim,Symfony框架的價(jià)值所在,他們封裝了HTTP的請(qǐng)求Request和相應(yīng)Respone為對(duì)象,有很多方法可用。
可以通過(guò)查看Symfony的http-foundation\Request的源碼的createFromGlobals()的函數(shù)看到這是一個(gè)
Creates a new request with values from PHP's super globals.是輸入php的超全局變量,輸出一個(gè)request對(duì)象。Laravel的request也是在此基礎(chǔ)上增加一些接口,繼續(xù)封裝一層而已。
后續(xù)會(huì)補(bǔ)充相關(guān)的輪子,優(yōu)化框架。而簡(jiǎn)單起見,這樣做也可以了。
至此,一個(gè)完整的RESTful的增刪改查已經(jīng)完成。
第四步:模型層(數(shù)據(jù)層)
Laravel的ORM很優(yōu)雅,但是太重,還消耗內(nèi)存等資源,不建議用于一個(gè)微型框架。
而且為了sql的優(yōu)化和學(xué)習(xí),這里采用PDO方式連接數(shù)據(jù)庫(kù)。我將此類庫(kù)獨(dú)立出組件放到了packagist上。
composer require biaoqianwo/simple-pdo加載改組件。
然后,入口文件新增:
use \Bee\PDO\Model;
Model::config(require_once __DIR__ . '/config/db.php');
新建config目錄,新建db.php:
<?php
//單個(gè)數(shù)據(jù)庫(kù)服務(wù)器
return [
'host' => '127.0.0.1',
'port' => 3306,
'dbname' => 'mysql_test',
'options' => null,
'username' => 'root',
'password' => '',
];
然后在app目錄下新建models目錄,再新建Users.php:
<?php
namespace Bee\Models;
use Bee\PDO\Model;
class User extends Model
{
}
修改composer.json,增加:
"classmap": [
"app/controllers",
"app/models"
]
然后執(zhí)行一次composer dump-autoload
修改controller\Users.php:
<?php
namespace Bee\Controllers;
use Bee\Models\User;
class Users
{
public function index()
{
$offset = !empty($_GET['start']) ? (int)$_GET['start'] : 0;
$rows = !empty($_GET['len']) ? (int)$_GET['len'] : 10;
$created_at = time() - 86400;//前24小時(shí)
//測(cè)試發(fā)現(xiàn):limit后面的數(shù)字不能使用預(yù)處理的'?'
$sql = 'select * from users where created_at > ? order by id desc limit ' . $offset . ',' . $rows;
$conditions = [$created_at];
$result = User::get($sql, $conditions);
var_dump($result);
}
public function store()
{
$name = !empty($_POST['name']) ? $_POST['name'] : 0;
$pwd = !empty($_POST['pwd']) ? $_POST['pwd'] : '123456';
$pwd = password_hash($pwd, PASSWORD_DEFAULT);
$sql = "insert users(`name`,`pwd`) values(?,?)";
$conditions = [$name, $pwd];
$result = User::insert($sql, $conditions);
var_dump($result);
}
public function show($id)
{
$sql = "select * from users where id = ?";
$conditions = [$id];
$result = User::first($sql, $conditions);
var_dump($result);
}
public function update()
{
$_PUT = array();
if ('put' == strtolower($_SERVER['REQUEST_METHOD'])) {
parse_str(file_get_contents('php://input'), $_PUT);
}
$id = !empty($_PUT['id']) ? $_PUT['id'] : 0;
$pwd = !empty($_PUT['pwd']) ? $_PUT['pwd'] : '123456';
$pwd = password_hash($pwd, PASSWORD_DEFAULT);
$sql = "update users set `pwd` = ? where id = ?";
$conditions = [$pwd, $id];
$result = User::update($sql, $conditions);
var_dump($result);
}
public function destroy($id)
{
$sql = "delete from users where id = ?";
$conditions = [$id];
$result = User::delete($sql, $conditions);
var_dump($result);
}
}
一個(gè)完整的CRUD的框架就完成了。完整代碼地址
其實(shí)這不能算是一個(gè)框架,這只是一個(gè)利用了一個(gè)路由組件和一個(gè)數(shù)據(jù)庫(kù)組件的RESTful小項(xiàng)目。
框架是利用依賴注入等將這2個(gè)組件(或者其他組件),外加一些服務(wù)封裝到一個(gè)App類,然后打包到一起。
項(xiàng)目就是利用這個(gè)打包,再加上一些目錄規(guī)范,不斷的重復(fù)性的CRUD等等。
待完善
- Request(和Respone)的封裝
- Route的豐富,比如路由分組
- 增加服務(wù),比如Redis。服務(wù)一般用到的是依賴注入容器。這個(gè)設(shè)計(jì)模式足夠喝一壺的。
這里才發(fā)現(xiàn)Laravel的強(qiáng)大,定時(shí)任務(wù)、ORM、隊(duì)列......
參考
https://lvwenhan.com/php/405.html
http://www.symfonychina.com/doc/current/create_framework/index.html