Completed
Push — master ( efe256...811ea7 )
by Alexander
11s
created

CachingTransformer   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 82.98%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 4
dl 0
loc 112
ccs 39
cts 47
cp 0.8298
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
C transform() 0 46 13
B processTransformers() 0 21 6
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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cacheState of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
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