file_get_contents mendapatkan hasil yang salah

10

Memperbarui

Saya memecahkan masalah dan mengirim jawaban. Namun, solusi saya tidak 100% ideal. Saya lebih suka hanya menghapus symlinkdari cachedengan clearstatcache(true, $target)atau clearstatcache(true, $link)tetapi itu tidak berhasil.

Saya juga lebih suka mencegah caching symlink di tempat pertama atau menghapus symlink dari cache segera setelah membuatnya. Sayangnya, saya tidak beruntung dengan itu. Untuk beberapa alasan clearstatcache(true)setelah membuat symlink tidak berfungsi, itu masih di-cache.

Saya akan dengan senang hati memberikan hadiah kepada siapa pun yang dapat meningkatkan jawaban saya dan menyelesaikan masalah-masalah itu.

Edit

Saya telah berusaha untuk mengoptimalkan kode saya dengan menghasilkan file setiap kali clearstatcachedijalankan, sehingga saya hanya perlu menghapus cache sekali untuk setiap symlink. Untuk beberapa alasan, ini tidak berhasil. clearstatcacheperlu dipanggil setiap kali a symlinktermasuk di jalan, tapi mengapa? Pasti ada cara untuk mengoptimalkan solusi yang saya miliki.


Saya menggunakan PHP 7.3.5dengan nginx/1.16.0. Terkadang file_get_contentsmengembalikan nilai yang salah saat menggunakan a symlink. Masalahnya adalah setelah menghapus dan menciptakan kembali symlink, nilainya yang lama tetap ada di cache. Terkadang nilai yang benar dikembalikan, terkadang nilai lama. Tampaknya acak.

Saya sudah mencoba menghapus cache atau mencegah caching dengan:

function symlink1($target, $link)
{
    realpath_cache_size(0);
    symlink($target, $link);
    //clearstatcache(true);
}

Saya tidak benar-benar ingin menonaktifkan caching tetapi saya masih membutuhkan akurasi 100% dengan file_get_contents.

Edit

Saya tidak dapat memposting kode sumber saya, karena terlalu panjang dan rumit, jadi saya telah membuat contoh minimal yang dapat direproduksi (index.php) yang menciptakan masalah:

<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>

Tampaknya sangat mungkin menjadi masalah dengan Nginxkonfigurasi. Tidak memiliki garis-garis ini dapat menyebabkan masalah:

fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;

Ini adalah Nginxkonfigurasi saya (Anda dapat melihat saya telah menyertakan baris di atas):

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;
}

Saat ini saya memiliki contoh di atas secara langsung di https://www.websemantica.co.uk .

Coba tambahkan beberapa nilai dalam formulir. Itu harus ditampilkan Success!dengan warna biru setiap kali. Terkadang ditampilkan Failure!dalam warna merah. Mungkin diperlukan beberapa kali penyegaran halaman untuk berubah dari Success!menjadi Failure!atau sebaliknya. Akhirnya, itu akan ditampilkan Success!setiap waktu, oleh karena itu harus ada semacam masalah caching.

Dan Bray
sumber
Saya sedang mencari kasus yang sama dan menemukan komentar yang sangat berguna di realpathhalaman fungsi . Mungkin itu bisa membantu Anda.
marv255
@ marv255 Saya mencoba menggunakan realpathdengan file_get_conentsdan tidak berhasil. Kadang-kadang masih memuat dari cache.
Dan Bray
2
Maksud saya tidak hanya realpath, tetapi sesuatu seperticlearstatcache(true); file_get_conents(realpath($fileName));
marv255
Coba linux.die.net/man/8/updatedb jalankan perintah antara panggilan berurutan. Meskipun saya tidak yakin bagaimana menyelesaikan masalah di php jika ini masalahnya.
Jannes Botis

Jawaban:

3

Ini terlalu tergantung pada level OS. Jadi bagaimana kalau memikirkan kotak. Bagaimana kalau mencoba membaca lokasi file sebenarnya readlink, dan menggunakan jalur lokasi sebenarnya?

$realPath = shell_exec("readlink " . $yourSymlink);
$fileContent = file_get_contents($realPath);
Vo Kim Nguyen
sumber
Saya tidak berpikir itu cukup (dari kotak), setelah semua, readlink juga tergantung pada panggilan level OS dan dipengaruhi oleh cache.
Bahram Ardalan
3

Ini adalah perilaku yang diinginkan dari PHP Anda dapat melihat ini di sini karena PHP digunakan realpath_cacheuntuk menyimpan path file karena peningkatan kinerja sehingga dapat mengurangi Operasi Disk.

Untuk menghindari perilaku ini, mungkin Anda dapat mencoba menghapusnya realpath_cachesebelum menggunakan get_file_contentsfungsinya

Anda dapat mencoba sesuatu seperti ini:


clearstatcache();
$data = file_get_contents("Your File");

Anda dapat membaca lebih lanjut untuk clearstatcache di PHP doc.

Touqeer Shafi
sumber
2

Ada dua cache.

Pertama cache OS dan kemudian cache PHP.

Dalam sebagian besar kasus clearstatcache(true)sebelum file_get_contents(...)melakukan pekerjaan.

Namun terkadang Anda juga perlu menghapus cache OS. Dalam kasus Linux, di sana saya bisa memikirkan dua tempat untuk dibersihkan. PageCache (1) dan gigi palsu / inode (2).

Ini membersihkan keduanya:

shell_exec('echo 3 > /proc/sys/vm/drop_caches')

Catatan: Ini bagus untuk pemecahan masalah tetapi tidak untuk panggilan yang sering dalam produksi karena membersihkan seluruh cache OS dan biaya sistem beberapa saat cache re-populasi.

Bahram Ardalan
sumber
Ini tidak berhasil, kadang-kadang masih memuat nilai yang di-cache dan saya membutuhkan solusi yang bagus untuk panggilan sering dalam produksi.
Dan Bray
2
@DanBray, bisakah Anda mencatat sesuatu untuk mencari tahu lebih banyak tentang sifat kadang - kadang ?
Bahram Ardalan
1
@DanBray, Dan bagaimana Anda mendeteksi tampilan nilai lama? Mungkinkah tes Anda mengembalikan nilai lama karena kondisi pengujian lain sementara nilai di sana benar-benar berubah?
Bahram Ardalan
2

"Masalahnya adalah setelah menghapus dan menciptakan kembali symlink"

Bagaimana Anda menghapus symlink? Menghapus file (atau symlink) akan secara otomatis menghapus cache.

Kalau tidak, Anda bisa melihat apa yang terjadi jika Anda melakukannya:

// This has "race condition" written all around it
unlink($link);
touch($link);
unlink($link); // Remove the empty file
symlink($target, $link);

Jika ini tidak menyelesaikan masalah, mungkinkah ini menjadi masalah dengan nginx seperti dalam masalah ini ?

Coba masuk semua operasi ke file log, untuk melihat apa yang sebenarnya terjadi.

atau mungkin...

... bisakah Anda melakukannya tanpa symlink sama sekali ? Misalnya, simpan dalam database, memcache, file SQLite, atau bahkan file JSON pemetaan antara "nama file" dan "target symlink yang sebenarnya". Menggunakan mis redis atau keystores lainnya, Anda dapat mengaitkan "nama file" dengan target symlink asli dan memotong resolusi OS sepenuhnya.

Bergantung pada use case, ini bahkan bisa berubah menjadi lebih cepat daripada menggunakan symlink.

LSerni
sumber
Saya tidak bisa melihat bagaimana ini berhubungan dengan nginx karena sepertinya tidak ada hal http antara proses php dan sistem file lokal. Apakah menjadi proses induk membuat nginx entah bagaimana relevan?
Bahram Ardalan
@BahramArdalan faktanya adalah, kita tidak tahu bagaimana masalahnya didiagnosis atau apa symlink atau bagaimana mereka digunakan. Jadi bisa dibayangkan bahwa ketidakcocokan konten terdeteksi hilir dari nginx, dan sebenarnya bisa tidak terkait dengan PHP. SCCCE akan sangat membantu.
LSerni
Iya. Kita harus menggali sedikit ke "bagaimana" hal itu.
Bahram Ardalan
1

Ada dua masalah yang menyebabkan masalah.

Masalah pertama

Saya sudah memposting dan mengedit dalam pertanyaan. Ini masalah dengan konfigurasi Nginx.

Garis-garis ini:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;

perlu diganti dengan:

fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;

Masalah kedua

Masalah kedua adalah saya harus menelepon clearstatcachesebelum menelepon file_get_contents. Saya hanya ingin menelepon clearstatcacheketika itu benar-benar diperlukan, jadi saya menulis sebuah fungsi yang hanya membersihkan cache ketika direktori menyertakan a symlink.

function file_get_contents1($dir)
{
    $realPath = realpath($dir);
    if ($realPath === false)
        return '';
    if ($dir !== $realPath)
    {
        clearstatcache(true);
    }
    return file_get_contents($dir);
}
Dan Bray
sumber
1

Saya meninggalkan jawaban pertama saya karena masih merupakan jawaban yang valid. Saya memperbaiki jawaban @DanBray dengan menerapkan clearstatcache (true, $ filename).

Ada dua masalah yang menyebabkan masalah.

Masalah pertama

Saya sudah memposting dan mengedit dalam pertanyaan. Ini masalah dengan konfigurasi Nginx.

Garis-garis ini:

fastcgi_param SCRIPT_FILENAME $ document_root $ fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $ document_root;

perlu diganti dengan:

fastcgi_param SCRIPT_FILENAME $ realpath_root $ fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $ realpath_root;

Masalah kedua

Masalah kedua adalah saya perlu memanggil clearstatcache sebelum memanggil file_get_contents. Saya hanya ingin memanggil clearstatcache ketika itu benar-benar diperlukan, jadi saya menulis sebuah fungsi yang hanya membersihkan cache ketika direktori menyertakan symlink.

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);
}

Saya menguji kode di atas dengan server web saya dan ternyata berhasil.

JTS
sumber
1
Sayangnya, itu tidak berfungsi untuk saya di server web saya.
Dan Bray
Baiklah saya akan kembali ke papan gambar. @DanBray
JTS
1
Terima kasih banyak, tetapi sayangnya, ada sangat sedikit waktu sebelum periode hadiah berakhir. Namun, jika Anda memikirkan solusi yang saya senang 100%, saya akan memberikan hadiah ekstra. Juga, file_get_contents1merupakan bagian dari kerangka kerja yang telah saya buat, jadi ini banyak digunakan, yang membuat optimasi menjadi penting.
Dan Bray
$dir_go=readdir("$realPath")mengembalikan nol.
Dan Bray
Itu mungkin perlu diubah ke While($dir_go!==null)@DanBray
JTS
0

Cobalah menempatkan kode di dalam elemen yang terus disegarkan menggunakan Jquery serta memaksa validasi ulang dan menghapus tangkapan statis. Kode ini telah dimodifikasi dari jawaban asli @naveed .

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 );
 ?>
JTS
sumber