iuravic / duktig-core
Duktig MVC 微型 Web 框架的 PHP 7.1 核心库
Requires
- php: ^7.1
- http-interop/http-factory: dev-master
- http-interop/http-middleware: ^0.4.1
- iuravic/duktig-auryn-adapter: dev-master
- psr/container: ^1.0
- psr/http-message: ^1.0
- psr/log: ^1.0
Requires (Dev)
- iuravic/duktig-http-factory-guzzle-adapter: dev-master
- iuravic/duktig-middleman-adapter: dev-master
- iuravic/duktig-symfony-event-dispatcher-adapter: dev-master
- iuravic/duktig-symfony-router-adapter: dev-master
- iuravic/duktig-twig-adapter: dev-master
- mockery/mockery: ^1.0
- monolog/monolog: ^1.22
- phpunit/phpunit: ^6.1
This package is not auto-updated.
Last update: 2024-09-29 01:49:10 UTC
README
duktig-core
这是 Duktig 微型 MVC Web 框架的核心包。
使用 Duktig 框架的全功能原型 Web 项目包含在 duktig-skeleton-web-app 包中,该包使用此核心包并实现所有依赖。
目录
关于
Duktig 是一个轻量级的 MVC 微型 Web 框架,专为 PHP 7.1 编写。它最初被创建为一个教育项目,但它经过了全面测试,也可用于生产环境。它实现了 MVC 模式,并具有 IoC 容器、事件系统和 HTTP 中间件。
duktig-skeleton-web-app 包
duktig-skeleton-web-app 包是一个全功能项目,基于 duktig-core 包。它可以作为开发自己的 Duktig 框架应用程序的起点,因为它已根据流行的开源项目和包预先实现了所有必要的依赖。
目的
Duktig 框架的目标是提供一种灵活而强大的框架,通过使用最可行和最新的特性和实践来创建 Web 应用程序。
通过学习当今一些最受欢迎的 PHP Web 框架(Aura、Silex、Slim、Stack、Yii2、Lumen、Symfony、Laravel、Bullet、Proton),Duktig 的核心架构依赖于现代原则和标准。
标准
- PSR 兼容
- PSR-2 编码和 PSR-4 自动加载标准
- 接口:PSR-3 日志记录器,PSR-7 HTTP 消息,PSR-11 容器,PSR-15 HTTP 中间件,PSR-17 HTTP 工厂
- 解耦的包设计
- 由流行的开源项目和库提供支持
- 通过 TDD 开发,单元测试、集成测试和功能测试
功能
- MVC 模式
- IoC 和 DI 容器
- HTTP 中间件
- 事件系统
- 懒加载
包设计
大多数 Duktig 核心服务都与核心包解耦,并打包到自己的模块中。这种方法提供了高度的灵活性和良好的包设计。通过接口注入,对象图在执行过程中自然组合。
核心服务
duktig-core 实现了其自身的几个核心服务,同时将大多数其他服务的实现留给了外部项目
这些核心服务已在核心的 'Config/services.php' 文件中注册。如果需要,它们可以被覆盖并由您自己的配置替换。这是通过使用 'skipCoreServices' 配置参数实现的,在这种情况下,它们必须由您自己的配置指定。
duktig-core 默认使用 Auryn DI 容器,或者更确切地说,使用适配器包 duktig-auryn-adapter,该包简单地将其 API 适配到 Duktig 的 规范。容器可以被更改为您自己的选择。
要求
duktig-core 包定义并使用了一组需要在运行时由可解析服务实现的接口。duktig-skeleton-web-app 展示了在实际情况下如何实现这一点,并且是编写您自己的 Duktig 框架应用的推荐起点。简而言之,为了满足这些要求,一个应用程序(例如 duktig-skeleton-web-app)首先将所有必需的包作为 composer 依赖项 包含,然后 将它们注册为服务。
一旦实现,应用程序就可以访问以下接口的实现
Interop\Http\Factory\ServerRequestFactoryInterface和Interop\Http\Factory\ResponseFactoryInterface- HTTP 消息工厂Interop\Http\ServerMiddleware\DelegateInterface- HTTP 中间件调度服务Duktig\Core\Event\Dispatcher\EventDispatcherInterface- 事件调度器Duktig\Core\View\RendererInterface- 模板渲染器Duktig\Core\Route\Router\RouterInterface- 路由服务Psr\Log\LoggerInterface- 日志服务
依赖注入
容器
DI 容器必须实现 Duktig\Core\DI\ContainerInterface。此接口是 Psr\Container\ContainerInterface 的扩展,并包含它自己的几个方法。Duktig 的依赖项解析基于构造函数参数注入,这是容器必须实现的核心功能。
默认情况下,Duktig 使用 Auryn 容器,或者更确切地说,使用适配器包 duktig-auryn-adapter,该包适配到定义的接口。这定义在 dutig-core 的 配置 中。然而,容器可以被更改为任何实现 Duktig\Core\DI\ContainerInterface 的 PSR-11 容器。
ContainerFactory
容器本身由 Duktig\Core\DI\ContainerFactory 实例化和配置。如果自定义容器类有任何构造函数参数,则 ContainerFactory 将尝试使用 ReflectionClass 解析和注入它们。
然后,通过运行服务配置器来配置容器。服务在您的应用程序的 services.php 配置文件中配置。
依赖解析
与其他标准PHP依赖注入容器一样,构造函数参数类型提示用于提供依赖注入。以下框架中的实体由容器解析:
- 服务
- 控制器
- 闭包类型路由处理器
- 中间件
- 事件监听器
这些实体将在运行时解析并注入其构造函数参数。任何依赖都可以通过这种方式注入,无论是之前已定义并作为服务添加到容器中,还是自动提供,这当然取决于您选择的容器是否支持自动提供功能(Auryn DI容器就支持)。
懒加载
框架本身利用了懒加载优化,并在几种情况下延迟对象创建,从而提高了性能。例如,控制器解析仅发生在中间件栈的末尾,而不会更早发生。懒加载自然与容器的make()方法实现相关。因此,如果您选择的容器使用懒加载(Auryn DI容器就是这样),它也将应用于框架的工作流程。
框架组件
路由
duktig-core根据几个实体定义其路由。它使用一个路由器,该路由器负责将当前请求匹配到适当的路由。它还使用一个路由提供者服务,该服务提供了一种简单的API来检索和识别可用的路由。
路由器
Duktig的路由器作为一个独立的服务。路由器必须实现Duktig\Core\Route\Router\RouterInterface。该接口定义了一个强制方法match,它将Psr\Http\Message\ServerRequestInterface对象匹配到Duktig\Core\Route\Router\RouterMatch对象。RouterMatch仅仅是一个值对象,表示匹配到的路由及其参数。
路由
Duktig\Core\Route\Route是Duktig的路由模型。其形式受到了Symfony Route的路由模型的强烈影响,因为它是在开源社区中最丰富和最受欢迎的路由表示之一。
路由提供者
Duktig\Core\Route\RouteProvider是一个服务,提供对路由的访问。它从配置服务中获取路由的配置,并将其转换为Route对象,通过几个用户友好的API方法暴露它们。
路由处理器
路由可以有两种不同类型的解析器:
- 第一种是具有操作方法的经典控制器,其中控制器扩展了
BaseController类,公开了对请求和一些基本组件的访问; - 第二种是直接在路由配置中给出的闭包类型处理器。
两种类型的路由处理器都必须返回一个ResponseInterface类型的对象。对于闭包类型处理器,建议使用Interop\Http\Factory\ResponseFactoryInterface来创建响应实例,而控制器将通过BaseController父类预先准备好响应以供使用。
两种类型的处理器都由容器动态解析,并在创建时注入其构造函数参数的依赖。
控制器
控制器分配给路由,并负责生成响应。或者,除了定义特殊的控制器类外,还可以使用更简单的闭包类型路由处理器。
BaseController
所有控制器类应扩展基本类Duktig\Core\Controller\BaseController,以便访问应用程序上下文,包括属性
$request- PSR-7 请求对象$response- 一个“新鲜”的 PSR-7 响应对象$queryParams- 解析的 URI 参数$renderer- 模板渲染服务$config- 配置服务
BaseController还提供了用于更快速操作响应对象及其渲染的方法。
路由参数
路由参数作为动作方法的参数传递给控制器。例如,如果路由使用一个字符串参数$param,并将其分配给exampleAction方法,则该参数将以这种方式在动作方法中可用
public function exampleAction(string $param) : ResponseInterface;
返回类型
每个控制器或路由处理程序都必须返回一个 PSR-7 响应对象。对于扩展主BaseController类的控制器,可以使用$response属性,该属性由内部由 PSR-17 $responseFactory服务生成。
依赖注入
控制器的构造函数参数将在运行时解析和注入。控制器,以及其他实体,默认情况下不提供对容器的访问,因为这通常被视为服务定位器反模式。然而,也没有对此方法施加特殊限制,并且可以轻松实现。然而,这种做法强烈不建议。
可能没有必要特别指出这一点,但当然,当您的控制器定义它自己的依赖关系时,它还必须尊重其父级的依赖关系,即。
<?php namespace MyProject\Controller; use Duktig\Core\Controller\BaseController; use Interop\Http\Factory\ResponseFactoryInterface; use Duktig\Core\View\RendererInterface; use Duktig\Core\Config\ConfigInterface; use MyProject\Service\CustomService; class IndexController extends BaseController { private $customService; public function __construct( CustomService $customService, ResponseFactoryInterface $responseFactory, RendererInterface $renderer, ConfigInterface $config ) { parent::__construct($responseFactory, $renderer, $config); $this->customService = $customService; } }
延迟加载
控制器仅在命令链到达时解析和实例化。使用特殊的ControllerResponder中间件解析和执行控制器,并将响应返回给应用程序。
中间件
Duktig使用与PSR-15规范对应的“单遍”HTTP中间件。这意味着实现Psr\Http\ServerMiddleware\MiddlewareInterface以及具有以下签名的函数
public function process(ServerRequestInterface $request, DelegateInterface $delegate);
同样,中间件调度系统必须实现Psr\Http\ServerMiddleware\DelegateInterface以及以下方法
public function process(ServerRequestInterface $request);
Duktig从其核心功能中省略了中间件调度系统的实现,并将其委托给外部包。
应用程序和路由中间件
Duktig使用两种类型的中间件
- 应用程序中间件 - 全局适用于整个应用程序,它在每个请求上运行
- 路由中间件 - 可变,可以分配给特定路由
ControllerResponder
ControllerResponder是一个特殊的中间件,位于中间件堆栈的末尾。它解析路由处理程序,调用它,并将响应返回给中间件堆栈。由于它作为路由处理程序视角中的“响应者”使用,因此得名。
中间件堆栈
请求穿越的完整中间件堆栈由以下内容组成
- 应用程序中间件
- 路由中间件
ControllerResponder中间件
依赖注入
所有中间件都由容器实例化,因此将注入它们的构造函数依赖关系。
模板
模板渲染服务由Duktig\Core\View\RendererInterface定义。它提供了一个简单的API,用于使用模板。
事件
事件调度器
事件调度器由Duktig\Core\Event\Dispatcher\EventDispatcherInterface定义。这意味着向调度器提供一个容器,然后用于解析监听器。因此,事件监听器将在它们的事件被分发时进行懒加载。
事件
Duktig中的事件是简单的值对象,包含监听器执行所需的上下文信息。也可以说,事件只是一个具有唯一名称的值对象。
在Duktig中可以使用两种不同的事件类型。
事件作为其独立的类
可以将事件创建为其自己的类,该类必须扩展Duktig\Core\Event\EventAbstract类。
在这种情况下,其名称可以但不一定需要特别提供(作为构造函数参数),默认事件的名称将是其全限定类名,不带前缀反斜杠。例如,对于事件类MyProject\Event\CustomEvent,其默认名称将是'MyProject\Event\CustomEvent'。
以下是一个简单示例,展示如何触发在其自己的独立类中定义的事件。假设UserEvent将参数$user作为构造函数参数。分发此事件就像这样
$event = new \DemoApp\Event\UserEvent($user); $eventDispatcher->dispatch($event);
简单事件
对于最简单的事件,它仅由其唯一的名称表示,并且不需要为监听器的处理程序提供任何其他信息,在这种情况下,不必为事件编写单独的类,而是可以使用现有的Duktig\Core\Event\EventSimple类即时创建事件。
在这种情况下,必须将一个唯一的事件名称提供给构造函数。由于EventSimple可以用来实例化不同的事件,因此每个事件都负责其唯一的命名。
可以通过即时实例化EventSimple对象来分发简单事件,即
$eventDispatcher->dispatch(new \Duktig\Core\Event\EventSimple('theEventName'));
监听器
事件监听器可以是可解析的类/服务名称或简单闭包。
在第一种情况下,如果监听器是一个单独的类,它必须实现Duktig\Core\Event\ListenerInterface。当事件被分发时,监听器将被容器解析,并且注入所有其构造函数依赖项。
如果监听器以简单闭包的形式提供,则它不会被容器解析,因此不会注入任何依赖项。闭包类型的监听器期望只有一个可选参数,即事件
function($event) { /* ... */ }
核心事件
框架在其应用流程的关键点上分发其核心事件。Duktig的核心事件列表可以在duktig-core的events.php文件中找到。一些核心事件仅通过其唯一的名称定义(例如,'duktig.core.app.afterConfiguring'),而其他事件则是作为单独的类创建的。
错误处理
Duktig使用其自己的错误和异常处理器,该处理器实现了Duktig\Core\Exception\Handler\HandlerInterface。其基本任务是注册整个应用中的错误处理,将\Throwable转换为响应,并报告此类错误的发生。
它考虑了PHP 7的错误和异常处理机制。从PHP 7版本开始,\Error和\Exception类都实现了\Throwable接口。现在,一些致命错误和可恢复错误会抛出异常,而不是停止脚本执行。未捕获的异常将继续产生致命错误,同样,从未捕获的致命错误抛出的\Error异常也会产生致命错误。
在生产环境中,异常和错误通过默认或自定义错误模板进行渲染。处理器根据其位置和名称对模板进行优先排序。它将搜索给定可抛出对象的最佳模板,首先尝试在应用自定义模板路径中定位模板,如果没有找到,则使用duktig-core包中的默认模板。它按照以下步骤搜索和渲染错误模板
- 如果抛出
HttpException,它将搜索具有错误代码名称的模板 - 如果没有找到这样的模板,以及所有其他异常类型,它将寻找一个名称等于异常类名称的模板。
- 最后,它将搜索一个通用的错误模板。
渲染服务本身被赋予了模板的位置,包括应用目录内的自定义模板和框架核心目录内的默认模板。这样,它首先寻找自定义模板,然后是默认模板。
配置
配置的详细信息在duktig-skeleton-web-app项目中进行了描述,在那里可以看到其实际应用。骨架应用程序采用了duktig-core,并为其提供了所有依赖项,将其应用于完整的Duktig环境中。
测试
duktig-core和由duktig-skeleton-web-app实现的所有包都使用PHPUnit和Mockery进行了全面测试。
针对测试环境,提供了一个特殊的Duktig\Test\AppTesting类。它扩展了对容器和响应对象的访问。它可以用来轻松地模拟服务,并获得对响应的直接访问。