zer0-framework / core
Requires
- php: >=7.2
- ext-ctype: *
- ext-gettext: *
- ext-iconv: *
- ext-json: *
- ext-mbstring: *
- ext-pcntl: *
- ext-posix: *
- vlucas/phpdotenv: ^3.3
Suggests
- ext-curl: *
- kakserpom/phpdaemon: dev-master
README
此部分可以跳过,但建议开发人员阅读。
该框架遵循极简主义,并基于三个基本原则——优雅、性能、实用主义。
性能
PHP 7 是一个性能良好的语言,但流行的框架开发者过于热衷于可替换性和通用性,同时不断增加运行时抽象,以至于没有注意到如何使一切变得混乱。通常可以看到,有大量的模块注册表,它们在每次请求时依次遍历,并且每个模块都会在状态中添加一些东西。
结果就是我们现在所看到的情况。平均网站在专用服务器上每秒只能处理大约 50 个请求(没有在 nginx 中缓存响应),该服务器占据机架上的两个单元。
零引导原理
这是 Hello world! 控制器的 backtrace,只需将其与您曾经使用过的框架中的调用链进行比较即可。您会大吃一惊。
1 0.0000 397392 {main}( ) .../index.php:0
2 0.0006 444616 Zer0\HTTP\HTTP->handleRequest( ) .../index.php:22
3 0.0008 520416 MyProject\HTTP\Controllers\Index->indexAction( ) .../HTTP.php:136
我们不执行运行时中的任何多余操作,也不执行不需要的代码。
例如,如果业务逻辑不需要执行此特定 HTTP 请求的会话,那么访问会话(从存储中读取)被视为不体面的行为。同样,如果会话数据没有变化,则不应将会话存储写入存储。
Composer 实现得如此之好,以至于连接的依赖关系可以无耻地添加文件到无条件自动加载中,因此,在 cli 目录中为 phinx 等不用于 Web 部分的包创建了单独的 composer.json。在 cli 中,性能无关紧要,并且没有问题连接任何东西,而我们的主要应用程序则保持干净。
PHP 不再会死亡
历史上,PHP 被视为在请求处理完成后完全重置其状态的解释器。然而,更有效的选项是真正的 FastCGI,其中 bootstrap 阶段在工作进程执行一次后,然后是进入请求的顺序处理。这就是 FastCGI 中“Fast”的含义。而经典的 CGI 则没有一次性执行 bootstrap 阶段的能力。
- 我们不使用 exit() 和 die()
- 我们使用异常来代替致命错误。
- 常量不应该是请求相关的。
接近 PHP
在大多数框架中(我们不会指出具体哪个),要访问请求参数,需要几十个类,创建一大堆对象,然后调用一个方法,该方法调用另一个方法,该方法调用另一个方法,该方法最终访问参数集合,检查是否存在参数并返回值或默认值 $default。这非常低效,不直观且庞大。
我们简单地写入 $_GET['name'] ?? 'John Doe'
。是的,在某个时候没有操作符 ??,必须到处携带 isset() 或使用辅助方法。但那些日子已经过去了。
测试
单元和集成测试是很好的。代码覆盖的测试越多,破坏的机会就越小,并且不会注意到。但是,测试必须是快速的,最重要的是支持多线程执行。
实用主义
对 PHP 版本的态度
我们始终针对最新的稳定版 PHP(目前为 7.2.10)进行优化,不关注旧版本的支持,这样我们就可以使用最新的功能,同时避免使用 PHP_VERSION 条件。
代码风格
我们不浪费时间手动维护代码风格,也不争论哪种风格更好。
在开发环境中,
make fmt
队伍(fmt
的目标也包含在all
中)会自动格式化所有 PHP、JS 和 CSS 源代码。
类型化
在开发框架时,我们几乎在所有情况下都坚持严格类型化的原则。也就是说,函数/方法应该指定参数类型和返回值类型。例如,
public function validate(?string $token = null): bool
这可以避免许多错误。同时,强烈建议在每个文件的开始处指定 declare(strict_types=1);
。
我们几乎从不使用 loose comparison
(运算符 ==
和 !=
)。少数例外是当需要按类型和属性而不是按引用比较两个对象时。
此外,我们不将字符串转换为 boolean、float 和 integer,因为这会导致 PHP 历史上不正确的识别。例如,
php > var_dump(('1helloWorld' == 1);
bool(true)
这可能在处理用户输入时导致不良后果。
代理
本质上,它们是组件和库的工厂。例如,$app->factory('Redis')
将返回一个 Redis 驱动器对象,其设置来自 conf/Redis
。get() 方法接受一个可选参数,指定配置名称,这使得可以拥有多个同一组件的配置。
有些代理在第一次创建后存储对象,例如 PDO
或 SessionStorage
。而其他代理,如 Session
,在每次调用 get()
时都会创建一个新的对象。
添加自己的代理
需要将以下内容添加到 Main 配置中
brokers:
MyCustomBroker: \My\Custom\Broker
并创建一个继承自 \Zer0\Brokers\Base 的相应类。
同样,也可以替换 \Zer0\Brokers\*
中不需要在配置中声明的代理。