如何优化这个简单的PHP脚本?

时间:2009-06-10 11:52:31

标签: php optimization file performance

通过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),至少我觉得它很贵!

5 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

此脚本应限制它将返回的位置和文件类型。

想想有人试图这样做:

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

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

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

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

答案 2 :(得分:0)

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

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

编辑:

您的服务器可以弄清楚它要写入日志的内容,将其放入内存缓存中,然后将其写入日志。最后一行的请求可以通过memcache而不是文件读取来完成。

答案 3 :(得分:0)

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

您无法真正有效地优化第一个脚本:如果您需要http请求,则需要http请求。跳过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);

答案 4 :(得分:0)

最可能的延迟来源是跨服务器HTTP请求。如果文件很小,fopen / fread / fclose的成本与整个HTTP请求相比毫无结果。

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

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

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

此外,如果您基于相同的文件更新了许多客户端,您可能会考虑使用comet(例如meteor)。它用于聊天等内容,必须向多个客户广播单个更改。