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

2018年12月10日 257点热度 0人点赞 0条评论

先解答上一篇: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.php
namespace 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加入代码:

<?php
use 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

<?php
namespace 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.php
return [
'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 ,那代码如下:

<?php
namespace 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, 代码如下:

<?php
namespace 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带来一些值得细细品味的精神食粮!

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

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

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

投稿请联系:

[email protected]


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

图片

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

这个人很懒,什么都没留下

文章评论