【问题标题】:How can I optimize this simple PHP script?如何优化这个简单的 PHP 脚本?
【发布时间】:2023-03-27 00:48:01
【问题描述】:

第一个脚本通过 AJAX 请求为每个用户调用多次。它调用不同服务器上的另一个脚本来获取文本文件的最后一行。它工作正常,但我认为还有很大的改进空间,但我不是一个很好的 PHP 编码器,所以我希望在社区的帮助下我可以优化它以提高速度和效率:

对此脚本发出 AJAX POST 请求

<?php session_start();
$fileName = $_POST['textFile'];
$result = file_get_contents($_SESSION['serverURL']."fileReader.php?textFile=$fileName");
echo $result;
?>

它向这个读取文本文件的外部脚本发出 GET 请求

<?php
$fileName = $_GET['textFile'];
if (file_exists('text/'.$fileName.'.txt')) {
    $lines = file('text/'.$fileName.'.txt');
    echo $lines[sizeof($lines)-1];
}
else{
    echo 0;
}
?>

如果有任何帮助,我将不胜感激。我认为在第一个脚本中可以做出更多改进。它进行了昂贵的函数调用(file_get_contents),至少我认为它很昂贵!

【问题讨论】:

  • 我知道它没有任何问题,因为它可以工作,但我只是想改进它,因为这个脚本将被调用 10000 次。 :)

标签: php optimization file performance


【解决方案1】:

这个脚本应该限制它要返回的位置和文件类型。

想想有人尝试这个:

http://www.yoursite.com/yourscript.php?textFile=../../../etc/passwd(或类似的东西)

尝试找出延迟发生的位置。HTTP 请求是否需要很长时间,或者文件太大以至于读取需要很长时间。

如果请求很慢,尝试在本地缓存结果。

如果文件很大,那么您可以设置一个 cron 作业,以定期(或每次更改)提取文件的最后一行,并将其保存到其他脚本可以直接访问的文件中。

【讨论】:

    【解决方案2】:

    readfile是你的朋友 它读取磁盘上的文件并将其流式传输到客户端。

    脚本 1:

    <?php
      session_start();
      // added basic argument filtering
      $fileName = preg_replace('/[^A-Za-z0-9_]/', '', $_POST['textFile']);
    
      $fileName = $_SESSION['serverURL'].'text/'.$fileName.'.txt';
      if (file_exists($fileName)) {
    
          // script 2 could be pasted here
    
          //for the entire file
          //readfile($fileName);
    
          //for just the last line
          $lines = file($fileName);
          echo $lines[count($lines)-1];
    
    
          exit(0);
      }
    
      echo 0;
    ?>
    

    这个脚本可以通过添加缓存来进一步改进。但这更复杂。 非常基本缓存可以。

    脚本 2:

    <?php
    
      $lastModifiedTimeStamp filemtime($fileName);
    
      if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
          $browserCachedCopyTimestamp = strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']));
          if ($browserCachedCopyTimestamp >= $lastModifiedTimeStamp) {
              header("HTTP/1.0 304 Not Modified");
              exit(0);
          }
      }
    
      header('Content-Length: '.filesize($fileName));
      header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + 604800)); // (3600 * 24 * 7)
      header('Last-Modified: '.date('D, d M Y H:i:s \G\M\T', $lastModifiedTimeStamp));
    ?>
    

    【讨论】:

    • 如果客户只需要最后一行,你会说我应该使用它吗?
    • 'script 1' 替换了您的 2 个脚本。 readfile() 读取整个文件并将其发送到浏览器。如果您不确定,请忘记“脚本 2”
    【解决方案3】:

    第一件事:你真的需要优化吗?这是您的用例中最慢的部分吗?您是否使用过xdebug 来验证这一点?如果你已经这样做了,请继续阅读:

    你不能真正有用地优化第一个脚本:如果你需要一个 http-request,你就需要一个 http-request。但是,如果可能的话,跳过 http 请求可能会提高性能(即,如果第一个脚本可以访问第二个脚本将操作的相同文件)。

    至于第二个脚本:将整个文件读入内存确实看起来有些开销,但如果文件很小,那可以忽略不计。代码看起来非常易读,在这种情况下我会保持原样。

    但是,如果您的文件很大,您可能希望使用fopen() 及其朋友fseek()fread()

    # Do not forget to sanitize the file name here!
    # An attacker could demand the last line of your password
    # file or similar! ($fileName = '../../passwords.txt')
    $filePointer = fopen($fileName, 'r');
    $i = 1;
    $chunkSize = 200;
    # Read 200 byte chunks from the file and check if the chunk
    # contains a newline
    do {
        fseek($filePointer, -($i * $chunkSize), SEEK_END);
        $line = fread($filePointer, $i++ * $chunkSize);
    } while (($pos = strrpos($line, "\n")) === false);
    return substr($line, $pos + 1);
    

    【讨论】:

    • 感谢您富有洞察力的回复。我以前没有使用过那个(xdebug),但我现在正在研究它。我读到的文本文件不超过 20 行,每行大约 5/6 个字长。
    • 那么我不会碰代码。解释循环的开销甚至可能大于该大小文件的增益:)
    【解决方案4】:

    如果文件不变,则应缓存最后一行。

    如果文件正在更改并且您控制它们的生成方式,则反转写入行的顺序可能会或可能不会是一种改进,具体取决于一行在其生命周期内被读取的频率。

    编辑:

    您的服务器可以确定要写入日志的内容,将其放入内存缓存中,然后将其写入日志。最后一行的请求可以从内存缓存而不是文件读取来完成。

    【讨论】:

    • 是的,文件一直在变化。我喜欢最后一个想法,颠倒行的顺序,所以我只检索第一行。我需要我能获得的每一点性能提升!
    【解决方案5】:

    最可能的延迟来源是跨服务器 HTTP 请求。如果文件很小,fopen/fread/fclose 的开销与整个 HTTP 请求相比是微不足道的。

    (不久前,我使用 HTTP 检索图像以动态生成基于图像的菜单。将 HTTP 请求替换为本地文件读取将延迟从几秒减少到十分之一秒。)

    我认为直接访问文件服务器文件系统的明显解决方案是不可能的。如果没有,那么这是最好和最简单的选择。

    如果没有,您可以使用缓存。您只需发出 HEAD 请求并将时间戳与本地副本进行比较,而不是获取整个文件。

    另外,如果您基于相同的文件对大量客户端进行 ajax 更新,您可能会考虑使用 comet(例如,meteor)。它用于诸如聊天之类的事情,其中​​必须将单个更改广播给多个客户端。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-27
      相关资源
      最近更新 更多