Maison > Questions et réponses > le corps du texte
P粉3511384622023-09-03 09:03:54
Voici la documentation de require
的标准行为,与 include
, le comportement est le même entre les deux :
Comme vous pouvez le voir, lorsque la valeur de retour n'est pas écrasée, un entier (1) est renvoyé sur le chemin heureux.
Cela a du sens pour votre exemple, jusqu'à présent le fichier existe (donc pas d'erreur fatale), mais comme le fichier vient d'être créé, il se peut qu'il soit simplement tronqué, c'est-à-dire qu'il est vide. p>
Ainsi, la valeur de retour n'est pas écrasée et vous pouvez voir le int(1).
Une autre explication est naturellement que vous avez écrasé avec des nombres entiers, ce qui est également possible puisque plusieurs processus peuvent écrire dans le même fichier, mais avec la façon dont vous avez écrit votre exemple, c'est moins probable. Je le mentionne seulement parce que c'est une autre explication valable.
Exemple comment suspendre une condition de concurrence critique lorsque vous recherchez $result
au lieu de (uniquement) lorsque le fichier existe :
if (($result = @include($cachePath)) && is_array($result) ) { # $result is array, which is required # ... }
L'idée derrière cela est que nous faisons très peu de gestion des erreurs, comme vérifier si le fichier existe, sinon il ne peut pas être inclus (include() émet simplement un avertissement et le transmet avec $result = false), et ensuite si $ le résultat est chargé, il le fait. S'applique aux tests is_array().
C'est ce que nous avons pour les erreurs, mais nous savons ce que nous recherchons, c'est-à-dire que $result est un tableau.
C'est ce qu'on appelle souvent une transaction ou une opération de transaction.
Dans ce nouvel exemple, nous n'entrons même pas le if-body lorsque le tableau $result est vide, c'est-à-dire qu'il ne contient aucune donnée.
Au niveau du traitement du programme, qui pourrait nous intéresser, la présence ou l'absence d'un fichier, être vide ou non, ou même être mal écrit sont autant de conditions d'erreur qui doivent être "mangées" et invalider le $result.
L'erreur de définition n'existe pas.
Depuis PHP 7.0, nous pouvons utiliser include() et si malheureusement le fichier include renvoyé est à moitié écrit, nous verrons une erreur d'analyse PHP qui peut être attrapée :
# start the transaction $result = null; assert( is_string($cachePath) && # pathnames are strings, '' !== $cachePath && # never empty, false === strpos($cachePath, "rrreee") # and must not contain null-bytes ); try { if (file_exists($cachePath)) { $result = include($cachePath); } # invalidate $result in case include() did technically work. if (!$result || !is_array($result) { $result = null; } } catch (Throwable $t) { # catch all errors and exceptions, # the fall-through is intended to invalidate $result. $result = null; } finally { # $result is not null, but a non-empty array if it worked. # $result is null, if it could not be acquired. }
Veuillez vous référer à PHP try-catch-finally pour savoir comment lancer des exceptions/travail de gestion des exceptions en détail, assert() est utilisé pour enregistrer la signification du paramètre d'entrée $cachePath dans l'exemple.
Le deuxième exemple n'utilise pas l'opération de suppression "@", la raison est que si vous l'utilisez comme l'exemple précédent, et que le fichier à inclure contiendra une véritable erreur fatale, l'erreur fatale sera réduite au silence. De nos jours, dans PHP moderne, ce n'est plus un gros problème, mais utiliser file_exists() + include() - bien qu'il existe une condition de concurrence due au temps de vérification par rapport au temps d'utilisation - est sûr (seulement un avertissement) pour les fichiers inexistants. et les erreurs fatales ne sont pas cachées.
Comme vous l'avez peut-être vu, plus vous connaissez de détails, plus il est difficile d'écrire un code aussi évolutif que possible. Nous ne devons pas nous perdre dans la gestion des erreurs elle-même, mais nous concentrer sur les résultats et définir que ces erreurs n'existent pas.
Cela dit, include() provoque toujours le chargement des données en mémoire, file_exists() n'est utilisé que pour "supprimer" les avertissements, nous savons que, néanmoins, include() peut émettre des avertissements et renvoyer un entier au lieu d'un tableau .
Maintenant, puisque la programmation est difficile : vous pouvez alors l'enrouler en boucle et dire de réessayer trois fois. Pourquoi ne pas utiliser une boucle for pour compter et protéger les tentatives numériques ?
P粉5503233382023-09-03 00:09:17
Ce problème ne peut pas être reproduit si le script n'a toujours qu'un seul exécuteur.
Si vous parlez d'exécuter ce script en parallèle, le problème est que l'écriture du fichier en mode exclusif ne vous protège pas de la lecture du fichier ultérieurement pendant le processus d'écriture.
Le processus peut écrire dans le fichier (et posséder le verrou), mais require
ne pas honorer le verrou (les verrous du système de fichiers sont consultatifs et non appliqués).
La bonne solution est donc :
<?php $f = function() use($a){ $cachePath = '/tmp/t.php'; /* Open the file for writing only. If the file does not exist, it is created. If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails (as is the case with 'x'). The file pointer is positioned on the beginning of the file. This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file, as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can be used after the lock is requested). */ $fp = fopen($cachePath, "c"); $code = '<?php'; $code .= "\n\n"; $code .= 'return ' . var_export([], true) . ';'; // acquire exclusive lock, waits until lock is acquired flock($fp, LOCK_EX); // clear the file ftruncate($fp, 0); // write the contents fwrite($fp, $code); //wrong (see my answer as to why) //file_put_contents($cachePath, $code, LOCK_EX); //not needed //if (file_exists($cachePath)) { // Lock is held during require $result = require($cachePath); if (!is_array($result)) { var_dump($result, $cachePath, file_get_contents($cachePath)); exit("ok"); } var_dump($result); //} // closing the file implicitly releases the lock fclose($fp); }; for($i=0;$i<1000000;$i++) { $f(); }
Notez que le verrou n'est pas libéré ni réacquis après l'écriture, car un autre processus peut attendre pour écraser le fichier.
Ne pas le publier pour garantir que le même morceau de code écrit est également require
d.
Cependant, tout cela était discutable dès le départ.
Pourquoi dois-je écrire dans un fichier pour qu'il puisse être renvoyé plus tard require
?