Aktualizacja
Rozwiązałem problem i opublikowałem odpowiedź. Moje rozwiązanie nie jest jednak w 100% idealne. Chętniej tylko usunąć symlink
z cache
ze clearstatcache(true, $target)
albo clearstatcache(true, $link)
ale to nie działa.
Wolałbym też przede wszystkim zapobiec buforowaniu dowiązań symbolicznych lub usunąć dowiązanie symboliczne z bufora natychmiast po jego wygenerowaniu. Niestety nie miałem z tym szczęścia. Z jakiegoś powodu clearstatcache(true)
po utworzeniu dowiązania symbolicznego nie działa, nadal jest buforowane.
Z przyjemnością przyznam nagrodę każdemu, kto może poprawić moją odpowiedź i rozwiązać te problemy.
Edytować
Próbowałem zoptymalizować kod, generując plik przy każdym clearstatcache
uruchomieniu, więc muszę wyczyścić pamięć podręczną tylko raz dla każdego łącza symbolicznego. Z jakiegoś powodu to nie działa. clearstatcache
musi być wywoływana za każdym razem, gdy a symlink
wchodzi na ścieżkę, ale dlaczego? Musi istnieć sposób na optymalizację rozwiązania, które mam.
Używam PHP 7.3.5
z nginx/1.16.0
. Czasami file_get_contents
zwraca niewłaściwą wartość podczas używania symlink
. Problem polega na usunięciu i ponownym utworzeniu dowiązania symbolicznego, jego stara wartość pozostaje w pamięci podręcznej. Czasami zwracana jest poprawna wartość, czasem stara wartość. Wydaje się losowy.
Próbowałem wyczyścić pamięć podręczną lub zapobiec buforowaniu za pomocą:
function symlink1($target, $link)
{
realpath_cache_size(0);
symlink($target, $link);
//clearstatcache(true);
}
Tak naprawdę nie chcę wyłączać buforowania, ale nadal potrzebuję 100% dokładności z file_get_contents.
Edytować
Nie mogę opublikować mojego kodu źródłowego, ponieważ jest on zbyt długi i skomplikowany, dlatego stworzyłem minimalny, powtarzalny przykład (index.php), który odtwarza problem:
<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>
Wydawało się bardzo prawdopodobne, że będzie to problem z Nginx
konfiguracją. Brak tych linii może powodować problem:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
Oto moja Nginx
konfiguracja (widać, że zawarłem powyższe wiersze):
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;
}
Obecnie mam powyższy przykład na żywo na https://www.websemantica.co.uk .
Spróbuj dodać kilka wartości w formularzu. Powinien być wyświetlany Success!
na niebiesko za każdym razem. Czasami pokazy są Failure!
na czerwono. Może minąć sporo odświeża stronę, aby zmienić Success!
się Failure!
lub vice versa. W końcu będzie się wyświetlać za Success!
każdym razem, dlatego musi istnieć jakiś problem z buforowaniem.
realpath
stronie funkcji . Może to może ci pomóc.realpath
zfile_get_conents
i bez powodzenia. Nadal czasami ładuje się z pamięci podręcznej.realpath
, ale coś w styluclearstatcache(true); file_get_conents(realpath($fileName));
Odpowiedzi:
To zbyt wiele zależy od poziomu systemu operacyjnego. A może spróbujesz wymyślić pudełko. A może spróbujesz odczytać rzeczywistą lokalizację pliku
readlink
i użyć tej prawdziwej ścieżki lokalizacji?źródło
Jest to pożądane zachowanie PHP, które możesz zobaczyć tutaj, ponieważ PHP używa
realpath_cache
do przechowywania ścieżek plików ze względu na ulepszenia wydajności dzięki czemu może zmniejszyć operacje dysku.Aby tego uniknąć, możesz spróbować wyczyścić
realpath_cache
przed użyciemget_file_contents
funkcjiMożesz spróbować czegoś takiego:
Możesz przeczytać więcej o clearstatcache w dokumentacji PHP.
źródło
Istnieją dwie skrzynki.
Najpierw pamięć podręczna systemu operacyjnego, a następnie pamięć podręczna PHP.
W większości przypadków
clearstatcache(true)
wcześniejfile_get_contents(...)
działa.Ale czasami trzeba również wyczyścić pamięć podręczną systemu operacyjnego. W przypadku Linuksa mogę wymyślić dwa miejsca do wyczyszczenia. PageCache (1) i dentries / i-węzły (2).
To usuwa zarówno:
Uwaga: Jest to przydatne do rozwiązywania problemów, ale nie do częstych wywołań produkcyjnych, ponieważ usuwa pamięć podręczną całego systemu operacyjnego i kosztuje system kilka chwil ponownego zapełniania pamięci podręcznej.
źródło
Jak usunąć dowiązanie symboliczne? Usunięcie pliku (lub dowiązania symbolicznego) powinno automatycznie wyczyścić pamięć podręczną.
W przeciwnym razie możesz zobaczyć, co się stanie, jeśli:
Jeśli to nie rozwiąże problemu, może to być problem z nginx, jak w tym numerze ?
Spróbuj zarejestrować wszystkie operacje w pliku dziennika, aby zobaczyć, co się faktycznie dzieje.
albo może...
... czy mógłbyś zrobić całkowicie bez dowiązań symbolicznych ? Na przykład przechowuj w bazie danych, pamięci podręcznej, pliku SQLite, a nawet pliku JSON mapowanie między „nazwą pliku” a „rzeczywistym celem dowiązania symbolicznego”. Używając np. Redis lub innych magazynów kluczy, możesz powiązać „nazwę pliku” z rzeczywistym celem dowiązania symbolicznego i całkowicie ominąć rozdzielczość systemu operacyjnego.
W zależności od przypadku użycia może to nawet okazać się szybsze niż użycie dowiązań symbolicznych.
źródło
Przyczyną problemu były dwa problemy.
Pierwsza sprawa
Już opublikowałem jako i edytuję pytanie. Jest to problem z konfiguracją Nginx.
Te linie:
potrzebne zastąpione przez:
Drugi numer
Drugi problem polegał na tym, że musiałem zadzwonić,
clearstatcache
zanim zadzwonięfile_get_contents
. Chcę dzwonić tylkoclearstatcache
wtedy, gdy jest to absolutnie konieczne, dlatego napisałem funkcję, która czyści pamięć podręczną tylko wtedy, gdy katalog zawierasymlink
.źródło
Pierwszą odpowiedź zostawiam, ponieważ wciąż jest poprawna. Poprawiam odpowiedź @DanBray poprzez implementację clearstatcache (true, $ filename).
Przetestowałem powyższy kod na moim serwerze internetowym i zadziałał.
źródło
file_get_contents1
częścią frameworku, który stworzyłem, więc jest często używany, dlatego optymalizacja jest ważna.$dir_go=readdir("$realPath")
zwraca null.While($dir_go!==null)
@DanBraySpróbuj umieścić kod w elemencie, który jest stale odświeżany za pomocą Jquery, a także wymuszając ponowną walidację i czyszczenie statycznego catch. Ten kod został zmodyfikowany w stosunku do oryginalnej odpowiedzi @naveed .
form.php:
profile.php:
źródło