2

从零实现一个 PHP 微框架 - 前言

 2 years ago
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.
neoserver,ios ssh client

从零实现一个 PHP 微框架 - 前言

2020-05-07 • Otstar Lin •
本文最后更新于 268 天前,文中所描述的信息可能已发生改变

前不久为了准备用 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 的视图
e748d64a 60a6 4bd9 b36e 540586180c31
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 参考了以下的框架:

感谢这些框架为我提供了实现和学习的思路。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK