本系列将通过一系列文章打造一个简单的开发框架,以便帮助大家理解:
-
和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地址: https://github.com/shenzhe/family
--------------伟大的分割线----------------
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。
也希望各位多多打赏(算作稿费给文章作者),更希望大家多多投搞。
投稿请联系:
shenzhe163@gmail.com
本文由 半桶水 授权 饭米粒 发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注)