Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of Psy Shell.
5
 *
6
 * (c) 2012-2022 Justin Hileman
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Psy\Command\TimeitCommand;
13
 
14
use PhpParser\Node;
15
use PhpParser\Node\Arg;
16
use PhpParser\Node\Expr;
17
use PhpParser\Node\Expr\StaticCall;
18
use PhpParser\Node\FunctionLike;
19
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
20
use PhpParser\Node\Stmt\Expression;
21
use PhpParser\Node\Stmt\Return_;
22
use PhpParser\NodeVisitorAbstract;
23
use Psy\CodeCleaner\NoReturnValue;
24
use Psy\Command\TimeitCommand;
25
 
26
/**
27
 * A node visitor for instrumenting code to be executed by the `timeit` command.
28
 *
29
 * Injects `TimeitCommand::markStart()` at the start of code to be executed, and
30
 * `TimeitCommand::markEnd()` at the end, and on top-level return statements.
31
 */
32
class TimeitVisitor extends NodeVisitorAbstract
33
{
34
    private $functionDepth;
35
 
36
    /**
37
     * {@inheritdoc}
38
     *
39
     * @return Node[]|null Array of nodes
40
     */
41
    public function beforeTraverse(array $nodes)
42
    {
43
        $this->functionDepth = 0;
44
    }
45
 
46
    /**
47
     * {@inheritdoc}
48
     *
49
     * @return int|Node|null Replacement node (or special return value)
50
     */
51
    public function enterNode(Node $node)
52
    {
53
        // keep track of nested function-like nodes, because they can have
54
        // returns statements... and we don't want to call markEnd for those.
55
        if ($node instanceof FunctionLike) {
56
            $this->functionDepth++;
57
 
58
            return;
59
        }
60
 
61
        // replace any top-level `return` statements with a `markEnd` call
62
        if ($this->functionDepth === 0 && $node instanceof Return_) {
63
            return new Return_($this->getEndCall($node->expr), $node->getAttributes());
64
        }
65
    }
66
 
67
    /**
68
     * {@inheritdoc}
69
     *
70
     * @return int|Node|Node[]|null Replacement node (or special return value)
71
     */
72
    public function leaveNode(Node $node)
73
    {
74
        if ($node instanceof FunctionLike) {
75
            $this->functionDepth--;
76
        }
77
    }
78
 
79
    /**
80
     * {@inheritdoc}
81
     *
82
     * @return Node[]|null Array of nodes
83
     */
84
    public function afterTraverse(array $nodes)
85
    {
86
        // prepend a `markStart` call
87
        \array_unshift($nodes, $this->maybeExpression($this->getStartCall()));
88
 
89
        // append a `markEnd` call (wrapping the final node, if it's an expression)
90
        $last = $nodes[\count($nodes) - 1];
91
        if ($last instanceof Expr) {
92
            \array_pop($nodes);
93
            $nodes[] = $this->getEndCall($last);
94
        } elseif ($last instanceof Expression) {
95
            \array_pop($nodes);
96
            $nodes[] = new Expression($this->getEndCall($last->expr), $last->getAttributes());
97
        } elseif ($last instanceof Return_) {
98
            // nothing to do here, we're already ending with a return call
99
        } else {
100
            $nodes[] = $this->maybeExpression($this->getEndCall());
101
        }
102
 
103
        return $nodes;
104
    }
105
 
106
    /**
107
     * Get PhpParser AST nodes for a `markStart` call.
108
     *
109
     * @return \PhpParser\Node\Expr\StaticCall
110
     */
111
    private function getStartCall(): StaticCall
112
    {
113
        return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markStart');
114
    }
115
 
116
    /**
117
     * Get PhpParser AST nodes for a `markEnd` call.
118
     *
119
     * Optionally pass in a return value.
120
     *
121
     * @param Expr|null $arg
122
     *
123
     * @return \PhpParser\Node\Expr\StaticCall
124
     */
125
    private function getEndCall(Expr $arg = null): StaticCall
126
    {
127
        if ($arg === null) {
128
            $arg = NoReturnValue::create();
129
        }
130
 
131
        return new StaticCall(new FullyQualifiedName(TimeitCommand::class), 'markEnd', [new Arg($arg)]);
132
    }
133
 
134
    /**
135
     * Compatibility shim for PHP Parser 3.x.
136
     *
137
     * Wrap $expr in a PhpParser\Node\Stmt\Expression if the class exists.
138
     *
139
     * @param \PhpParser\Node $expr
140
     * @param array           $attrs
141
     *
142
     * @return \PhpParser\Node\Expr|\PhpParser\Node\Stmt\Expression
143
     */
144
    private function maybeExpression(Node $expr, array $attrs = [])
145
    {
146
        return \class_exists(Expression::class) ? new Expression($expr, $attrs) : $expr;
147
    }
148
}