simovative / zeus
PHP 的后重定向获取(Post-Redirect-Get)专用框架
Requires
- php: ^8.2 || ^8.3
- ext-intl: *
- ext-json: *
- guzzlehttp/guzzle: >=6.0.0, <8.0.0
- psr/http-message: ^1.0 || ^2.0
- psr/http-server-handler: ^1.0
- roave/security-advisories: dev-latest
- smarty/smarty: >=3.1.48 <5.0.0
- symfony/console: >=4.0.0
- symfony/debug: <5.0.0
Suggests
- ext-dom: For BootstrapFormValidation
- ext-fileinfo: Better recognition for file mime types in the file system classes.
- ext-libxml: For BootstrapFormValidation
- dev-master
- 16.1.0
- 16.0.0
- 15.0.0
- 14.0.0
- 13.0.0
- 12.0.2
- 12.0.1
- 12.0.0
- 11.0.0
- 10.0.1
- 10.0.0
- 9.0.3
- 9.0.2
- 9.0.1
- 9.0.0
- 8.0.1
- 8.0.0
- 7.0.2
- 7.0.1
- 7.0.0
- 6.2.2
- 6.2.1
- 6.2.0
- 6.1.0
- 6.0.3
- 6.0.2
- 6.0.1
- 6.0.0
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 4.2.0
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.2
- 4.0.1
- 4.0.0
- 3.0.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.1
- 2.0.0
- 1.2.0
- 1.1.0
- 1.0.1
- 1.0.0-RC
- 0.3.0
- 0.2.0
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0
- dev-dependabot/composer/smarty/smarty-gte-3.1.48lt-6.0.0
- dev-ACFIVE-38341-option-requests
- dev-dev-middleware
This package is auto-updated.
Last update: 2024-09-21 14:07:34 UTC
README
索引
设计原则
这个框架专为依赖于 HTTP 协议和后重定向获取(PRG)设计模式的程序而设计。从我们的角度看,它可以扩展来创建任何类型的程序,你只需要扩展 HttpKernel 或编写自己的 Kernel。但目前的意图并非如此。
要使用自动表单填充功能,您需要使用 bootstrap(getbootstrap.com)作为您的前端框架。如果您不想使用它,您需要编写自己的 FormPopulation 类并将其替换为现有类。我们将在未来为其编写教程。请参阅修改组件。
为什么我们开发了这个框架?
这个框架是与 PHP 咨询公司(thePHP.cc)的 Stefan Priebsch 合作创建的。它旨在为将我们的单体应用程序构建为模块化可维护产品提供一种可靠的解决方案。它应该对年轻和不经验丰富的开发者易于理解,并为以可维护的方式开发新功能提供坚实的边界。它内置了 CQRS(命令查询责任分离)的实现方式,但如果你不需要或不想使用它,你可以忽略它(但我们不建议这样做)。
什么是 PRG?
后重定向获取(Post-Redirect-Get)是一种常见的 Web 开发设计模式。请参阅 https://en.wikipedia.org/wiki/Post/Redirect/Get
为什么我要使用这个框架而不是其他框架?
这个框架并不旨在为应用程序可能遇到的每个问题提供解决方案,而是试图提供一种优雅的方式来实施一个非常具体的任务。与其他框架不同,它并不试图成为一切的基础,而是为您的应用程序特定的 PRG 需提供一个可靠的解决方案。我们还实现了所有其他 HTTP 方法中的大多数,因此它也易于用作 SPA 的后端或实现 Web API。
它旨在与其他框架兼容以处理其他任务。命令是可重用的,您应该能够使用命令接口将任何现有的动作注入到框架中。
兼容性
目前支持 PHP 7.1 及以上版本。我们将很快移除对旧版本的 PHP 的支持,因此如果您打算保持在 7.1 或更早版本,则不应使用此框架。
快速入门
本节解释了如何尽快将框架安装并运行。
注意:将框架注册到现有应用程序超出了本指南的范围,但这不应是一个大问题,因为我们也这样做过。
生成您的应用程序
使用 composer 安装
php composer.phar require simovative/zeus
并让 cli 帮助您设置默认应用程序
vendor/bin/zeus c:a Simovative\\Demo Demo
这将创建一个包含 index.php 文件和包含 ApplicationBundle 和 ApplicationKernel 的 "bundles" 文件的 public 文件夹。
您可能想立即配置您的 Web 服务器以测试是否成功。
Web 服务器配置
Apache
将您的web根目录指向 $cwd/public
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php [NC,L,QSA]
Nginx
location / {
try_files = $uri index.php?$args;
root /var/www/site/public;
index index.php;
}
PHP内部web服务器
只需运行web服务器,并使用public/index.php作为路由脚本
php -S localhost:8000 public/index.php
如果一切正常,您应该看到一个基本设置页面,告诉您添加另一个包。
生成一个包
vendor/bin/zeus c:b Test
接下来,在您的应用程序内核中注册该包,我们将其命名为 "Test"(在这种情况下为 bundles/Application/DemoKernel.php)
protected function registerBundles(HttpRequestInterface $request) { $bundles = array(); $bundles[] = new DemoApplicationBundle(); $bundles[] = new \Simovative\Demo\Test\TestBundle(); return $bundles; }
如果一切正常,路由器将接管,您将看到一个带有消息的页面
Replace me with some serious content, please
生成一个用于显示内容的页面(GET)
vendor/bin/zeus c:p Home Test
这将在您的 bundles/Test/Page 文件夹中创建两个文件
- HomePage.php:这是要显示的页面
- HomePageFactoryMethods.txt:将此代码移动到您的 Bundle-Factory(TestFactory.php)中,以便从路由器创建页面。(文件之后可以删除)
为此页面创建一个路由,向您的 GetRequestRouter 添加类似的内容
if ($request->getUrl() == '/test/home') {
return $this->factory->createTestHomePage();
}
生成一个提交表单的页面(GET)
vendor/bin/zeus c:f Login Test
这将在您的 bundles/Test/Page 文件夹中创建两个文件
- LoginForm.php
- LoginFormFactoryMethods.txt 它包含与普通页面相同类型的代码,应按相同方式处理。还使用类似的代码为其创建路由。
唯一的区别是,如果表单提交到命令,并且验证器验证失败,错误消息和表单内容将自动填充到表单中。这发生在 CommandDispatcher 中,如果您想查看它。
生成一个命令(POST)
vendor/bin/zeus c:c Login Test
这将在您的 bundles/Test/Command 文件夹中创建五个文件
- LoginCommand.php:包含执行所需所有数据的命令对象。
- LoginCommandBuilder.php 命令构建器连接所有命令类,将由命令分发器用于获取单个组件。
- LoginCommandHandler.php:处理程序执行命令。
- LoginCommandValidator.php:验证器检查命令请求,如果提供的数据有效以创建命令并执行处理程序。我们建议进行基本检查,例如必需的数据、数据类型是否正确以及数据格式是否正确(例如,如电子邮件地址)。如果由于应用程序的当前状态(例如,数据库中缺少某些数据)无法成功执行命令,则命令处理程序应返回一个表示此情况的响应。
- LoginCommandFactoryMethods.txt:将此代码移动到您的 Bundle-Factory(TestFactory.php)中,以创建所有必需的组件,并从命令路由器获取构建器创建方法。
为此命令创建一个路由,向您的 CommandRouter 添加类似的内容
if ($request->getUrl() == '/test/login') {
return $this->factory->createTestLoginCommandBuilder();
}
一般结构
配置
如何实现环境取决于您。这样做可以非常简单,例如编写一个包含信息的ini文件。
<?php /* public/index.php */ $basePath = __DIR__ . '/..'; require_once $basePath . '/vendor/autoload.php'; if (is_readable($basePath . '/config.ini')) { $config = parse_ini_file($basePath . '/config.ini'); } else { $config = array(); } $masterFactory = new Simovative\Zeus\Dependency\MasterFactory( new \Simovative\Zeus\Configuration\Configuration($config, $basePath) ); $kernel = new \Simovative\Skeleton\Application\SkeletonKernel($masterFactory); $kernel->run($masterFactory->createRequestFromGlobals());
更复杂的方法是从环境中获取配置值。
<?php /* public/index.php */ $basePath = __DIR__ . '/..'; require_once $basePath . '/vendor/autoload.php'; $config = array( getenv('database_url'), getenv('redis_url'), getenv('queue_url'), ); $masterFactory = new Simovative\Zeus\Dependency\MasterFactory( new \Simovative\Zeus\Configuration\Configuration($config, $basePath) ); $kernel = new \Simovative\Skeleton\Application\SkeletonKernel($masterFactory); $kernel->run($masterFactory->createRequestFromGlobals());
但是,如果您的环境变得非常复杂,您将需要一个更复杂的解决方案。只要结果配置可以缩小到键值数组,框架就可以处理一切。
MasterFactory
框架的所有组件都可以通过此工厂访问
其他工厂可以使用 Factory Interface 在此工厂中注册。
约定:框架的所有组件通常调用工厂方法
以 "get" 前缀调用单例,如果它们明确要求类的新实例,则以 "create" 前缀调用。因此,当将另一个工厂注入主工厂时,不以 "get" 或 "create" 开头的方法将被忽略。
请求
框架有自己的请求类,用于访问请求值。
HTTPKernel
如前所述,框架没有提供完整的内核,您必须扩展抽象的 HttpKernel 或使用 KernelInterface 实现自己的。默认的 HttpKernel 将
- 处理(可选)应用程序状态
- 注册包
- 分发请求
- 处理响应
示例实现
<?php namespace Simovative\Skeleton\Application; use Simovative\Zeus\Http\HttpKernel; use Simovative\Zeus\Http\Request\HttpRequestInterface; /** * My Applications wonderful Kernel */ class Kernel extends HttpKernel { /** * @inheritdoc */ protected function registerBundles(HttpRequestInterface $request) { return array(new ApplicationBundle()); } /** * @inheritdoc */ protected function getApplicationState() { return $this->getMasterFactory()->createApplicationState(); } }
在大多数情况下,您需要提供的只是要加载的包和一个应用程序状态(用于处理会话等)。
包
包是一系列命令、工厂、路由器、控制器和模型的集合。当您的应用程序变得更大时,您可能希望将代码拆分为不同的包,处理不同的部分。您至少应该创建一个包来注册路由器和工厂。尽管这不是强制性的,但我们强烈建议您创建一个“应用程序包”,用于处理应用程序的非常基本的事情,例如其核心元素、对其他软件包的依赖关系、与持久存储系统的连接、应用程序状态等。其他所有内容都应放入其他包中。例如,一个“用户包”来处理与用户登录、注册、密码更改、显示个人资料页面等相关的一切。您应尽量使您的包与其他包尽可能独立,否则您将创建一个依赖地狱。
包的注册发生在内核中
为了使包能够与框架提供的HttpKernel正常工作,它们必须实现《BundleInterface》。
包工厂
包能够提供一个或多个将被注册到MasterFactory的工厂。
待办事项:示例/代码
命令
无论您的应用程序想要做什么,它都会在命令中发生。为了使命令可以被其他框架和您的应用程序的其他入口点(如API或CLI用于cron作业等)重用,请求被分离到CommandRequest、Command、CommandValidation和CommandHandler中。所有命令都应在包内部处理。
命令仅在Post-Requests中执行
请求
扩展抽象类CommandRequest。您必须实现两个方法
- createCommand:接收一个请求并将命令组装起来
- createCommandHandler:使用包工厂组装命令处理器。
待办事项:示例/代码
命令
命令本身基本上只是一个容器,用于存储处理特定任务所需的所有信息。
验证
接收PostRequest并验证命令使用的参数。
框架不强制进行验证。
这意味着如果您太懒于正确验证,则您的应用程序可能很容易被利用。
处理器
CommandHandler使命令可执行,并且已经抽象了所有与传输协议相关的功能。它由一个单独的execute方法组成,该方法接收命令并返回一个CommandResponse。
路由
框架分别处理POST、GET以及其他所有类型的HTTP请求。对于所有类型都有不同的RouterChains,其中可以注册路由器。包将只根据请求类型注册适当的链。
待办事项:示例
控制器
控制器仅用于Command-Requests。命令分发器将使用包提供的控制器来确定在命令执行后显示哪种内容。在命令未执行或验证失败的情况下,框架知道要做什么非常重要。
命令响应
命令响应通知应用程序/包控制器命令处理器的结果,并允许应用程序相应地做出反应。已实现两个默认响应(CommandSuccessResponse、CommandFailureResponse),应该适用于大多数情况,但您可以自己实现任何类型的响应。
内容
Http响应 & 定位器
此HttpResponseLocator类将内容转换为其HTTP表示形式。框架中并非所有可能的内容和响应都可用。如果您需要尚未实现的内容,您可以使用自己的HttpResponseLocator类,并为它实现任何类型的http响应。对于非常特殊的情况,您可以直接在路由器或控制器中绕过此过程并直接返回HTTP响应。
模板引擎
默认的模板引擎是smarty。您可以在应用程序工厂中将其替换为您想要的任何内容。
应用程序状态
这相当于一个会话,但它可以是代表您应用程序当前状态的任何事物。
实现登录墙
对于应用程序来说,一个非常常见的场景是拥有一个登录墙,其他所有功能都隐藏在这个墙后面。Zeus提供了一个非常简单的方式来实现这一点。我们建议在单独的包中实现登录功能以及登录检查,并在您的内核的"registerBundles"方法中直接在应用程序包之后注册此包。
protected function registerBundles(HttpRequestInterface $request) { return array( new ApplicationBundle(), new LoginBundle(), new OtherBundleThatNeedsLogin(), ); }
登录Bundle将在其路由器中实现登录检查,如果用户未登录,则返回登录页面。这样,其他包的路由器永远不会接收到请求,因为Zeus调用路由器的顺序与包注册的顺序相同。这样,您可以进行任何类型的预处理。
修改组件
这些示例应使您熟悉使用应用程序修改框架行为的过程。注意:不应需要以任何方式修改框架的源代码。
创建您自己的表单填充
场景:我们不想使用bootstrap作为前端框架,但我们非常喜欢自动表单填充
待办事项
示例:实现预过滤和后过滤链
场景:我们有一个拥有数百条路由的庞大应用程序,并且没有关心本地化。现在团队决定通过在URI开头添加en/ fr/ de/ es/等来实现本地化。待办事项
示例:更改路由器
待办事项
示例:更改模板引擎
待办事项
示例:强制通用ACL
待办事项
扩展您的应用程序
待办事项
添加语言/翻译支持
待办事项
使用doctrine添加数据库支持
待办事项
添加consul键/值存储
待办事项
构建
Windows
cd {YOUR_PATH_TO_PROJECTS}/zeus
docker run --rm --interactive --tty --volume %cd%:/app composer install --ignore-platform-reqs --no-scripts --prefer-dist -v
docker-compose up -d --build
Linux
cd {YOUR_PATH_TO_PROJECTS}/zeus
docker run --rm -it --volume $PWD:/app composer install --ignore-platform-reqs --prefer-dist -v
docker-compose up -d --build
运行测试
cd tests
php ../build/phpunit.phar