跳转到内容

微型应用


概览

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注册。开发者需要显式地管理该应用实例。

方法

public function __construct(
    DiInterface $container = null
)
构造函数。接受一个可选的 Di 容器。

public function after(
    callable $handler
): Micro
添加一个after中间件,在路由执行后调用

public function afterBinding(
    callable $handler
): Micro
添加一个afterBinding中间件,在模型绑定后调用

public function before(
    callable $handler
): Micro
添加一个前置中间件,在路由执行之前调用

public function delete(
    string $routePattern, 
    callable $handler
): RouteInterface
将路由映射到处理器上,只有当 HTTP 方法为 DELETE 时才匹配

public function error(
    callable $handler
): Micro
设置一个在处理路由时抛出异常时调用的处理器

public function finish(
    callable $handler
): Micro
添加一个finish中间件,在请求完成后调用

public function get(
    string $routePattern, 
    callable $handler
): RouteInterface
将路由映射到处理器上,只有当 HTTP 方法为 GET 时才匹配

public function getActiveHandler(): callable
返回用于匹配路由的处理器

public function getBoundModels(): array
返回绑定实例中的绑定模型

public function getHandlers(): array
返回附加到应用程序的内部处理器

public function getModelBinder(): BinderInterface | null
获取模型绑定器

public function getReturnedValue(): mixed
返回已执行处理器返回的值

public function getRouter(): RouterInterface
返回应用程序使用的内部路由器

public function getService(
    string $serviceName
): object
从 DI 获取服务

public function getSharedService(
    string $serviceName
)
从 DI 获取共享服务

public function handle(
    string $uri
): mixed
处理整个请求

public function hasService(
    string $serviceName
): bool
检查 DI 中是否注册了服务

public function head(
    string $routePattern, 
    callable $handler
): RouteInterface
将路由映射到处理器上,只有当 HTTP 方法为 HEAD 时才匹配

public function map(
    string $routePattern, 
    callable $handler
): RouteInterface
将路由映射到没有 HTTP 方法限制的处理器

public function mount(
    CollectionInterface $collection
): Micro
挂载一组处理器

public function notFound(
    callable $handler
): Micro
设置当路由器无法匹配任何定义的路由时调用的处理器

public function offsetExists(
    mixed $alias
): bool
使用数组语法检查内部 DI 容器中是否注册了服务

public function offsetGet(
    mixed $alias
): mixed
使用数组语法从内部 DI 容器获取 DI 服务

public function offsetSet(
    mixed $alias, 
    mixed $definition
)
使用数组语法在内部 DI 容器中注册服务

$app["request"] = new \Phalcon\Http\Request();

public function offsetUnset(
    mixed $alias
): void
使用数组语法从内部 DI 容器中移除服务

public function options(    
    string $routePattern, 
    callable $handler
): RouteInterface
时匹配的处理器OPTIONS

public function patch(
    string $routePattern, 
    callable $handler
): RouteInterface
时匹配的处理器PATCH

public function post(
    string $routePattern, 
    callable $handler
): RouteInterface
时匹配的处理器POST

public function put(
    string $routePattern, 
    callable $handler
): RouteInterface
时匹配的处理器PUT

public function setActiveHandler(
    callable $activeHandler
)
外部设置与路由匹配时必须调用的处理器

public function setModelBinder(
    BinderInterface $modelBinder, 
    mixed $cache = null
): Micro
设置模型绑定器

$micro = new Micro($di);

$micro->setModelBinder(
    new Binder(),
    'cache'
);

public function setResponseHandler(
    callable $handler
): Micro
添加一个自定义response处理器替代默认处理器

public function setService(
    string $serviceName, 
    mixed $definition, 
    bool $shared = false
): ServiceInterface
在内部 DI 容器中设置服务。如果没有预设容器,Phalcon\Di\FactoryDefault将自动创建

public function stop()
停止中间件执行

路由

Phalcon\Mvc\Micro应用程序中定义路由非常简单。路由的定义格式如下:

   Application : (http method): (route url/regex, callable PHP function/handler)

激活

路由由Phalcon\Mvc\Router对象中的相关方法手动

注意

路由必须始终以/

通常,应用程序的第一个路由是/,可通过以下GETHTTP 方法访问:

<?php

$application->get(
    '/',
    function () {
        echo '<h1>3.1459</h1>';
    }
);

注意

请参阅我们的更多信息,请参阅文档了解关于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 提供了多种方式来将处理器绑定到路由。选择哪种方式取决于您的应用程序需求、设计以及编码风格。

匿名函数

您可以使用匿名函数来处理请求

<?php

$app->get(
    '/invoices/view/{id}',
    function ($id) {
        echo "<h1>#{$id}!</h1>";
    }
);

在匿名函数内部访问$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);
The 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}

<?php

$app->delete(
    '/api/products/delete/{id}',
    'deleteProduct'
);

get

匹配GET且路由为/api/products

<?php

$app->get(
    '/api/products',
    'getProducts'
);

head

匹配HEAD且路由为/api/products

<?php

$app->get(
    '/api/products',
    'getProducts'
);

map

map允许你将同一端点附加到多个 HTTP 方法。以下示例匹配如果 HTTP 方法是GETPOST且路由为/repos/store/refs

<?php

$app
    ->map(
        '/repos/store/refs',
        'actionProduct'
    )
    ->via(
        [
            'GET',
            'POST',
        ]
    );

options

匹配OPTIONS且路由为/api/products/options

<?php

$app->options(
    '/api/products/options',
    'infoProduct'
);

patch

匹配PATCH且路由为/api/products/update/{id}

<?php

$app->patch(
    '/api/products/update/{id}',
    'updateProduct'
);

post

匹配POST且路由为/api/products/add

<?php

$app->post(
    '/api/products',
    'addProduct'
);

put

匹配PUT且路由为/api/products/update/{id}

<?php

$app->put(
    '/api/products/update/{id}',
    'updateProduct'
);

集合

集合是一种方便的方式来将路由分组到一个处理器以及一个公共前缀(如需要)。对于假设的 /invoices 端点,你可以有以下路由:

/invoices/get/{id}
/invoices/add/{payload}
/invoices/update/{id}
/invoices/delete/{id}

所有这些路由都由我们的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 getHandler(): mixed
返回主处理器

public function getHandlers(): array
返回已注册的处理器

public function getPrefix(): string
返回集合前缀(如果有)

public function head(
    string $routePattern, 
    callable $handler, 
    string $name = null
): CollectionInterface
时匹配的处理器HEAD.

public function isLazy(): bool
返回主处理器是否需要延迟加载

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
通过方法将路由映射到处理器。

$collection->mapVia(
    "/invoices",
    "indexAction",
    [
        "POST", 
        "GET"
    ],
    "invoices"
);

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.

public function setHandler(
    callable $handler, 
    bool $lazy = false
): CollectionInterface
设置主处理器。

public function setLazy(
    bool $lazy
): CollectionInterface
设置主处理器是否需要延迟加载

public function setPrefix(
    string $prefix
): CollectionInterface
为集合中添加的所有路由设置一个前缀

参数

路由中的参数通过将参数名放在花括号 {} 中进行定义:

<?php

$app->get(
    '/invoices/view/{id}',
    function ($id) {
        echo "<h1>#{$id}!</h1>";
    }
);

你可以使用正则表达式对参数强制规则。名称后面的正则表达式用:.

<?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.

直接输出

<?php

$app->get(
    '/invoices/view/{id}',
    function ($id) {
        echo "<h1>#{$id}!</h1>";
    }
);

包含文件

<?php

$app->get(
    '/invoices/view/{id}',
    function ($id) {
        require 'views/results.php';
    }
);

直接 - 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()
        );
    }
);
在上面的示例中,处理程序返回一个数据数组,after 事件调用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, afterfinish方法调用即可。

<?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方法,可用于捕获源自异常的任何错误。以下代码片段展示了此功能的基本用法:

<?php

use Phalcon\Mvc\Micro;

$app = new Micro();

$app->get(
    '/',
    function () {
        throw new \Exception(
            'Error', 
            401
        );
    }
);

$app->error(
    function ($exception) {
        echo json_encode(
            [
                'code'    => $exception->getCode(),
                'status'  => 'error',
                'message' => $exception->getMessage(),
            ]
        );
    }
);
无噪 Logo
无噪文档
25 年 6 月翻译
版本号 5.9
文档源↗