file_get_contents 잘못된 결과를 얻는 중


10

최신 정보

문제를 해결하고 답변을 게시했습니다. 그러나 내 솔루션은 100 % 이상적이지 않습니다. 나는 차라리만을 제거 할 symlink으로부터 cacheclearstatcache(true, $target)또는 clearstatcache(true, $link)그러나 그것은 작동하지 않습니다.

또한 심볼릭 링크를 처음에 캐싱하는 것을 막거나 심볼을 생성 한 직후 캐시에서 심볼릭 링크를 제거합니다. 불행히도, 나는 그것에 운이 없었습니다. 어떤 이유로 clearstatcache(true)symlink를 작성한 후에도 작동하지 않더라도 여전히 캐시됩니다.

답변을 개선 하고 해당 문제를 해결할 수있는 사람에게 현상금을 기꺼이 수여합니다 .

편집하다

매번 clearstatcache실행될 때마다 파일을 생성하여 코드를 최적화하려고 시도 했기 때문에 각 심볼릭 링크마다 캐시를 ​​한 번만 지우면됩니다. 어떤 이유로, 이것은 작동하지 않습니다. 경로 clearstatcache에 포함될 때마다 호출해야 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);
}

캐싱을 비활성화하고 싶지 않지만 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!매번 표시 되므로 일종의 캐싱 문제가 있어야합니다.


나는 같은 경우를 둘러보고 있었고 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 2016

답변:


3

OS 수준에 너무 의존합니다. 그렇다면 상자를 생각하는 것은 어떻습니까. 파일의 실제 위치를 읽어 readlink보고 실제 위치 경로를 사용하는 것은 어떻습니까?

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

나는 그것이 충분하지 않다고 생각합니다. 결과적으로 readlink는 OS 레벨 호출에 달려 있으며 캐시의 영향을받습니다.
Bahram Ardalan

3

이것은 당신이 PHP의 원하는 동작을 볼 수 있습니다 여기에 PHP를 사용하기 때문에realpath_cache 디스크 성능을 향상 시키기 위해 성능 향상 으로 인해 파일 경로를 저장하는 하기 수 있습니다.

이 동작을 피하려면 어쩌면 realpath_cacheget_file_contents 함수 를 사용하기 전에

다음과 같이 시도해보십시오.


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

PHP doc 에서 clearstatcache 에 대한 자세한 내용을 읽을 수 있습니다 .


2

두 개의 캐시가 있습니다.

먼저 OS 캐시와 PHP 캐시.

대부분의 경우에 clearstatcache(true)이전 file_get_contents(...)작업을 수행합니다.

그러나 때로는 OS 캐시를 지워야 할 수도 있습니다. 리눅스의 경우, 두 곳을 정리할 수 있습니다. PageCache (1) 및 dentries / inodes (2).

이렇게하면 다음이 모두 지워집니다.

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

참고 : 이는 전체 OS 캐시를 지우고 시스템에 몇 분 동안 캐시를 다시 채우므로 문제 해결에 유용하지만 프로덕션에서 자주 호출하는 것은 아닙니다.


이것은 작동하지 않으며 때로는 캐시 된 값을로드하므로 프로덕션의 빈번한 호출에 적합한 솔루션이 필요합니다.
Dan Bray

2
@ DanBray, 가끔 의 본질에 대해 더 많은 것을 알기 위해 물건을 기록 할 수 있습니까?
Bahram Ardalan

1
@ DanBray, 그리고 이전 값 의 모양 을 어떻게 감지 합니까? 값이 실제로 변경된 동안 다른 테스트 조건으로 인해 테스트에서 이전 값을 반환 할 수 있습니까?
Bahram Ardalan

2

"문제는 심볼릭 링크를 삭제하고 다시 만든 후입니다"

어떻게 당신은 심볼릭 링크를 삭제합니까? 파일 (또는 심볼릭 링크)을 삭제하면 캐시가 자동으로 지워집니다.

그렇지 않으면 다음과 같은 경우 어떻게되는지 알 수 있습니다.

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

이 문제가 해결되지 않으면, 그것은 아마도 같이의 nginx에 문제가 될 수 이 문제 ?

실제 작업을 확인하려면 모든 작업을 로그 파일에 기록하십시오 .

아니면 ...

... 링크 없이도 할 수 있을까요? 예를 들어, 데이터베이스, memcache, SQLite 파일 또는 JSON 파일에 "filename"과 "actual symlink target"간의 매핑을 저장하십시오. 예를 들어 redis 또는 다른 키 저장소를 사용하면 "파일 이름"을 실제 symlink 대상과 연관시키고 OS 해상도를 완전히 무시할 수 있습니다.

유스 케이스에 따라 심볼릭 링크를 사용하는 것보다 빠를 수도 있습니다.


PHP 프로세스와 로컬 파일 시스템 사이에 http가없는 것처럼 보이기 때문에 이것이 nginx와 어떤 관련이 있는지 알 수 없었습니다. 부모 프로세스로 인해 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. clearstatcache절대적으로 필요할 때만 호출하고 싶기 때문에 디렉토리에을 포함 할 때만 캐시를 지우는 함수를 작성했습니다 symlink.

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

1

여전히 유효한 답변이므로 첫 번째 답변을 남기고 있습니다. clearstatcache (true, $ filename)를 구현하여 @DanBray 답변을 개선하고 있습니다.

문제를 일으킨 두 가지 문제가있었습니다.

첫 번째 문제

질문에 이미 게시하고 수정했습니다. 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;

두 번째 문제

두 번째 문제는 file_get_contents를 호출하기 전에 clearstatcache를 호출해야한다는 것입니다. 절대적으로 필요한 경우에만 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")null을 돌려줍니다.
Dan Bray

While($dir_go!==null)@DanBray
JTS로

0

Jquery를 사용하여 지속적으로 새로 고치는 요소와 코드의 유효성을 다시 확인하고 정적 캐치를 지우십시오. 이 코드는 @naveed original 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 );
 ?>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.