先解答上一篇:swoole4.0 mysql连接池之读写分离 留了个问题,主从分离后,如果写入之后马上读,大概率会读不到?怎么处理呢?后台很多小伙伴留了言,给出了自己的解决方案:
-
一般是可以强制读主库,有兴趣的可以改造下代码
-
加入一个cache层
本篇开始,将通过一系列文章打造一个简单的开发框架,以便帮助大家理解:
-
和fpm模式下有何区别?
-
如何整合现有的开发框架
在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,输出如下
但以这里,我们啥也做不了,和单一的文件基本没什么区别,接下我们做三件事:
-
实现一个类的自动加载
-
把配置参数抽离出来
-
实现一个简单的路由
自动加载
我们这里先不用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带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。
也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投搞。
投稿请联系:
本文由 半桶水 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)
文章评论