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 Go\Instrument\ClassLoading\CachePathManager; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Caching transformer that is able to take the transformed source from a cache |
19
|
|
|
*/ |
20
|
|
|
class CachingTransformer extends BaseSourceTransformer |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* Mask of permission bits for cache files. |
24
|
|
|
* By default, permissions are affected by the umask system setting |
25
|
|
|
* |
26
|
|
|
* @var integer|null |
27
|
|
|
*/ |
28
|
|
|
protected $cacheFileMode; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var array|callable|SourceTransformer[] |
32
|
|
|
*/ |
33
|
|
|
protected $transformers = []; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Cache manager |
37
|
|
|
*/ |
38
|
|
|
protected $cacheManager; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Class constructor |
42
|
|
|
* |
43
|
|
|
* @param array|callable $transformers Source transformers or callable that should return transformers |
44
|
|
|
*/ |
45
|
1 |
|
public function __construct(AspectKernel $kernel, $transformers, CachePathManager $cacheManager) |
46
|
|
|
{ |
47
|
1 |
|
parent::__construct($kernel); |
48
|
1 |
|
$this->cacheManager = $cacheManager; |
49
|
1 |
|
$this->cacheFileMode = $this->options['cacheFileMode']; |
50
|
1 |
|
$this->transformers = $transformers; |
51
|
1 |
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* This method may transform the supplied source and return a new replacement for it |
55
|
|
|
* |
56
|
|
|
* @return string See RESULT_XXX constants in the interface |
57
|
|
|
*/ |
58
|
1 |
|
public function transform(StreamMetaData $metadata): string |
59
|
|
|
{ |
60
|
1 |
|
$originalUri = $metadata->uri; |
61
|
1 |
|
$processingResult = self::RESULT_ABSTAIN; |
62
|
1 |
|
$cacheUri = $this->cacheManager->getCachePathForResource($originalUri); |
63
|
|
|
// Guard to disable overwriting of original files |
64
|
1 |
|
if ($cacheUri === $originalUri) { |
65
|
|
|
return self::RESULT_ABORTED; |
66
|
|
|
} |
67
|
|
|
|
68
|
1 |
|
$lastModified = filemtime($originalUri); |
69
|
1 |
|
$cacheState = $this->cacheManager->queryCacheState($originalUri); |
70
|
1 |
|
$cacheModified = $cacheState ? $cacheState['filemtime'] : 0; |
71
|
|
|
|
72
|
1 |
|
if ($cacheModified < $lastModified |
73
|
|
|
|| (isset($cacheState['cacheUri']) && $cacheState['cacheUri'] !== $cacheUri) |
74
|
1 |
|
|| !$this->container->isFresh($cacheModified) |
75
|
|
|
) { |
76
|
1 |
|
$processingResult = $this->processTransformers($metadata); |
77
|
1 |
|
if ($processingResult === self::RESULT_TRANSFORMED) { |
78
|
1 |
|
$parentCacheDir = dirname($cacheUri); |
79
|
1 |
|
if (!is_dir($parentCacheDir)) { |
80
|
1 |
|
mkdir($parentCacheDir, $this->cacheFileMode, true); |
81
|
|
|
} |
82
|
1 |
|
file_put_contents($cacheUri, $metadata->source, LOCK_EX); |
83
|
|
|
// For cache files we don't want executable bits by default |
84
|
1 |
|
chmod($cacheUri, $this->cacheFileMode & (~0111)); |
85
|
|
|
} |
86
|
1 |
|
$this->cacheManager->setCacheState($originalUri, [ |
87
|
1 |
|
'filemtime' => $_SERVER['REQUEST_TIME'] ?? time(), |
88
|
1 |
|
'cacheUri' => ($processingResult === self::RESULT_TRANSFORMED) ? $cacheUri : null |
89
|
|
|
]); |
90
|
|
|
|
91
|
1 |
|
return $processingResult; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
if ($cacheState) { |
|
|
|
|
95
|
|
|
$processingResult = isset($cacheState['cacheUri']) ? self::RESULT_TRANSFORMED : self::RESULT_ABORTED; |
96
|
|
|
} |
97
|
|
|
if ($processingResult === self::RESULT_TRANSFORMED) { |
98
|
|
|
// Just replace all tokens in the stream |
99
|
|
|
$metadata->tokenStream = token_get_all(file_get_contents($cacheUri)); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return $processingResult; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Iterates over transformers |
107
|
|
|
* |
108
|
|
|
* @return string See RESULT_XXX constants in the interface |
109
|
|
|
*/ |
110
|
1 |
|
private function processTransformers(StreamMetaData $metadata): string |
111
|
|
|
{ |
112
|
1 |
|
$overallResult = self::RESULT_ABSTAIN; |
113
|
1 |
|
if (is_callable($this->transformers)) { |
114
|
1 |
|
$delayedTransformers = $this->transformers; |
115
|
1 |
|
$this->transformers = $delayedTransformers(); |
116
|
|
|
} |
117
|
1 |
|
foreach ($this->transformers as $transformer) { |
118
|
1 |
|
$transformationResult = $transformer->transform($metadata); |
119
|
1 |
|
if ($overallResult === self::RESULT_ABSTAIN && $transformationResult === self::RESULT_TRANSFORMED) { |
120
|
1 |
|
$overallResult = self::RESULT_TRANSFORMED; |
121
|
|
|
} |
122
|
|
|
// transformer reported about termination, next transformers will be skipped |
123
|
1 |
|
if ($transformationResult === self::RESULT_ABORTED) { |
124
|
|
|
$overallResult = self::RESULT_ABORTED; |
125
|
1 |
|
break; |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
|
129
|
1 |
|
return $overallResult; |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.