swoole4.0之打造自己的web开发框架(1)

316 阅读4分钟
原文链接: mp.weixin.qq.com

先解答上一篇:swoole4.0 mysql连接池之读写分离 留了个问题,主从分离后,如果写入之后马上读,大概率会读不到?怎么处理呢?后台很多小伙伴留了言,给出了自己的解决方案:

  1. 一般是可以强制读主库,有兴趣的可以改造下代码

  2. 加入一个cache层

本篇开始,将通过一系列文章打造一个简单的开发框架,以便帮助大家理解:

  1. 和fpm模式下有何区别?

  2. 如何整合现有的开发框架

swoole4.0协程初探这篇文章中,我们已经有了一个http server的雏形了,但就一个单一的文件,代码耦合在一起,下面我们开始改造之旅了

框架先起个名吧?

我们的公众号是php饭米粒,是由phpfamily谐音过来的,那框架就叫:Family 了

规划目录结构

为什么要先规划目录结构,整体前期我们先通过 约定大于配置的 的方式来组织的我们的代码,这样方便大家的统一理解, 另一个重要的约定就是:符合PSR规范

.├── application         //应用目录│   ├── config          //配置文件│   ├── controller      //controller│   └── index.php       //统一入口├── bin│   └── family.service  //服务管理脚本,类php-fpm.service└── framework           //框架约定目录    └── Family          //框架主目录            └── family.php      //框架主文件

这是一个雏形了,我们将在这个基础上一步一步打造自己的框架, 

统一入口

接下来我们要定义一个方法,做为程序的入口,让程序能够跑起来,这里我们只支持 http server,所以代码如下:

<?php//path framework/Family/Family.phpnamespace Family;use Family\Core\Config;use Swoole;class Family{    final public static function run()    {        $http = new Swoole\Http\Server('0.0.0.0', 9501);        $http->set([            "worker_num" => 1,        ]);        $http->on('request', function ($request, $response) {            $response->end("hello, family is run");        });        $http->start();    }}

然后我们在application/index.php加入代码:

<?phpuse Family\Family;require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR . 'Family' . DIRECTORY_SEPARATOR . 'Family.php';Family::run();

执行 php application/index.php, 浏览器执行:http://127.0.0.1:9501,输出如下

但以这里,我们啥也做不了,和单一的文件基本没什么区别,接下我们做三件事:

  1. 实现一个类的自动加载

  2. 把配置参数抽离出来

  3. 实现一个简单的路由

自动加载

我们这里先不用composer,自己实现一个简单的自动加载功能, 在Family类添加一个方法:

/** * @param $class * @desc 自动加载类 */final public static function autoLoader($class){    //定义rootPath    $rootPath = dirname(dirname(__DIR__));    //把类转为目录,eg \a\b\c => /a/b/c.php    $classPath = \str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';    //约定框架类都在framework目录下, 业务类都在application下    $findPath = [        $rootPath . DIRECTORY_SEPARATOR . 'framework' . DIRECTORY_SEPARATOR,        $rootPath . DIRECTORY_SEPARATOR . 'application' . DIRECTORY_SEPARATOR,    ];    //遍历目录,查找文件    foreach ($findPath as $path) {        //如果找到文件,则require进来        $realPath = $path . $classPath;        if (is_file($realPath)) {            require "{$realPath}";            return;        }    }}

实现一个配置加载和读取类

在框架创建一个Core目录,此目录是框架的一些核心类, 第一个核心类就是Config.php

<?phpnamespace Family\Core;class Config{    /**     * @var 配置map     */    public static $configMap;    /**     * @desc 读取配置,默认是application/config/default.php     */    public static function load()    {        $configPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'application' . DIRECTORY_SEPARATOR . 'config';        self::$configMap = require $configPath . DIRECTORY_SEPARATOR . 'default.php';    }    /**     * @param $key     * @desc 读取配置     * @return string|null     *     */    public static function get($key)    {        if(isset(self::$configMap[$key])) {            return self::$configMap[$key];        }        return null;    }}

然后我们就可以把配置抽离出来:

<?php//file: application/config/default.phpreturn [    'host' => '0.0.0.0',    'port' => 9501,    'worker_num' => 1];

现在run方法就变成了:

final public static function run(){    //先注册自动加载    \spl_autoload_register(__CLASS__ . '::autoLoader');    //加载配置    Config::load();    //通过读取配置获得ip、端口等    $http = new Swoole\Http\Server(Config::get('host'), Config::get('port'));    $http->set([        "worker_num" => Config::get('worker_num'),    ]);    $http->on('request', function ($request, $response) {        $response->end("hello, family is run");    });    $http->start();}

实现一个简单的路由

这里我们还是通过约定大于配置的方式,规定url的组成方式是:http://127.0.0.1:9501/controller/method ,那代码如下:

<?phpnamespace Family\Core;class Route{    public static function dispatch($path)    {        //默认访问 controller/index.php 的 index方法        if (empty($path) || '/' == $path) {            $controller = 'Index';            $method = 'Index';        } else {            $maps = explode('/', $path);            $controller = $maps[1];            $method = $maps[2];        }        $controllerClass = "controller\\{$controller}";        $class = new $controllerClass;        return $class->$method();    }}

接下来我们在controller目录下新建一个Index.php, 代码如下:

<?phpnamespace controller;class Index{    public function index()    {        return 'i am family by route';    }    public function tong()    {        return 'i am tong ge';    }}

接下来,我们把run方法改造下

final public static function run(){    //先注册自动加载    \spl_autoload_register(__CLASS__ . '::autoLoader');    //加载配置    Config::load();    //通过读取配置获得ip、端口等    $http = new Swoole\Http\Server(Config::get('host'), Config::get('port'));    $http->set([        "worker_num" => Config::get('worker_num'),    ]);    $http->on('request', function ($request, $response) {        //自动路由        $result = Route::dispatch($request->server['path_info']);        $response->end($result);    });    $http->start();}

执行 php application/index.php,

浏览器输入 http://127.0.0.1:9501/Index/tong 

可输出:  i am tong ge

现在我们的代码结构如下:

           .├── application             //应用目录│   ├── config              //配置目录│   │   └── default.php     //默认配置文件│   ├── controller          //controller目录│   │   └── Index.php       //controller文件│   └── index.php           //统一入口├── bin                     │   └── family.service      //预留的服务管理脚本,类php-fpm.service└── framework               //框架目录    └── Family              //框架主目录        ├── Core            //框架核心类目录        │   ├── Config.php  //配置读取        │   └── Route.php   //路由管理        └── Family.php      //框架主文件

至此已经实现了一个基础的web框架了

下一篇,将实现各种的异常处理和日志处理

查看原文,可以从github上获取代码

--------------伟大的分割线----------------

PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!

饭米粒只发原创或授权发表的文章,不转载网上的文章

所发的文章,均可找到原作者进行沟通。

也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投搞。

投稿请联系:

shenzhe163@gmail.com

本文由 半桶水 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)