惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
博客园 - 聂微东
罗磊的独立博客
W
WeLiveSecurity
博客园_首页
Scott Helme
Scott Helme
V
Visual Studio Blog
T
The Exploit Database - CXSecurity.com
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
L
Lohrmann on Cybersecurity
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
About on SuperTechFans
F
Full Disclosure
Y
Y Combinator Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 司徒正美
博客园 - Franky
C
CXSECURITY Database RSS Feed - CXSecurity.com
F
Fortinet All Blogs
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
阮一峰的网络日志
阮一峰的网络日志
S
Schneier on Security
雷峰网
雷峰网
博客园 - 【当耐特】
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
Engineering at Meta
Engineering at Meta
aimingoo的专栏
aimingoo的专栏
MongoDB | Blog
MongoDB | Blog
J
Java Code Geeks
T
Tor Project blog
V
V2EX
爱范儿
爱范儿
C
Check Point Blog
T
Threatpost
Project Zero
Project Zero
量子位
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
I
Intezer
G
GRAHAM CLULEY
P
Privacy & Cybersecurity Law Blog
GbyAI
GbyAI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
从零实现一个 PHP 微框架 - 前言 | 青空之蓝
Otstar Lin · 2020-05-07 · via 青空之蓝

前言

前不久为了准备用 PHP(原本打算是用 Spring,但是还不太会 233) 写一个博客项目,因为不打算使用任何框架,于是便打算自己写一个应用模板来完成博客这个坑。由于之前用过 Laravel 并且很喜欢 Laravel 接口的风格,一开始打算是弄一个接口与 Laravel 类似的模板,所以就没有考虑到 PSR 相关的标准。后来由于博客项目暂时咕掉了 ?,而且 PHP 模板也逐渐完善便打算将其作为一个独立的项目来进行开发。

在开发的期间学习了许多有趣的功能和设计模式,看了不少 Laravel 的文章和源码(XK-PHP 有部分代码是基于 Laravel 缩水而来,当然也有不少添加了一些功能 ?)。

最近 XK-PHP 已经趋于完善,于是就打算把开发过程遇到的坑和学到的知识写成文章,做下记录顺便分享给有想了解框架如何实现的同学们。

本系列文章(没错,我要水好几篇文章 ?)主要是围绕着 XK-PHP 的实现过程展开,同时也会提及 Laravel 和 Swoft 等 PHP 框架的代码或问题。(预计可能会写好几个月

目录

  1. 从零实现一个 PHP 微框架 - 前言
  2. 从零实现一个 PHP 微框架 – PSR & Composer
  3. 从零实现一个 PHP 微框架 – IoC 容器
  4. 从零实现一个 PHP 微框架 – Bootstrap 启动加载
  5. 从零实现一个 PHP 微框架 – 服务提供者

Github 地址

目前 XK-PHP 大部分的功能均已完成,如果不想看文章的话也可以直接到 Github 上查看代码。

主要功能

  • IoC 容器,兼容 PSR-11
  • 中间件,兼容 PSR-15
  • 请求和响应,兼容 PSR-7
  • 注解
  • Aop
  • MVC
  • Facade
  • ReactJS 集成,类似于 Laravel Mix
  • 简单事件系统
  • 简单任务队列,类似于协程,但是是同步阻塞的,只是可以主动让出
  • PHPUnit 单元测试,HTTP 测试
  • 响应异常处理
  • 日志系统
  • 模板系统,类似于 Yii 的视图
  • ......

流程图

框架成果

<?php

namespace App\Controllers;

use ...

class HomeController
{
    /**
     * @var Request
     * @Autowired("App\Http\Request")
     */
    public $request;

    /**
     * @var Hash
     */
    public $hash;

    public function __construct(Hash $hash)
    {
        // 如果不使用注解注入类属性,则可以使用构造器注入
        $this->hash = $hash;
    }

    /**
     * @param Request $request
     * @param AnnotationReader $reader
     * @return View
     *
     * @DI\Set({
     *  @DI\Item(name="request", value="request")
     * })
     */
    public function index($request, AnnotationReader $reader): View
    {
        return view('home')->filter(function (string $content) {
            return preg_replace('/>(\s*)</', '><', $content);
        });
    }

    /**
     * @param Request $request
     * @return View
     * @Route\Get("/home/home")
     */
    public function home(Request $request): string
    {
        return view('home');
    }

    /**
     * @param Request $request
     * @return string
     *
     * @Route\Get("/jwt")
     */
    public function jwt(Request $request): string
    {
        return JWT::decode($request->query('jwt'));
    }

    /**
     * @param Request $request
     * @return bool
     */
    public function get(Request $request): bool
    {
        return true;
    }

    /**
     * @param Request $request
     * @return bool
     *
     * @Route\Get("/exce")
     */
    public function exception(Request $request): bool
    {
        Log::info('Info', 'Info', ['Info']);
        Log::debug('Debug', 'Debug', ['Debug']);
        Log::warn('Warn', 'Warn', ['Warn']);
        Log::error(new MethodNotAllowedException('Error'));
        Log::fatal(new MethodNotAllowedException('Fatal'));
        report('info', 'Info Function');
        abort(403);
        return true;
    }

    /**
     * @param int $path
     * @param int $query
     * @return string
     *
     * @Route\Get("/inject/{path}")
     */
    public function inject(int $path, int $query): string
    {
        // IoC 容器会自动将参数名作为 key 在绑定的实例和 Request 中寻找匹配的字段,然后进行注入
        return $path . ',' . $query;
    }

    /**
     * @return Response
     *
     * @Route\Get("/cookie")
     */
    public function cookie(): Response
    {
        $response = \response('Cookie')->cookie('cookie1', 'value');
        Cookie::queue(\App\Http\Cookie::make('cookie2', 'value'));
        return $response;
    }

    /**
     * @return string
     *
     * @Route\Get("/aspect")
     */
    public function aspect(): string
    {
        report('debug', 'hash');
        $hash = \App\Facades\Hash::make('123');
        report('debug', 'encrypt');
        $encrypt = App::callWithAspect(
            [Crypt::class, 'encrypt'],
            [
                'value' => '123'
            ]
        );
        report('debug', 'function');
        App::callWithAspect(
            function () {
                report('debug', 'function-in');
            },
            [],
            null,
            false,
            [LogAspect::class]
        );
        report('debug', App::make('path'));
        return '';
    }

    /**
     * @return string
     *
     * @Route\Get("/event")
     */
    public function event(): string
    {
        Event::dispatch('event.str_config');
        Event::listen(LogEvent::class, [LogListener::class, 'handle']);
        Event::subscribe(LogSubscriber::class);
        Event::dispatch(LogEvent::class);
        Event::listen('event.str', StrListener::class);
        Event::dispatch('event.str');
        return '';
    }

    /**
     * @return string
     *
     * @Route\Get("/task")
     */
    public function task(): string
    {
        $scheduler = new Scheduler();
        $req = function () {
            report('debug', 'task1-start');
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, "http://ixk.me");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            $data = curl_exec($ch);
            curl_close($ch);
            report('debug', 'task1-end');
            return $data;
        };
        $task1 = function () use ($req) {
            for ($i = 0; $i < 5; $i++) {
                yield $req();
            }
        };
        $task2 = function () {
            for ($i = 0; $i < 5; $i++) {
                report('debug', 'task2-start');
                yield;
            }
        };
        $scheduler->add($task1);
        $scheduler->add($task2);
        $scheduler->then();
        return '';
    }
}
<?php

namespace App\Controllers;

use ...

class HomeController
{
    /**
     * @var Request
     * @Autowired("App\Http\Request")
     */
    public $request;

    /**
     * @var Hash
     */
    public $hash;

    public function __construct(Hash $hash)
    {
        // 如果不使用注解注入类属性,则可以使用构造器注入
        $this->hash = $hash;
    }

    /**
     * @param Request $request
     * @param AnnotationReader $reader
     * @return View
     *
     * @DI\Set({
     *  @DI\Item(name="request", value="request")
     * })
     */
    public function index($request, AnnotationReader $reader): View
    {
        return view('home')->filter(function (string $content) {
            return preg_replace('/>(\s*)</', '><', $content);
        });
    }

    /**
     * @param Request $request
     * @return View
     * @Route\Get("/home/home")
     */
    public function home(Request $request): string
    {
        return view('home');
    }

    /**
     * @param Request $request
     * @return string
     *
     * @Route\Get("/jwt")
     */
    public function jwt(Request $request): string
    {
        return JWT::decode($request->query('jwt'));
    }

    /**
     * @param Request $request
     * @return bool
     */
    public function get(Request $request): bool
    {
        return true;
    }

    /**
     * @param Request $request
     * @return bool
     *
     * @Route\Get("/exce")
     */
    public function exception(Request $request): bool
    {
        Log::info('Info', 'Info', ['Info']);
        Log::debug('Debug', 'Debug', ['Debug']);
        Log::warn('Warn', 'Warn', ['Warn']);
        Log::error(new MethodNotAllowedException('Error'));
        Log::fatal(new MethodNotAllowedException('Fatal'));
        report('info', 'Info Function');
        abort(403);
        return true;
    }

    /**
     * @param int $path
     * @param int $query
     * @return string
     *
     * @Route\Get("/inject/{path}")
     */
    public function inject(int $path, int $query): string
    {
        // IoC 容器会自动将参数名作为 key 在绑定的实例和 Request 中寻找匹配的字段,然后进行注入
        return $path . ',' . $query;
    }

    /**
     * @return Response
     *
     * @Route\Get("/cookie")
     */
    public function cookie(): Response
    {
        $response = \response('Cookie')->cookie('cookie1', 'value');
        Cookie::queue(\App\Http\Cookie::make('cookie2', 'value'));
        return $response;
    }

    /**
     * @return string
     *
     * @Route\Get("/aspect")
     */
    public function aspect(): string
    {
        report('debug', 'hash');
        $hash = \App\Facades\Hash::make('123');
        report('debug', 'encrypt');
        $encrypt = App::callWithAspect(
            [Crypt::class, 'encrypt'],
            [
                'value' => '123'
            ]
        );
        report('debug', 'function');
        App::callWithAspect(
            function () {
                report('debug', 'function-in');
            },
            [],
            null,
            false,
            [LogAspect::class]
        );
        report('debug', App::make('path'));
        return '';
    }

    /**
     * @return string
     *
     * @Route\Get("/event")
     */
    public function event(): string
    {
        Event::dispatch('event.str_config');
        Event::listen(LogEvent::class, [LogListener::class, 'handle']);
        Event::subscribe(LogSubscriber::class);
        Event::dispatch(LogEvent::class);
        Event::listen('event.str', StrListener::class);
        Event::dispatch('event.str');
        return '';
    }

    /**
     * @return string
     *
     * @Route\Get("/task")
     */
    public function task(): string
    {
        $scheduler = new Scheduler();
        $req = function () {
            report('debug', 'task1-start');
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, "http://ixk.me");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            $data = curl_exec($ch);
            curl_close($ch);
            report('debug', 'task1-end');
            return $data;
        };
        $task1 = function () use ($req) {
            for ($i = 0; $i < 5; $i++) {
                yield $req();
            }
        };
        $task2 = function () {
            for ($i = 0; $i < 5; $i++) {
                report('debug', 'task2-start');
                yield;
            }
        };
        $scheduler->add($task1);
        $scheduler->add($task2);
        $scheduler->then();
        return '';
    }
}

由于框架已经实现,所以我们就先看看结果吧。可以看到使用的方式和 Laravel 类似,当我们需要某些对象的时候,只需要在方法参数声明即可,IoC 容器会自动注入到方法中,也可以在构造器中或使用注解的方式注入到类中,同时也支持切面,日志,事件等功能。

结语

XK-PHP 参考了以下的框架:

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

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

https://blog.ixk.me/post/implement-a-php-microframework-from-zero-1
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2020/05/07

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

从零实现一个 PHP 微框架 - PSR & ComposerMVVM 简单实现