跳转到内容

注解


概览

Phalcon 引入了第一个为 PHP 编写的用 C 实现的注解解析组件。该Phalcon\Annotations命名空间涵盖了提供简单方式解析和缓存 PHP 应用程序中注解的通用组件。

使用

注解从类、方法和属性的文档块中提取。注解可以放置在文档块中的任何位置:

<?php

/**
 * This is the class description
 *
 * @AmazingClass(true)
 */
class Example
{
    /**
     * This is a property with a special feature
     *
     * @SpecialFeature
     */
    protected $someProperty;

    /**
     * This is a method
     *
     * @SpecialFeature
     */
    public function someMethod()
    {
        // ...
    }
}

注解的语法如下:

/**
 * @Annotation-Name
 * @Annotation-Name(param1, param2, ...)
 */

此外,注解可以放在文档块的任何部分:

<?php

/**
 * This is a property with a special feature
 *
 * @SpecialFeature
 *
 * More comments
 *
 * @AnotherSpecialFeature(true)
 */
虽然解析器非常灵活,但为了代码的可维护性和易理解性,建议将注解放在文档块的末尾:

<?php

/**
 * This is a property with a special feature
 * More comments
 *
 * @SpecialFeature({someParameter='the value', false})
 * @AnotherSpecialFeature(true)

模型的一个例子是:

<?php

use Phalcon\Mvc\Model;

/**
 * Customers
 *
 * Represents a customer record
 *
 * @Source('co_customers');
 * @HasMany("cst_id", "Invoices", "inv_cst_id")
 */
class Customers extends Model
{
    /**
     * @Primary
     * @Identity
     * @Column(type="integer", nullable=false, column="cst_id")
     */
    public $id;

    /**
     * @Column(type="string", nullable=false, column="cst_name_first")
     */
    public $nameFirst;

    /**
     * @Column(type="string", nullable=false, column="cst_name_last")
     */
    public $nameLast;
}

类型

注解可能有或没有参数。参数可以是一个简单的字面量 (strings, number, boolean, null),一个array,一个哈希列表,或者另一个注解:

/**
 * @SomeAnnotation
简单注解

/**
 * @SomeAnnotation('hello', 'world', 1, 2, 3, false, true)
带参数的注解

/**
 * @SomeAnnotation(first='hello', second='world', third=1)
 * @SomeAnnotation(first: 'hello', second: 'world', third: 1)
带命名参数的注解

/**
 * @SomeAnnotation([1, 2, 3, 4])
 * @SomeAnnotation({1, 2, 3, 4})
传递数组

/**
 * @SomeAnnotation({first=1, second=2, third=3})
 * @SomeAnnotation({'first'=1, 'second'=2, 'third'=3})
 * @SomeAnnotation({'first': 1, 'second': 2, 'third': 3})
 * @SomeAnnotation(['first': 1, 'second': 2, 'third': 3])
作为参数传递哈希

/**
 * @SomeAnnotation({'name'='SomeName', 'other'={
 *     'foo1': 'bar1', 'foo2': 'bar2', {1, 2, 3},
 * }})
嵌套数组/哈希

/**
 * @SomeAnnotation(first=@AnotherAnnotation(1, 2, 3))
嵌套注解

适配器

该组件使用适配器来缓存或不缓存解析和处理后的注解,从而提高性能:

适配器 描述
Phalcon\Annotations\Adapter\Apcu 使用 APCu 存储解析和处理后的注解(生产环境)
Phalcon\Annotations\Adapter\Memory 使用内存存储注解(开发环境)
Phalcon\Annotations\Adapter\Stream 使用文件流存储注解。必须与字节码缓存配合使用。

Apcu

Phalcon\Annotations\Adapter\Apcu使用 APCu 缓存存储解析和处理后的注解。此适配器适用于生产系统。但是,一旦 Web 服务器重启,缓存将被清除并需要重新构建。适配器在构造函数的选项数组中接受两个参数:

  • prefix- 存储键的前缀
  • lifetime- 缓存生命周期

<?php

use Phalcon\Annotations\Adapter\Apcu;

$adapter = new Apcu(
    [
        'prefix'   => 'my-prefix',
        'lifetime' => 3600,
    ]
);
内部实现中,适配器通过在每个键前加上 _PHAN来存储数据。这个设置不能更改。然而,它为您提供了一个扫描 APCu 中以 _ 为前缀的键并根据需要清除它们的选项。PHAN and clear them if needed.

<?php

use APCuIterator;

$result   = true;
$pattern  = "/^_PHAN/";
$iterator = new APCuIterator($pattern);

if (true === is_object($iterator)) {
    return false;
}

foreach ($iterator as $item) {
    if (true !== apcu_delete($item["key"])) {
        $result = false;
    }
}

return $result;

内存

Phalcon\Annotations\Adapter\Memory在内存中存储解析和处理后的注解。此适配器适用于开发系统。每次请求都会重建缓存,因此可以在开发应用程序时立即反映更改。

<?php

use Phalcon\Annotations\Adapter\Memory;

$adapter = new Memory();

Phalcon\Annotations\Adapter\Stream将解析和处理后的注解存储在服务器上的文件中。此适配器可以在生产系统中使用,但由于每次请求都需要从文件系统读取注解缓存文件,因此会增加 I/O。适配器在构造函数的$options数组中接受一个参数:

  • annotationsDir- 存储注解缓存的目录
<?php

use Phalcon\Annotations\Adapter\Stream;

$adapter = new Stream(
    [
        'annotationsDir' => '/app/storage/cache/annotations',
    ]
);

如果由于权限或其他原因无法在指定文件夹中存储数据,则会抛出Phalcon\Annotations\Exception异常。

自定义

Phalcon\Annotations\Adapter\AdapterInterface可用

异常

Phalcon\Annotations命名空间将是类型Phalcon\Annotations\Exception。您可以使用这些异常来选择性地捕获仅从此组件抛出的异常。

<?php

use Phalcon\Annotations\Adapter\Memory;
use Phalcon\Annotations\Exception;
use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function index()
    {
        try {
            $adapter = new Memory();

            $reflector   = $adapter->get('Invoices');
            $annotations = $reflector->getClassAnnotations();

            foreach ($annotations as $annotation) {
                echo $annotation->getExpression('unknown-expression');
            }
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }
    }
}

示例

基于控制器的访问

您可以使用注解来定义哪些区域由 ACL 控制。这可以通过在事件管理器中注册一个监听beforeExecuteRoute事件的插件,或者简单地在基础控制器中实现该方法来实现。

首先,在 DI 容器中设置注解管理器:

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Annotations\Adapter\Apcu;

$container = new FactoryDefault();

$container->set(
    'annotations',
    function () {
        return new Apcu(
            [
                'lifetime' => 86400
            ]
        );
    }
);
现在,在基础控制器中实现beforeExecuteRoute方法:

<?php

namespace MyApp\Controllers;

use Phalcon\Annotations\Adapter\Apcu;
use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Controller;
use MyApp\Components\Auth;

/**
 * @property Apcu $annotations
 * @property Auth $auth 
 */
class BaseController extends Controller
{
    /**
     * @param Event $event
     * @param Dispatcher $dispatcher
     *
     * @return bool
     */
    public function beforeExecuteRoute(
        Dispatcher $dispatcher
    ) {
        $controllerName = $dispatcher->getControllerClass();

        $annotations = $this
            ->annotations
            ->get($controllerName)
        ;

        $exists = $annotations
            ->getClassAnnotations()
            ->has('Private')
        ;

        if (!$exists) {
            return true;
        }

        if ($this->auth->isLoggedIn()) {
            return true;
        }

        $dispatcher->forward(
            [
                'controller' => 'session',
                'action'     => 'login',
            ]
        );

        return false;
    }
}
在您的控制器中指定:

<?php

namespace MyApp\Controllers;

use MyApp\Controllers\BaseController;

/**
 * @Private(true) 
 */
class Invoices extends BaseController
{
    public function indexAction()
    {
    }
}

基于组的访问

您可能希望扩展上述内容,并为您的应用程序提供更细粒度的访问控制。为此,还可以在控制器中使用beforeExecuteRoute,但在每个操作上添加访问元数据。如果您需要某个特定控制器“锁定”,您还可以使用 initialize 方法。

首先,在 DI 容器中设置注解管理器:

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Annotations\Adapter\Apcu;

$container = a FactoryDefault();

$container->set(
    'annotations',
    function () {
        return new Apcu(
            [
                'lifetime' => 86400
            ]
        );
    }
);

现在,在基础控制器中实现beforeExecuteRoute方法:

<?php

namespace MyApp\Controllers;

use Phalcon\Annotations\Adapter\Apcu;
use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Controller;
use MyApp\Components\Auth;

/**
 * @property Apcu $annotations
 * @property Auth $auth 
 */
class BaseController extends Controller
{
    /**
     * @param Event $event
     * @param Dispatcher $dispatcher
     *
     * @return bool
     */
    public function beforeExecuteRoute(
        Dispatcher $dispatcher
    ) {
        $controllerName = $dispatcher->getControllerClass();
        $actionName     = $dispatcher->getActionName() . 'Action';

        $data = $this
            ->annotations
            ->getMethod($controllerName, $actionName)
        ;
        $access    = $data->get('Access');
        $aclGroups = $access->getArguments();

        $user   = $this->acl->getUser();
        $groups = $user->getRelated('groups');

        $userGroups = [];
        foreach ($groups as $group) {
            $userGroups[] = $group->grp_name;
        }

        $allowed = array_intersect($userGroups, $aclGroups);
        $allowed = (count($allowed) > 0);

        if ($allowed) {
            return true;
        }

        $dispatcher->forward(
            [
                'controller' => 'session',
                'action'     => 'login',
            ]
        );

        return false;
    }
}
在您的控制器中:

<?php

namespace MyApp\Controllers;

use MyApp\Controllers\BaseController;

/**
 * @Private(true) 
 */
class Invoices extends BaseController
{
    /**
     * @Access(
     *     'Administrators',
     *     'Accounting',
     *     'Users',
     *     'Guests'
     * )
     */
    public function indexAction()
    {
    }

    /**
     * @Access(
     *     'Administrators',
     *     'Accounting',
     * )
     */
    public function listAction()
    {
    }

    /**
     * @Access(
     *     'Administrators',
     *     'Accounting',
     * )
     */
    public function viewAction()
    {
    }
}

额外资源

无噪 Logo
无噪文档
25 年 6 月翻译
文档源↗