Issues (27)

src/Handlers/FileHandler.php (2 issues)

1
<?php
2
3
/**
4
 * This file is part of BlitzPHP Parametres.
5
 *
6
 * (c) 2025 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Parametres\Handlers;
13
14
use BlitzPHP\Parametres\Exceptions\ParametresException;
15
use BlitzPHP\Utilities\Date;
16
use BlitzPHP\Utilities\Iterable\Collection;
17
18
class FileHandler extends ArrayHandler
19
{
20
    /**
21
     * Chemin d'accès du fichier de stockage des paramètres
22
     */
23
    private string $path;
24
25
    /**
26
     * Tableau des contextes qui ont été stockés.
27
     *
28
     * @var list<null>|list<string>
0 ignored issues
show
The type BlitzPHP\Parametres\Handlers\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
     */
30
    private array $hydrated = [];
31
32
    /**
33
     * @param array<string,mixed> $config
34
     */
35
    public function __construct(array $config = [])
36
    {
37
        if ($config === []) {
38
            $config = config('parametres.file', []);
39
        }
40
41
        if ('' === $this->path = ($config['path'] ?? '')) {
42
            throw ParametresException::fileForStorageNotDefined();
43
        }
44
        if (! is_dir(pathinfo($this->path, PATHINFO_DIRNAME))) {
0 ignored issues
show
It seems like pathinfo($this->path, Bl...dlers\PATHINFO_DIRNAME) can also be of type array; however, parameter $filename of is_dir() does only seem to accept string, 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

44
        if (! is_dir(/** @scrutinizer ignore-type */ pathinfo($this->path, PATHINFO_DIRNAME))) {
Loading history...
45
            throw ParametresException::directoryOfFileNotFound($this->path);
46
        }
47
        if (! file_exists($this->path)) {
48
            file_put_contents($this->path, '[]');
49
        }
50
    }
51
52
    /**
53
     * {@inheritDoc}
54
     */
55
    public function has(string $file, string $property, ?string $context = null): bool
56
    {
57
        $this->hydrate($context);
58
59
        return $this->hasStored($file, $property, $context);
60
    }
61
62
    /**
63
     * {@inheritDoc}
64
     */
65
    public function set(string $file, string $property, mixed $value = null, ?string $context = null): void
66
    {
67
        $time     = Date::now()->format('Y-m-d H:i:s');
68
        $type     = gettype($value);
69
        $prepared = $this->prepareValue($value);
70
71
        $data = $this->getData();
72
73
        // S'il a été stocké, nous devons le mettre à jour
74
        if ($this->has($file, $property, $context)) {
75
            $updated = $data->where('file', $file)->where('key', $property)->whereStrict('context', $context)->first();
76
77
            $data = $data->map(fn ($item) => $item['id'] !== $updated['id'] ? $item : array_merge($item, [
78
                'value'      => $prepared,
79
                'type'       => $type,
80
                'context'    => $context,
81
                'updated_at' => $time,
82
            ]));
83
            // ...sinon l'insérer
84
        } else {
85
            $data = $data->add([
86
                'id'         => uniqid(more_entropy: true),
87
                'file'       => $file,
88
                'key'        => $property,
89
                'value'      => $prepared,
90
                'type'       => $type,
91
                'context'    => $context,
92
                'created_at' => $time,
93
                'updated_at' => $time,
94
            ]);
95
        }
96
97
        $this->saveDate($data);
98
99
        // Modifier dans la memoire locale
100
        $this->setStored($file, $property, $value, $context);
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106
    public function forget(string $file, string $property, ?string $context = null): void
107
    {
108
        $this->hydrate($context);
109
110
        $data = $this->getData();
111
112
        $deleted = $data->where('file', $file)->where('key', $property)->whereStrict('context', $context)->first();
113
        $data    = $data->filter(fn ($item) => $item['id'] !== $deleted['id']);
114
115
        $this->saveDate($data);
116
117
        // Supprimer dans la mémoire locale
118
        $this->forgetStored($file, $property, $context);
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124
    public function flush(): void
125
    {
126
        $this->saveDate(collect([]));
127
128
        parent::flush();
129
    }
130
131
    /**
132
     * Récupère les valeurs de la base de données en vrac pour minimiser les appels.
133
     * Le général (null) est toujours récupéré une fois, les contextes sont récupérés dans leur intégralité pour chaque nouvelle requête.
134
     */
135
    private function hydrate(?string $context = null): void
136
    {
137
        // Vérification de l'achèvement des travaux
138
        if (in_array($context, $this->hydrated, true)) {
139
            return;
140
        }
141
142
        $data = $this->getData();
143
144
        if ($context === null) {
145
            $this->hydrated[] = null;
146
            $data             = $data->whereNull('context');
147
        } else {
148
            // Si le général n'a pas été hydraté, on l'hydrate donc.
149
            if (! in_array(null, $this->hydrated, true)) {
150
                $this->hydrated[] = null;
151
            } else {
152
                $data = $data->where('context', $context);
153
            }
154
155
            $this->hydrated[] = $context;
156
        }
157
158
        foreach ($data->all() as $row) {
159
            $this->setStored($row['file'], $row['key'], $this->parseValue($row['value'], $row['type']), $row['context']);
160
        }
161
    }
162
163
    /**
164
     * Recupère les données à partir du fichier servant de source de données
165
     */
166
    private function getData(): Collection
167
    {
168
        $data = json_decode(file_get_contents($this->path), true) ?: [];
169
170
        return collect($data);
171
    }
172
173
    /**
174
     * Persiste les données dans le fichier servant de source de données
175
     */
176
    private function saveDate(Collection $data): void
177
    {
178
        $data = $data->toArray();
179
180
        file_put_contents($this->path, json_encode($data, JSON_PRETTY_PRINT));
181
    }
182
}
183