ResourcesFreshnessChecker::isFresh()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.0208

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 11
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 23
ccs 11
cts 12
cp 0.9167
crap 6.0208
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the ekino Drupal Debug project.
7
 *
8
 * (c) ekino
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Ekino\Drupal\Debug\Resource;
15
16
use Ekino\Drupal\Debug\Resource\Model\ResourcesCollection;
17
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
18
use Symfony\Component\Filesystem\Exception\IOException;
19
use Symfony\Component\Filesystem\Filesystem;
20
21
class ResourcesFreshnessChecker
22
{
23
    /**
24
     * @var string
25
     */
26
    private $filePath;
27
28
    /**
29
     * @var ResourcesCollection
30
     */
31
    private $resourcesCollection;
32
33
    /**
34
     * Lazy loaded current resources collection.
35
     *
36
     * Do not use directly, call getCurrentResourcesCollection() method instead.
37
     *
38
     * @var ResourcesCollection|null
39
     */
40
    private $currentResourcesCollection;
41
42
    /**
43
     * @param string              $filePath
44
     * @param ResourcesCollection $resourcesCollection
45
     */
46 74
    public function __construct(string $filePath, ResourcesCollection $resourcesCollection)
47
    {
48 74
        $this->filePath = $filePath;
49 74
        $this->resourcesCollection = $resourcesCollection;
50
51 74
        $this->currentResourcesCollection = null;
52 74
    }
53
54
    /**
55
     * @return ResourcesCollection
56
     */
57 48
    public function getCurrentResourcesCollection(): ResourcesCollection
58
    {
59 48
        if (!$this->currentResourcesCollection instanceof ResourcesCollection) {
60 48
            if (\is_file($this->filePath)) {
61 47
                $currentResourcesSerializedContent = @\file_get_contents($this->filePath);
62 47
                if (false === $currentResourcesSerializedContent) {
63 1
                    throw new \RuntimeException('The current resources serialized content could not be read.');
64
                }
65
66 46
                $this->currentResourcesCollection = \unserialize($currentResourcesSerializedContent);
67 46
                if (!$this->currentResourcesCollection instanceof ResourcesCollection) {
68 46
                    throw new \RuntimeException(\sprintf('The current resources unserialized content class should be "%s".', ResourcesCollection::class));
69
                }
70
            } else {
71 1
                $this->currentResourcesCollection = new ResourcesCollection();
72
            }
73
        }
74
75 46
        return $this->currentResourcesCollection;
76
    }
77
78
    /**
79
     * @return bool
80
     */
81 51
    public function isFresh(): bool
82
    {
83 51
        if (!\is_file($this->filePath)) {
84 10
            return false;
85
        }
86
87 42
        if ($this->didTheResourcesChanged()) {
88 18
            return false;
89
        }
90
91 24
        $time = \filemtime($this->filePath);
92 24
        if (false === $time) {
93
            return false;
94
        }
95
96
        /** @var SelfCheckingResourceInterface $currentResource */
97 24
        foreach ($this->getCurrentResourcesCollection()->all() as $currentResource) {
98 24
            if (!$currentResource->isFresh($time)) {
99 2
                return false;
100
            }
101
        }
102
103 22
        return true;
104
    }
105
106 26
    public function commit(): void
107
    {
108 26
        $umask = \umask();
109 26
        $filesystem = new Filesystem();
110
111 26
        $filesystem->dumpFile($this->filePath, \serialize($this->resourcesCollection));
112
113
        try {
114 26
            $filesystem->chmod($this->filePath, 0666, $umask);
115
        } catch (IOException $e) {
116
            // discard chmod failure (some filesystem may not support it)
117
        }
118
119 26
        if (\function_exists('opcache_invalidate') && \filter_var(\ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
120 26
            @\opcache_invalidate($this->filePath, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for opcache_invalidate(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

120
            /** @scrutinizer ignore-unhandled */ @\opcache_invalidate($this->filePath, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
121
        }
122 26
    }
123
124
    /**
125
     * @return bool
126
     */
127 42
    private function didTheResourcesChanged(): bool
128
    {
129 42
        $currentResourcesCollection = $this->getCurrentResourcesCollection();
130
131 42
        if ($currentResourcesCollection->count() !== $this->resourcesCollection->count()) {
132 1
            return true;
133
        }
134
135 41
        $currentResourcesUniqueRepresentation = $this->getResourcesUniqueRepresentation($currentResourcesCollection);
136 41
        $resourcesUniqueRepresentation = $this->getResourcesUniqueRepresentation($this->resourcesCollection);
137
138 41
        \sort($currentResourcesUniqueRepresentation);
139 41
        \sort($resourcesUniqueRepresentation);
140
141 41
        return $currentResourcesUniqueRepresentation !== $resourcesUniqueRepresentation;
142
    }
143
144
    /**
145
     * @param ResourcesCollection $resourcesCollection
146
     *
147
     * @return string[]
148
     */
149 41
    private function getResourcesUniqueRepresentation(ResourcesCollection $resourcesCollection): array
150
    {
151
        return \array_map(static function (SelfCheckingResourceInterface $resource): string {
152 41
            return \sprintf('%s:%s', \get_class($resource), $resource->__toString());
153 41
        }, $resourcesCollection->all());
154
    }
155
}
156