利用composer搭建一个PHP微框架(API微项目)

为什么搭建一个框架(搭建一个怎样的框架)

  • 通过搭建一个框架更好的学习PHP
  • 搭建一个专门用于构建API的微型框架。

微型框架基本上是一个封装的路由,用来转发HTTP请求至一个闭包,控制器,或方法等等,尽可能地加快开发的速度,有时还会使用一些类库来帮助开发,例如一个基本的数据库封装等等。

  • 为了快速实现,我们尽可能的使用他人造好的轮子,然后进行组装。

概念了解

  • 框架,就是遵照一定的规范(PHP-FIG),借助他人的轮子,实现快速业务,安全等附加价值。
  • MVC,一种分工协作的模式。还有依赖注入、单例等等很多设计模式。
  • 框架流程大体是:URI--》入口文件--》路由--》控制器--》服务层--》模型层--》返回结果(html、json、xml等)

准备工作

  • 了解composer,使用Packagist中国镜像
  • 安装LAMP或者LNMP,,windows下可以安装集成环境,如wamp server等。
  • 最好安装了git:1.可以在windows下使用Git Bash;2.方便代码同步到github等。
    如果你对上述3个问题都不熟悉,可以点击这里

第一步:利用composer创建项目

选择一个目录,如/tmp或者C:\Users\Public\

mkdir bee //给一个项目命名往往很烧脑,我取名‘小蜜蜂’,。
cd bee
composer init

按照提示输入即可,如图。


composer init

bee目录下生成了composer.json,
然后执行composer install,
bee目录下生成了vendor目录,效果如图。

vendor

恭喜你完成了最艰难的第一步!
composer主要用到了spl_autoload_register等方法,关于PHP自动加载问题,请补充相关知识。

第二步:路由

选择一个路由轮子

github上路由轮子太多了,选择往往也很纠结。
目前star最多的FastRoute被用于Slim框架,它遵照最新的PSR7规范,有些复杂。
这里选择一个简单的Macaw
在命令行执行composer require noahbuscher/macaw:dev-master
效果就是在composer.json中多出了

   "require": {
        "noahbuscher/macaw": "dev-master"
    }

新建入口文件index.php(也可以叫前端控制器)

用来转发HTTP的请求

touch index.php

依照文档在index.php内容添加:

<?php
require_once __DIR__ . '/vendor/autoload.php';
use \NoahBuscher\Macaw\Macaw;
Macaw::get('/', function() {
    echo 'Welcome to Bee!';
});
Macaw::dispatch();

执行命令php -S 127.0.0.1:8000,启动PHP自带的server。
在浏览器输入http://127.0.0.1:8000/,见证奇迹的时刻:浏览器输出了“Welcome to Bee!”;
恭喜您,一个框架即将诞生!

第三步:控制器

Macaw路由组件可以HTTP请求转发给控制器。
增加路由:

<?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个请求。
比如:
GET http://127.0.0.1:8000/users?start=10&len=20
得到

array(2) {
  ["start"]=>
  string(2) "10"
  ["len"]=>
  string(2) "20"
}

注意:当请求是PUT时,postman中的请求方式选择‘x-www-form-urlencoded’


image.png

这种content-type问题,也是Slim,Symfony框架的价值所在,他们封装了HTTP的请求Request和相应Respone为对象,有很多方法可用。
可以通过查看Symfony的http-foundation\Request的源码的createFromGlobals()的函数看到这是一个
Creates a new request with values from PHP's super globals.是输入php的超全局变量,输出一个request对象。Laravel的request也是在此基础上增加一些接口,继续封装一层而已。
后续会补充相关的轮子,优化框架。而简单起见,这样做也可以了。

至此,一个完整的RESTful的增删改查已经完成。

第四步:模型层(数据层)

Laravel的ORM很优雅,但是太重,还消耗内存等资源,不建议用于一个微型框架。
而且为了sql的优化和学习,这里采用PDO方式连接数据库。我将此类库独立出组件放到了packagist上。
composer require biaoqianwo/simple-pdo加载改组件。
然后,入口文件新增:

use \Bee\PDO\Model;
Model::config(require_once __DIR__ . '/config/db.php');

新建config目录,新建db.php:

<?php
//单个数据库服务器
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"
    ]

然后执行一次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小时
        //测试发现:limit后面的数字不能使用预处理的'?'
        $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);
    }
}

一个完整的CRUD的框架就完成了。完整代码地址
其实这不能算是一个框架,这只是一个利用了一个路由组件和一个数据库组件的RESTful小项目。
框架是利用依赖注入等将这2个组件(或者其他组件),外加一些服务封装到一个App类,然后打包到一起。
项目就是利用这个打包,再加上一些目录规范,不断的重复性的CRUD等等。

待完善

  • Request(和Respone)的封装
  • Route的丰富,比如路由分组
  • 增加服务,比如Redis。服务一般用到的是依赖注入容器。这个设计模式足够喝一壶的。
    这里才发现Laravel的强大,定时任务、ORM、队列......

参考

https://lvwenhan.com/php/405.html
http://www.symfonychina.com/doc/current/create_framework/index.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,432评论 25 707
  • Welcome 目前网络上充斥着大量的陈旧信息,让PHP新手误入歧途,传播着错误的实践和糟糕的代码,这必须得到纠正...
    layjoy阅读 21,655评论 7 118
  • 是什么 如果你知道yum、apt-get、npm、bower等命令中的一种或者多种,那么,你也能很快知道compo...
    旱魃一样阅读 3,113评论 0 9
  • 每天下班都是7点!今天也是!下班临走前同事给了我一个玉米!说是水果玉米!没舍得吃!还热乎着!!回到家!女儿没在家!...
    圊罙儀鍾阅读 205评论 0 2
  • 这篇文章诞生于今天装完第五次系统之后,一些细节问题为了以后不会忘记,便在这里整理成文章,以便以后查阅。 1.win...
    古寒飞阅读 447评论 0 3