Completed
Push — master ( eccf6a...345cbf )
by Alexander
03:07
created

wrapReflectionGetFileName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3.0026

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 21
ccs 14
cts 15
cp 0.9333
rs 9.3142
cc 3
eloc 14
nc 3
nop 1
crap 3.0026
1
<?php
2
declare(strict_types = 1);
3
/*
4
 * Go! AOP framework
5
 *
6
 * @copyright Copyright 2012, Lisachenko Alexander <[email protected]>
7
 *
8
 * This source file is subject to the license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Go\Instrument\Transformer;
13
14
use Go\Core\AspectKernel;
15
use PhpParser\Node\Expr\MethodCall;
16
use PhpParser\Node\Scalar\MagicConst;
17
use PhpParser\Node\Scalar\MagicConst\Dir;
18
use PhpParser\Node\Scalar\MagicConst\File;
19
use PhpParser\NodeTraverser;
20
21
/**
22
 * Transformer that replaces magic __DIR__ and __FILE__ constants in the source code
23
 *
24
 * Additionally, ReflectionClass->getFileName() is also wrapped into normalizer method call
25
 */
26
class MagicConstantTransformer extends BaseSourceTransformer
27
{
28
29
    /**
30
     * Root path of application
31
     *
32
     * @var string
33
     */
34
    protected static $rootPath = '';
35
36
    /**
37
     * Path to rewrite to (cache directory)
38
     *
39
     * @var string
40
     */
41
    protected static $rewriteToPath = '';
42
43
    /**
44
     * Class constructor
45
     *
46
     * @param AspectKernel $kernel Instance of kernel
47
     */
48 6
    public function __construct(AspectKernel $kernel)
49
    {
50 6
        parent::__construct($kernel);
51 6
        self::$rootPath      = $this->options['appDir'];
52 6
        self::$rewriteToPath = $this->options['cacheDir'];
53 6
    }
54
55
    /**
56
     * This method may transform the supplied source and return a new replacement for it
57
     *
58
     * @param StreamMetaData $metadata Metadata for source
59
     * @return string See RESULT_XXX constants in the interface
60
     */
61 5
    public function transform(StreamMetaData $metadata): string
62
    {
63 5
        $this->replaceMagicDirFileConstants($metadata);
64 5
        $this->wrapReflectionGetFileName($metadata);
65
66
        // We should always vote abstain, because if there is only changes for magic constants, we can drop them
67 5
        return self::RESULT_ABSTAIN;
68
    }
69
70
    /**
71
     * Resolves file name from the cache directory to the real application root dir
72
     *
73
     * @param string $fileName Absolute file name
74
     *
75
     * @return string Resolved file name
76
     */
77 1
    public static function resolveFileName(string $fileName): string
78
    {
79 1
        return str_replace(
80 1
            [self::$rewriteToPath, DIRECTORY_SEPARATOR . '_proxies'],
81 1
            [self::$rootPath, ''],
82
            $fileName
83
        );
84
    }
85
86
    /**
87
     * Wraps all possible getFileName() methods from ReflectionFile
88
     *
89
     * @param StreamMetaData $metadata
90
     */
91 5
    private function wrapReflectionGetFileName(StreamMetaData $metadata)
92
    {
93 5
        $methodCallFinder = new NodeFinderVisitor([MethodCall::class]);
94 5
        $traverser        = new NodeTraverser();
95 5
        $traverser->addVisitor($methodCallFinder);
96 5
        $traverser->traverse($metadata->syntaxTree);
97
98
        /** @var MethodCall[] $methodCalls */
99 5
        $methodCalls = $methodCallFinder->getFoundNodes();
100 5
        foreach ($methodCalls as $methodCallNode) {
101 1
            if ($methodCallNode->name !== 'getFileName') {
102
                continue;
103
            }
104 1
            $startPosition    = $methodCallNode->getAttribute('startTokenPos');
105 1
            $endPosition      = $methodCallNode->getAttribute('endTokenPos');
106 1
            $expressionPrefix = '\\' . __CLASS__ . '::resolveFileName(';
107
108 1
            $metadata->tokenStream[$startPosition][1] = $expressionPrefix . $metadata->tokenStream[$startPosition][1];
109 1
            $metadata->tokenStream[$endPosition][1] .= ')';
110
        }
111 5
    }
112
113
    /**
114
     * Replaces all magic __DIR__ and __FILE__ constants in the file with calculated value
115
     *
116
     * @param StreamMetaData $metadata
117
     */
118 5
    private function replaceMagicDirFileConstants(StreamMetaData $metadata)
119
    {
120 5
        $magicConstFinder = new NodeFinderVisitor([Dir::class, File::class]);
121 5
        $traverser        = new NodeTraverser();
122 5
        $traverser->addVisitor($magicConstFinder);
123 5
        $traverser->traverse($metadata->syntaxTree);
124
125
        /** @var MagicConst[] $magicConstants */
126 5
        $magicConstants = $magicConstFinder->getFoundNodes();
127 5
        $magicFileValue = $metadata->uri;
128 5
        $magicDirValue  = dirname($magicFileValue);
129 5
        foreach ($magicConstants as $magicConstantNode) {
130 2
            $tokenPosition = $magicConstantNode->getAttribute('startTokenPos');
131 2
            $replacement   = $magicConstantNode instanceof Dir ? $magicDirValue : $magicFileValue;
132
133 2
            $metadata->tokenStream[$tokenPosition][1] = "'{$replacement}'";
134
        }
135 5
    }
136
}
137