file_get_contents ได้รับผลลัพธ์ที่ผิด


10

ปรับปรุง

ฉันแก้ไขปัญหาและโพสต์คำตอบ อย่างไรก็ตามโซลูชันของฉันไม่เหมาะอย่างยิ่ง 100% ฉันจะค่อนข้างลบเฉพาะsymlinkจากcacheด้วยclearstatcache(true, $target)หรือclearstatcache(true, $link)แต่ไม่ได้ทำงาน

ฉันจะค่อนข้างป้องกันการแคชของ symlink ในสถานที่แรกหรือลบ symlink จากแคชทันทีหลังจากสร้างมัน โชคไม่ดีที่ฉันไม่มีโชค ด้วยเหตุผลบางอย่างclearstatcache(true)หลังจากสร้าง symlink ไม่ทำงานก็ยังคงได้รับแคช

ฉันจะมอบรางวัลให้กับทุกคนอย่างมีความสุขที่สามารถปรับปรุงคำตอบของฉันและแก้ปัญหาเหล่านั้นได้

แก้ไข

ฉันพยายามเพิ่มประสิทธิภาพรหัสของฉันด้วยการสร้างไฟล์ทุกครั้งที่clearstatcacheทำงานเพื่อที่ฉันจะต้องล้างแคชเพียงครั้งเดียวสำหรับแต่ละ symlink ด้วยเหตุผลบางอย่างสิ่งนี้ไม่ทำงาน clearstatcacheต้องถูกเรียกทุกครั้งที่มีการsymlinkรวมไว้ในเส้นทาง แต่ทำไม ต้องมีวิธีในการเพิ่มประสิทธิภาพโซลูชันที่ฉันมี


ฉันใช้กับPHP 7.3.5 nginx/1.16.0บางครั้งส่งกลับค่าที่ไม่ถูกต้องเมื่อใช้file_get_contents symlinkปัญหาคือหลังจากลบและสร้าง 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 บางทีมันอาจช่วยคุณได้
marv255

@ marv255 ฉันลองใช้realpathด้วยfile_get_conentsและไม่มีโชค บางครั้งมันยังโหลดจากแคช
Dan Bray

2
ฉันหมายถึงไม่เพียงrealpathแต่สิ่งที่ชอบclearstatcache(true); file_get_conents(realpath($fileName));
marv255

ลองlinux.die.net/man/8/updatedb เรียกใช้คำสั่งระหว่างการโทรติดต่อกัน แม้ว่าฉันจะไม่แน่ใจว่าจะแก้ปัญหาใน php ได้อย่างไรในกรณีนี้
Jannes Botis

คำตอบ:


3

มันมากเกินไปขึ้นอยู่กับระดับระบบปฏิบัติการ ลองคิดดูสิ ลองอ่านตำแหน่งที่แท้จริงของไฟล์ด้วยreadlinkและใช้เส้นทางตำแหน่งจริงนั้น

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

ฉันไม่คิดว่าเพียงพอ (จากกล่อง) readlink ยังขึ้นอยู่กับการเรียกระดับ OS และได้รับผลกระทบจากแคช
Bahram Ardalan

3

นี่เป็นพฤติกรรมที่ต้องการของ PHP คุณสามารถดูได้ที่นี่เพราะ PHP ใช้realpath_cacheเพื่อจัดเก็บพา ธ ไฟล์เนื่องจากการปรับปรุงประสิทธิภาพเพื่อให้สามารถลดการใช้งานดิสก์ได้

เพื่อที่จะหลีกเลี่ยงพฤติกรรมนี้คุณอาจลองล้างฟังก์ชั่นrealpath_cacheก่อนใช้get_file_contentsงาน

คุณสามารถลองสิ่งนี้:


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

คุณสามารถอ่านเพิ่มเติมสำหรับclearstatcacheบน PHP doc


2

มีแคชสองแห่ง

ก่อนอื่นให้ทำการแคชระบบปฏิบัติการแล้วทำการแคช PHP

ในกรณีส่วนใหญ่clearstatcache(true)ก่อนที่file_get_contents(...)จะทำงาน

แต่บางครั้งคุณต้องล้างแคชของระบบปฏิบัติการด้วย ในกรณีของลีนุกซ์, ฉันสามารถคิดถึงสถานที่สองแห่งให้ชัดเจน. PageCache (1) และ dentries / inodes (2)

วิธีนี้จะล้างทั้ง:

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

หมายเหตุ:สิ่งนี้ดีสำหรับการแก้ไขปัญหา แต่ไม่เหมาะสำหรับการโทรบ่อยในการใช้งานเนื่องจากมันล้างแคชของระบบปฏิบัติการทั้งหมดและทำให้ระบบเสียเวลาในการแคชอีกครั้ง


ไม่ทำงานบางครั้งมันยังโหลดค่าแคชและฉันต้องการโซลูชันที่ดีสำหรับการโทรบ่อยในการผลิต
Dan Bray

2
@DanBray คุณสามารถบันทึกสิ่งต่าง ๆ เพื่อหาข้อมูลเพิ่มเติมเกี่ยวกับธรรมชาติของบางครั้งได้หรือไม่?
Bahram Ardalan

1
@DanBray และวิธีการตรวจสอบการปรากฏตัวของค่าเก่าหรือไม่ เป็นไปได้ไหมว่าการทดสอบของคุณคืนค่าเดิมเนื่องจากเงื่อนไขการทดสอบอื่น ๆ
Bahram Ardalan

2

"ปัญหาเกิดขึ้นหลังจากการลบและสร้าง symlink ใหม่"

วิธีการทำคุณลบ symlink หรือไม่ การลบไฟล์ (หรือ symlink) ควรล้างแคชโดยอัตโนมัติ

มิฉะนั้นคุณสามารถดูว่าจะเกิดอะไรขึ้นถ้าคุณ:

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

หากวิธีนี้ไม่ช่วยแก้ปัญหาอาจเป็นปัญหากับ nginx เหมือนในปัญหานี้หรือไม่?

ลองบันทึกการทำงานทั้งหมดลงในไฟล์บันทึกเพื่อดูว่าเกิดอะไรขึ้นจริง

หรืออาจจะ...

... คุณจะทำอย่างไรถ้าไม่มี symlink เลย ? ตัวอย่างเช่นจัดเก็บในฐานข้อมูล memcache ไฟล์ SQLite หรือแม้กระทั่งไฟล์ JSON การแมประหว่าง "ชื่อไฟล์" และ "เป้าหมาย symlink จริง" เมื่อใช้เช่น redis หรือ keystores อื่น ๆ คุณสามารถเชื่อมโยง "filename" กับเป้าหมาย symlink จริงและหลีกเลี่ยงความละเอียดของระบบปฏิบัติการได้อย่างสมบูรณ์

ขึ้นอยู่กับกรณีการใช้งานสิ่งนี้อาจกลายเป็นเร็วกว่าการใช้ symlink


ฉันไม่เห็นว่าสิ่งนี้อาจเกี่ยวข้องกับ nginx เนื่องจากไม่มีสิ่ง http ระหว่างกระบวนการ PHP และระบบไฟล์ในท้องถิ่น การเป็นโพรเซสแม่นั้นทำให้ nginx มีความเกี่ยวข้องหรือไม่?
Bahram Ardalan

@BahramArdalan ความจริงก็คือเราไม่ทราบว่าปัญหาได้รับการวินิจฉัยหรือเชื่อมโยงกันอย่างไรหรือมีการใช้อย่างไร ดังนั้นจึงเป็นไปได้ว่ามีการตรวจพบเนื้อหาที่ไม่ตรงกันจาก nginx และอาจไม่เกี่ยวข้องกับ PHP SCCCE จะช่วยได้มาก
LSerni

ใช่. เราต้องขุดลงไปในสิ่งที่ "วิธี" ที่
Bahram Ardalan

1

มีสองประเด็นที่ทำให้เกิดปัญหา

ประเด็นแรก

ฉันโพสต์แล้วและแก้ไขในคำถาม เป็นปัญหากับการกำหนดค่า 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;

ประเด็นที่สอง

ปัญหาที่สองคือผมต้องโทรก่อนที่จะเรียกclearstatcache file_get_contentsฉันเพียงต้องการที่จะเรียกเมื่อมันจำเป็นอย่างยิ่งเพื่อให้ผมเขียนฟังก์ชั่นที่ล้างเท่านั้นแคชเมื่อไดเรกทอรีรวมถึงclearstatcachesymlink

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

1

ฉันจะทิ้งคำตอบแรกไว้เพราะยังเป็นคำตอบที่ถูกต้อง ฉันกำลังปรับปรุงคำตอบ @DanBray โดยใช้ clearstatcache (จริง, $ filename)

มีสองประเด็นที่ทำให้เกิดปัญหา

ประเด็นแรก

ฉันโพสต์แล้วและแก้ไขในคำถาม เป็นปัญหากับการกำหนดค่า 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;

ประเด็นที่สอง

ปัญหาที่สองคือฉันต้องโทร clearstatcache ก่อนโทร file_get_contents ฉันต้องการโทร clearstatcache เมื่อจำเป็นเท่านั้นดังนั้นฉันจึงเขียนฟังก์ชันที่จะล้างแคชเมื่อไดเรกทอรีมี 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);
}

ฉันทดสอบโค้ดด้านบนกับเว็บเซิร์ฟเวอร์ของฉันและใช้งานได้


1
น่าเสียดายที่มันไม่ได้ผลสำหรับฉันบนเว็บเซิร์ฟเวอร์ของฉัน
Dan Bray

ฉันจะกลับไปที่กระดานวาดภาพ @DanBray
JTS

1
ขอบคุณมาก แต่น่าเสียดายที่มีเวลาน้อยมากก่อนที่ช่วงเวลาเงินรางวัลจะหมดอายุ อย่างไรก็ตามถ้าคุณคิดว่าวิธีการแก้ปัญหาฉันมีความสุข 100% กับฉันจะได้รับรางวัลพิเศษ นอกจากนี้ยังfile_get_contents1เป็นส่วนหนึ่งของกรอบงานที่ฉันทำดังนั้นจึงมีการใช้งานจำนวนมากซึ่งทำให้การเพิ่มประสิทธิภาพมีความสำคัญ
Dan Bray

$dir_go=readdir("$realPath")ผลตอบแทนที่เป็นโมฆะ
Dan Bray

อาจต้องเปลี่ยนเป็นWhile($dir_go!==null)@DanBray
JTS

0

ลองวางรหัสในองค์ประกอบที่สดชื่นอย่างต่อเนื่องโดยใช้ Jquery รวมถึงบังคับให้ตรวจสอบความถูกต้องและล้างการจับแบบคงที่ รหัสนี้ได้รับการแก้ไขจากเดิม @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 );
 ?>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.