file_get_contents yanlış sonuçlar alıyor


10

Güncelleme

Sorunu çözdüm ve bir cevap gönderdim. Ancak benim çözümüm% 100 ideal değil. Ben daha çok sadece çıkarıyoruz symlinkgelen cacheile clearstatcache(true, $target)ya clearstatcache(true, $link)ama bu işi yapmaz.

Ayrıca, ilk olarak semboliklerin önbelleğe alınmasını önlerim veya oluşturduktan hemen sonra symlink'i önbellekten kaldırırım. Ne yazık ki, hiç şansım yoktu. Herhangi bir nedenle clearstatcache(true)bir symlink oluşturduktan sonra çalışmıyor, yine de önbelleğe alınır.

Cevabımı geliştirebilecek ve bu sorunları çözebilecek olan herkese ödül vereceğim .

Düzenle

Ben clearstatcachesadece her symlink için önbelleği temizlemek gerekir, böylece her zaman çalıştırılır bir dosya oluşturarak kodumu optimize etmeye çalıştım . Bazı nedenlerden dolayı, bu işe yaramaz. clearstatcachea her symlinkyola dahil edildiğinde çağrılmalı ama neden? Sahip olduğum çözümü optimize etmenin bir yolu olmalı.


Ben kullanıyorum PHP 7.3.5ile nginx/1.16.0. Bazen file_get_contentsa kullanırken yanlış değeri döndürür symlink. Sorun, bir symlink'i sildikten ve yeniden oluşturduktan sonra eski değeri önbellekte kalır. Bazen doğru değer, bazen de eski değer döndürülür. Rastgele görünüyor.

Önbelleği temizlemeye veya önbelleğe almayı engellemeye çalıştım:

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

Önbelleğe almayı gerçekten devre dışı bırakmak istemiyorum ancak file_get_contents ile% 100 doğruluk oranına ihtiyacım var.

Düzenle

Çok uzun ve karmaşık olduğu için kaynak kodumu gönderemiyorum, bu yüzden sorunu yeniden yaratan minimal, tekrarlanabilir bir örnek (index.php) oluşturduk:

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

NginxYapılandırma ile ilgili bir sorun olması muhtemel görünüyordu . Bu çizgilere sahip olmamak soruna neden olabilir:

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

İşte benim Nginxyapılandırma (yukarıdaki satırları dahil ettik görebilirsiniz):

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

Şu anda yukarıdaki örnek https://www.websemantica.co.uk adresinde canlı var .

Forma birkaç değer eklemeyi deneyin. Success!Her seferinde mavi renkte görüntülenmelidir . Bazen Failure!kırmızı renkte gösterilir. Bu değiştirmek için epeyce Sayfa yenilemeleri sürebilir Success!için Failure!veya tersi. Sonunda, Success!her seferinde gösterecektir , bu nedenle bir tür önbellek sorunu olmalıdır.


Ben aynı davaya bakıyordum ve realpathişlev sayfasında çok yararlı bir yorum buldum . Belki size yardımcı olabilir.
marv255

@ marv255 realpathile file_get_conentsve hiç şans kullanmayı denedim . Hala bazen önbellekten yüklenir.
Dan Bray

2
Sadece demek değil realpath, aynı şeyclearstatcache(true); file_get_conents(realpath($fileName));
marv255 6:19

Ardışık çağrılar arasındaki komutu çalıştırmak için linux.die.net/man/8/updatedb komutunu deneyin . Her ne kadar bu durumda php sorunu çözmek için emin değilim.
Jannes Botis

Yanıtlar:


3

Çok fazla işletim sistemi seviyesine bağlıdır. Peki kutuyu düşünmeye ne dersiniz? Dosyanın gerçek konumunu okumaya readlinkve bu gerçek konum yolunu kullanmaya ne dersiniz ?

$realPath = shell_exec("readlink " . $yourSymlink);
$fileContent = file_get_contents($realPath);

Bunun yeterli olduğunu düşünmüyorum (sonuçta), readlink OS seviyesi çağrılarına da bağlıdır ve önbellekten etkilenir.
Bahram Ardalan

3

PHP, Disk İşlemlerini azaltabilmesi için performans geliştirmeleri nedeniyle dosya yollarını saklamak için kullandığı için bunu burada görebileceğiniz PHP'nin istenen davranışıdır .realpath_cache

Bu davranışı önlemek realpath_cacheiçin, get_file_contentsfonksiyonu kullanmadan önce

Böyle bir şey deneyebilirsiniz:


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

Clearstatcache için daha fazlasını PHP doc'den okuyabilirsiniz .


2

İki önbellek var.

Önce OS önbelleği, sonra PHP önbelleği.

Çoğu durumda daha clearstatcache(true)önce file_get_contents(...)iş yapar.

Ancak bazen OS önbelleğini de temizlemeniz gerekir. Linux durumunda, orada temizlenecek iki yer düşünebilirim. PageCache (1) ve dişçilik / inot (2).

Bu her ikisini de temizler:

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

Not: Bu, sorun giderme için iyidir, ancak tüm işletim sistemi önbelleğini temizlediği ve sisteme önbellek yeniden popülasyonunun birkaç dakikasına mal olduğu için üretimde sık yapılan çağrılar için iyi değildir.


Bu işe yaramaz, yine de bazen önbelleğe alınan değeri yükler ve üretimde sık aramalar için iyi bir çözüme ihtiyacım var.
Dan Bray

2
@DanBray, bazen doğası hakkında daha fazla şey öğrenmek için bir şeyler kaydedebilir misiniz ?
Bahram Ardalan

1
@DanBray, Ve eski değerin görünümünü nasıl tespit edersiniz ? Oradaki değer gerçekten değişirken, testiniz diğer test koşulları nedeniyle eski değeri döndürüyor olabilir mi?
Bahram Ardalan

2

"Sorun, bir symlink'i sildikten ve yeniden oluşturduktan sonra"

Symlink'i nasıl silebilirsiniz? Bir dosyanın (veya bir sembol bağlantısının) silinmesi önbelleği otomatik olarak temizlemelidir.

Aksi takdirde, ne yaparsanız görebilirsiniz:

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

Bu sorunu çözmezse, belki gibi nginx ile ilgili bir sorun olabilir bu konuda ?

Gerçekte ne olduğunu görmek için tüm işlemleri bir günlük dosyasına kaydetmeyi deneyin .

ya da belki...

... sembolik bağlar olmadan yapabilir misiniz ? Örneğin, bir veritabanında, memcache'de, SQLite dosyasında ve hatta bir JSON dosyasında "dosya adı" ile "gerçek symlink hedefi" arasındaki eşlemeyi saklayın. Örneğin, redis veya diğer anahtar depolarını kullanarak, "dosya adını" gerçek sembolik hedefle ilişkilendirebilir ve işletim sistemi çözünürlüğünü tamamen atlayabilirsiniz.

Kullanım durumuna bağlı olarak, bu sembolik bağlantıları kullanmaktan daha hızlı olabilir.


Ben php süreci ve yerel dosya sistemi arasında hiçbir http şey gibi nasıl bu nginx ile ilgili olabilir göremiyordu. Üst süreç olmak nginx'i bir şekilde alakalı mı?
Bahram Ardalan

@BahramArdalan aslında, sorunun nasıl teşhis edildiğini veya semboliklerin ne olduğunu veya nasıl kullanıldığını bilmiyoruz . Dolayısıyla, içerik uyumsuzluğunun nginx'ten aşağı akışta algılanması ve aslında PHP ile ilgisi bulunmaması düşünülebilir. Bir SCCCE çok yardımcı olacaktır.
LSerni

Evet. O "nasıl" bir şeye biraz kazmamız gerekiyor.
Bahram Ardalan

1

Soruna neden olan iki sorun vardı.

İlk konu

Zaten soru olarak yayınladım ve düzenledim. Nginx yapılandırmasında bir sorun var.

Bu satırlar:

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

gerekli ile değiştirildi:

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

İkinci sayı

İkinci sorun, aramadan clearstatcacheönce aramam gerekti file_get_contents. Sadece clearstatcachekesinlikle gerekli olduğunda aramak istiyorum , bu yüzden sadece bir dizin içerdiğinde önbelleği temizleyen bir işlev yazdım symlink.

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

1

Hala geçerli bir cevap olduğu için ilk cevabımı bırakıyorum. Ben clearstatcache (true, $ dosyaadı) uygulayarak @DanBray yanıtını geliştiriyorum.

Soruna neden olan iki sorun vardı.

İlk konu

Zaten soru olarak yayınladım ve düzenledim. Nginx yapılandırmasında bir sorun var.

Bu satırlar:

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

gerekli ile değiştirildi:

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

İkinci sayı

İkinci sorun ben file_get_contents çağırmadan önce clearstatcache çağırmak gerekiyordu. Sadece kesinlikle gerekli olduğunda clearstatcache çağırmak istiyorum, bu yüzden sadece dizin bir symlink içerdiğinde önbelleği temizleyen bir işlev yazdım.

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

Yukarıdaki kodu web sunucumla test ettim ve çalıştı.


1
Ne yazık ki, web sunucumda benim için çalışmıyor.
Dan Bray

Peki ya tahtaya geri döneceğim. @DanBray
JTS

1
Çok teşekkür ederim, ancak maalesef ödül süresinin dolmasından önce çok az zaman var. Ancak, çözümden% 100 memnun olduğumu düşünüyorsanız, ekstra bir ödül vereceğim. Ayrıca, file_get_contents1yaptığım çerçevenin bir parçası, bu yüzden çok kullanılıyor, bu da optimizasyonu önemli kılıyor.
Dan Bray

$dir_go=readdir("$realPath")null değerini döndürür.
Dan Bray

Bu, While($dir_go!==null)@DanBray olarak değiştirilmiş olabilir
JTS

0

Kodu, Jquery kullanarak sürekli olarak yenilenen bir öğenin içine yerleştirmeyi, yeniden doğrulamayı zorlamayı ve statik yakalamayı temizlemeyi deneyin. Bu kod, @naveed orijinal yanıttan değiştirildi .

giriş başlıkları:

 <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 );
 ?>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.