IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> php-parser在Aop编程中的使用 -> 正文阅读

[PHP知识库]php-parser在Aop编程中的使用

在laravel下使用php-parser实现aop

composer require nikic/php-parser

Test.php

<?php
/**
 * Created by PhpStorm.
 * User: CSH
 * Date: 2019/4/4
 * Time: 11:26
 */

namespace app;

/**
 * 为该类创建代理,并植入切面 埋点
 * 使用parser生成对应的语法树,然后主动修改方法体内的逻辑
 *
 * Class Test
 * @package app
 */
class Test
{
    // do something before
    // do something
    // do something after

    public function show()
    {
        return 'Hello World';
    }

    public function say()
    {
        return 'I Can Fly';
    }

}

ProxyVisitor.php

<?php
/**
 * Created by PhpStorm.
 * User: CSH
 * Date: 2019/4/4
 * Time: 11:16
 */

namespace App\Aop;

use PhpParser\Node;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\TraitUse;
use PhpParser\NodeFinder;
use PhpParser\NodeVisitorAbstract;

/**
 * PHP Parser在Aop编程中的使用
 *
 * 流程:
 * 1、当我们拿到节点是类时,我们重置这个类,让新建的类继承这个类。
 * 2、当我们拿到的节点是类方法时,我们使用proxyCall来重写方法。
 * 3、当遍历完成之后,给类加上我们定义好的AopTrait。
 *
 * NodeVisitor接口调用顺序:beforeTraverse -> enterNode -> leaveNode -> afterTraverse
 *
 * Class ProxyVisitor
 * @package App\Aop
 */
class ProxyVisitor extends NodeVisitorAbstract
{
    protected $className;

    protected $proxyId;

    public function __construct($className, $proxyId)
    {
        $this->className = $className;
        $this->proxyId = $proxyId;
    }

    public function getProxyClassName()
    {
        return \basename(str_replace('\', '/', $this->className)).'_'.$this->proxyId;
    }

    public function getClassName()
    {
        return '\'.$this->className.'_'.$this->proxyId;
    }

    /**
     * @return \PhpParser\Node\Stmt\TraitUse
     */
    private function getAopTraitUseNode(): TraitUse
    {
        // Use AopTrait trait use node
        return new TraitUse([new Name('\App\Aop\AopTrait')]);
    }

    /**
     * Called when leaving a node
     * 把类方法里的逻辑重置掉
     *
     * @param Node $node
     * @return int|null|Node|Node[]|Class_|ClassMethod
     */
    public function leaveNode(Node $node)
    {
        // Proxy Class
        if ($node instanceof Class_) {
            // Create proxy class base on parent class
            return new Class_($this->getProxyClassName(), [
                'flags' => $node->flags,
                'stmts' => $node->stmts,
                'extends' => new Name('\'.$this->className),
            ]);
        }
        // Rewrite public and protected methods, without static methods
        if ($node instanceof ClassMethod && !$node->isStatic() && ($node->isPublic() || $node->isProtected())) {
            $methodName = $node->name->toString();
            // Rebuild closure uses, only variable
            $uses = [];
            foreach ($node->params as $key => $param) {
                if ($param instanceof Param) {
                    $uses[$key] = new Param($param->var, null, null, true);
                }
            }
            $params = [
                // Add method to an closure
                new Closure([
                    'static' => $node->isStatic(),
                    'uses' => $uses,
                    'stmts' => $node->stmts,
                ]),
                new String_($methodName),
                new FuncCall(new Name('func_get_args')),
            ];
            $stmts = [
                new Return_(new MethodCall(new Variable('this'), '__proxyCall', $params))
            ];
            $returnType = $node->getReturnType();
            if ($returnType instanceof Name && $returnType->toString() === 'self') {
                $returnType = new Name('\'.$this->className);
            }
            return new ClassMethod($methodName, [
                'flags' => $node->flags,
                'byRef' => $node->byRef,
                'params' => $node->params,
                'returnType' => $returnType,
                'stmts' => $stmts,
            ]);
        }
    }

    /**
     * Called once after traversal
     * 把AopTrait扔到类里
     *
     * @param array $nodes
     * @return array|null|Node[]
     */
    public function afterTraverse(array $nodes)
    {
        $addEnhancementMethods = true;
        $nodeFinder = new NodeFinder();
        $nodeFinder->find($nodes, function (Node $node) use (&$addEnhancementMethods) {
            if ($node instanceof TraitUse) {
                foreach ($node->traits as $trait) {
                    // Did AopTrait trait use ?
                    if ($trait instanceof Name && $trait->toString() === '\App\Aop\AopTrait') {
                        $addEnhancementMethods = false;
                        break;
                    }
                }
            }
        });
        // Find Class Node and then Add Aop Enhancement Methods nodes and getOriginalClassName() method
        $classNode = $nodeFinder->findFirstInstanceOf($nodes, Class_::class);
        $addEnhancementMethods && array_unshift($classNode->stmts, $this->getAopTraitUseNode());
        return $nodes;
    }
}

/**
 * 切面
 *
 * Trait AopTrait
 * @package App\Aop
 */
trait AopTrait
{
    /**
     * AOP proxy call method
     *
     * @param \Closure $closure
     * @param string $method
     * @param array $params
     * @return mixed|null
     * @throws \Throwable
     */
    public function __proxyCall(\Closure $closure, string $method, array $params)
    {
        $res = $closure(...$params);
        if (is_string($res)) {
            $res .= ' !!!';
        }
        return $res;
    }
}

AopController.php

<?php

namespace App\Http\Controllers;

use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
use PhpParser\NodeTraverser;
use App\Aop\ProxyVisitor;
use PhpParser\PrettyPrinter\Standard;

class AopController extends Controller
{
    public function index()
    {
        $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
        $ast = $parser->parse(file_get_contents(base_path().'/app/Test.php'));

        // 把parser代码后的语法树(对象)转为字符串形式
//        $dumper = new NodeDumper();
//        dd($dumper->dump($ast));

        $className = 'App\Test';
        $proxyId = uniqid();
        $visitor = new ProxyVisitor($className, $proxyId);

        $traverser = new NodeTraverser();
        $traverser->addVisitor($visitor);
        // 使用已注册的访问者遍历节点数组,返回遍历节点数组
        $proxyAst = $traverser->traverse($ast);
        if (!$proxyAst) {
            throw new \Exception(sprintf('Class %s AST optimize failure', $className));
        }
        $printer = new Standard();
        // 打印一个节点数组
        $proxyCode = $printer->prettyPrint($proxyAst);

//        dd($proxyCode);

        eval($proxyCode);
        $class = $visitor->getClassName();
        $bean = new $class();
        echo $bean->show();
    }
}

以上内容希望帮助到大家,更多免费PHP大厂PDF,PHP进阶架构视频资料,PHP精彩好文可以微信搜索关注:PHP开源社区

2021金三银四大厂面试真题集锦,必看!

四年精华PHP技术文章整理合集——PHP框架篇

四年精华PHP技术文合集——微服务架构篇

四年精华PHP技术文合集——分布式架构篇

四年精华PHP技术文合集——高并发场景篇

四年精华PHP技术文章整理合集——数据库篇

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 10:56:29  更:2021-08-03 10:57:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/5 13:03:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码