blitz-php /
framework
| 1 | <?php |
||||
| 2 | |||||
| 3 | /** |
||||
| 4 | * This file is part of Blitz PHP framework. |
||||
| 5 | * |
||||
| 6 | * (c) 2022 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\Publisher; |
||||
| 13 | |||||
| 14 | use BlitzPHP\Exceptions\PublisherException; |
||||
| 15 | use BlitzPHP\Filesystem\Files\FileCollection; |
||||
| 16 | use BlitzPHP\Http\Uri; |
||||
| 17 | use RuntimeException; |
||||
| 18 | use Throwable; |
||||
| 19 | |||||
| 20 | /** |
||||
| 21 | * Les éditeurs lisent les chemins d'accès aux fichiers à partir de diverses sources et copient les fichiers vers différentes destinations. |
||||
| 22 | * Cette classe sert à la fois de base pour les directives de publication individuelles et de mode de découverte pour lesdites instances. |
||||
| 23 | * Dans cette classe, un "fichier" est un chemin complet vers un fichier vérifié tandis qu'un "chemin" est relatif à sa source ou à sa destination et peut indiquer soit un fichier, soit un répertoire dont l'existence n'est pas confirmée. |
||||
| 24 | * |
||||
| 25 | * Les échecs de classe lancent l'exception PublisherException, |
||||
| 26 | * mais certaines méthodes sous-jacentes peuvent percoler différentes exceptions, |
||||
| 27 | * comme FileException, FileNotFoundException ou InvalidArgumentException. |
||||
| 28 | * |
||||
| 29 | * Les opérations d'écriture intercepteront toutes les erreurs dans le fichier spécifique |
||||
| 30 | * Propriété $errors pour minimiser l'impact des opérations par lots partielles. |
||||
| 31 | * |
||||
| 32 | * @credit <a href="http://codeigniter.com">CodeIgniter 4 - \CodeIgniter\Publisher\Publisher</a> |
||||
| 33 | */ |
||||
| 34 | class Publisher extends FileCollection |
||||
| 35 | { |
||||
| 36 | /** |
||||
| 37 | * Tableau des éditeurs découverts. |
||||
| 38 | * |
||||
| 39 | * @var array<string, list<self>|null> |
||||
|
0 ignored issues
–
show
Documentation
Bug
introduced
by
Loading history...
|
|||||
| 40 | */ |
||||
| 41 | private static array $discovered = []; |
||||
| 42 | |||||
| 43 | /** |
||||
| 44 | * Répertoire à utiliser pour les méthodes nécessitant un stockage temporaire. |
||||
| 45 | * Créé à la volée selon les besoins. |
||||
| 46 | */ |
||||
| 47 | private ?string $scratch = null; |
||||
| 48 | |||||
| 49 | /** |
||||
| 50 | * Exceptions pour des fichiers spécifiques de la dernière opération d'écriture. |
||||
| 51 | * |
||||
| 52 | * @var array<string, Throwable> |
||||
| 53 | */ |
||||
| 54 | private array $errors = []; |
||||
| 55 | |||||
| 56 | /** |
||||
| 57 | * Liste des fichiers publiés traitant la dernière opération d'écriture. |
||||
| 58 | * |
||||
| 59 | * @var list<string> |
||||
|
0 ignored issues
–
show
The type
BlitzPHP\Publisher\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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||
| 60 | */ |
||||
| 61 | private array $published = []; |
||||
| 62 | |||||
| 63 | /** |
||||
| 64 | * Liste des répertoires autorisés et leur regex de fichiers autorisés. |
||||
| 65 | * Les restrictions sont intentionnellement privées pour éviter qu'elles ne soient dépassées. |
||||
| 66 | * |
||||
| 67 | * @var array<string,string> |
||||
| 68 | */ |
||||
| 69 | private readonly array $restrictions; |
||||
| 70 | |||||
| 71 | private readonly ContentReplacer $replacer; |
||||
| 72 | |||||
| 73 | /** |
||||
| 74 | * Chemin de base à utiliser pour la source. |
||||
| 75 | */ |
||||
| 76 | protected string $source = ROOTPATH; |
||||
| 77 | |||||
| 78 | /** |
||||
| 79 | * Chemin de base à utiliser pour la destination. |
||||
| 80 | */ |
||||
| 81 | protected string $destination = WEBROOT; |
||||
|
0 ignored issues
–
show
|
|||||
| 82 | |||||
| 83 | // -------------------------------------------------------------------- |
||||
| 84 | // Méthodes d'assistance |
||||
| 85 | // -------------------------------------------------------------------- |
||||
| 86 | |||||
| 87 | /** |
||||
| 88 | * Découvre et renvoie tous les éditeurs dans le répertoire d'espace de noms spécifié. |
||||
| 89 | * |
||||
| 90 | * @return list<self> |
||||
| 91 | */ |
||||
| 92 | final public static function discover(string $directory = 'Publishers', string $namespace = ''): array |
||||
| 93 | { |
||||
| 94 | 4 | $key = implode('.', [$directory, $namespace]); |
|||
| 95 | |||||
| 96 | if (isset(self::$discovered[$key])) { |
||||
| 97 | 4 | return self::$discovered[$key]; |
|||
| 98 | } |
||||
| 99 | |||||
| 100 | 4 | self::$discovered[$key] = []; |
|||
| 101 | |||||
| 102 | /** @var \BlitzPHP\Contracts\Autoloader\LocatorInterface */ |
||||
| 103 | 4 | $locator = service('locator'); |
|||
| 104 | |||||
| 105 | $files = $namespace === '' |
||||
| 106 | ? $locator->listFiles($directory) |
||||
| 107 | 2 | : $locator->listNamespaceFiles($namespace, $directory); |
|||
| 108 | |||||
| 109 | if ([] === $files) { |
||||
| 110 | 2 | return []; |
|||
|
0 ignored issues
–
show
|
|||||
| 111 | } |
||||
| 112 | |||||
| 113 | // Boucle sur chaque fichier en vérifiant s'il s'agit d'un Publisher |
||||
| 114 | foreach (array_unique($files) as $file) { |
||||
| 115 | 4 | $className = $locator->findQualifiedNameFromPath($file); |
|||
| 116 | |||||
| 117 | if ($className !== false && class_exists($className) && is_a($className, self::class, true)) { |
||||
| 118 | 4 | self::$discovered[$key][] = service('factory', $className); |
|||
| 119 | } |
||||
| 120 | } |
||||
| 121 | |||||
| 122 | 4 | sort(self::$discovered[$key]); |
|||
| 123 | |||||
| 124 | 4 | return self::$discovered[$key]; |
|||
| 125 | } |
||||
| 126 | |||||
| 127 | /** |
||||
| 128 | * Supprime un répertoire et tous ses fichiers et sous-répertoires. |
||||
| 129 | */ |
||||
| 130 | private static function wipeDirectory(string $directory): void |
||||
| 131 | { |
||||
| 132 | if (is_dir($directory)) { |
||||
| 133 | // Essayez plusieurs fois en cas de mèches persistantes |
||||
| 134 | 6 | $attempts = 10; |
|||
| 135 | |||||
| 136 | while ((bool) $attempts && ! delete_files($directory, true, false, true)) { |
||||
| 137 | // @codeCoverageIgnoreStart |
||||
| 138 | $attempts--; |
||||
| 139 | usleep(100000); // .1s |
||||
| 140 | // @codeCoverageIgnoreEnd |
||||
| 141 | } |
||||
| 142 | |||||
| 143 | 6 | @rmdir($directory); |
|||
|
0 ignored issues
–
show
It seems like you do not handle an error condition for
rmdir(). 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
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...
|
|||||
| 144 | } |
||||
| 145 | } |
||||
| 146 | |||||
| 147 | /** |
||||
| 148 | * Charge l'assistant et vérifie les répertoires source et destination. |
||||
| 149 | */ |
||||
| 150 | public function __construct(?string $source = null, ?string $destination = null) |
||||
| 151 | { |
||||
| 152 | 12 | helper('filesystem'); |
|||
| 153 | |||||
| 154 | 12 | $this->source = self::resolveDirectory($source ?? $this->source); |
|||
| 155 | 12 | $this->destination = self::resolveDirectory($destination ?? $this->destination); |
|||
| 156 | |||||
| 157 | 12 | $this->replacer = new ContentReplacer(); |
|||
|
0 ignored issues
–
show
|
|||||
| 158 | |||||
| 159 | // Les restrictions ne sont intentionnellement pas injectées pour empêcher le dépassement |
||||
| 160 | 12 | $this->restrictions = config('publisher.restrictions'); |
|||
|
0 ignored issues
–
show
It seems like
config('publisher.restrictions') can also be of type T or object. However, the property $restrictions is declared as type array<string,string>. Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
|||||
| 161 | |||||
| 162 | // Assurez-vous que la destination est autorisée |
||||
| 163 | foreach (array_keys($this->restrictions) as $directory) { |
||||
| 164 | if (str_starts_with($this->destination, $directory)) { |
||||
| 165 | 12 | return; |
|||
| 166 | } |
||||
| 167 | } |
||||
| 168 | |||||
| 169 | 2 | throw PublisherException::destinationNotAllowed($this->destination); |
|||
| 170 | } |
||||
| 171 | |||||
| 172 | /** |
||||
| 173 | * Nettoie tous les fichiers temporaires dans l'espace de travail. |
||||
| 174 | */ |
||||
| 175 | public function __destruct() |
||||
| 176 | { |
||||
| 177 | if (isset($this->scratch)) { |
||||
| 178 | 6 | self::wipeDirectory($this->scratch); |
|||
|
0 ignored issues
–
show
It seems like
$this->scratch can also be of type null; however, parameter $directory of BlitzPHP\Publisher\Publisher::wipeDirectory() 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
Loading history...
|
|||||
| 179 | |||||
| 180 | 6 | $this->scratch = null; |
|||
| 181 | } |
||||
| 182 | } |
||||
| 183 | |||||
| 184 | /** |
||||
| 185 | * Lit les fichiers à partir des sources et les copie vers leurs destinations. |
||||
| 186 | * Cette méthode devrait être réimplémentée par les classes filles destinées à la découverte. |
||||
| 187 | * |
||||
| 188 | * @throws RuntimeException |
||||
| 189 | */ |
||||
| 190 | public function publish(): bool |
||||
| 191 | { |
||||
| 192 | // Protection contre une mauvaise utilisation accidentelle |
||||
| 193 | if ($this->source === ROOTPATH && $this->destination === WEBROOT) { |
||||
|
0 ignored issues
–
show
|
|||||
| 194 | 2 | throw new RuntimeException('Les classes enfants de Publisher doivent fournir leur propre méthode de publication ou une source et une destination.'); |
|||
| 195 | } |
||||
| 196 | |||||
| 197 | 2 | return $this->addPath('/')->merge(true); |
|||
| 198 | } |
||||
| 199 | |||||
| 200 | // -------------------------------------------------------------------- |
||||
| 201 | // Accesseurs de propriété |
||||
| 202 | // -------------------------------------------------------------------- |
||||
| 203 | |||||
| 204 | /** |
||||
| 205 | * Renvoie le répertoire source. |
||||
| 206 | */ |
||||
| 207 | final public function getSource(): string |
||||
| 208 | { |
||||
| 209 | 2 | return $this->source; |
|||
| 210 | } |
||||
| 211 | |||||
| 212 | /** |
||||
| 213 | * Renvoie le répertoire de destination. |
||||
| 214 | */ |
||||
| 215 | final public function getDestination(): string |
||||
| 216 | { |
||||
| 217 | 4 | return $this->destination; |
|||
| 218 | } |
||||
| 219 | |||||
| 220 | /** |
||||
| 221 | * Renvoie l'espace de travail temporaire, en le créant si nécessaire. |
||||
| 222 | */ |
||||
| 223 | final public function getScratch(): string |
||||
| 224 | { |
||||
| 225 | if ($this->scratch === null) { |
||||
| 226 | 6 | $this->scratch = rtrim(sys_get_temp_dir(), DS) . DS . bin2hex(random_bytes(6)) . DS; |
|||
| 227 | 6 | mkdir($this->scratch, 0o700); |
|||
|
0 ignored issues
–
show
$this->scratch of type null is incompatible with the type string expected by parameter $directory of mkdir().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 228 | $this->scratch = realpath($this->scratch) ? realpath($this->scratch) . DS |
||||
|
0 ignored issues
–
show
$this->scratch of type null is incompatible with the type string expected by parameter $path of realpath().
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 229 | : $this->scratch; |
||||
| 230 | } |
||||
| 231 | |||||
| 232 | 6 | return $this->scratch; |
|||
|
0 ignored issues
–
show
|
|||||
| 233 | } |
||||
| 234 | |||||
| 235 | /** |
||||
| 236 | * Renvoie les erreurs de la dernière opération d'écriture, le cas échéant. |
||||
| 237 | * |
||||
| 238 | * @return array<string,Throwable> |
||||
| 239 | */ |
||||
| 240 | final public function getErrors(): array |
||||
| 241 | { |
||||
| 242 | 8 | return $this->errors; |
|||
| 243 | } |
||||
| 244 | |||||
| 245 | /** |
||||
| 246 | * Renvoie les fichiers publiés par la dernière opération d'écriture. |
||||
| 247 | * |
||||
| 248 | * @return list<string> |
||||
| 249 | */ |
||||
| 250 | final public function getPublished(): array |
||||
| 251 | { |
||||
| 252 | 4 | return $this->published; |
|||
|
0 ignored issues
–
show
|
|||||
| 253 | } |
||||
| 254 | |||||
| 255 | // -------------------------------------------------------------------- |
||||
| 256 | // Gestionnaires supplémentaires |
||||
| 257 | // -------------------------------------------------------------------- |
||||
| 258 | |||||
| 259 | /** |
||||
| 260 | * Vérifie et ajoute des chemins à la liste. |
||||
| 261 | * |
||||
| 262 | * @param list<string> $paths |
||||
| 263 | */ |
||||
| 264 | final public function addPaths(array $paths, bool $recursive = true): static |
||||
| 265 | { |
||||
| 266 | foreach ($paths as $path) { |
||||
| 267 | 2 | $this->addPath($path, $recursive); |
|||
| 268 | } |
||||
| 269 | |||||
| 270 | 2 | return $this; |
|||
| 271 | } |
||||
| 272 | |||||
| 273 | /** |
||||
| 274 | * Ajoute un chemin unique à la liste de fichiers. |
||||
| 275 | */ |
||||
| 276 | final public function addPath(string $path, bool $recursive = true): static |
||||
| 277 | { |
||||
| 278 | 6 | $this->add($this->source . $path, $recursive); |
|||
| 279 | |||||
| 280 | 6 | return $this; |
|||
| 281 | } |
||||
| 282 | |||||
| 283 | /** |
||||
| 284 | * Télécharge et met en scène des fichiers à partir d'un tableau d'URI. |
||||
| 285 | * |
||||
| 286 | * @param list<string> $uris |
||||
| 287 | */ |
||||
| 288 | final public function addUris(array $uris): static |
||||
| 289 | { |
||||
| 290 | foreach ($uris as $uri) { |
||||
| 291 | 2 | $this->addUri($uri); |
|||
| 292 | } |
||||
| 293 | |||||
| 294 | 2 | return $this; |
|||
| 295 | } |
||||
| 296 | |||||
| 297 | /** |
||||
| 298 | * Télécharge un fichier à partir de l'URI et l'ajoute à la liste des fichiers. |
||||
| 299 | * |
||||
| 300 | * @param string $uri Parce que HTTP\URI est stringable, il sera toujours accepté |
||||
| 301 | */ |
||||
| 302 | final public function addUri(string $uri): static |
||||
| 303 | { |
||||
| 304 | // Trouvez un bon nom de fichier (en utilisant des requêtes et des fragments de bandes d'URI) |
||||
| 305 | 2 | $file = $this->getScratch() . basename((new Uri($uri))->getPath()); |
|||
| 306 | |||||
| 307 | // Obtenez le contenu et écrivez-le dans l'espace de travail |
||||
| 308 | 2 | write_file($file, service('httpclient')->get($uri)->body()); |
|||
| 309 | |||||
| 310 | 2 | return $this->addFile($file); |
|||
| 311 | } |
||||
| 312 | |||||
| 313 | // -------------------------------------------------------------------- |
||||
| 314 | // Méthodes d'écriture |
||||
| 315 | // -------------------------------------------------------------------- |
||||
| 316 | |||||
| 317 | /** |
||||
| 318 | * Supprime la destination et tous ses fichiers et dossiers. |
||||
| 319 | */ |
||||
| 320 | final public function wipe(): static |
||||
| 321 | { |
||||
| 322 | 2 | self::wipeDirectory($this->destination); |
|||
| 323 | |||||
| 324 | 2 | return $this; |
|||
| 325 | } |
||||
| 326 | |||||
| 327 | /** |
||||
| 328 | * Copie tous les fichiers dans la destination, ne crée pas de structure de répertoire. |
||||
| 329 | * |
||||
| 330 | * @param bool $replace S'il faut écraser les fichiers existants. |
||||
| 331 | * |
||||
| 332 | * @return bool Si tous les fichiers ont été copiés avec succès |
||||
| 333 | */ |
||||
| 334 | final public function copy(bool $replace = true): bool |
||||
| 335 | { |
||||
| 336 | 4 | $this->errors = $this->published = []; |
|||
|
0 ignored issues
–
show
It seems like
array() of type array is incompatible with the declared type BlitzPHP\Publisher\list of property $published.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||||
| 337 | |||||
| 338 | foreach ($this->get() as $file) { |
||||
| 339 | 4 | $to = $this->destination . basename($file); |
|||
| 340 | |||||
| 341 | try { |
||||
| 342 | 4 | $this->safeCopyFile($file, $to, $replace); |
|||
| 343 | 2 | $this->published[] = $to; |
|||
| 344 | } catch (Throwable $e) { |
||||
| 345 | 4 | $this->errors[$file] = $e; |
|||
| 346 | } |
||||
| 347 | } |
||||
| 348 | |||||
| 349 | 4 | return $this->errors === []; |
|||
| 350 | } |
||||
| 351 | |||||
| 352 | /** |
||||
| 353 | * Fusionne tous les fichiers dans la destination. |
||||
| 354 | * Crée une structure de répertoires en miroir uniquement pour les fichiers de la source. |
||||
| 355 | * |
||||
| 356 | * @param bool $replace Indique s'il faut écraser les fichiers existants. |
||||
| 357 | * |
||||
| 358 | * @return bool Si tous les fichiers ont été copiés avec succès |
||||
| 359 | */ |
||||
| 360 | final public function merge(bool $replace = true): bool |
||||
| 361 | { |
||||
| 362 | 4 | $this->errors = $this->published = []; |
|||
|
0 ignored issues
–
show
It seems like
array() of type array is incompatible with the declared type BlitzPHP\Publisher\list of property $published.
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. Loading history...
|
|||||
| 363 | |||||
| 364 | // Obtenez les fichiers de la source pour un traitement spécial |
||||
| 365 | 4 | $sourced = self::filterFiles($this->get(), $this->source); |
|||
| 366 | |||||
| 367 | // Obtenez les fichiers de la source pour un traitement spécial |
||||
| 368 | 4 | $this->files = array_diff($this->files, $sourced); |
|||
| 369 | 4 | $this->copy($replace); |
|||
| 370 | |||||
| 371 | // Copiez chaque fichier source vers sa destination relative |
||||
| 372 | foreach ($sourced as $file) { |
||||
| 373 | // Résoudre le chemin de destination |
||||
| 374 | 2 | $to = $this->destination . substr($file, strlen($this->source)); |
|||
| 375 | |||||
| 376 | try { |
||||
| 377 | 2 | $this->safeCopyFile($file, $to, $replace); |
|||
| 378 | 2 | $this->published[] = $to; |
|||
| 379 | } catch (Throwable $e) { |
||||
| 380 | 2 | $this->errors[$file] = $e; |
|||
| 381 | } |
||||
| 382 | } |
||||
| 383 | |||||
| 384 | 4 | return $this->errors === []; |
|||
| 385 | } |
||||
| 386 | |||||
| 387 | /** |
||||
| 388 | * Remplacer le contenu |
||||
| 389 | * |
||||
| 390 | * @param array $replaces [search => replace] |
||||
| 391 | */ |
||||
| 392 | public function replace(string $file, array $replaces): bool |
||||
| 393 | { |
||||
| 394 | 2 | $this->verifyAllowed($file, $file); |
|||
| 395 | |||||
| 396 | 2 | $content = file_get_contents($file); |
|||
| 397 | |||||
| 398 | 2 | $newContent = $this->replacer->replace($content, $replaces); |
|||
| 399 | |||||
| 400 | 2 | $return = file_put_contents($file, $newContent); |
|||
| 401 | |||||
| 402 | 2 | return $return !== false; |
|||
| 403 | } |
||||
| 404 | |||||
| 405 | /** |
||||
| 406 | * Ajouter une ligne après la ligne avec la chaîne |
||||
| 407 | * |
||||
| 408 | * @param string $after Chaîne à rechercher. |
||||
| 409 | */ |
||||
| 410 | public function addLineAfter(string $file, string $line, string $after): bool |
||||
| 411 | { |
||||
| 412 | 2 | $this->verifyAllowed($file, $file); |
|||
| 413 | |||||
| 414 | 2 | $content = file_get_contents($file); |
|||
| 415 | |||||
| 416 | 2 | $result = $this->replacer->addAfter($content, $line, $after); |
|||
| 417 | |||||
| 418 | if ($result !== null) { |
||||
| 419 | 2 | $return = file_put_contents($file, $result); |
|||
| 420 | |||||
| 421 | 2 | return $return !== false; |
|||
| 422 | } |
||||
| 423 | |||||
| 424 | return false; |
||||
| 425 | } |
||||
| 426 | |||||
| 427 | /** |
||||
| 428 | * Ajouter une ligne avant la ligne avec la chaîne |
||||
| 429 | * |
||||
| 430 | * @param string $before String à rechercher. |
||||
| 431 | */ |
||||
| 432 | public function addLineBefore(string $file, string $line, string $before): bool |
||||
| 433 | { |
||||
| 434 | 2 | $this->verifyAllowed($file, $file); |
|||
| 435 | |||||
| 436 | 2 | $content = file_get_contents($file); |
|||
| 437 | |||||
| 438 | 2 | $result = $this->replacer->addBefore($content, $line, $before); |
|||
| 439 | |||||
| 440 | if ($result !== null) { |
||||
| 441 | 2 | $return = file_put_contents($file, $result); |
|||
| 442 | |||||
| 443 | 2 | return $return !== false; |
|||
| 444 | } |
||||
| 445 | |||||
| 446 | return false; |
||||
| 447 | } |
||||
| 448 | |||||
| 449 | /** |
||||
| 450 | * Vérifiez qu'il s'agit d'un fichier autorisé pour sa destination |
||||
| 451 | */ |
||||
| 452 | private function verifyAllowed(string $from, string $to) |
||||
| 453 | { |
||||
| 454 | // Vérifiez qu'il s'agit d'un fichier autorisé pour sa destination |
||||
| 455 | foreach ($this->restrictions as $directory => $pattern) { |
||||
| 456 | if (str_starts_with($to, $directory) && self::matchFiles([$to], $pattern) === []) { |
||||
| 457 | 2 | throw PublisherException::fileNotAllowed($from, $directory, $pattern); |
|||
| 458 | } |
||||
| 459 | } |
||||
| 460 | } |
||||
| 461 | |||||
| 462 | /** |
||||
| 463 | * Copie un fichier avec création de répertoire et reconnaissance de fichier identique. |
||||
| 464 | * Permet intentionnellement des erreurs. |
||||
| 465 | * |
||||
| 466 | * @throws PublisherException Pour les collisions et les violations de restriction |
||||
| 467 | */ |
||||
| 468 | private function safeCopyFile(string $from, string $to, bool $replace): void |
||||
| 469 | { |
||||
| 470 | // Vérifiez qu'il s'agit d'un fichier autorisé pour sa destination |
||||
| 471 | 4 | $this->verifyAllowed($from, $to); |
|||
| 472 | |||||
| 473 | // Rechercher un fichier existant |
||||
| 474 | if (file_exists($to)) { |
||||
| 475 | // S'il n'est pas remplacé ou si les fichiers sont identiques, envisagez de réussir |
||||
| 476 | if (! $replace || same_file($from, $to)) { |
||||
| 477 | 2 | return; |
|||
| 478 | } |
||||
| 479 | |||||
| 480 | // S'il s'agit d'un répertoire, n'essayez pas de le supprimer |
||||
| 481 | if (is_dir($to)) { |
||||
| 482 | 2 | throw PublisherException::collision($from, $to); |
|||
| 483 | } |
||||
| 484 | |||||
| 485 | // Essayez de supprimer autre chose |
||||
| 486 | 2 | unlink($to); |
|||
| 487 | } |
||||
| 488 | |||||
| 489 | // Assurez-vous que le répertoire existe |
||||
| 490 | if (! is_dir($directory = pathinfo($to, PATHINFO_DIRNAME))) { |
||||
|
0 ignored issues
–
show
It seems like
$directory = pathinfo($t...isher\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
Loading history...
|
|||||
| 491 | 2 | mkdir($directory, 0o775, true); |
|||
|
0 ignored issues
–
show
It seems like
$directory can also be of type array; however, parameter $directory of mkdir() 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
Loading history...
|
|||||
| 492 | } |
||||
| 493 | |||||
| 494 | // Autoriser copy() à générer des erreurs |
||||
| 495 | 2 | copy($from, $to); |
|||
| 496 | } |
||||
| 497 | } |
||||
| 498 |