Passed
Push — master ( 1f8075...93e91b )
by Fabien
02:17
created

CacheProcessFactory::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 13
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Churn\Process;
6
7
use Churn\File\File;
8
use Churn\File\FileHelper;
9
use Churn\Result\Result;
10
use InvalidArgumentException;
11
use Throwable;
12
13
class CacheProcessFactory implements ProcessFactory
14
{
15
16
    /**
17
     * @var string The cache file path.
18
     */
19
    private $cachePath;
20
21
    /**
22
     * @var ProcessFactory Inner process factory.
23
     */
24
    private $processFactory;
25
26
    /**
27
     * @var array<string, array<mixed>> The cached data.
28
     */
29
    private $cache;
30
31
    /**
32
     * @param string $cachePath The cache file path.
33
     * @param ProcessFactory $processFactory Inner process factory.
34
     * @throws InvalidArgumentException If the path is invalid.
35
     */
36
    public function __construct(string $cachePath, ProcessFactory $processFactory)
37
    {
38
        try {
39
            FileHelper::ensureFileIsWritable($cachePath);
40
        } catch (Throwable $e) {
41
            $message = 'Invalid cache file path: ' . $e->getMessage();
42
43
            throw new InvalidArgumentException($message, $e->getCode(), $e);
44
        }
45
46
        $this->cachePath = $cachePath;
47
        $this->processFactory = $processFactory;
48
        $this->cache = $this->loadCache($cachePath);
49
    }
50
51
    /**
52
     * @param File $file File that the processes will execute on.
53
     * @return iterable<ProcessInterface> The list of processes to execute.
54
     */
55
    public function createProcesses(File $file): iterable
56
    {
57
        if (!$this->isCached($file)) {
58
            return $this->processFactory->createProcesses($file);
59
        }
60
61
        $key = $file->getFullPath();
62
63
        $countChanges = (int) $this->cache[$key][1];
64
        $cyclomaticComplexity = (int) $this->cache[$key][2];
65
        $this->cache[$key][3] = true;
66
67
        return [new PredefinedProcess($file, $countChanges, $cyclomaticComplexity)];
68
    }
69
70
    /**
71
     * @param Result $result The result to save.
72
     */
73
    public function addToCache(Result $result): void
74
    {
75
        $path = $result->getFile()->getFullPath();
76
        $this->cache[$path][0] = $this->cache[$path][0] ?? \md5_file($path);
77
        $this->cache[$path][1] = $result->getCommits();
78
        $this->cache[$path][2] = $result->getComplexity();
79
        $this->cache[$path][3] = true;
80
    }
81
82
    /**
83
     * Write the cache in its file.
84
     */
85
    public function writeCache(): void
86
    {
87
        $data = [];
88
89
        foreach ($this->cache as $path => $values) {
90
            if (!$values[3]) {
91
                continue;
92
            }
93
94
            unset($values[3]);
95
            $data[] = \implode(',', \array_merge([$path], $values));
96
        }
97
98
        \file_put_contents($this->cachePath, \implode("\n", $data));
99
    }
100
101
    /**
102
     * @param File $file The file to process.
103
     */
104
    private function isCached(File $file): bool
105
    {
106
        $key = $file->getFullPath();
107
108
        if (!isset($this->cache[$key])) {
109
            return false;
110
        }
111
112
        $md5 = $this->cache[$key][0];
113
        $this->cache[$key][0] = $newMd5 = \md5_file($file->getFullPath());
114
115
        return $md5 === $newMd5;
116
    }
117
118
    /**
119
     * @param string $cachePath Cache file path.
120
     * @return array<string, array<mixed>>
121
     */
122
    private function loadCache(string $cachePath): array
123
    {
124
        if (!\is_file($cachePath)) {
125
            return [];
126
        }
127
128
        $cache = [];
129
130
        foreach (\file($cachePath) as $row) {
131
            $data = \explode(',', $row);
132
            $cache[$data[0]] = \array_slice($data, 1);
133
            $cache[$data[0]][3] = false;
134
        }
135
136
        return $cache;
137
    }
138
}
139