Aktualisieren
Ich habe das Problem gelöst und eine Antwort gepostet. Meine Lösung ist jedoch nicht 100% ideal. Ich würde viel lieber nur symlink
das cache
mit clearstatcache(true, $target)
oder entfernen, clearstatcache(true, $link)
aber das funktioniert nicht.
Ich würde auch viel lieber das Zwischenspeichern von Symlinks verhindern oder den Symlink sofort nach dem Generieren aus dem Cache entfernen. Leider hatte ich damit kein Glück. Aus irgendeinem Grund clearstatcache(true)
funktioniert das Erstellen eines Symlinks nach dem Erstellen nicht.
Ich werde das Kopfgeld gerne an jeden vergeben, der meine Antwort verbessern und diese Probleme lösen kann.
Bearbeiten
Ich habe versucht, meinen Code zu optimieren clearstatcache
, indem ich bei jeder Ausführung eine Datei generiere , sodass ich den Cache für jeden Symlink nur einmal leeren muss. Aus irgendeinem Grund funktioniert dies nicht. clearstatcache
muss jedes Mal aufgerufen werden, wenn a symlink
in den Pfad aufgenommen wird, aber warum? Es muss eine Möglichkeit geben, meine Lösung zu optimieren.
Ich benutze PHP 7.3.5
mit nginx/1.16.0
. Gibt file_get_contents
bei Verwendung von a manchmal den falschen Wert zurück symlink
. Das Problem ist, dass nach dem Löschen und Neuerstellen eines Symlinks sein alter Wert im Cache verbleibt. Manchmal wird der richtige Wert zurückgegeben, manchmal der alte Wert. Es erscheint zufällig.
Ich habe versucht, den Cache zu leeren oder das Caching zu verhindern mit:
function symlink1($target, $link)
{
realpath_cache_size(0);
symlink($target, $link);
//clearstatcache(true);
}
Ich möchte das Caching nicht wirklich deaktivieren, benötige aber dennoch eine 100% ige Genauigkeit mit file_get_contents.
Bearbeiten
Ich kann meinen Quellcode nicht veröffentlichen, da er viel zu lang und komplex ist. Daher habe ich ein minimales, reproduzierbares Beispiel (index.php) erstellt, das das Problem neu erstellt:
<h1>Symlink Problem</h1>
<?php
$dir = getcwd();
if (isset($_POST['clear-all']))
{
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
foreach ($nos as $no)
{
unlink($dir.'/nos/'.$no.'/id.txt');
rmdir($dir.'/nos/'.$no);
}
foreach (array_values(array_diff(scandir($dir.'/ids'), array('..', '.'))) as $id)
unlink($dir.'/ids/'.$id);
}
if (!is_dir($dir.'/nos'))
mkdir($dir.'/nos');
if (!is_dir($dir.'/ids'))
mkdir($dir.'/ids');
if (isset($_POST['submit']) && !empty($_POST['id']) && ctype_digit($_POST['insert-after']) && ctype_alnum($_POST['id']))
{
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
$total = count($nos);
if ($total <= 100)
{
for ($i = $total; $i >= $_POST['insert-after']; $i--)
{
$id = file_get_contents($dir.'/nos/'.$i.'/id.txt');
unlink($dir.'/ids/'.$id);
symlink($dir.'/nos/'.($i + 1), $dir.'/ids/'.$id);
rename($dir.'/nos/'.$i, $dir.'/nos/'.($i + 1));
}
echo '<br>';
mkdir($dir.'/nos/'.$_POST['insert-after']);
file_put_contents($dir.'/nos/'.$_POST['insert-after'].'/id.txt', $_POST['id']);
symlink($dir.'/nos/'.$_POST['insert-after'], $dir.'/ids/'.$_POST['id']);
}
}
$nos = array_values(array_diff(scandir($dir.'/nos'), array('..', '.')));
$total = count($nos) + 1;
echo '<h2>Ids from nos directory</h2>';
foreach ($nos as $no)
{
echo ($no + 1).':'.file_get_contents("$dir/nos/$no/id.txt").'<br>';
}
echo '<h2>Ids from using symlinks</h2>';
$ids = array_values(array_diff(scandir($dir.'/ids'), array('..', '.')));
if (count($ids) > 0)
{
$success = true;
foreach ($ids as $id)
{
$id1 = file_get_contents("$dir/ids/$id/id.txt");
echo $id.':'.$id1.'<br>';
if ($id !== $id1)
$success = false;
}
if ($success)
echo '<b><font color="blue">Success!</font></b><br>';
else
echo '<b><font color="red">Failure!</font></b><br>';
}
?>
<br>
<h2>Insert ID after</h2>
<form method="post" action="/">
<select name="insert-after">
<?php
for ($i = 0; $i < $total; $i++)
echo '<option value="'.$i.'">'.$i.'</option>';
?>
</select>
<input type="text" placeholder="ID" name="id"><br>
<input type="submit" name="submit" value="Insert"><br>
</form>
<h2>Clear all</h2>
<form method="post" action="/">
<input type="submit" name="clear-all" value="Clear All"><br>
</form>
<script>
if (window.history.replaceState)
{
window.history.replaceState( null, null, window.location.href );
}
</script>
Es schien sehr wahrscheinlich ein Problem mit der Nginx
Konfiguration zu sein. Das Fehlen dieser Leitungen kann das Problem verursachen:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
Hier ist meine Nginx
Konfiguration (Sie können sehen, dass ich die obigen Zeilen eingefügt habe):
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.websemantica.co.uk;
root "/path/to/site/root";
index index.php;
location / {
try_files $uri $uri/ $uri.php$is_args$query_string;
}
location ~* \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $realpath_root$fastcgi_path_info;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param HTTPS $https;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
fastcgi_index index.php;
fastcgi_read_timeout 3000;
}
if ($request_uri ~ (?i)^/([^?]*)\.php($|\?)) {
return 301 /$1$is_args$args;
}
rewrite ^/index$ / permanent;
rewrite ^/(.*)/$ /$1 permanent;
}
Derzeit habe ich das obige Beispiel live unter https://www.websemantica.co.uk .
Versuchen Sie, dem Formular einige Werte hinzuzufügen. Es sollte Success!
jedes Mal blau angezeigt werden. Manchmal sind Shows Failure!
in rot. Es kann einige Seitenaktualisierungen dauern, um von Success!
zu Failure!
oder umgekehrt zu wechseln . Irgendwann wird es Success!
jedes Mal angezeigt, daher muss es eine Art Caching-Problem geben.
realpath
Funktionsseite gefunden . Vielleicht könnte es dir helfen.realpath
mitfile_get_conents
und kein Glück. Es wird immer noch manchmal aus dem Cache geladen.realpath
, sondern so etwas wieclearstatcache(true); file_get_conents(realpath($fileName));
Antworten:
Es hängt zu sehr vom Betriebssystem ab. Wie wäre es also mit einem Versuch, die Schachtel zu überdenken? Wie wäre es, wenn Sie versuchen, den tatsächlichen Speicherort der Datei zu lesen
readlink
und diesen Pfad für den tatsächlichen Speicherort zu verwenden?quelle
Dies ist das gewünschte Verhalten von PHP. Sie können dies hier sehen, da PHP
realpath_cache
die Dateipfade aufgrund von Leistungsverbesserungen speichert , um die Festplattenoperationen zu reduzieren.Um dieses Verhalten zu vermeiden, können Sie möglicherweise versuchen, das zu löschen,
realpath_cache
bevor Sie dieget_file_contents
Funktion verwendenSie können so etwas ausprobieren:
Sie können mehr für clearstatcache auf PHP doc lesen .
quelle
Es gibt zwei Caches.
Zuerst der Betriebssystem-Cache und dann der PHP-Cache.
In den meisten Fällen erledigt
clearstatcache(true)
vorherfile_get_contents(...)
die Arbeit.Manchmal müssen Sie aber auch den Betriebssystem-Cache leeren. Im Falle von Linux kann ich mir zwei Stellen zum Löschen vorstellen. PageCache (1) und Einträge / Inodes (2).
Dies löscht beide:
Hinweis: Dies ist gut für die Fehlerbehebung, jedoch nicht für häufige Aufrufe in der Produktion, da der gesamte Betriebssystem-Cache geleert wird und das System einige Momente der Cache-Neupopulation kostet.
quelle
Wie löscht man den Symlink? Das Löschen einer Datei (oder eines Symlinks) sollte den Cache automatisch leeren.
Andernfalls könnten Sie sehen, was passiert, wenn Sie Folgendes tun:
Wenn dies das Problem nicht löst, könnte es möglicherweise ein Problem mit Nginx wie in dieser Ausgabe sein ?
Versuchen Sie, alle Vorgänge in einer Protokolldatei zu protokollieren, um zu sehen, was tatsächlich passiert.
oder vielleicht...
... könnten Sie ganz auf Symlinks verzichten ? Speichern Sie beispielsweise die Zuordnung zwischen "Dateiname" und "tatsächlichem Symlink-Ziel" in einer Datenbank, einem Memcache, einer SQLite-Datei oder sogar einer JSON-Datei. Wenn Sie beispielsweise Redis oder andere Schlüsselspeicher verwenden, können Sie den "Dateinamen" dem tatsächlichen Symlink-Ziel zuordnen und die Betriebssystemauflösung vollständig umgehen.
Je nach Anwendungsfall ist dies möglicherweise sogar schneller als die Verwendung von Symlinks.
quelle
Es gab zwei Probleme, die das Problem verursachten.
Erste Ausgabe
Ich habe bereits als gepostet und in der Frage bearbeitet. Es ist ein Problem mit der Nginx-Konfiguration.
Diese Zeilen:
benötigt ersetzt durch:
Zweite Ausgabe
Das zweite Problem war, dass ich anrufen musste,
clearstatcache
bevor ich anrieffile_get_contents
. Ich möchte nur aufrufen,clearstatcache
wenn es absolut notwendig ist, deshalb habe ich eine Funktion geschrieben, die den Cache nur löscht, wenn das Verzeichnis a enthältsymlink
.quelle
Ich hinterlasse meine erste Antwort, da es sich immer noch um eine gültige Antwort handelt. Ich verbessere die @ DanBray-Antwort durch Implementierung von clearstatcache (true, $ filename).
Ich habe den obigen Code mit meinem Webserver getestet und es hat funktioniert.
quelle
file_get_contents1
Teil des Frameworks, das ich erstellt habe, daher wird es häufig verwendet, was die Optimierung wichtig macht.$dir_go=readdir("$realPath")
gibt null zurück.While($dir_go!==null)
@DanBrayVersuchen Sie, den Code in ein Element einzufügen, das mit Jquery ständig aktualisiert wird, und erzwingen Sie eine erneute Validierung und löschen Sie den statischen Fang. Dieser Code wurde gegenüber der ursprünglichen Antwort von @naveed geändert .
form.php:
profile.php:
quelle