注解¶
概览¶
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()
{
// ...
}
}
注解的语法如下:
此外,注解可以放在文档块的任何部分:
<?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(first='hello', second='world', third=1)
* @SomeAnnotation(first: 'hello', second: 'world', third: 1)
/**
* @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},
* }})
适配器¶
该组件使用适配器来缓存或不缓存解析和处理后的注解,从而提高性能:
适配器 | 描述 |
---|---|
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在内存中存储解析和处理后的注解。此适配器适用于开发系统。每次请求都会重建缓存,因此可以在开发应用程序时立即反映更改。
流¶
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()
{
}
}