Обновить
Я решил проблему и отправил ответ. Тем не менее, мое решение не на 100% идеально. Я бы предпочел только удалить symlink
из cache
с clearstatcache(true, $target)
или, clearstatcache(true, $link)
но это не работает.
Я также предпочел бы предотвратить кеширование символических ссылок в первую очередь или удалить символическую ссылку из кэша сразу после ее генерации. К сожалению, мне не повезло с этим. По какой-то причине clearstatcache(true)
после создания символическая ссылка не работает, она все еще кэшируется.
Я с радостью присужду награду любому, кто сможет улучшить мой ответ и решить эти проблемы.
редактировать
Я пытался оптимизировать свой код, генерируя файл при каждом clearstatcache
запуске, поэтому мне нужно очищать кэш только один раз для каждой символической ссылки. По какой-то причине это не работает. clearstatcache
нужно вызывать каждый раз, когда a symlink
включается в путь, но почему? Должен быть способ оптимизировать решение, которое у меня есть.
Я использую PHP 7.3.5
с nginx/1.16.0
. Иногда file_get_contents
возвращает неправильное значение при использовании symlink
. Проблема в том, что после удаления и повторного создания символической ссылки ее старое значение остается в кэше. Иногда возвращается правильное значение, иногда старое значение. Это кажется случайным.
Я пытался очистить кеш или предотвратить кеширование с помощью:
function symlink1($target, $link)
{
realpath_cache_size(0);
symlink($target, $link);
//clearstatcache(true);
}
Я действительно не хочу отключать кеширование, но мне все еще нужна 100% точность с file_get_contents.
редактировать
Я не могу опубликовать свой исходный код, так как он слишком длинный и сложный, поэтому я создал минимальный воспроизводимый пример (index.php), который воссоздает проблему:
<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>
Казалось, очень вероятно, что проблема с Nginx
конфигурацией. Отсутствие этих строк может вызвать проблему:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
Вот моя Nginx
конфигурация (вы можете видеть, что я включил вышеупомянутые строки):
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;
}
В настоящее время у меня есть приведенный выше пример в прямом эфире на https://www.websemantica.co.uk .
Попробуйте добавить несколько значений в форму. Он должен отображаться Success!
синим цветом каждый раз. Иногда это показывает Failure!
в красном. Это может занять довольно много обновления страницы , чтобы перейти от Success!
к Failure!
или наоборот. В конце концов, это будет показываться Success!
каждый раз, поэтому должна быть какая-то проблема с кэшированием.
realpath
странице функций . Может быть, это может помочь вам.realpath
сfile_get_conents
и не повезло. Он все еще иногда загружается из кеша.realpath
, но что-то вродеclearstatcache(true); file_get_conents(realpath($fileName));
Ответы:
Это слишком сильно зависит от уровня ОС. Так как насчет того, чтобы попытаться придумать коробку. Как насчет того, чтобы попытаться прочитать реальное местоположение файла
readlink
и использовать этот реальный путь к файлу?источник
Это желаемое поведение PHP, вы можете увидеть это здесь, потому что PHP использует
realpath_cache
для хранения путей к файлам из-за повышения производительности, чтобы он мог уменьшить операции на диске.Чтобы избежать такого поведения, возможно, вы можете попытаться очистить
realpath_cache
перед использованиемget_file_contents
функцииВы можете попробовать что-то вроде этого:
Вы можете прочитать больше о clearstatcache на PHP документ.
источник
Есть два кеша.
Сначала кеш ОС, а затем кеш PHP.
В большинстве случаев
clearstatcache(true)
раньшеfile_get_contents(...)
делает работу.Но иногда вам также необходимо очистить кэш ОС. В случае с Linux я могу подумать о двух местах, которые нужно очистить. PageCache (1) и dentries / inode (2).
Это очищает оба:
Примечание. Это полезно для устранения неполадок, но не для частых вызовов в производственном процессе, поскольку оно очищает весь кэш ОС и обходится системе в несколько секунд для повторного заполнения кеша.
источник
Как удалить символическую ссылку? Удаление файла (или символической ссылки) должно автоматически очистить кеш.
В противном случае вы можете увидеть, что произойдет, если вы сделаете:
Если это не решит проблему, возможно, это проблема с nginx, как в этом выпуске ?
Попробуйте записать все операции в файл журнала, чтобы увидеть, что на самом деле происходит.
или, может быть...
... не могли бы вы вообще обойтись без символических ссылок ? Например, сохраните в базе данных, memcache, файле SQLite или даже в файле JSON соответствие между «filename» и «фактической целью символической ссылки». Используя, например, redis или другие хранилища ключей, вы можете связать «имя файла» с реальной целью символической ссылки и полностью обойти разрешение ОС.
В зависимости от варианта использования это может даже оказаться быстрее, чем использование символических ссылок.
источник
Были две проблемы, которые вызвали проблему.
Первый выпуск
Я уже выкладывал как и редактирую в вопросе. Это проблема с конфигурацией Nginx.
Эти строки:
необходимо заменить на:
Второй выпуск
Второй вопрос - мне нужно было позвонить
clearstatcache
перед звонкомfile_get_contents
. Я хочу вызывать толькоclearstatcache
тогда, когда это абсолютно необходимо, поэтому я написал функцию, которая очищает кэш только тогда, когда каталог включает в себяsymlink
.источник
Я оставляю свой первый ответ, так как это все еще правильный ответ. Я улучшаю ответ @DanBray путем реализации clearstatcache (true, $ filename).
Я протестировал приведенный выше код на своем веб-сервере, и он заработал.
источник
file_get_contents1
это часть фреймворка, который я сделал, поэтому он часто используется, что делает оптимизацию важной.$dir_go=readdir("$realPath")
возвращает нольWhile($dir_go!==null)
@DanBrayПопробуйте поместить код в элемент, который постоянно обновляется с помощью Jquery, а также принудительно выполнить повторную проверку и очистить статический улов. Этот код был изменен с оригинального ответа @naveed .
form.php:
profile.php:
источник