Completed
Push — 2.x ( 2a04a1...0da1ca )
by Alexander
02:03
created

MagicConstantTransformer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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