Passed
Push — master ( f7ef7a...65c26d )
by Dmitriy
02:46
created

FileStorage::collectSummaryData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 16
rs 10
cc 3
nc 3
nop 0
ccs 5
cts 5
cp 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Storage;
6
7
use Yiisoft\Aliases\Aliases;
8
use Yiisoft\Files\FileHelper;
9
use Yiisoft\Json\Json;
10
use Yiisoft\Yii\Debug\Collector\CollectorInterface;
11
use Yiisoft\Yii\Debug\Collector\SummaryCollectorInterface;
12
use Yiisoft\Yii\Debug\DebuggerIdGenerator;
13
use Yiisoft\Yii\Debug\Dumper;
14
15
use function array_merge;
16
use function array_slice;
17
use function count;
18
use function dirname;
19
use function filemtime;
20
use function glob;
21
use function strlen;
22
use function substr;
23
use function uasort;
24
25
final class FileStorage implements StorageInterface
26
{
27
    /**
28
     * @var CollectorInterface[]
29
     */
30
    private array $collectors = [];
31
32
    private int $historySize = 50;
33
34
    public function __construct(
35
        private string $path,
36
        private DebuggerIdGenerator $idGenerator,
37
        private Aliases $aliases,
38
        private array $excludedClasses = []
39
    ) {
40
        $this->path = $this->aliases->get($this->path);
41
    }
42
43
    public function addCollector(CollectorInterface $collector): void
44
    {
45 40
        $this->collectors[$collector->getName()] = $collector;
46
    }
47
48
    public function setHistorySize(int $historySize): void
49
    {
50
        $this->historySize = $historySize;
51
    }
52 40
53 40
    public function read($type = self::TYPE_SUMMARY): array
54 40
    {
55 40
        clearstatcache();
56 40
        $data = [];
57
        $dataFiles = glob($this->path . '/**/**/' . $type . '.json', GLOB_NOSORT);
58
        uasort($dataFiles, static fn ($a, $b) => filemtime($a) <=> filemtime($b));
59 40
60
        foreach ($dataFiles as $file) {
61 40
            $dir = dirname($file);
62
            $id = substr($dir, strlen(dirname($file, 2)) + 1);
63
            $data[$id] = Json::decode(file_get_contents($file));
64 8
        }
65
66 8
        return $data;
67
    }
68
69 16
    public function flush(): void
70
    {
71 16
        $basePath = $this->path . '/' . date('Y-m-d') . '/' . $this->idGenerator->getId() . '/';
72 16
73 16
        try {
74 16
            FileHelper::ensureDirectory($basePath);
75 16
            $dumper = Dumper::create($this->getData(), $this->excludedClasses);
76
            $jsonData = $dumper->asJson();
77 16
            file_put_contents($basePath . self::TYPE_DATA . '.json', $jsonData);
78
79
            $jsonObjects = Json::decode($dumper->asJsonObjectsMap());
80
            $jsonObjects = $this->reindexObjects($jsonObjects);
0 ignored issues
show
Bug introduced by
It seems like $jsonObjects can also be of type null; however, parameter $objectsAsArraysCollection of Yiisoft\Yii\Debug\Storag...orage::reindexObjects() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

80
            $jsonObjects = $this->reindexObjects(/** @scrutinizer ignore-type */ $jsonObjects);
Loading history...
81
            file_put_contents($basePath . self::TYPE_OBJECTS . '.json', Dumper::create($jsonObjects)->asJson());
82
83 16
            $summaryData = Dumper::create($this->collectSummaryData())->asJson();
84
            file_put_contents($basePath . self::TYPE_SUMMARY . '.json', $summaryData);
85
86 32
            $this->gc();
87
        } finally {
88 32
            $this->collectors = [];
89
        }
90 32
    }
91 32
92 32
    public function getData(): array
93
    {
94 32
        $data = [];
95 32
        foreach ($this->collectors as $name => $collector) {
96 32
            $data[$name] = $collector->getCollected();
97
        }
98 32
99 32
        return $data;
100
    }
101 32
102 32
    public function clear(): void
103 32
    {
104
        FileHelper::removeDirectory($this->path);
105
    }
106
107 40
    /**
108
     * Collects summary data of current request.
109 40
     */
110 40
    private function collectSummaryData(): array
111 40
    {
112
        $summaryData = [
113
            [
114 40
                'id' => $this->idGenerator->getId(),
115
                'collectors' => array_keys($this->collectors),
116
            ],
117 8
        ];
118
119 8
        foreach ($this->collectors as $collector) {
120
            if ($collector instanceof SummaryCollectorInterface) {
121
                $summaryData[] = $collector->getSummary();
122
            }
123
        }
124
125
        return array_merge(...$summaryData);
126
    }
127 32
128
    /**
129 32
     * Removes obsolete data files
130 32
     */
131 32
    private function gc(): void
132
    {
133
        $summaryFiles = glob($this->path . '/**/**/sumamry.json', GLOB_NOSORT);
134 32
        if ((is_countable($summaryFiles) ? count($summaryFiles) : 0) >= $this->historySize + 1) {
135 32
            uasort($summaryFiles, static fn ($a, $b) => filemtime($b) <=> filemtime($a));
136
            $excessFiles = array_slice($summaryFiles, $this->historySize);
137
            foreach ($excessFiles as $file) {
138
                $path1 = dirname($file);
139
                $path2 = dirname($file, 2);
140 32
                $path3 = dirname($file, 3);
141
                $resource = substr($path1, strlen($path3));
142
143
144
                FileHelper::removeDirectory($this->path . $resource);
145
146
                // Clean empty group directories
147
                $group = substr($path2, strlen($path3));
148 32
                if (FileHelper::isEmptyDirectory($this->path . $group)) {
149
                    FileHelper::removeDirectory($this->path . $group);
150 32
                }
151 32
            }
152
        }
153
    }
154
155
    private function reindexObjects(array $objectsAsArraysCollection): array
156
    {
157
        $toMerge = [];
158
        foreach ($objectsAsArraysCollection as $objectAsArray) {
159
            $toMerge[] = $objectAsArray;
160
        }
161
162
        return array_merge(...$toMerge);
163
    }
164
}
165