微型应用¶
概览¶
Phalcon 提供了一种轻量级的应用结构,称为微应用Micro
可以帮助开发者使用最少的 PHP 代码和更低的开销来创建应用程序。Micro
微应用非常适合用于小型项目,例如 API 和原型开发,这些场景对效率和低开销有较高要求。
<?php
use Phalcon\Mvc\Micro;
$app = new Micro();
$app->get(
'/invoices/view/{id}',
function ($id) {
echo "<h1>#{$id}!</h1>";
}
);
$app->handle(
$_SERVER["REQUEST_URI"]
);
激活¶
初始化一个微应用,请使用Phalcon\Mvc\Micro类。
<?php
use Phalcon\Di\Di;
use Phalcon\Mvc\Micro;
$container = new Di();
$app = new Micro($container);
注意
从 Phalcon v5.3.0 开始,Micro
对象将不再自动在依赖注入容器中以名称application
注册。开发者需要显式地管理该应用实例。
方法¶
构造函数。接受一个可选的 Di 容器。 添加一个after
中间件,在路由执行后调用 添加一个afterBinding
中间件,在模型绑定后调用 添加一个前置中间件,在路由执行之前调用 将路由映射到处理器上,只有当 HTTP 方法为 DELETE 时才匹配 设置一个在处理路由时抛出异常时调用的处理器 添加一个finish
中间件,在请求完成后调用 将路由映射到处理器上,只有当 HTTP 方法为 GET 时才匹配 返回用于匹配路由的处理器 返回绑定实例中的绑定模型 返回附加到应用程序的内部处理器 获取模型绑定器 返回已执行处理器返回的值 返回应用程序使用的内部路由器 从 DI 获取服务 从 DI 获取共享服务 处理整个请求 检查 DI 中是否注册了服务 将路由映射到处理器上,只有当 HTTP 方法为 HEAD 时才匹配 将路由映射到没有 HTTP 方法限制的处理器 挂载一组处理器 设置当路由器无法匹配任何定义的路由时调用的处理器 使用数组语法检查内部 DI 容器中是否注册了服务 使用数组语法从内部 DI 容器获取 DI 服务 使用数组语法在内部 DI 容器中注册服务 使用数组语法从内部 DI 容器中移除服务 时匹配的处理器OPTIONS
时匹配的处理器PATCH
时匹配的处理器POST
时匹配的处理器PUT
外部设置与路由匹配时必须调用的处理器 设置模型绑定器 添加一个自定义response
处理器替代默认处理器 public function setService(
string $serviceName,
mixed $definition,
bool $shared = false
): ServiceInterface
路由¶
在Phalcon\Mvc\Micro应用程序中定义路由非常简单。路由的定义格式如下:
激活¶
路由由Phalcon\Mvc\Router对象中的相关方法手动
注意
路由必须始终以/
通常,应用程序的第一个路由是/
,可通过以下GET
HTTP 方法访问:
注意
请参阅我们的更多信息,请参阅文档了解关于Phalcon\Mvc\Router.
应用对象的更多信息
可以使用Phalcon\Mvc\Micro应用对象按照如下方式设置路由:
<?php
use Phalcon\Mvc\Micro;
$app = new Micro();
$app->get(
'/invoices/view/{id}',
function ($id) {
echo "<h1>#{$id}!</h1>";
}
);
路由对象
或者,您可以创建一个Phalcon\Mvc\Router对象,定义路由,然后将其注入到依赖注入容器中。
<?php
use Phalcon\Di\Di;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Router;
$router = new Router();
$router->addGet(
'/invoices/view/{id}',
'InvoicesClass::view'
);
$container = new Di();
$application = new Micro($container);
$application->setService('router', $router, true);
使用Phalcon\Mvc\Micro应用的 HTTP 方法(如get
, post
等)来设置路由比配置带有相关路由的路由器对象并将其注入应用程序要更简单。采用哪种方法取决于您的应用程序设计和需求。
重写规则¶
为了使路由正常工作,您的 Web 服务器需要特定的配置。请参阅Web 服务器设置文档获取详细信息。
处理程序¶
处理器是一段可以绑定到路由的可调用代码。当路由匹配时,处理器将带着所有定义的参数执行。处理器可以是任意合法的 PHPcallable
.
注册¶
Phalcon 提供了多种方式来将处理器绑定到路由。选择哪种方式取决于您的应用程序需求、设计以及编码风格。
匿名函数
您可以使用匿名函数来处理请求
在匿名函数内部访问$app
对象可以通过如下方式进行注入:
<?php
$app->get(
'/invoices/view/{id}',
function ($id) use ($app){
$content = "<h1>#{$id}!</h1>";
$app->response->setContent($content);
$app->response->send();
}
);
函数
将一个函数定义为处理器,并将其绑定到特定的路由上。
<?php
function invoiceView($id) {
echo "<h1>#{$id}!</h1>";
}
$app->get(
'/invoices/view/{id}',
'invoicesView'
);
静态方法
使用静态方法作为处理器。
<?php
class InvoicesClass
{
public static function view($id) {
echo "<h1>#{$id}!</h1>";
}
}
$app->get(
'/invoices/view/{id}',
'InvoicesClass::View'
);
对象中的方法
使用对象中的方法作为处理器。
<?php
class InvoicesClass
{
public function view($id) {
echo "<h1>#{$id}!</h1>";
}
}
$invoices = new InvoicesClass();
$app->get(
'/invoices/view/{id}',
[
$invoices,
'view'
]
);
控制器
对于在微架构基础上扩展的中等规模应用程序,您可以将处理器组织在控制器中。
<?php
use Phalcon\Mvc\Micro\Collection as MicroCollection;
$invoices = new MicroCollection();
$invoices
->setHandler(new InvoicesController())
->setPrefix('/invoices')
->get('/', 'index')
->get('/view/{id}', 'view')
;
$app->mount($invoices);
InvoicesController
看起来可能像这样: <?php
use Phalcon\Mvc\Controller;
class InvoicesController extends Controller
{
public function index()
{
// ...
}
public function view($id) {
// ...
}
}
由于控制器继承自Phalcon\Mvc\Controller,所有依赖注入的服务都可以通过它们各自的注册名称进行访问。
<?php
use Phalcon\Http\Response;
use Phalcon\Mvc\Controller;
/**
* @property Response $response
*/
class InvoicesController extends Controller
{
public function index()
{
// ...
}
public function view($id)
{
$content = "<h1>#{$id}!</h1>";
$this->response->setContent($content);
return $this->response;
}
}
延迟加载¶
为了提升性能,可以考虑为控制器(处理器)实现延迟加载。延迟加载确保只有在匹配到相关路由时才加载控制器。通过在Phalcon\Mvc\Micro\Collection中使用第二个参数设置处理器,或通过使用setLazy
方法设置容器。
<?php
use MyApp\Controllers\InvoicesController;
$invoices->setHandler(
InvoicesController::class,
true
);
$invoices
->setHandler(InvoicesController::class)
->setLazy(true)
->setPrefix('/invoices')
->get('/', 'index')
->get('/view/{id}', 'view')
;
$app->mount($invoices);
使用案例
考虑一个在线商店的 API 开发场景,包含多个端点/users
, /invoices
和/products
。每个端点都通过处理器注册,其中每个处理器是一个带有相关操作的控制器。
注册控制器:
<?php
use Phalcon\Mvc\Controller;
class UsersController extends Controller
{
public function get($id)
{
// ...
}
public function add($payload)
{
// ...
}
}
class InvoicesController extends Controller
{
public function get($id)
{
// ...
}
public function add($payload)
{
// ...
}
}
class ProductsController extends Controller
{
public function get($id)
{
// ...
}
public function add($payload)
{
// ...
}
}
注册处理器:
<?php
use Phalcon\Mvc\Micro\Collection as MicroCollection;
$users = new MicroCollection();
$users
->setHandler(new UsersController())
->setPrefix('/users')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($users);
$invoices = new MicroCollection();
$invoices
->setHandler(new InvoicesController())
->setPrefix('/invoices')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($invoices);
$products = new MicroCollection();
$products
->setHandler(new ProductsController())
->setPrefix('/products')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($products);
在上述方法中,每个处理器按顺序加载并挂载到我们的应用程序对象中。缺点是每次请求只执行一个端点,从而也只执行一个类方法。其余的方法/处理器保留在内存中而不被使用。
通过引入延迟加载,减少了内存中的对象数量,从而更高效地使用资源。实现方式更改如下:
<?php
use Phalcon\Mvc\Micro\Collection as MicroCollection;
$users = new MicroCollection();
$users
->setHandler(
UsersController::class,
true
)
->setPrefix('/users')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($users);
$invoices = new MicroCollection();
$invoices
->setHandler(
InvoicesController::class,
true
)
->setPrefix('/invoices')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($invoices);
$products = new MicroCollection();
$products
->setHandler(
ProductsController::class,
true
)
->setPrefix('/products')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
$app->mount($products);
通过这个简单的更改,所有处理器会保持未实例化状态,直到被调用者请求。/invoices/get/2
当调用者请求InvoicesController
时,我们的应用程序会实例化get
并调用
额外性能提示¶
对于大型应用程序,即使集合是懒加载的,也没有必要挂载所有集合。Phalcon 使用regex
来匹配路由,并且为了加快路由过程,可以运行一个预过滤器。例如:
$uri = new \Phalcon\Http\Message\Uri($_SERVER['REQUEST_URI']);
$path = $uri->getPath();
$parts = explode("/", $path);
$collection = $parts[1];
switch ($collection) {
case "users":
$users = new MicroCollection();
$users
->setHandler(
UsersController::class,
true
)
->setPrefix('/users')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($users);
break;
case "invoices":
$invoices = new MicroCollection();
$invoices
->setHandler(
InvoicesController::class,
true
)
->setPrefix('/invoices')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
;
$app->mount($invoices);
break;
case "products":
$products = new MicroCollection();
$products
->setHandler(
ProductsController::class,
true
)
->setPrefix('/products')
->get(
'/get/{id}',
'get'
)
->get(
'/add/{payload}',
'add'
)
$app->mount($products);
break;
default:
// ...
}
这种方法允许 Phalcon 处理大量路由而不会受到正则表达式的性能影响。使用explode()
比正则表达式更快。
未找到 (404)¶
我们的应用程序中未匹配的任何路由Phalcon\Mvc\Micro将触发使用notFound
方法定义的处理程序的执行。类似于其他 HTTP 方法 (get
, post
等),你可以在notFound
方法中注册一个处理程序,它可以是任何可调用的 PHP 函数。
<?php
$app->notFound(
function () use ($app) {
$message = 'XXXXXX';
$app
->response
->setStatusCode(404, 'Not Found')
->sendHeaders()
->setContent($message)
->send()
;
}
);
未匹配的路由(404)也可以通过中间件来处理,相关内容将在下面讨论。
HTTP 方法¶
The Phalcon\Mvc\Micro应用程序提供了一组方法将 HTTP 方法与目标路由绑定:
delete
匹配DELETE
且路由为/api/products/delete/{id}
get
匹配GET
且路由为/api/products
head
匹配HEAD
且路由为/api/products
map
map
允许你将同一端点附加到多个 HTTP 方法。以下示例匹配如果 HTTP 方法是GET
或POST
且路由为/repos/store/refs
options
匹配OPTIONS
且路由为/api/products/options
patch
匹配PATCH
且路由为/api/products/update/{id}
post
匹配POST
且路由为/api/products/add
put
匹配PUT
且路由为/api/products/update/{id}
集合¶
集合是一种方便的方式来将路由分组到一个处理器以及一个公共前缀(如需要)。对于假设的 /invoices 端点,你可以有以下路由:
所有这些路由都由我们的InvoicesController
处理。使用集合作为你的路由设置如下:
<?php
use Phalcon\Mvc\Micro\Collection as MicroCollection;
$invoices = new MicroCollection();
$invoices->setHandler(new InvoicesController());
$invoices->setPrefix('/invoices');
$invoices->get('/get/{id}', 'displayAction');
$invoices->get('/add/{payload}', 'addAction');
$invoices->get('/update/{id}', 'updateAction');
$invoices->get('/delete/{id}', 'deleteAction');
$app->mount($invoices);
注意
我们为每个路由绑定的名称都有一个后缀Action
。这不是必须的,你的方法可以命名为你喜欢的任何名称。
方法
的可用方法Phalcon\Mvc\Micro\Collection对象包括:
public function delete(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
DELETE
. public function get(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
GET
. 返回主处理器 返回已注册的处理器 返回集合前缀(如果有) public function head(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
HEAD
. 返回主处理器是否需要延迟加载 public function map(
string $routePattern,
callable $handler,
string | array $method,
string $name = null
): CollectionInterface
public function mapVia(
string $routePattern,
callable $handler,
string | array $method,
string $name = null
): CollectionInterface
public function options(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
OPTIONS
. public function patch(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
PATCH
. public function post(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
POST
. public function put(
string $routePattern,
callable $handler,
string $name = null
): CollectionInterface
PUT
. 设置主处理器。 设置主处理器是否需要延迟加载 为集合中添加的所有路由设置一个前缀 参数¶
路由中的参数通过将参数名放在花括号 {} 中进行定义:
你可以使用正则表达式对参数强制规则。名称后面的正则表达式用:
.
<?php
$app->get(
'/invoices/view/{id:[0-9]+}',
function ($id) {
echo "<h1>#{$id}!</h1>";
}
);
$app->get(
'/invoices/search/year/{year:[0-9][4]}/title/{title:[a-zA-Z\-]+}',
function ($year, $title) {
echo "'<h1>{$title}</h1>", PHP_EOL,
"'<h2>{$year}</h2>"
;
}
);
注意
分隔设置。更多信息,请参阅文档
重定向¶
你可以像在完整应用中一样,使用Phalcon\Http\Response对象对一个已匹配的路由重定向到另一个路由。
<?php
$app->get('/invoices/show/{id}',
function ($id) use ($app) {
$app
->response
->redirect(
"invoices/view/{$id}"
)
->sendHeaders()
;
}
);
$app->get('/invoices/view/{id}',
function ($id) use ($app) {
echo "<h1>#{$id}!</h1>";
}
);
注意
确保在匿名函数中传递 $app 对象以访问响应对象。
当使用控制器作为处理器时,你也可以轻松完成重定向:
<?php
use Phalcon\Http\Response;
use Phalcon\Mvc\Controller;
/**
* @property Response $response
*/
class InvoicesController extends Controller
{
public function show($id)
{
return $this
->response
->redirect(
"invoices/view/{$id}"
)
;
}
public function get($id)
{
// ...
}
}
最后,你可以在中间件中执行重定向(如果你使用了中间件)。相关示例见下面的小节。
URL¶
路由的另一项功能是设置命名路由并为这些路由生成 URL。
要利用此功能,你需要为路由命名。可以通过应用程序中暴露的setName()
方法实现(该方法属于我们应用程序中的 HTTP 方法之一,get
, post
等)。
<?php
$app
->get(
'/invoices/view/{id}',
function ($id) use ($app) {
// ...
}
)
->setName('view-invoice');
如果你使用的是Phalcon\Mvc\Micro\Collection对象的情况下,名称需要是设置路由方法的第三个参数。
<?php
$invoices = new MicroCollection();
$invoices
->setHandler(
InvoicesController::class,
true
)
->setPrefix('/invoices')
->get(
'/view/{id}',
'get',
'view-invoice'
)
->post(
'/add',
'post',
'add-invoice'
)
;
$app->mount($invoices);
最后,你需要Phalcon\Url组件为命名路由生成 URL。
<?php
$app->get(
'/',
function () use ($app) {
$url = sprintf(
'<a href="%s">#</a>',
$app
->url
->get(
[
'for' => 'view-invoice',
'id' => 1234,
]
)
);
echo $url;
}
);
依赖注入器¶
当微应用程序被创建时,会自动创建一个Phalcon\Di\FactoryDefault服务容器。
<?php
use Phalcon\Mvc\Micro;
$app = new Micro();
$app->get(
'/',
function () use ($app) {
$app
->response
->setContent('3.1459')
->send()
;
}
);
你还可以自己创建 DI 容器并将其分配给微应用程序,从而根据应用程序的需求操作服务。
<?php
use Phalcon\Di\Di;
use Phalcon\Mvc\Micro;
use Phalcon\Config\Adapter\Ini;
$container = new Di();
$container->set(
'config',
function () {
return new Ini(
'config.ini'
);
}
);
$app = new Micro($container);
$app->get(
'/',
function () use ($app) {
echo $app
->config
->app_name;
}
);
$app->post(
'/contact',
function () use ($app) {
$app
->flash
->success('++++++')
;
}
);
你也可以使用数组语法,通过应用程序对象在依赖注入容器中注册服务:
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Db\Adapter\Pdo\Mysql;
$app = new Micro();
$app['db'] = function () {
return new Mysql(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'test_db',
]
);
};
$app->get(
'/blog',
function () use ($app) {
$invoices = $app['db']->query(
'SELECT * FROM co_invoices'
);
foreach ($invoices as $invoice) {
echo $invoice->inv_title;
}
}
);
响应¶
微型应用可以返回多种类型的响应:直接输出、使用模板引擎、计算数据、基于视图的数据、JSON 等。
处理程序可以使用纯文本返回原始响应,Phalcon\Http\Response对象,或者实现的自定义构建组件Phalcon\Http\ResponseInterface.
直接输出¶
包含文件¶
直接 - JSON¶
<?php
$app->get(
'/invoices/view/{id}',
function ($id) {
echo json_encode(
[
'code' => 200,
'id' => $id,
]
);
}
);
新响应¶
您可以使用setContent
方法以新的Phalcon\Http\Response对象将响应返回。
<?php
use Phalcon\Http\Response;
$app->get(
'/invoices/list',
function () {
return (new Response())
->setContentType('text/plain')
->setContent(
file_get_contents('data.txt')
)
;
}
);
应用程序响应¶
您还可以使用Phalcon\Http\Response从应用程序返回响应给调用者。
<?php
$app->get(
'/invoices/list',
function () use ($app) {
$app
->response
->setContentType('text/plain')
->sendHeaders()
;
readfile('data.txt');
}
);
返回响应¶
将数据返回给调用者的另一种方法是直接从应用程序返回Phalcon\Http\Response对象。当处理程序返回响应时,它们会由应用程序自动发送。
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Http\Response;
$app = new Micro();
$app->get(
'/invoices//list',
function () {
return (new Response())
->setStatusCode(
401,
'Unauthorized'
)
->setContent(
'401 - Unauthorized'
)
;
}
);
JSON¶
使用Phalcon\Http\Response对象中的相关方法手动
<?php
$app->get(
'/invoices/index',
function () use ($app) {
$data = [
'code' => 401,
'status' => 'error',
'message' => 'Unauthorized access',
'payload' => [],
];
return $this
->response
->setJsonContent($data)
;
}
);
事件¶
A Phalcon\Mvc\Micro应用程序与一个事件管理器如果存在的话,来触发可以在整个应用程序中使用的事件。这些事件的类型是micro
。这些事件会在你的应用程序中触发,并可以连接到相关的处理程序,执行应用程序所需的操作。
可用事件¶
支持以下事件:
事件名称 | 触发时机 | 可以停止 |
---|---|---|
afterBinding | 在模型绑定之后但在执行处理程序之前触发 | 是 |
afterExecuteRoute | 处理程序刚刚运行完毕 | 否 |
afterHandleRoute | 路由刚刚执行完毕 | 是 |
beforeExecuteRoute | 路由已匹配,处理程序有效,但尚未执行 | 是 |
beforeHandleRoute | 主方法被调用;尚未检查路由 | 是 |
beforeNotFound | 路由未找到 | 是 |
认证示例¶
你可以轻松地使用beforeExecuteRoute
事件。以下示例演示了此类场景:
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Events\Event;
use Phalcon\Events\Manager;
$manager = new Manager();
$manager->attach(
'micro:beforeExecuteRoute',
function (Event $event, $app) {
if ($app->session->get('auth') === false) {
$app->flashSession->error(
"The user is not authenticated"
);
$app->response->redirect('/');
$app->response->sendHeaders();
return false;
}
}
);
$app = new Micro();
$app->setEventsManager($manager);
未找到示例¶
你还可以为不存在的路由(404)创建重定向。为此,你可以使用beforeNotFound
事件。以下示例演示了此类场景:
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Events\Event;
use Phalcon\Events\Manager;
$manager = new Manager();
$manager->attach(
'micro:beforeNotFound',
function (Event $event, $app) {
$app->response->redirect('/404');
$app->response->sendHeaders();
return $app->response;
}
);
$app = new Micro();
$app->setEventsManager($manager);
中间件¶
在微型应用的上下文中,中间件指的是可以附加以增强应用程序架构的类。这些类引入了一个额外的层,在其中可以封装业务逻辑,按其注册顺序依次运行。这不仅通过模块化特定功能提升了可维护性,也增强了性能。当中间件类检测到特定业务规则未满足时,可以中断执行流程,允许应用程序提前退出而不完成完整的请求周期。
注意
微型应用管理的中间件不兼容PSR-15。未来的 Phalcon 版本预计将 HTTP 层完全与 PSR-7 和 PSR-15 对齐。
存在一个Phalcon\Events\Manager对于中间件的运行至关重要;因此,必须在我们的依赖注入(DI)容器中注册。
附加的事件¶
中间件可以附加到微型应用的三个不同事件上:
事件 | 描述 |
---|---|
before | 在执行处理程序之前 |
after | 在执行处理程序之后 |
finish | 在响应发送给调用者之后 |
注意
每个上述事件都可以附加多个中间件类,当相关事件触发时,它们将按顺序执行。
before
事件 此事件非常适合在某些条件未满足时暂停应用程序的执行。在下面的例子中,我们检查用户是否已认证,并通过必要的重定向暂停执行。
<?php
$app->before(
function () use ($app) {
if (false === $app['session']->get('auth')) {
$app
->flashSession
->error("The user is not authenticated")
;
$app
->response
->redirect('/error')
;
return false;
}
return true;
}
);
上述代码在每个路由之前执行,返回false
会取消路由的执行。
after
事件
此事件可用于在处理程序执行完成后操作数据或执行所需的操作。
<?php
$app->map(
'/invoices/list',
function () {
return [
1234 => [
'total' => 100,
'customerId' => 3,
'title' => 'Invoice for ACME Inc.',
]
];
}
);
$app->after(
function () use ($app) {
echo json_encode(
$app->getReturnedValue()
);
}
);
json_encode
,返回有效的 JSON。 注意
可能需要进一步的工作来设置 JSON 的必要头部信息。上述代码的替代方案是使用 Response 对象和setJsonContent
.
finish
事件
当整个请求周期完成后触发此事件。
<?php
$app->finish(
function () use ($app) {
if (true === file_exists('/tmp/processing.cache')) {
unlink('/tmp/processing.cache');
}
}
);
finish
事件用于缓存清理。 激活¶
将中间件附加到你的应用程序非常简单,使用before
, after
和finish
方法调用即可。
<?php
$app->before(
function () use ($app) {
if (false === $app['session']->get('auth')) {
$app['flashSession']
->error("The user is not authenticated")
;
$app['response']
->redirect('/error')
;
return false;
}
return true;
}
);
$app->after(
function () use ($app) {
echo json_encode(
$app->getReturnedValue()
);
}
);
或者,可以使用类并将其作为监听器附加到事件管理器中,这样可以提供更大的灵活性并减少引导文件的大小。
<?php
use Phalcon\Events\Manager;
use Phalcon\Mvc\Micro;
use Website\Middleware\CacheMiddleware;
use Website\Middleware\NotFoundMiddleware;
use Website\Middleware\ResponseMiddleware;
/**
* Create a new Events Manager.
*/
$manager = new Manager();
$application = new Micro();
// before
$manager->attach(
'micro',
new CacheMiddleware()
);
$application->before(
new CacheMiddleware()
);
$manager->attach(
'micro',
new NotFoundMiddleware()
);
$application->before(
new NotFoundMiddleware()
);
// after
$manager->attach(
'micro',
new ResponseMiddleware()
);
$application->after(
new ResponseMiddleware()
);
$application->setEventsManager($manager);
A Phalcon\Events\Manager对象是必需的,并且中间件类附加到事件管理器中的micro
钩子中。通过将类附加到特定事件(例如micro:beforeExecuteRoute
.
实现¶
中间件可以是任何 PHP 可调用函数,你可以根据自己的喜好组织代码。如果你选择使用类作为中间件,它们需要实现Phalcon\Mvc\Micro\MiddlewareInterface.
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* CacheMiddleware
*/
class CacheMiddleware implements MiddlewareInterface
{
/**
* Calls the middleware
*
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
$cache = $application['cache'];
$router = $application['router'];
$key = preg_replace(
'/^[a-zA-Z0-9]/',
'',
$router->getRewriteUri()
);
// Check if the request is cached
if ($cache->exists($key)) {
echo $cache->get($key);
return false;
}
return true;
}
}
中间件事件¶
The 事件触发我们应用程序的事件同样适用于实现了Phalcon\Mvc\Micro\MiddlewareInterface的类中。这为开发人员提供了更大的灵活性和能力来与请求过程进行交互。
API 示例¶
假设我们使用微型应用程序实现了一个 API。附加了不同的中间件类以更好地控制应用程序的执行。使用的中间件包括:
- 防火墙
- 未找到
- 重定向
- 跨域资源共享(CORS)
- 请求
- 响应
防火墙¶
此中间件绑定到我们Micro应用程序的before
事件,用来检查调用者的身份是否在白名单内。
<?php
use Phalcon\Events\Event;
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* FirewallMiddleware
*
* @property Request $request
* @property Response $response
*/
class FirewallMiddleware implements MiddlewareInterface
{
/**
* @param Event $event
* @param Micro $application
*
* @returns bool
*/
public function beforeHandleRoute(
Event $event,
Micro $application
) {
$whitelist = [
'10.4.6.1',
'10.4.6.2',
'10.4.6.3',
'10.4.6.4',
];
$ipAddress = $application
->request
->getClientAddress()
;
if (true !== array_key_exists($ipAddress, $whitelist)) {
$this
->response
->redirect('/401')
->send()
;
return false;
}
return true;
}
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
return true;
}
}
未找到(404)¶
当请求 IP 允许访问我们的应用程序时,该中间件被执行。如果应用程序找不到匹配的路由,则beforeNotFound
事件被触发。此时,处理过程停止,并向用户返回相应的404响应。此中间件绑定到before
我们的Micro应用程序的事件。
<?php
use Phalcon\Http\Response;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* NotFoundMiddleware
*
* @property Response $response
*/
class NotFoundMiddleware implements MiddlewareInterface
{
/**
* @param Event $event
* @param Micro $application
*
* @returns bool
*/
public function beforeNotFound(Event $event, Micro $application)
{
$application
->response
->redirect('/404')
->send()
;
return false;
}
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
return true;
}
}
重定向¶
此中间件绑定到before
我们的Micro应用程序的事件。如果请求的端点需要重定向,则阻止请求继续执行。
<?php
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Events\Event;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* RedirectMiddleware
*
* @property Request $request
* @property Response $response
*/
class RedirectMiddleware implements MiddlewareInterface
{
/**
* Before anything happens
*
* @param Event $event
* @param Micro $application
*
* @returns bool
*/
public function beforeHandleRoute(
Event $event,
Micro $application
) {
if ('github' === $application->request->getURI()) {
$application
->response
->redirect('https://github.com')
->send()
;
return false;
}
return true;
}
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
return true;
}
}
跨域资源共享(CORS)¶
此中间件绑定到我们Micro应用程序的before
事件,确保在我们的应用程序发生任何操作之前先触发它。
<?php
use Phalcon\Events\Event;
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* CORSMiddleware
*
* @property Request $request
* @property Response $response
*/
class CORSMiddleware implements MiddlewareInterface
{
/**
* @param Event $event
* @param Micro $application
*
* @returns bool
*/
public function beforeHandleRoute(
Event $event,
Micro $application
) {
if ($application->request->getHeader('ORIGIN')) {
$origin = $application
->request
->getHeader('ORIGIN')
;
} else {
$origin = '*';
}
$application
->response
->setHeader(
'Access-Control-Allow-Origin',
$origin
)
->setHeader(
'Access-Control-Allow-Methods',
'GET,PUT,POST,DELETE,OPTIONS'
)
->setHeader(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Range, ' .
'Content-Disposition, Content-Type, Authorization'
)
->setHeader(
'Access-Control-Allow-Credentials',
'true'
)
;
}
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
return true;
}
}
请求¶
此中间件接收一个JSON负载并对其进行验证。如果JSON负载无效,则会停止执行。
<?php
use Phalcon\Events\Event;
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* RequestMiddleware
*
* @property Request $request
* @property Response $response
*/
class RequestMiddleware implements MiddlewareInterface
{
/**
* @param Event $event
* @param Micro $application
*
* @returns bool
*/
public function beforeExecuteRoute(
Event $event,
Micro $application
) {
json_decode(
$application
->request
->getRawBody()
);
if (JSON_ERROR_NONE !== json_last_error()) {
$application
->response
->redirect('/malformed')
->send()
;
return false;
}
return true;
}
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
return true;
}
}
响应¶
此中间件负责操作我们的响应,并将其作为JSON字符串返回给调用者。因此,我们需要将其绑定到after
我们的Micro应用程序的事件。
注意
我们使用了call
此中间件的方法,因为我们几乎已完成整个请求周期。
<?php
use Phalcon\Http\Response;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;
/**
* ResponseMiddleware
*
* @property Response $response
*/
class ResponseMiddleware implements MiddlewareInterface
{
/**
* @param Micro $application
*
* @returns bool
*/
public function call(Micro $application)
{
$payload = [
'code' => 200,
'status' => 'success',
'message' => '',
'payload' => $application->getReturnedValue(),
];
$application
->response
->setJsonContent($payload)
->send()
;
return true;
}
}
模型¶
可以通过指示应用程序如何通过自动加载器找到相关类,在Micro应用程序中使用模型。
注意
相关的db
服务必须在你的DI容器中注册。
<?php
use MyApp\Models\Invoices;
use Phalcon\Autoload\Loader;
use Phalcon\Mvc\Micro;
$loader = new Loader();
$loader
->setDirectories(
[
__DIR__ . '/models/',
]
)
->register();
$app = new Micro();
$app->get(
'/invoices/find',
function () {
$invoices = Invoices::find();
foreach ($invoices as $invoice) {
echo $invoice->inv_id, '<br>';
}
}
);
$app->handle(
$_SERVER["REQUEST_URI"]
);
模型注入¶
使用Phalcon\Mvc\Model\Binder类可以将模型实例注入到你的路由中:
<?php
use MyApp\Models\Invoices;
use Phalcon\Autoload\Loader;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Model\Binder;
$loader = new Loader();
$loader->setDirectories(
[
__DIR__ . '/models/',
]
)->register();
$app = new Micro();
$app->setModelBinder(
new Binder()
);
$app->get(
"/invoices/view/{id:[0-9]+}",
function (Invoices $id) {
// ...
}
);
$app->handle(
$_SERVER["REQUEST_URI"]
);
由于Binder对象在内部使用PHP的反射API,这需要额外的CPU周期,因此有一个选项可以设置缓存来加快该过程。可以通过使用setModelBinder()
的第二个参数来完成此操作,该参数也可以接受一个服务名称,或者直接传递一个缓存实例给Binder
构造函数中传递缓存实例。
目前,绑定器只会使用模型的主键来执行一个findFirst()
。上面的示例路由可能是这样的/invoices/view/1
.
视图¶
Phalcon\Mvc\Micro并没有内置的视图服务。但是,你可以使用Phalcon\Mvc\View\Simple组件来渲染视图。
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\View\Simple;
$app = new Micro();
$app['view'] = function () {
$view = new Simple();
$view->setViewsDir('app/views/');
return $view;
};
$app->get(
'/invoices/show',
function () use ($app) {
// app/views/invoices/view.phtml
echo $app['view']
->render(
'invoices/view',
[
'id' => 4,
'customerId' => 3,
'title' => 'ACME Inc.',
'total' => 100,
]
)
;
}
);
注意
上面的示例使用的是Phalcon\Mvc\View\Simple组件,它使用相对路径而不是控制器和动作。你也可以改用Phalcon\Mvc\View组件,但要这样做,你需要更改传递给render()
.
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\View;
$app['view'] = function () {
$view = new View();
$view->setViewsDir('app/views/');
return $view;
};
$app->get(
'/invoices/view',
function () use ($app) {
// app/views/invoices/view.phtml
echo $app['view']
->render(
'invoices',
'view',
[
'id' => 4,
'customerId' => 3,
'title' => 'ACME Inc.',
'total' => 100,
]
)
;
}
);
异常¶
在Phalcon\Mvc\Micro组件中抛出的任何异常类型为Phalcon\Mvc\Micro\Exception。你可以使用此异常选择性地捕获仅从此组件抛出的异常。
<?php
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\Exception;
try {
$app = new Micro();
$app->before(false);
$app->handle(
$_SERVER["REQUEST_URI"]
);
} catch (Exception $ex) {
echo $ex->getMessage();
}
错误处理¶
The Phalcon\Mvc\Micro应用程序还有一个error
方法,可用于捕获源自异常的任何错误。以下代码片段展示了此功能的基本用法: