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
类。它扩展了对容器和响应对象的访问。它可以用来轻松地模拟服务,并获得对响应的直接访问。