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

Posted

技术标签:

【中文标题】如何从单独的 php 脚本执行 php 脚本而不会丢失第二个脚本的输出?【英文标题】:How to execute a php script from a separate php script without losing output of second script? 【发布时间】:2016-10-02 10:44:03 【问题描述】:

我目前正在开发一个项目,该项目使用 htmlphp 允许用户将信息输入 Web 表单以生成 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);
?>

当从终端调用 xl.php 文件然后填写表单时,这将按预期工作。但是,当通过exec('php xl.php') 方法从main.php 调用xl.php 时,Excel 文件没有得到更新,我认为这意味着xl.php 没有成功执行。

除了exec(),我还尝试了system()shell_exec、反引号运算符和passthru(),结果相同。另外,我尝试过 javascript 和 JQuery 方法,使用 $.get('xl.php')$.ajax(url: 'xl.php') 调用 xl.php 文件,但没有成功。

任何对此问题的见解将不胜感激,因为我仍然是使用 PHP 的新手。谢谢。

【问题讨论】:

这么多文字 - 你的问题是什么?你不能从 php 脚本调用 xl.php? 为什么要启动一个单独的 php 来执行该外部脚本?你不能把第二个脚本写成函数调用,然后简单地include(),然后做run_excel_stuff()吗? @Xatenev:我可以调用它,但它不会修改我在服务器端拥有的 Excel 文件。 @MarcB:我也尝试了include() 方法,结果相同。我必须分离文件的原因是,如果 main.php 中的 readfile() 方法在它之前调用函数会导致错误。所以我需要在调用readfile()之前修改Excel文件。 系统调用可能被禁止。为什么readfile 在以前执行过一个函数时会导致错误?当您输出错误和警告消息而不是记录它们时,会出现文件格式错误。如果修改只是针对单个请求,则无需将内容存储到磁盘。 【参考方案1】:

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

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

【讨论】:

我同意这一点。我认为只有一个 php 文件和 2 个按顺序调用的函数是最简单的方法。如果要将它们放在两个文件中,可以将一个文件导入另一个文件,然后调用第二个文件中的函数。 就像我说的,我对 PHP 还很陌生,所以我会再试一次函数调用,以防万一我的错误是由我而不是 PHP 引起的。 搞定了。我不得不移动一些东西,创建一些函数,并更改一些文件权限,但它终于奏效了。非常感谢您的帮助。 然而,这不是你应该做的!尽可能避免使用临时文件,它们会引入不必要的开销。在您的情况下,您甚至将其保存在脚本文件夹而不是临时文件夹中,并且由于文件名固定,您甚至会出现竞争条件!想象一下,如果两个人同时下载 excel 文件会发生什么……正确的做法是使用$objWriter-&gt;save("php://output");,其中php://output 是一个假文件名,它将直接将其流式传输给用户。因此,您需要使用require("xl.php"); 而不是exec("php xl.php"); 另外,您发送的 mime 类型错误。我不认为excel文件是纯文本。你需要header('Content-type: application/vnd.ms-excel');【参考方案2】:

问题 1

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

您应该改用require("xl.php");,此时它将运行xl.php 文件,就好像它的内容直接属于您的外部脚本一样。

问题 2

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

假设您解决了权限问题(这应该通过使用 PHP 脚本目录之外的单独目录来完成,可能只是临时目录 - 而不是通过更改权限以允许写入脚本目录!),那么您还有另一个问题:您的文件名是不变的。现在假设两个用户同时下载一个 Excel 文件......一切都会变得一团糟,因为两个会话将尝试写入/读取同一个文件。 假设您通过使用随机临时名称解决了此问题(然后在最后再次删除该文件),那么仍然存在您在不需要硬盘时访问硬盘的问题。您可以直接将 Excel 文件的内容从内存流式传输到用户的浏览器。

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

问题 3

您的内容类型标头将 text/plain 和 UTF-8 指定为字符集,但您发送的是 Excel 文件。由于 Excel 文件不是纯文本(也可能不是 UTF-8),因此您应该改用正确的 MIME 类型,即application/vnd.ms-excel

问题 4

只有在所有操作都发生后,您才开启错误报告。这意味着如果发生错误,将不会按照您预期的方式报告,因为它发生在启用报告之前。

最后一个例子

结合上述修复,您的代码可能如下所示:

ma​​in.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 文件的这种构造,如果需要,您是否可能希望将代码包装在一个函数中并从外部脚本调用它,这样你也可以很好地传递参数,因为我假设你并不总是静态地写“它有效!”在那里。

【讨论】:

以上是关于如何从单独的 php 脚本执行 php 脚本而不会丢失第二个脚本的输出?的主要内容,如果未能解决你的问题,请参考以下文章

Crontab 不会从 PHP 脚本中保存图像

从 PHP 控制器使用 request.post() 调用 python 脚本永远不会返回。为啥?

PHP脚本显示图像BLOB执行了两次?

如何通过php页面执行shell脚本?

php载入脚本的几种方式对比

如何使用 AJAX 执行 PHP 电子邮件脚本? [当前代码不起作用]