peppers/example-app

展示Peppers PHP框架核心功能及其使用方法的示例应用程序

v1.0 2022-11-01 23:33 UTC

This package is auto-updated.

Last update: 2024-09-30 01:47:20 UTC


README

Peppers需要一组固定的目录和文件才能正常运行。这些文件存储路由和服务描述符以及其他信息。下面提供这些信息以及Peppers内部工作原理的简要介绍。

应用程序基础

以下目录结构和文件是构建和运行Peppers应用程序所必需的

配置文件及其内容

简要介绍启动/运行Peppers应用程序所需的数据结构

credentials.php

此文件包含Peppers应用程序已知并需要访问外部数据源的身份验证信息。身份验证信息存储在CredentialStore实例中(然后作为单例存储在ServiceLocator中)。此文件包含以下关联数组

return [
    'default' => 'mysql.peppers',
    'mysql' => [
        'peppers' => [
            'user' => 'peppers',
            'password' => 'peppers'
        ]
    ]
];

default键指定在没有特别请求CredentialStore的情况下应使用哪些身份验证信息,这是唯一的必填键值对。默认值必须对应于1级键名连接点(.)然后是2级键名。

在上面的示例中,1级键指示外部数据源是MySQL数据库。2级键指示数据库名称。示例中的键名仅用于类比目的,您可以使用任何名称。3级由身份验证信息组成。

目前仅支持用户名和密码,这些键是必需的。

datasources.php

此文件包含Peppers应用程序已知并可通过ConnectionManager实例(然后作为单例存储在ServiceLocator中)访问的外部数据源列表。此文件包含以下关联数组

return [
    'default' => 'pdo.peppers',
    'pdo' => [
        'peppers' => [
            'dsn' => 'mysql:host=172.17.0.3;port=3306;dbname=peppers;charset=utf8mb4',
        ]
    ],
];

default键指定在没有特别请求ConnectionManager的情况下应使用哪个数据源,这是唯一的必填键值对。默认值必须对应于1级键名连接点(.)然后是2级键名。在上面的示例中,1级键对应于包装器(未限定)类名,必须存在,用于访问数据源。2级键指示数据库名称。3级是DSN,对于所有定义的数据源,此键是必需的。

目前仅支持pdo包装器。

eventshandlers.php

此文件包含事件和事件处理类之间的映射。此文件包含以下关联数组

use App\Events as E;
use App\Events\Handlers as H;
return [
    E\BuildRoutesCacheFileEvent::class => [
        H\BuildRoutesCacheFileHandler::class,
    ],
    E\LogRequestEvent::class => [
        H\LogRequestHandler::class,
    ],
];

上述是任何Peppers应用程序都提供的基线文件。数组键是表示事件的(完全限定)类的名称,值是接收事件进行处理的(完全限定)类名的数组。事件可以从应用程序的任何地方启动,并实时(阻塞)或响应发送后(延迟)处理。

事件及其映射存储在EventStore实例中(作为单例存储在ServiceLocator中)。

routes.php

此文件包含路由到控制器的映射。映射形式为RouteRegister数组。您还可以使用辅助类Route,它具有更多描述性方法,结果相同,如以下示例所示

use Peppers\Helpers\Http\Route;
return [
 Route::get(
   '/example/{name}/{phone}',
   [App\Controllers\OneExampleController::class, 'nameOrSurname']
 )->setPathExpression('name', '[a-z]+')
  ->setPathExpression('phone', '\d+')
  ->setQueryExpression('whatever', 'blabla')
  ->setAllowFreeQuery(true),
 Route::post(
   '/example/person',
   [App\Controllers\AnotherController::class, 'newPerson']
 )->setAllowFreeQuery(false),
 Route::restful('/model/peppers', App\Models\Repositories\PeppersRepository::class)
];

Peppers 路由系统使用正则表达式。在上述示例中,您可以看到声明了3条路由,使用了 Route 辅助类。这个类的 方法是 Peppers 允许的 HTTP 方法名称,以及一个特殊的 restful() 方法。它们接收

  1. URL 路径,可能包含 {占位符} 用于变量值;您还可以传递查询;
  2. 其中之一为 array|Closure|string。如果您发送
    • 一个数组,第一个值是控制器类的(完全限定)名称,第二个是要调用的方法的名称;
    • PHP 的 Closure,并使用其中的任何类,这些类必须使用完全限定名称声明。闭包必须始终返回一个 Response 实例;
    • 一个字符串,这个字符串必须是实现了 ModelRepository 合约的类的完全限定名称。如果是这种情况,并且
      • 该路由是为 GET 请求,要返回特定的模型实例,将模型主键列作为路径参数(...不要忘记对应的正则表达式以匹配),否则必须发送带有与模型列匹配的参数的查询,以便在数据源上执行更通用的查询,客户端会收到一个模型集合作为响应;
      • 该路由是为 DELETE 请求,URL 路径必须包含主键作为路径参数,否则不会发生任何事情。Peppers 只允许使用主键值来删除模型;
      • 该路由是为 POST 请求,将模型主键列作为路径参数(...不要忘记对应的正则表达式以匹配)以更新特定的模型实例(...模型数据从请求体中读取)。如果请求路径中没有设置主键参数,Peppers 将请求解释为创建新的模型实例,并从请求体中读取模型数据。

方法调用的返回值是一个 RouteRegister 实例,这允许设置路径参数和查询参数的正则表达式。

* 此方法返回一个 RestfulRouteRegister 实例,在路由解析过程中扩展为 nRouteRegister 实例,每个允许的 HTTP 方法一个。

回到上面的示例,第一个 RouteRegister 实例代表一个 HTTP GET 请求,路径有 2 个参数 - {name}{phone} - 必须使用 setPathExpression() 设置正则表达式。该请求的 URL 必须有一个查询,该查询通过 setQueryExpression() 设置,必须对应于 whatever=blabla。调用 setAllowFreeQuery() 允许客户端发送更多查询参数,对于这些参数,开发人员不必编写正则表达式。该请求由一个 OneExampleControllernameOrSurname() 方法处理。第二个 RouteRegister 实例代表一个没有动态路径参数且无法查询的 HTTP POST 请求,由一个 AnotherController 实例的 newPerson() 方法处理。第三个 Route 实例是不同的,在其最基本的形式中,您只需要声明 URL 路径,在路由解析过程中,这个 RouteRegister 扩展为 n 个自己,但增加了主键列作为 DELETE、GET 和 POST 请求的路径参数以及简化的形式。

您可以根据需要添加任意多的 Route 实例。如果没有匹配发生,Peppers 将发送 404 错误给用户代理,或使用默认控制器来显示您的自定义 404 页面或运行其他自定义业务逻辑。

最后但同样重要的是...

在所有处理器情况下,请求处理代码必须返回一个 ResponseResponseSent 实例。第一种情况允许根据请求的 Accept HTTP 头正确格式化响应数据。第二种情况绕过了所有这些,意味着这是开发人员的责任:输出缓冲区刷新、设置 header() 等。

services.php

该文件存储了Peppers所知的所有服务,并存储在ServiceLocator实例中。服务的实现有2种形式:抽象和具体。

抽象

抽象实现允许开发者将接口绑定到ServiceLocator。为了解析服务类,开发者需要设置一个提供者。该提供者可以是返回所需服务类实例的Closure实例,或者是一个指向返回所需服务类实例的Strategy类的(完全限定)类名。也可以将实现绑定到特定类(arrayabstract()方法的第二个参数),这样开发者就可以在Closure提供者中为BoundTo实例提供类型提示,并决定如何解析服务类实例。

具体

具体实现是抽象实现的相反。它们不需要提供者,因此开发者必须提供完全限定的类名,该类名将由ServiceLocator解析。这种类型的实现可以立即加载(不是延迟加载),与抽象实现相反。

抽象和具体实现都支持依赖注入。如果需要,Factory将使用ServiceLocator注入必要的依赖。如果依赖项有依赖项,它们将以递归方式解决。

以下是一个Peppers应用程序服务注册的示例

// use Peppers\Helpers\Http\Request\BodyParameter;
// use Peppers\Helpers\Http\Request\QueryParameter;
// use Peppers\Helpers\Http\Request\PathParameter;
use Peppers\Helpers\Service\BoundTo;
use Peppers\Helpers\Service\Implementation as Imp;
use Peppers\Services;
use Peppers\Contracts;
use App\Contracts\HelpfulHelper;

return [
 Imp::abstract(Contracts\RouteResolver::class)
  ->setProvider(Peppers\Strategy\Boot\RouteResolver::class)
  ->setIsSingleton(true),
 Imp::concrete(Services\RequestBody::class)
  ->setIsLazyLoad(true)
  ->setIsSingleton(true),
 Imp::abstract(Contracts\EventStore::class)
  ->setProvider(\Peppers\Strategy\Boot\EventStore::class)
  ->setIsSingleton(true),
 Imp::abstract(Contracts\CredentialStore::class)
  ->setProvider(Peppers\Strategy\Boot\CredentialStore::class)
  ->setIsSingleton(true),
 Imp::abstract(Contracts\ConnectionManager::class)
  ->setProvider(Peppers\Strategy\Boot\ConnectionManager::class)
  ->setIsSingleton(true),
 Imp::abstract(
  App\Contracts\HelpfulHelper::class,
   [App\Controllers\ExampleController::class]
  )->setProvider(function (BoundTo $caller): HelpfulHelper {
   return $caller->getName() == App\Controllers\ExampleController::class 
    ? new App\Helpers\AnotherHelpfulHelper() 
    : new App\Helpers\OneHelpfulHelper();
  })
];

如上例所示,有6个服务被描述

  1. RouteResolver;它是一个抽象实现,因此开发者可以选择构建不同的一个(注意合同!)。由于它是抽象的,因此有一个提供者执行自定义逻辑并返回一个RouteResolver实例。它还设置为单例,因此每次开发者调用ServiceLocator时,都返回相同的实例。这是Peppers的核心服务之一。
  2. RequestBody;它表示请求正文,设置为单例和延迟加载,因此仅在需要时才实例化(这发生在FactoryServiceLocator之间)。这是Peppers的核心服务之一。注意:此服务处理请求正文编码,因此开发者不必这样做。它不打算直接使用。如果开发者需要访问正文数据,应使用BodyParameter实例。
  3. EventStore;作为延迟事件存储库,并将事件分发给已注册的处理程序。这是Peppers的核心服务之一(Kernel使用它记录信息)。
  4. CredentialStore;凭证存储文件中存储的凭证存储在这里。它由ConnectionManager服务使用。这是Peppers的核心服务之一。
  5. ConnectionManager;作为外部数据源访问数据的存储库,以及存储实际连接。这是Peppers的核心服务之一。
  6. 这是将接口绑定到ServiceLocator并绑定到特定类的示例。在解析服务类型提示时,使用BoundTo $caller,这样调用者类名就可以在解析服务实例类时的Closure内部使用。在这种情况下,如果调用者是App\Controllers\ExampleController::class,则注入/返回一个App\Helpers\AnotherHelpfulHelper实例;否则是App\Helpers\OneHelpfulHelper

最后但同样重要的是...

服务是开发者根据其需求设计的类,Peppers没有设定具体要求。如果开发者需要在将响应发送给客户端后运行一些业务逻辑,则实现一个shutdown()方法。它将在最后调用。请注意,如果此代码需要其他服务,则该服务可能不再可用!

提供者类只有一个要求:它们必须扩展 Strategy 基类。Strategy 基类的类允许失败,这意味着如果提供者无法完成其业务(返回的不是预期的结果)并且允许失败,则 ServiceLocator 会向调用者返回一个 StrategyFail 实例;如果不抛出 StrategyFail 异常,则必须在某处捕获它,最终由 Kernel 捕获,这将停止所有请求处理(如果处于启动阶段之外,Peppers 的关闭仍然会按正常方式发生)。

strategies.php

该文件包含将 Kernel 阶段映射到每个阶段应运行的代码的映射。以下是一个标准 Peppers 应用的示例

use Peppers\Strategy;
use Peppers\Strategy\Boot;
use Peppers\Strategy\Response;
return [
    'boot' => [/* these classes are NOT run in a pipeline */
        Boot\ServiceLocator::class,
        Boot\SessionStart::class
    ],
    'requestResponse' => [/* these classes are run in a pipeline */
        Strategy\RouteToHandler::class,
        Response\Html::class,
        Response\Json::class,
        Response\Redirect::class,
        Response\NoBody::class,
        Response\File::class,
        Response\PlainText::class,
        Response\Xml::class
    ],
    'exceptionHandling' => [/* these classes are run in a pipeline */
        Strategy\ResolveException::class,
        Response\Html::class,
        Response\Json::class,
        Response\PlainText::class,
        Response\Xml::class
    ],
    'shutdown' => [/* these classes are NOT run in a pipeline */
        Strategy\ShutdownServices::class,
    ]
];

上述示例中,键

  • boot;启动框架所需的代码。这些类不会在管道中运行,因此 Kernel 不提供任何输入,只是检查返回值是否符合预期或策略失败。任何失败都会完全停止请求处理!
  • requestResponse;处理请求并将响应发送给客户端所需的代码。这些类在管道中运行,第一个接收一个 RouteRegister 实例作为输入(由 RouteResolver 解析),从那时起,下一个接收上一个调用的返回值作为输入。这里设置的类必须实现 PipelineStage 合约;
  • ExceptionHandling;尝试处理未捕获的异常并向客户端提供有意义的信息的代码。此代码是“环境感知”的,意味着:如果应用程序处于生产模式,则响应中显示的信息非常少,否则显示所有异常信息。这些类在管道中运行,第一个接收一个未捕获的异常实例作为输入,从那时起,下一个接收上一个调用的返回值作为输入。这里设置的类必须实现 PipelineStage 合约;
  • shutdown;关闭框架所需的代码:触发延迟事件处理并调用在 ServiceLocator 中注册的服务上的 shutdown() 方法。这些类不会在管道中运行,因此 Kernel 不提供任何输入。如果这些策略中的任何一个失败,客户端将不会看到任何内容,因为此时 Peppers 完全“无输出”,它只是在 Kernel 紧急文件中记录。

注意 1:不应修改键,它们在 Kernel 中是硬编码的。注意 2:任何未捕获的异常都会记录在内核紧急文件中。检查 index.php 中的 Settings 类以获取其位置。

注释和建议

发送到 peppers.php.framework@gmail.com

谢谢 :)