Call a .phar executable from inside a web app controller - php

I need to run composer.phar update from a web controller.
I can run all kinds of regular commands in this way (ls, cp, etc) but when I invoke the phar file I get empty output.
The code I have looks like this:
class Maintenance_Controller
{
public function do_maintenance()
{
echo exec("/usr/bin/env php composer.phar", $out, $ret);
var_dump($out); // outputs -> array()
var_dump($ret); // outputs -> int(127)
}
}
127 indicates a bad path, but I am sure I'm in the right directory.
Also, this works when using a php_cli wrapper, so maybe it has to do with the www-data user? chmod 777 does not help, and I hate to do that anyway.
I have also used passthru(), system() and the backtic syntax. I am unable to get to the reason this doesn't work. I can't seem to interrogate the stderr or stdout from the exec() call beyond the 127 code.
Obvious Question:
What am I doing wrong?
Better Question:
Is there a better way to interrogate and execute .phar files from within a script?

UPDATE:
From this question:
Value 127 is returned by /bin/sh when the given command is not found within your PATH system variable and it is not a built-in shell command.
Try using exec('php composer.phar', $out, $ret); and see if that works. You might also need to use the full path to php if its in a non-standard location which you can probably get from which php.
Im not sure why you are using passthru here. I would use exec for better handling.
Id use exec here instead of passthru
class Maintenance_Controller
{
public function do_maintenance()
{
exec("composer.phar update", $out, $ret);
if(!$ret) {
// handle success
} else {
// handle error
}
}
}
This way you have all the output by line in $out as well as the shell return val (0 if ok, > 0 if not). If you wanna get really fancy you can loop over $out and scan for the errors and then build and exception to throw.

Related

PHP: how can I call a function in a non-blocking fashion?

I saw a couple of other question on the issue but not a clear answer.
I've a PHP file (must be PHP, cannot cron or other stuff) running from CLI where I must call the same function multiple time with different arguments:
doWork($param1);
doWork($param2);
doWork($param2);
function doWork($data)
{
//do stuff, write result to db
}
Each call makes HTTPs requests and parses the response. The operation can require up to a minute to complete. I must prevent the "convoy effect": each call must be executed without waiting for the previous one to complete.
PECL pthread is not an option due to server constraints.
Any ideas?
As far as I know you cannot do what you are looking for.
Instead of calling a function with its parameters, you have to call another cli php script in a nonblocking manner and put your function in that script.
This is your main script:
callDoWork($param1);
callDoWork($param2);
callDoWork($param3);
function callDoWork($param){
$cmd = 'start "" /b php doWork.php '.$param;
//if $param contains spaces or other special caracters for the command line,
// you have to escape them.
pclose(popen($cmd);
}
doWork.php would look like :
if(is_array($_SERVER['argv'])) $param = $_SERVER['argv'][1];
doWork($param);
function doWork($data)
{
//do stuff, write result to db
}
More information about argv.
How about adding "> /dev/null 2>/dev/null &"
exec('php myFile.php > /dev/null 2>/dev/null &');
You can check the documentation for more

How to run a linux command to compile a c program, from php script

I am trying to make an online judge for c programming. When user enters the c code and submits it, my form redirects to judge.php which is the action file for the form.
Here is what I have written in judge.php
<?php
$text=$_POST['code'];
//echo $text;
$var_str = var_export($text, true);
file_put_contents('code.c', $text);
$ans=exec('pwd');
$ans= exec('gcc code.c');
echo $ans;
?>
I have captured user input in $text and wrote it to a c file(code.c). Till now, it is fine.
But exec(gcc code.c) is not working and not giving any output. I tried other linux commnads like pwd, date, etc. They are working fine. What may be the reason for this and how to fix it?
It is not a directory issue i tried exec(pwd) and it gave the output as the same directory in which code is present.
I tried to run same code.c file from terminal and it is running fine. So, it is also not a 'permission' problem.
One more thing, how to echo the error message generated if any exec() command is not working properly?
After getting suggestion from the answer below, i tried
$cmd="gcc -std=c99 code.c -g -Wall mysql_config --libs --cflags -o db_obj.o --pedantic";
exec($cmd,$out,$status);
But it is also not working. The status returned is 1
Most probably it is permission issue. "whoami" says nobody. Please tell how to change the owner from nobody to root or how to assign the permission to execute gcc from nobody
Three main aspects to my answer
improper use of the exec function.
Look at the man pages. First, the exec function's signature is:
string exec ( string $command [, array &$output [, int &$return_var ]] )
So exec can take up to 3 arguments. It returns the last line of the command's output, like the docs state quite clearly:
The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.
To get the output of the executed command, be sure to set and use the output parameter.
So in your case:
$lastLine = exec($command, $fullOutput, $status);
Is what you're looking for. If $status is anything else than 0, your command was unsuccessful. That's what you should check to react accordingly.
The full output of any command can be found in $fullOutput as a line-per-line array.
Output like:
all went well
except for this
Will look like this in the $fullOutput array:
array('all went well', 'except for this');
permissions can be an issue, still.
You say permissions aren't likely to be the cause of the problem, because you can run gcc from the command-line. All fine and dandy, but what user is running the PHP script on the server?
In the case of web-servers, that user is often called nobody, apache or something, and that user is very likely not permitted to run gcc. It's PHP that runs a new instance of whatever default shell it has set up (bash, probably), and it's PHP's user that logs in to that shell, and it's that user that is calling gcc...
Know who you are, and what groups you belong to. Try adding this to your script:
echo 'Script is running under user: ', exec('whoami'), '<br>', PHP_EOL;
echo 'member of the following groups: ', exec('groups'), '<br>', PHP_EOL;
And before you ask: yes, those are comma's... no need to concatenate, you can pass multiple variables/values to echo, separated by a comma. It's actually faster (think of it as C++'s std::cout << some_var << another_var;)
general issues + security
This all said and done: compiling C code from a php script isn't as simple as you seem to think it is. Suppose I were to write this:
#include <stdio.h>
#include <time.h>
int main ( void )
{
time_t t = time(NULL);
if (t%2)
{
float val = (float) t/2.0;
//do stuff with float...
}
else
{
unsigned long long val = t/2;
//do stuff with unsigned long long...
}
}
Your gcc test.c command would fail, because you failed to pass the argument -std=c99, for example.
If I wanted a script to compile a given file, I'd also expect that script to allow me to choose which arguments I compiled my code with, too -g, -Wall and, not to mention: cflags and libs (the output of pkg-config or mysql_config --cflags --libs, to name a specific example I recently used).
Basically, your script simply cannot deal with my wanting to compile something with a commind like
gcc -std=c99 code.c -g -Wall `mysql_config --libs --cflags` -o db_obj.o --pedantic
Which still is a simplified version of what many compilation commands look like, especially when debugging code under development. For stable releases, you'd probably drop -g and --pedantic, but you get my point...
Just think of what it means, allowing the user to pass a set of cli arguments, along with the code. They might pass an argument like -DSOME_MACRO or -O0, which means they might also pass -O0 && rm -Rf *. That means you'll have to call escapeshellcmd or escapeshellarg. Both of which will prohibit me from passing a valid argument, being:
`mysql_config --libs --cflags`
Which contains back-ticks, and thus will be escaped.
To be frank, I struggle to see the point of this exercise... and I'm leaving a lot out, still: the dangers of compiling (let alone running) user-provided code on your machine, for example, are not to be overlooked. You can't just compile code, and run it on your server: memory leaks, segfaults... heck, pure evil code is all getting compiled on your server unchecked if this is the code you have. Really, save yourself a lot of tears, and include an iframe that loads codepad or some similar service...
Recap:
always check the man for a function, see if you're getting all information it returns
check the permissions and runtime for the user that is actually executing the commands
Never trust the network, don't blindly assume people will submit valid, harmless code for you to compile.
Don't reinvent the wheel: compilation services exist, just forward those (but ask for permission first)
Try this code to execute c program from PHP file
<?php
// used to compile the c file using exec() in php
exec('gcc helloworld.c -o helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
// used to execute the c file using exec() in php
exec('./helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
} else {
echo "Command failed with status: $status";
}
} else {
echo "Command failed with status: $status";
}
?>

retrieve R output in PHP

Here's the issue:
I am using R to run some statistical analysis. The results of which will eventually be sent to a an embedded swf on the user's client machine.
To do this, I have PHP execute a shell script to run the R program, and I want to retrieve the results of that program so I can parse them in PHP and respond with the appropriate data.
So, it's simply:
$output = shell_exec("R CMD BATCH /home/bitnami/r_script.R");
echo $output;
But, I receive nothing of course, because R CMD BATCH writes to a file. I've tried redirecting the output in a manner similar to this question which changes my script to
$output = shell_exec('R CMD BATCH /home/bitnami/raschPL.R /dev/tty');
echo $output;
But what I get on the console is a huge spillout of the source code, and nothing is echoed.
I've also tried this question's solution in my R script.
tl;dr; I need to retrieve the results of an R script in PHP.
Cheers!
If it writes to file perhaps you could use file_get_contents to read it?
http://php.net/manual/en/function.file-get-contents.php
Found it, the answer is through Rscript. Rscript should be included in the latest install of R.
Using my code as an example, I would enter this at the very top of r_script.R
#!/usr/bin/Rscript --options-you-need
This should be the path to your Rscript executable. This can be found easily by typing
which Rscript
in the terminal. Where I have --options-you-need, place the options you would normally have when doing the CMD BATCH, such as --slave to remove extraneous output.
You should now be able to run your script like so:
./r_script.R arg1 arg2
Important! If you get the error
Error in `contrasts<-`(`*tmp*`, value = "contr.treatment") :
could not find function "is"
You need to include the "methods" package, like so:
require("methods");
Perhaps,a much simpler workaround, would be:
$output = shell_exec('R CMD BATCH /home/bitnami/raschPL.R > /dev/tty 2>&1');
echo $output;
Redirects both STDOUT and STDERR, since R outputs to STDERR, by default.

How can I execute the LESS compiler from PHP (using Symfony)?

I'm having trouble executing the lessc compiler from PHP. I'm using Symfony, and have tried using the sfLESSPlugin, but have been unsuccessful. I've put my code in a filter that executes before the page renders, so that every time the page is refreshed, my LESS files are compiled into one CSS file (don't want to have recompile manually every time I make a change, at least while I'm developing). Here are the different variations that I've attempted:
$fs = new sfFilesystem();
$command = '/Users/jordanb/node/node_modules/less/bin/lessc less/bootstrap.less css/bootstrap.css';
try
{
$fs->execute($command, null, array($this, 'throwCompilerError'));
}
catch (RuntimeException $e)
{
return false;
}
This returns an error: "Problem executing command", with an error code of 127. Digging deeper into Symfony's execute(), it calls proc_open() and then proc_close(). Some research online told me that an error code of 127 means that the command was not found.
Running the exact same command on the command line works just fine.
To be extra sure, I executed chmod 777 on /Users/jordanb/node/node_modules/less/bin/lessc, just to make sure it wasn't a permissions issue. Still didn't work.
I also tried just "lessc" instead of the full path, which didn't work. I've added lessc to my classpath, so typing "which lessc" gives me "/Users/jordanb/node/node_modules/less/bin/lessc".
I also tried a simple:
shell_exec('lessc less/bootstrap.less css/bootstrap.css');
which didn't seem to do anything. I printed the output to the PHP error log as well as to a text file, and the output was empty in both cases. I also tried using the full path in this case.
You can make a shell script that compiles things the way you want:
compile_css.sh
/Users/jordanb/node/node_modules/less/bin/lessc less/bootstrap.less css/bootstrap.css
Then
chmod +x compile_css.sh
And finally call it from your script
$command = '/path/to/compile_css.sh';
This will give you more control over what you can execute, but if you still want to do it from php, try this:
$command = '/Users/jordanb/node/node_modules/less/bin/lessc "less/bootstrap.less css/bootstrap.css"';
// note the quotes around the arguments.

PHP exec() return value for background process (linux)

Using PHP on Linux, I'd like to determine whether a shell command run using exec() was successfully executed. I'm using the return_var parameter to check for a successful return value of 0. This works fine until I need to do the same thing for a process that has to run in the background. For example, in the following command $result returns 0:
exec('badcommand > /dev/null 2>&1 &', $output, $result);
I have put the redirect in there on purpose, I do not want to capture any output. I just want to know that the command was executed successfully. Is that possible to do?
Thanks, Brian
My guess is that what you are trying to do is not directly possible. By backgrounding the process, you are letting your PHP script continue (and potentially exit) before a result exists.
A work around is to have a second PHP (or Bash/etc) script that just does the command execution and writes the result to a temp file.
The main script would be something like:
$resultFile = '/tmp/result001';
touch($resultFile);
exec('php command_runner.php '.escapeshellarg($resultFile).' > /dev/null 2>&1 &');
// do other stuff...
// Sometime later when you want to check the result...
while (!strlen(file_get_contents($resultFile))) {
sleep(5);
}
$result = intval(file_get_contents($resultFile));
unlink($resultFile);
And the command_runner.php would look like:
$outputFile = $argv[0];
exec('badcommand > /dev/null 2>&1', $output, $result);
file_put_contents($outputFile, $result);
Its not pretty, and there is certainly room for adding robustness and handling concurrent executions, but the general idea should work.
Not using the exec() method. When you send a process to the background, it will return 0 to the exec call and php will continue execution, there's no way to retrieve the final result.
pcntl_fork() however will fork your application, so you can run exec() in the child process and leave it waiting until it finishes. Then exit() with the status the exec call returned.
In the parent process you can access that return code with pcntl_waitpid()
Just my 2 cents, how about using the || or && bash operator?
exec('ls && touch /tmp/res_ok || touch /tmp/res_bad');
And then check for file existence.

Categories