从零实现一个 PHP 微框架 - 前言
source link: https://blog.ixk.me/post/implement-a-php-microframework-from-zero-1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
从零实现一个 PHP 微框架 - 前言
前不久为了准备用 PHP(原本打算是用 Spring,但是还不太会 233) 写一个博客项目,因为不打算使用任何框架,于是便打算自己写一个应用模板来完成博客这个坑。由于之前用过 Laravel 并且很喜欢 Laravel 接口的风格,一开始打算是弄一个接口与 Laravel 类似的模板,所以就没有考虑到 PSR 相关的标准。后来由于博客项目暂时咕掉了 ?,而且 PHP 模板也逐渐完善便打算将其作为一个独立的项目来进行开发。
在开发的期间学习了许多有趣的功能和设计模式,看了不少 Laravel 的文章和源码(XK-PHP 有部分代码是基于 Laravel 缩水而来,当然也有不少添加了一些功能 ?)。
最近 XK-PHP 已经趋于完善,于是就打算把开发过程遇到的坑和学到的知识写成文章,做下记录顺便分享给有想了解框架如何实现的同学们。
本系列文章(没错,我要水好几篇文章 ?)主要是围绕着 XK-PHP 的实现过程展开,同时也会提及 Laravel 和 Swoft 等 PHP 框架的代码或问题。(预计可能会写好几个月
Github 地址
目前 XK-PHP 大部分的功能均已完成,如果不想看文章的话也可以直接到 Github 上查看代码。
- IoC 容器,兼容 PSR-11
- 中间件,兼容 PSR-15
- 请求和响应,兼容 PSR-7
- Facade
- ReactJS 集成,类似于 Laravel Mix
- 简单事件系统
- 简单任务队列,类似于协程,但是是同步阻塞的,只是可以主动让出
- PHPUnit 单元测试,HTTP 测试
- 响应异常处理
- 模板系统,类似于 Yii 的视图
1<?php 2 3namespace App\Controllers; 4 5use ... 6 7class HomeController 8{ 9 /** 10 * @var Request 11 * @Autowired("App\Http\Request") 12 */ 13 public $request; 14 15 /** 16 * @var Hash 17 */ 18 public $hash; 19 20 public function __construct(Hash $hash) 21 { 22 // 如果不使用注解注入类属性,则可以使用构造器注入 23 $this->hash = $hash; 24 } 25 26 /** 27 * @param Request $request 28 * @param AnnotationReader $reader 29 * @return View 30 * 31 * @DI\Set({ 32 * @DI\Item(name="request", value="request") 33 * }) 34 */ 35 public function index($request, AnnotationReader $reader): View 36 { 37 return view('home')->filter(function (string $content) { 38 return preg_replace('/>(\s*)</', '><', $content); 39 }); 40 } 41 42 /** 43 * @param Request $request 44 * @return View 45 * @Route\Get("/home/home") 46 */ 47 public function home(Request $request): string 48 { 49 return view('home'); 50 } 51 52 /** 53 * @param Request $request 54 * @return string 55 * 56 * @Route\Get("/jwt") 57 */ 58 public function jwt(Request $request): string 59 { 60 return JWT::decode($request->query('jwt')); 61 } 62 63 /** 64 * @param Request $request 65 * @return bool 66 */ 67 public function get(Request $request): bool 68 { 69 return true; 70 } 71 72 /** 73 * @param Request $request 74 * @return bool 75 * 76 * @Route\Get("/exce") 77 */ 78 public function exception(Request $request): bool 79 { 80 Log::info('Info', 'Info', ['Info']); 81 Log::debug('Debug', 'Debug', ['Debug']); 82 Log::warn('Warn', 'Warn', ['Warn']); 83 Log::error(new MethodNotAllowedException('Error')); 84 Log::fatal(new MethodNotAllowedException('Fatal')); 85 report('info', 'Info Function'); 86 abort(403); 87 return true; 88 } 89 90 /** 91 * @param int $path 92 * @param int $query 93 * @return string 94 * 95 * @Route\Get("/inject/{path}") 96 */ 97 public function inject(int $path, int $query): string 98 { 99 // IoC 容器会自动将参数名作为 key 在绑定的实例和 Request 中寻找匹配的字段,然后进行注入 100 return $path . ',' . $query; 101 } 102 103 /** 104 * @return Response 105 * 106 * @Route\Get("/cookie") 107 */ 108 public function cookie(): Response 109 { 110 $response = \response('Cookie')->cookie('cookie1', 'value'); 111 Cookie::queue(\App\Http\Cookie::make('cookie2', 'value')); 112 return $response; 113 } 114 115 /** 116 * @return string 117 * 118 * @Route\Get("/aspect") 119 */ 120 public function aspect(): string 121 { 122 report('debug', 'hash'); 123 $hash = \App\Facades\Hash::make('123'); 124 report('debug', 'encrypt'); 125 $encrypt = App::callWithAspect( 126 [Crypt::class, 'encrypt'], 127 [ 128 'value' => '123' 129 ] 130 ); 131 report('debug', 'function'); 132 App::callWithAspect( 133 function () { 134 report('debug', 'function-in'); 135 }, 136 [], 137 null, 138 false, 139 [LogAspect::class] 140 ); 141 report('debug', App::make('path')); 142 return ''; 143 } 144 145 /** 146 * @return string 147 * 148 * @Route\Get("/event") 149 */ 150 public function event(): string 151 { 152 Event::dispatch('event.str_config'); 153 Event::listen(LogEvent::class, [LogListener::class, 'handle']); 154 Event::subscribe(LogSubscriber::class); 155 Event::dispatch(LogEvent::class); 156 Event::listen('event.str', StrListener::class); 157 Event::dispatch('event.str'); 158 return ''; 159 } 160 161 /** 162 * @return string 163 * 164 * @Route\Get("/task") 165 */ 166 public function task(): string 167 { 168 $scheduler = new Scheduler(); 169 $req = function () { 170 report('debug', 'task1-start'); 171 $ch = curl_init(); 172 curl_setopt($ch, CURLOPT_URL, "http://ixk.me"); 173 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 174 $data = curl_exec($ch); 175 curl_close($ch); 176 report('debug', 'task1-end'); 177 return $data; 178 }; 179 $task1 = function () use ($req) { 180 for ($i = 0; $i < 5; $i++) { 181 yield $req(); 182 } 183 }; 184 $task2 = function () { 185 for ($i = 0; $i < 5; $i++) { 186 report('debug', 'task2-start'); 187 yield; 188 } 189 }; 190 $scheduler->add($task1); 191 $scheduler->add($task2); 192 $scheduler->then(); 193 return ''; 194 } 195}
由于框架已经实现,所以我们就先看看结果吧。可以看到使用的方式和 Laravel 类似,当我们需要某些对象的时候,只需要在方法参数声明即可,IoC 容器会自动注入到方法中,也可以在构造器中或使用注解的方式注入到类中,同时也支持切面,日志,事件等功能。
XK-PHP 参考了以下的框架:
感谢这些框架为我提供了实现和学习的思路。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK