sunel/aspect-me

在函数调用之前、之后或周围拦截并更改类的公共方法的行为。

dev-master 2017-11-08 03:07 UTC

This package is auto-updated.

Last update: 2024-09-05 04:17:04 UTC


README

在函数调用之前、之后或周围拦截并更改类的公共方法的行为。

面向方面编程(AOP)是一种编程范式,旨在通过允许跨切面关注点的分离来增加模块化。它是通过向现有代码(建议)添加额外的行为来实现的,而不修改代码本身,而是通过“切入点”规范分别指定哪些代码被修改。这允许将不是业务逻辑核心的行为添加到程序中,而不会使代码变得杂乱。

为什么选择 APO?

假设你想更改类的函数输出或在方法被调用时执行某些操作。通常,我们需要重写类并扩展方法定义以覆盖它。

类重写很受欢迎,因为它们允许非常具体地重新定义系统功能。

然而,类重写存在一些问题。你只能重写一个类一次。一旦一个模块声明了一个方法,其他模块就无权使用。

此包允许第三方开发者创建模块或包,这些模块或包可以监听并更改类方法的操作,从而避免相互冲突。

局限性

  • 此功能仅在使用依赖注入容器 PSR-11 的系统上有效。
  • 只能用于公共方法。
  • 类必须通过 DI 容器解析。

安装

通过 composer

$ composer require sunel/aspect-me

对于 #laravel

如何操作?

让我们考虑有一个名为 Post 的类

interface PostInterface {

    public function setTitle($title);

    public function getTitle();

}


class Post implements PostInterface {
	
    public function setTitle($title)
    {
    	$this->title = $title;
    }

    public function getTitle()
    {
    	return  $this->title;
    }
}


$container->bind(\PostInterface::class, \Post::class);

我们需要将第一个字符更改为大写,在这种情况下,我们可以在 getTitle 被调用后进行更改。所以首先让我们让系统知道这件事

\Aspect\Advice::after('change_post_title_to_uppercase', \PostInterface::class.'@getTitle', function(\PostInterface $subject,  $result, ...$args) {
    return ucfirst($result);
}, 100);

现在我们需要根据建议准备系统进行更新。

下面将给出命令的详细信息

$ aspect:inspect

好的,所以现在如果我们从 Post 类通过 DI 调用 getTitle,结果将包含第一个要大写的字符。

$post = $container->get(\PostInterface::class);

$post->setTitle('aspect');

$post->getTitle(); ## Aspect

AOP 概念

\Aspect\Advice:在特定的连接点注册方面采取的操作。

连接点:在程序执行期间的一个点,例如方法的执行。包括“之前”、“周围”和“之后”建议。

之前

即使有自动构造函数依赖注入的系统,也无法在 setTitle 方法被调用之前更改此参数。

这种类型的问题正是 before 插件方法可以解决的问题。如果方法没有更改被观察方法中的参数,它应该返回 null。

\Aspect\Advice::before('trim_post_title', \PostInterface::class.'@setTitle', function(\PostInterface $subject, ...$args) {
	
	# We trim the given title
    return [trim($args[0])];

}, 100);

之后

这就像是一个类方法的观察者。系统将在 getTitle 方法被调用后调用它。你可以使用这些方法通过修改原始结果并在方法结束时返回它来更改被观察方法的结果。

\Aspect\Advice::after('change_post_title_to_uppercase', \PostInterface::class.'@getTitle', function(\PostInterface $subject,  $result, ...$args) {
    return ucfirst($result);
}, 100);

当方法可以访问其观察方法的所有参数后。当观察方法完成时,系统将结果和参数传递给下一个后续的after方法。如果观察方法不返回结果,则将null传递给下一个after方法。

在...

这将在观察方法之前和之后运行代码。around方法在原始方法执行期间或作为原始方法的替代执行。

\Aspect\Advice::around('change_post_title', \PostInterface::class.'@getTitle', function(\PostInterface $subject,  callable $proceed, ...$args) {

	echo 'Calling'.' -- before',"\n";
    $result = $proceed(...$args)
    echo 'Calling' . ' -- after',"\n";

    return $result;

}, 100);

around插件方法允许您在单个地方执行代码,在原始方法之前和之后。它是通过第二个参数来实现这一点的。

这个第二个参数$proceed是一个匿名函数Closure。如果您正在使用around插件方法,当您希望系统调用原始方法时,您将调用/调用此闭包。您负责从插件向proceed调用传递参数。

这也意味着您可以取消对原始方法的调用,并用您自己的返回值代替。

注意:可以通过提供介于10-100之间的第四个参数来对所有的Advices进行优先级排序。值越高,优先级越高。

集成

Laravel

您需要在config/app.php中注册服务提供者。

'providers' => [
	...
	Aspect\Provider\LaravelServiceProvider::class
]

将以下内容添加到composer.json中

{
    "autoload": {
        "classmap": [
            "resources/generated/"
        ]
    }
}

运行此命令,让composer了解新的自动加载

$ composer dump-autoload

对于artisan命令

$ php artisan aspect:inspect