通过注解在 Joomla! 中添加拦截器过滤器
在这篇文章中,我将向您展示如何在 Joomla 应用程序中通过注解使用拦截器过滤器。它使用了面向方面编程的原理。
如果您阅读过我的其他关于 Joomla 和自动化绑定 或 在 Joomla 中使用 Doctrine 的文章,那么您可能知道我也大量使用 Java 进行开发。Java 大量使用注解,结合面向 方面编程 的原理,为开发者提供了控制应用程序流程的强大能力。我们在应用程序中经常使用的一种功能是拦截器。基本原理是在执行应用程序代码之前设置一个“插入点”来执行代码。在这篇文章中,我将向您展示我在我的一个业余项目中实现的类似方法。
首先,让我解释一下问题。在这个例子中,我有一个控制器,它定义了一些查看和编辑账户的操作。对于这个特定的应用程序,我使用标准的 Joomla! 用户并将其链接到应用程序中的自定义账户实体。因此,用户编辑自定义账户实体,但我们根据登录到网站上的当前 Joomla! 用户来确定自定义账户。
我们可以做的一件事是将此功能写入一个服务并在控制器中的每个操作方法中使用这个服务。但这会使方法充斥着重复的代码。我们可以使用拦截器在每个控制器调用上执行此操作。
那么,我们如何定义一个过滤器呢?我们可以使用注解来定义。不幸的是,PHP 没有原生的注解支持,但我们可以使用注释来定义。在我们的控制器中,它看起来像这样。
[php]
/**
* FrontController to manage functionality around the 'Mijn Westlandia' functions
*
* @author Paul de Raaij <This email address is being protected from spambots. You need JavaScript enabled to view it. >
*
* @Filter(\Services\MemberAuthentication)
*/
class IvejoControllerAccount extends IvejoControllerFrontBase {
public function index() {
$this->assign('account', $this->getAccount());
$this->render('view');
}
}
[/php]
这并不难,我们可以用一行代码使用过滤器。在注解的值中,我们定义执行过滤器代码的类。
让我们先解释一下过滤器。每个过滤器都应该实现InterceptorFilter接口,其代码如下
[php]
/**
*
* @author pderaaij
*/
interface InterceptorFilter {
function setContainer(\Services\DI\Container $container);
function doFilter();
}
[/php]
我在这篇文章和业余项目中使用的过滤器代码如下。注意,这里只展示了过滤器实现的代码。我已经删除了一些工具方法,以保持实现代码的清晰。
[php]
/**
* Filter to ensure that logged in Joomla users are matched to accounts in Ivejo.
*
* @author pderaaij
*/
class MemberAuthentication implements InterceptorFilter {
/**
* @var \Services\DI\Container;
*/
private $container = null;
/**
* Register the active account if any and make it available
* thru the DI container.
*/
public function doFilter() {
$this->container->register('activeAccount', null);
$user = $this->getJoomlaUser();
if( $user != null && $this->container != null ) {
$account = $this->getAccount( $user );
if( count($account) == 1 ) {
$this->container->register('activeAccount', $account[0]);
}
}
}
public function setContainer(DI\Container $container) {
$this->container = $container;
}
}
[/php]
这就是过滤器的实现。你看,使用过滤器并不难,你可以在过滤器中做任何你想做的事。在这个示例代码中,我使用了一个依赖注入容器。这是一个非常简单的基于Pimple DI容器的依赖注入容器。
现在让我们看看过滤器注解是如何被读取和执行的。对于Joomla来说,在这一点上最重要的事情是,使用这个过滤器拦截器的每个控制器都需要从一个基类扩展。就我个人而言,我在为Joomla网站工作的时候总是这样做,Joomla有一些东西我希望能通过这个基类来填补这些空白。
在这个基类中,我们扩展了JController的execute方法,这样我们就可以看到是否有定义过滤器注解。扩展的execute方法在基类中定义如下。
[php]
public function execute($task) {
$discoverer = new InterceptingFilterAnnotationDiscoverer( $this->getContainer() );
if( $discoverer->hasFilterAnnotation($this) ) {
$interceptor = $discoverer->getInterceptor($this);
$interceptor->doFilter();
}
parent::execute($task);
}
[/php]
每次execute方法通过Joomla!框架被调用时,我们都会在控制器中检查是否有定义注解。在InterceptingFilterAnnotationDiscoverer类中,我们会检查是否有注解定义。让我们看一下这个类。
[php]
/**
* Class to see if a given class has an @Filter annotation.
*
* @author Paul de Raaij <This email address is being protected from spambots. You need JavaScript enabled to view it. >
*/
class InterceptingFilterAnnotationDiscoverer {
/**
* @var \Services\DI\Container;
*/
private $container = null;
public function __construct(Container $cont) {
$this->container = $cont;
}
/**
* Check if the class has an filter annotation
*
* @param String $clazz
* @return boolean
*/
public function hasFilterAnnotation( $clazz ) {
$reflectionClass = new \ReflectionClass(get_class($clazz));
return strpos($reflectionClass->getDocComment(), '@Filter') !== false;
}
/**
* Get the interceptor which has been set by the @Filter annotation
*
* @param String $clazz
* @return \Services\interceptors|null
*/
public function getInterceptor( $clazz ) {
if( $this->hasFilterAnnotation($clazz) ) {
$reflectionClass = new \ReflectionClass(get_class($clazz));
preg_match_all('/@Filter\((.*?)\)/s', $reflectionClass->getDocComment(), $matches);
$interceptors = $matches[1];
if(count($interceptors) > 0) {
if(class_exists($interceptors[0]) ) {
$obj = new $interceptors[0];
$obj->setContainer( $this->container );
return $obj;
}
}
}
return null;
}
}
[/php]
这个类中有两个方法。检查是否有过滤器注解定义是非常简单的。通过反射,我们加载类的注释块。在这个注释中,我们检查是否找到了@Filter定义。在getInterceptor()方法中,我们将获取注解的值,以查看定义了哪个拦截器。如果类存在,我们将创建一个新的实例并返回它。这个方法的调用者将得到一个InterceptorFilter实例,并需要自己执行doFilter方法。
这就是全部内容。当然,就像我写的其他文章一样,你可以对这个代码进行很多改进。如果你想在生产环境中使用它,添加一些额外的验证是必要的(如果问我)。此外,允许在单个注解中定义多个过滤器也是一个很好的改进。
我写这些文章是为了给你一些灵感,我很乐意得到一些反馈并从中受到启发。只是不要把它当作复制粘贴的代码,而要用它来开发你自己的实现。
这篇文章最初发表在我的个人博客上。
在Joomla社区杂志上发表的一些文章代表了作者在特定主题上的个人观点或经验,可能并不与Joomla项目的官方立场一致。
通过接受,您将访问https://magazine.joomla.net.cn/之外的第三方外部服务
评论