如何从一个单独的PHP脚本执行一个PHP脚本,而不会丢失第二个脚本的输出?

我目前正在使用HTML和PHP来允许用户在Web表单中input信息以生成Excel文件。 提交时,表单运行一个PHP文件main.php:

<?php // output headers so that the file is downloaded rather than displayed $order = $_POST["order"]; header('Content-Type: text/plain; charset=utf-8'); header('Content-Disposition: attachment; filename="'.$_POST['order'].'.xls"'); exec('php xl.php'); sleep(2); readfile('xl_template.xls'); error_reporting(E_ALL); ini_set("display_errors", 1); ?> 

其中“xl.php”是另一个PHP文件,“xl_template”是我希望修改的Excel表格的模板。 main.php的目的是抓取修改后的模板并下载到用户计算机上,而xl.php实际上修改了Excel模板并将其保存到服务器计算机上(使用PHPExcel库):

 <?php // this file will be called by main.php // after execution, there should be a newfile.xls // for the main.php to read from error_reporting(E_ALL); require('Classes/PHPExcel.php'); // variable definitions $template = "xl_template.xls"; $objPHPExcel = PHPExcel_IOFactory::load($template); $objWorksheet = $objPHPExcel->getActiveSheet(); $objWorksheet->getCell('A2')->setValue('123400000000000000000001'); $objWorksheet->getCell('B2')->setValue('it worked!'); $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save($template); ?> 

这可以按照预期的方式从terminal调用xl.php文件,然后填写表单。 然而,当通过exec('php xl.php')方法从main.php中调用exec('php xl.php') ,Excel文件并没有得到更新,我认为这意味着xl.php没有成功执行。

除了exec() ,我已经尝试了system()shell_exec ,反引号操作符和passthru() ,结果相同。 另外,我尝试了Javascript和JQuery方法,用$.get('xl.php')$.ajax({url: 'xl.php'})调用xl.php文件,但没有运气。

任何洞察到这个问题将不胜感激,因为我还是使用PHP的新手。 谢谢。

我会build议做两个单独的函数,并按照您需要的顺序调用它们,以防止发生错误。 这是做到这一点的最有效的方法。

如果你真的想使用不同的页面,你可能可以使用PHP中的header()函数redirect到第二个php页面,并包含任何需要在URL中传递的variables,并使用$ _GET()检索它们。

问题1

您正在使用一种非常奇怪的方式加载第二个PHP文件。 虽然exec("php xl.php")确实会执行xl.php ,但它会像从命令行执行一样。 你的xl.php将无法访问外部脚本中的任何variables或其他信息,也不能向发送给用户的输出写入任何内容。 另外,以这种方式debugging第二个脚本中出现的任何错误要困难得多。

你应该改用require("xl.php"); 这将运行xl.php文件,就好像它的内容是你的外部脚本的一部分。

问题2

您正在写入服务器上的文件。 这是你应该避免做的事情,除非绝对必要(例如用户上传)。

  • 在你的情况下,写入可能首先失败,因为你把它写入到你的PHP文件所在的同一个目录中。通常,PHP没有写入权限 – 这是一个很好的理由,因为如果你的web服务器Apache)只能在这个目录下运行PHP文件,而PHP没有写入的权利,那么攻击者就很难把他们自己的恶意PHP代码放在那里运行,或者修改其中一个您的脚本添加恶意代码。
  • 假设你已经解决了权限问题(这应该通过在PHP脚本目录之外使用一个单独的目录来完成,可能只是临时目录 – 而不是通过改变权限来允许写入脚本目录!),那么你还有另一个问题:你的文件名是不变的。 现在假设两个用户同时下载一个Excel文件…一切都会搞砸,因为两个会话将尝试从同一个文件写入/读取。
  • 假设你使用一个随机的临时名字来解决这个问题(然后在最后再次删除这个文件),那么当你不需要的时候,你仍然在访问硬盘。 您可以直接将Excel文件的内容从内存直接传输到用户的浏览器。

所以,最好的解决scheme是根本不使用文件,而是直接将数据发送给用户。 这可以使用伪文件名php://output来完成,它基本上将所有写入数据的数据直接发送给用户。

问题3

您的内容types标题指定text/plain和UTF-8为字符集,但是您要发送Excel文件。 由于Excel文件不是纯文本(也可能不是UTF-8),所以应该使用正确的MIMEtypes,即application/vnd.ms-excel

问题4

只有在所有操作都已经发生之后,您才能进行错误报告。 这意味着如果发生错误,将不会以您期望的方式报告,因为在报告启用之前就会发生错误。

最后的例子

结合上面提到的修复,你的代码可能看起来像这样:

main.php:

 <?php error_reporting(E_ALL); ini_set("display_errors", 1); // output headers so that the file is downloaded rather than displayed $order = $_POST["order"]; header('Content-Type: application/vnd.ms-excel'); header('Content-Disposition: attachment; filename="'.$_POST['order'].'.xls"'); require("xl.php"); ?> 

xl.php:

 <?php // this file will be called by main.php require('Classes/PHPExcel.php'); $objPHPExcel = PHPExcel_IOFactory::load($template); $objWorksheet = $objPHPExcel->getActiveSheet(); $objWorksheet->getCell('A2')->setValue('123400000000000000000001'); $objWorksheet->getCell('B2')->setValue('it worked!'); $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); $objWriter->save("php://output"); ?> 

最后的想法

现在你改变了这样的代码,最好是考虑这个构造是否需要第二个PHP文件,如果是这样的话,是否你可能想把代码包装在一个函数中,然后调用它从外部的脚本,所以你可以很好地传递参数,因为我认为你不会总是写静态“它的工作! 在那里。