I am trying to have a script start another script and put its data into a session variable for the other script to use. The problem is that when the second script, data.php, runs it doesn't seem to be able to access the session variables. They are blank and nothing gets written to data.txt. If I run data.php by itself it writes the last value that $_SESSION["data"] was set to properly, but not when it's run with exec. I am not sure what the problem is. Any ideas?
input.php:
session_start();
$_SESSION["data"] = "Data!";
exec("/usr/bin/php /path/to/data.php > /dev/null 2>&1 &");
data.php:
session_start();
$fp = fopen('data.txt', 'w');
fwrite($fp, $_SESSION["data"]);
fclose($fp);
Edit: I am trying to start data.php from inside input.php and have the variables from input.php accessible in data.php.
You can pass data to PHP scripts running with the CLI as command line arguments. This data will be available to the child script in the $argv array.
input.php:
$arg = "Data!";
exec("/usr/bin/php /path/to/data.php ".escapeshellarg($arg)." > /dev/null 2>&1 &");
data.php
$fp = fopen('data.txt', 'w');
fwrite($fp, $argv[1]);
fclose($fp);
A couple of notes:
It is important to pass each argument through escapeshellarg() to ensure that users are not able inject commands into your shell. This will also stop special shell characters in arguments from breaking your scripts.
$argv is a global variable, not a superglobal like $_GET and $_POST. It is only available in the global scope. If you need to access it in a function scope, you can use $GLOBALS['argv']. This is about the only situation in which I consider the use of $GLOBALS acceptable, although it is still better to handle the arguments in the global scope on startup, and pass them through the scopes as arguments.
$argv is a 0-indexed array, but the first "argument" is in $argv[1]. $argv[0] always contains the path to the currently executing script, because $argv actually represents the arguments passed to the PHP binary, of which the path to your script is the first.
Values from command line arguments always have a string type. PHP is very promiscuous with its typing so with scalar values this doesn't matter, but you (fairly obviously) can't pass vector types (objects, arrays, resources) through the command line. It is possible to pass objects and arrays by encoding them with e.g. serialize() or json_encode(). There is no way to pass resources through the command line.
EDIT When passing vector types I prefer to use serialize() because it carries with it information about the classes that objects belong to.
Here is an example:
input.php:
$arg = array(
'I\'m',
'a',
'vector',
'type'
);
exec("/usr/bin/php /path/to/data.php ".escapeshellarg(serialize($arg))." > /dev/null 2>&1 &");
data.php
$arg = unserialize($argv[1]);
$fp = fopen('data.txt', 'w');
foreach ($arg as $val) {
fwrite($fp, "$val\n");
}
fclose($fp);
Here is a couple of functions from my clip collection I use to simplify this process:
// In the parent script call this to start the child
// This function returns the PID of the forked process as an integer
function exec_php_async ($scriptPath, $args = array()) {
$cmd = "php ".escapeshellarg($scriptPath);
foreach ($args as $arg) {
$cmd .= ' '.escapeshellarg(serialize($arg));
}
$cmd .= ' > /dev/null 2>&1 & echo $$';
return (int) trim(exec($cmd));
}
// At the top of the child script call this function to parse the arguments
// Returns an array of parsed arguments converted to their correct types
function parse_serialized_argv ($argv) {
$temp = array($argv[0]);
for ($i = 1; isset($argv[$i]); $i++) {
$temp[$i] = unserialize($argv[$i]);
}
return $temp;
}
If you need to pass a large amount of data (larger than the output of getconf ARG_MAX bytes) you should dump the serialized data to a file and pass the path to the file as a command line argument.
You could try to urlencode the $_SESSION ["data"] and pass it as an argument to the CLI script:
Script 1
$URLENCODED = urlencode($_SESSION["data"]);
exec("/usr/bin/php /path/to/data.php " . $URLENCODED . " > /dev/null 2>&1 &")
Script 2
$args = urldecode($argv[1]); // thanks for the reminder daverandom ..forgot to do this :)
fwrite($fp, $args);
Related
below is my PS1 code to call PHP. But I want to know how to call the PHP page with 3 arguments and how to get it in PHP.
$workflow_id = 170
$task_num = 3
$next_script = 'testing.php'
$PhpExe = "C:\Admin\bin\php\php7.4.26\php.exe"
$PhpFile = "C:\Admin\www\xpress\"+$next_script+" "+$workflow_id+" "+$task_num
echo $PhpFile
$PhpArgs = '-f "{0}"' -f $PhpFile
$PhpOutput = & $PhpExe $PhpArgs
echo $PhpOutput // could not open file. but the file is present in this path
// testing.php
<?php include("commons/connection.php"); ?>
<?php
$workflow_id = $argv[1];
$task_num = $argv[2];
$update_pass = "UPDATE workflow_details SET Status ='mm' where `Workflow_Number` = $workflow_id and Work_type = 'PrepWork' and Task_Number = $task_num " ;
$status_result=mysqli_query($con,$update_pass);
?>
I am getting could not open file. while running ps1
Your attempt to call your PHP script from PowerShell is flawed in two respects:
$PhpArgs is a single string, which is therefore passed as a single argument, whereas you need to pass -f and the PHP script file name/path as individual arguments.
You're not passing any arguments to pass through to the PHP script.
Therefore, invoke $PhpExe as follows:
# Call script file $PhpFile with the values of $workflow_id and $task_num
# as arguments, and capture the output in variable $PhpOutput
$PhpOutput = & $PhpExe -f $PhpFile -- $workflow_id $task_num
I have a php script that calls a go script. It gets results every 1-2 seconds, and print's them. Using php's exec and output, I only get the results when the program finishes. Is there a way I can check the output to see when it changes and output that while it's still running?
Something like this, but pausing the execution?:
$return_status = 0;
$output = [];
$old_output = ["SOMETHING ELSE"];
while ($return_status == 0) {
exec($my_program,$output,$return_status); #somehow pause this?
if $output != $old_output {
echo($output);
$old_output = $output;
}
}
Yes. Use the popen() function to get a file handle for the command's output, then read from it a line at a time.
I have a php script that is called and it executes a shell command through shell_exec(). This shell command requires multiple user inputs at different stages. I am stuck with getting the interactivity piece working at each input stage.
This is just an example of how I imagine it working...
<?php
$return = shell_exec('runcmd');
//Let runcmd run until first break
echo $return;
$userinput1 = 'foo';
//Let runcmd run until next break
echo $return;
$userinput2 = 'bar';
//Let runcmd run until nth break
echo $return;
$userinputNth = 'nth';
To feed input to a shell command, use popen.
$handle = popen('runcmd', 'w');
fputs($handle, 'foo
bar
nth
');
pclose($handle);
Since output is not captured, we needn't echo it.
I try to execute a python script with PHP But I got no results:I tried
$tmp = passthru("C:\\Python27\\python.exe C:\\Python27\\script.py C:\\Python27\\file.pdf",$output);
print($output)
The results :"1"
While
$tmp = exec("C:\\Python27\\python.exe C:\\Python27\\script.py C:\\Python27\\file.pdf",$output);
returns Array ( )
I'm excepted to return a string,any suggestions?
I verified in my php.ini file
safe_mode = Off
Thanks!!
The documentation of exec() says that the $output parameter is an array that will be filled in with each line. If you want to turn this into a single string, use:
$output_string = implode("\n", $output);
This should work for you:
ob_start();
passthru("C:\\Python27\\python.exe C:\\Python27\\script.py C:\\Python27\\file.pdf");
$filedata = ob_get_clean();
echo $filedata;
python buffers output by default. Set environment variable PYTHONUNBUFFERED to a nonempty string, or run python with -u.
Background
I am writing a simple online judge (a code grading system) using PHP and MySQL. It takes submitted codes in C++ and Java, compiles them, and tests them.
This is Apache running PHP 5.2 on an old version of Ubuntu.
What I am currently doing
I have a php program that loops infinitely, calling another php program by
//for(infinity)
exec("php -f grade.php");
//...
every tenth of a second. Let's call the first one looper.php and the second one grade.php. (Checkpoint: grade.php should completely finish running before the "for" loop continues, correct?)
grade.php pulls the earliest submitted code that needs to be graded from the MySQL database, puts that code in a file (test.[cpp/java]), and calls 2 other php programs in succession, named compile.php and test.php, like so:
//...
exec("php -f compile.php");
//...
//for([all tests])
exec("php -f test.php");
//...
(Checkpoint: compile.php should completely finish running before the "for" loop calling test.php even starts, correct?)
compile.php then compiles the program in test.[cpp/java] as a background process. For now, let's assume that it's compiling a Java program and that test.java is located in a subdirectory. I now have
//...
//$dir = "./sub/" or some other subdirectory; this may be an absolute path
$start_time = microtime(true); //to get elapsed compilation time later
exec("javac ".$dir."test.java -d ".$dir." 2> ".$dir
."compileError.txt 1> ".$dir."compileText.txt & echo $!", $out);
//...
in compile.php. It's redirecting the output from javac, so javac should be running as a background process... and it seems like it works. The $out should be grabbing the process id of javac in $out[0].
The real problem
I want to stop compiling if for some reason compiling takes more than 10 seconds, and I want to end compile.php if the program stops compiling before 10 seconds. Since the exec("javac... I called above is a background process (or is it?), I have no way of knowing when it has completed without looking at the process id, which should have been stored in $out earlier. Right after, in compile.php, I do this with a 10 second loop calling exec("ps ax | grep [pid].*javac"); and seeing if the pid still exists:
//...
$pid = (int)$out[0];
$done_compile = false;
while((microtime(true) - $start_time < 10) && !$done_compile) {
usleep(20000); // only sleep 0.02 seconds between checks
unset($grep);
exec("ps ax | grep ".$pid.".*javac", $grep);
$found_process = false;
//loop through the results from grep
while(!$found_process && list(, $proc) = each($grep)) {
$boom = explode(" ", $proc);
$npid = (int)$boom[0];
if($npid == $pid)
$found_process = true;
}
$done_compile = !$found_process;
}
if(!done_compile)
exec("kill -9 ".$pid);
//...
... which doesn't seem to be working. At least some of the time. Often, what happens is test.php starts running before the javac even stops, resulting in test.php not being able to find the main class when it tries to run the java program. I think that the loop is bypassed for some reason, though this may not be the case. At other times, the entire grading system works as intended.
Meanwhile, test.php also uses the same strategy (with the X-second loop and the grep) in running a program in a certain time limit, and it has a similar bug.
I think the bug lies in the grep not finding javac's pid even when javac is still running, resulting in the 10 second loop breaking early. Can you spot an obvious bug? A more discreet bug? Is there a problem with my usage of exec? Is there a problem with $out? Or is something entirely different happening?
Thank you for reading my long question. All help is appreciated.
I just came up with this code that will run a process, and terminate it if it runs longer than $timeout seconds. If it terminates before the timeout, it will have the program output in $output and the exit status in $return_value.
I have tested it and it seems to work well. Hopefully you can adapt it to your needs.
<?php
$command = 'echo Hello; sleep 30'; // the command to execute
$timeout = 5; // terminate process if it goes longer than this time in seconds
$cwd = '/tmp'; // working directory of executing process
$env = null; // environment variables to set, null to use same as PHP
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
// start the process
$process = proc_open($command, $descriptorspec, $pipes, $cwd, $env);
$startTime = time();
$terminated = false;
$output = '';
if (is_resource($process)) {
// process was started
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
// loop infinitely until timeout, or process finishes
for(;;) {
usleep(100000); // dont consume too many resources
$stat = proc_get_status($process); // get info on process
if ($stat['running']) { // still running
if (time() - $startTime > $timeout) { // check for timeout
// close descriptors
fclose($pipes[1]);
fclose($pipes[0]);
proc_terminate($process); // terminate process
$return_value = proc_close($process); // get return value
$terminated = true;
break;
}
} else {
// process finished before timeout
$output = stream_get_contents($pipes[1]); // get output of command
// close descriptors
fclose($pipes[1]);
fclose($pipes[0]);
proc_close($process); // close process
$return_value = $stat['exitcode']; // set exit code
break;
}
}
if (!$terminated) {
echo $output;
}
echo "command returned $return_value\n";
if ($terminated) echo "Process was terminated due to long execution\n";
} else {
echo "Failed to start process!\n";
}
References: proc_open(), proc_close(), proc_get_status(), proc_terminate()