I am currently working on a project that uses HTML and PHP to allow a user to enter information into a web form in order to generate an Excel file. On submission, the form runs a PHP file, 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);
?>
where "xl.php" is another PHP file and "xl_template" is a template for the Excel sheet I wish to modify. The purpose of main.php is to grab the modified template and download it to the user's computer, while xl.php actually modifies the Excel template and saves it to the server computer (using PHPExcel library):
<?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);
?>
This works as expected when the xl.php file is called from terminal and then the form is filled out. However, when xl.php is called from main.php through the exec('php xl.php')
method, the Excel file does not get updated, which I assume to mean that xl.php was not successfully executed.
In addition to exec()
, I have tried system()
,shell_exec
, the backtick operator, and passthru()
, with the same results. Also, I have tried Javascript and JQuery methods, calling the xl.php file with $.get('xl.php')
and $.ajax({url: 'xl.php'})
with no luck.
Any insight into this problem would be greatly appreciated, as I am still new to using PHP. Thanks.
I would suggest making two separate functions and calling them in the order you need to to prevent the error from occurring. This is the most efficient way to do this.
If you really want to use a different page, you could could possibly use the header() function in php to redirect to the second php page, and include any variables you need to pass in the URL and retrieve them using $_GET().
You are using a pretty weird way of loading your second PHP file. While exec("php xl.php")
will indeed execute xl.php
, it will do it as if you were executing it from a command line. Your xl.php
will not have access to any variables or other information from your outer script, and it can't write anything to the output which is sent to the user. Also, it is much harder to debug any errors occuring in the second script this way.
You should instead use require("xl.php");
which will run the xl.php
file at this point as if its content were directly part of your outer script.
You are writing to a file on the server. This is something you should avoid doing unless absolutely necessary (e.g. for user uploads).
So, the best solution would be not using a file at all, but directly sending the data to the user. This can be done using the pseudo-filename php://output
which basically sends all data written to it directly to the user.
Your content type header specifies text/plain
and UTF-8 as charset, but you are sending an Excel file. Since an Excel file is not plain text (and probably not UTF-8 either), you should use the right MIME type instead, which is application/vnd.ms-excel
.
You are turning error reporting on only after all the action already happened. This means that if an error occured, it will not be reported the way you expect, because it happens before the reporting is enabled.
Incorporating the fixes mentioned above, your code could look something like this:
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");
?>
Now that you change the code like this, it would probably be best to think about whether this construct with the second PHP file is even needed like this, and if it is, whether you maybe want to wrap the code in a function and call it from the outer script so you can nicely pass parameters as well, because I assume you are not always going to write just staticly "It works!" in there.