更新
問題を解決して回答を投稿しました。ただし、私のソリューションは100%理想的ではありません。私はむしろsymlink
をcache
からclearstatcache(true, $target)
またはclearstatcache(true, $link)
で削除するだけのほうがいいですが、それはうまくいきません。
また、シンボリックリンクのキャッシュを最初から防ぐか、シンボリックリンクを生成した直後にキャッシュから削除します。残念ながら、私はそれで運がありませんでした。何らかの理由でclearstatcache(true)
は、シンボリックリンクを作成しても機能しませんが、キャッシュされます。
私は answer を改善し、それらの問題を解決できる人に賞金を喜んで授与します。
編集
clearstatcache
が実行されるたびにファイルを生成してコードを最適化しようとしたので、シンボリックリンクごとに1回だけキャッシュをクリアする必要があります。何らかの理由で、これは機能しません。 clearstatcache
は、symlink
がパスに含まれるたびに呼び出す必要がありますが、なぜですか?私のソリューションを最適化する方法がなければなりません。
PHP 7.3.5
とnginx/1.16.0
を併用しています。 symlink
を使用すると、file_get_contents
が誤った値を返すことがあります。問題は、シンボリックリンクを削除して再作成した後、その古い値がキャッシュに残っていることです。正しい値が返されることもあれば、古い値が返されることもあります。ランダムに見えます。
私はキャッシュをクリアするか、キャッシュを防止しようとしました:
function symlink1($target, $link)
{
realpath_cache_size(0);
symlink($target, $link);
//clearstatcache(true);
}
キャッシュを無効にしたくないのですが、file_get_contentsで100%の精度が必要です。
編集
ソースコードを投稿することはできません。非常に長く複雑すぎるため、問題を再現する最小限の再現可能な例(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!
が表示されるため、何らかのキャッシュの問題が発生しているはずです。
問題の原因となった2つの問題がありました。
創刊号
私はすでに質問として投稿し、編集しています。 Nginxの設定に問題があります。
これらの行:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
置き換えが必要:
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
2番目の問題
2番目の問題は、file_get_contents
を呼び出す前にclearstatcache
を呼び出す必要があったことです。絶対に必要な場合にのみclearstatcache
を呼び出したいので、ディレクトリにsymlink
が含まれている場合にのみキャッシュをクリアする関数を作成しました。
function file_get_contents1($dir)
{
$realPath = realpath($dir);
if ($realPath === false)
return '';
if ($dir !== $realPath)
{
clearstatcache(true);
}
return file_get_contents($dir);
}
これはPHPの望ましい動作です ここ を見ることができますPHPはrealpath_cache
を使用してファイルパスを格納するため パフォーマンスの向上 により、ディスク操作を減らすことができます。
この動作を回避するには、realpath_cache
関数を使用する前にget_file_contents
をクリアしてみてください
あなたはこのようなことを試すことができます:
clearstatcache();
$data = file_get_contents("Your File");
clearstatcache の詳細については、PHP doc。
OSレベルに依存しすぎです。では、箱を考えてみてはいかがでしょうか。ファイルの実際の場所をreadlink
で読み取って、その実際の場所のパスを使用してみませんか?
$realPath = Shell_exec("readlink " . $yourSymlink);
$fileContent = file_get_contents($realPath);
「問題はシンボリックリンクを削除して再作成した後です」
どうやってシンボリックリンクを削除しますか?ファイル(またはシンボリックリンク)を削除すると、自動的にキャッシュがクリアされます。
それ以外の場合は、次のようにするとどうなるかを確認できます。
// This has "race condition" written all around it
unlink($link);
touch($link);
unlink($link); // Remove the empty file
symlink($target, $link);
これで問題が解決しない場合は、nginxの問題である可能性があります this issue ?
すべての操作をログファイルに記録して、実際にがどうなるかを確認してください。
...シンボリックリンクなしでできますか?たとえば、「ファイル名」と「実際のシンボリックリンクターゲット」の間のマッピングをデータベース、memcache、SQLiteファイル、またはJSONファイルに保存します。例: redisまたは他のキーストアでは、「ファイル名」を実際のsymlinkターゲットに関連付け、OSの解決を完全にバイパスできます。
ユースケースによっては、シンボリックリンクを使用するよりも高速になる場合もあります。
2つのキャッシュがあります。
最初にOSキャッシュ、次にPHPキャッシュ。
ほとんどの場合、clearstatcache(true)
がfile_get_contents(...)
の前に仕事をします。
ただし、OSキャッシュをクリアする必要がある場合もあります。 Linuxの場合、2か所を明確にする必要があります。 PageCache(1)およびdentries/inodes(2)。
これにより、両方がクリアされます。
Shell_exec('echo 3 > /proc/sys/vm/drop_caches')
注:これはトラブルシューティングに適していますが、OSキャッシュ全体をクリアし、システムにキャッシュの再設定の数分のコストがかかるため、本番環境での頻繁な呼び出しには適していません。
最初の回答は有効な回答であるため、そのままにしておきます。 clearstatcache(true、$ filename)を実装することにより、@ DanBrayの回答を改善しています。
問題の原因となった2つの問題がありました。
創刊
私はすでに質問として投稿し、編集しています。 Nginxの設定に問題があります。
これらの行:
fastcgi_param SCRIPT_FILENAME $ document_root $ fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $ document_root;
置き換えが必要:
fastcgi_param SCRIPT_FILENAME $ realpath_root $ fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $ realpath_root;
第二号
2番目の問題は、file_get_contentsを呼び出す前にclearstatcacheを呼び出す必要があったことです。絶対に必要な場合にのみclearstatcacheを呼び出したいので、ディレクトリにシンボリックリンクが含まれている場合にのみキャッシュをクリアする関数を作成しました。
function file_get_contents1234_hard_drives($dir_go_1){
$realPath = realpath($dir_go_1);
$myDirectory=opendir(dirname($realPath));
while($entryName=readdir($myDirectory)) {
$dirArray[]=$entryName;
}
/* Finds extensions of files used for my site theelectronichandbook.tech
function findexts ($filename) {
$filename=strtolower($filename);
$exts=split("[/\\.]", $filename);
$n=count($exts)-1;
$exts=$exts[$n];
return $exts;
}*/
// Closes directory
closedir($myDirectory);
// Counts elements in array
$indexCount=count($dirArray);
for($ArPos=1;$ArPos<=$indexCount;$ArPos++){
/*used for my site theelectronichandbook.tech
if($_SERVER['QUERY_STRING']=="hidden"){
$H="";
$af="./";
$atext="Hide";
}else{
$H=".";
$af="./?hidden";
$at="Show";
}*/
if(strpos($dirArray[$ArPos], "Symlink") !== false){
clearstatcache(true,$dir_go_1);
}
}
return file_get_contents($dir_go_1);
}
上記のコードを自分のWebサーバーでテストしたところ、うまくいきました。
Jqueryを使用して継続的に更新されている要素内にコードを配置し、再検証を強制して静的キャッチをクリアしてみてください。このコードは、@ naveedの元の answer から変更されています。
form.php:
<meta http-equiv="Cache-Control" content="no-store, must-revalidate" />
<meta http-equiv="Expires" content="0"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
jQuery(document).ready(function(){
jQuery('.ajaxform').submit( function() {
$.ajax({
url : $(this).attr('action'),
type : $(this).attr('method'),
dataType: 'json',
data : $(this).serialize(),
success : function( data ) {
// loop to set the result(value)
// in required div(key)
for(var id in data) {
jQuery('#' + id).html( data[id] );
}
}
});
return false;
});
});
var timer, delay = 30;
timer = setInterval(function(){
$.ajax({
type : 'POST',
url : 'profile.php',
dataType: 'json',
data : $('.ajaxform').serialize(),
success : function(data){
for(var id in data) {
jQuery('#' + id).html( data[id] );
}
}
}); }, delay);
</script>
<form action='profile.php' method='post' class='ajaxform'></form>
<div id='result'></div>
profile.php:
<?php
// All form data is in $_POST
// Now perform actions on form data here and create an result array something like this
clearstatcache();
$arr = array( 'result' => file_get_contents("./myfile.text") );
echo json_encode( $arr );
?>