Passed
Pull Request — master (#79)
by Rustam
02:37
created

FileStorage::read()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 13
ccs 0
cts 10
cp 0
crap 6
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Storage;
6
7
use Yiisoft\Aliases\Aliases;
8
use Yiisoft\Json\Json;
9
use Yiisoft\VarDumper\VarDumper;
10
use Yiisoft\Yii\Debug\Collector\CollectorInterface;
11
use Yiisoft\Yii\Debug\Collector\IndexCollectorInterface;
12
use Yiisoft\Yii\Debug\DebuggerIdGenerator;
13
use Yiisoft\Yii\Filesystem\FilesystemInterface;
14
15
final class FileStorage implements StorageInterface
16
{
17
    /**
18
     * @var CollectorInterface[]
19
     */
20
    private array $collectors = [];
21
22
    private string $path;
23
24
    private int $historySize = 50;
25
26
    private DebuggerIdGenerator $idGenerator;
27
28
    private FilesystemInterface $filesystem;
29
30
    private Aliases $aliases;
31
32
    public function __construct(
33
        string $path,
34
        FilesystemInterface $filesystem,
35
        DebuggerIdGenerator $idGenerator,
36
        Aliases $aliases
37
    ) {
38
        $this->path = $path;
39
        $this->filesystem = $filesystem;
40
        $this->idGenerator = $idGenerator;
41
        $this->aliases = $aliases;
42
    }
43
44
    public function addCollector(CollectorInterface $collector): void
45
    {
46
        $this->collectors[get_class($collector)] = $collector;
47
    }
48
49
    public function setHistorySize(int $historySize): void
50
    {
51
        $this->historySize = $historySize;
52
    }
53
54
    public function getData(): array
55
    {
56
        $data = [];
57
        foreach ($this->collectors as $collector) {
58
            $data[get_class($collector)] = $collector->getCollected();
59
        }
60
61
        return $data;
62
    }
63
64
    public function read($type = self::TYPE_INDEX): array
65
    {
66
        clearstatcache();
67
        $data = [];
68
        $path = $this->aliases->get($this->path);
69
        $dataFiles = \glob($path . '/**/**/' . $type . '.json', GLOB_NOSORT);
70
        foreach ($dataFiles as $file) {
71
            $dir = \dirname($file);
72
            $id = \substr($dir, \strlen(\dirname($file, 2)) + 1);
73
            $data[$id] = Json::decode(file_get_contents($file));
74
        }
75
76
        return $data;
77
    }
78
79
    public function flush(): void
80
    {
81
        $basePath = $this->path . '/' . date('Y-m-d') . '/' . $this->idGenerator->getId() . '/';
82
        try {
83
            $varDumper = VarDumper::create($this->getData());
84
            $jsonData = $varDumper->asJson();
85
            $this->filesystem->write($basePath . self::TYPE_DATA . '.json', $jsonData);
86
87
            $jsonObjects = $varDumper->asJsonObjectsMap();
88
            $this->filesystem->write($basePath . self::TYPE_OBJECTS . '.json', $jsonObjects);
89
90
            $indexData = VarDumper::create($this->collectIndexData())->asJson();
91
            $this->filesystem->write($basePath . self::TYPE_INDEX . '.json', $indexData);
92
93
            $this->gc();
94
        } finally {
95
            $this->collectors = [];
96
        }
97
    }
98
99
    /**
100
     * Collects summary data of current request.
101
     *
102
     * @return array
103
     */
104
    private function collectIndexData(): array
105
    {
106
        $indexData = ['id' => $this->idGenerator->getId()];
107
108
        foreach ($this->collectors as $collector) {
109
            if ($collector instanceof IndexCollectorInterface) {
110
                $indexData = \array_merge($indexData, $collector->getIndexData());
111
            }
112
        }
113
114
        return $indexData;
115
    }
116
117
    /**
118
     * Removes obsolete data files
119
     *
120
     * @throws \League\Flysystem\FilesystemException
121
     */
122
    private function gc(): void
123
    {
124
        $indexFiles = \glob($this->aliases->get($this->path) . '/**/**/index.json', GLOB_NOSORT);
125
        if (\count($indexFiles) >= $this->historySize + 1) {
0 ignored issues
show
Bug introduced by
It seems like $indexFiles can also be of type false; however, parameter $var of count() does only seem to accept Countable|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

125
        if (\count(/** @scrutinizer ignore-type */ $indexFiles) >= $this->historySize + 1) {
Loading history...
126
            \uasort($indexFiles, static fn ($a, $b) => \filemtime($b) <=> \filemtime($a));
0 ignored issues
show
Bug introduced by
It seems like $indexFiles can also be of type false; however, parameter $array of uasort() 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

126
            \uasort(/** @scrutinizer ignore-type */ $indexFiles, static fn ($a, $b) => \filemtime($b) <=> \filemtime($a));
Loading history...
127
            $excessFiles = \array_slice($indexFiles, $this->historySize);
128
            foreach ($excessFiles as $file) {
129
                $path1 = \dirname($file);
130
                $path2 = \dirname($file, 2);
131
                $path3 = \dirname($file, 3);
132
                $resource = substr($path1, strlen($path3));
133
                $this->filesystem->deleteDirectory($this->path . $resource);
134
135
                // Clean empty group directories
136
                $group = substr($path2, strlen($path3));
137
                $list = $this->filesystem->listContents($this->path . $group);
138
                if (empty($list->toArray())) {
139
                    $this->filesystem->deleteDirectory($this->path . $group);
140
                }
141
            }
142
        }
143
    }
144
}
145