mikehaertl / php-shellcommand Goto Github PK
View Code? Open in Web Editor NEWA simple object oriented interface to execute shell commands in PHP
License: MIT License
A simple object oriented interface to execute shell commands in PHP
License: MIT License
It looks like setStdIn()
doesn't work with useExec
.
I propose the following method to allow setStdIn()
to work with useExec
, at least when $this->_stdIn
is a string:
$tmp_file = tmpfile();
$tmp_path = stream_get_meta_data($tmp_file)['uri'];
fwrite($tmp_file, $this->_stdIn);
fflush($tmp_file);
$command = "cat ".escapeshellarg($tmp_path)." | $command";
...
fclose($tmp_file);
Using cat
is a bit ugly, but maybe someone can come up with an improvement. Something similar can be done when $this->_stdIn
is not a string.
Hanging test: https://github.com/dmstr/phd5-app/blob/master/tests/codeception/cli/DbDumpCept.php
Output:
Cli Tests (5) ------------------------------------------------------------------
✔ 1-InitCept: (4.90s)
✔ AuditCept: (0.10s)
make: *** [run-tests] Error 137
stdin: is not a tty
ERROR: Build failed: execution took longer than 3600 seconds
It's a rather simple DB-dump which takes <1s in the test setup.
Works fine after reverting to 1.2.2
How I can output to file ("command > result.txt") using methods ?
I expect that addArg()
escapes all arguments preventing possibility of command injection from untrusted sources.
Because in the README.md
I see:
Handle argument escaping
$escapeArgs
: Whether to escape any argument passed throughaddArg()
. Default istrue
.
But it is not actually happens.
<?php
require_once 'vendor/autoload.php';
use mikehaertl\shellcommand\Command;
$command = new Command(array(
'command' => 'curl',
'escapeArgs' => true,
));
// In this example "escapeArgs" is set to "true", but escaping is not happens.
$command->addArg('http://example.com --wrong-argument || echo "RCE 1"');
$command->execute();
echo $command->getOutput(); // RCE 1
$command = new Command(array(
'command' => 'curl http://example.com',
'escapeArgs' => true,
));
$command->addArg('http://example.com');
// In this example, the second argument will be escaped properly, but the first one - not.
$command->addArg('--header foo --wrong-argument || echo "RCE 2" ||', 'bar');
$command->execute();
echo $command->getOutput(); // RCE 2
This thread was initially started as a private email conversation. @mikehaertl asked me to open an issue here.
According to http://php.net/manual/en/function.proc-close.php
Hence we can't rely on the result, e.g. I see that PDF was created via mikehaertl/phpwkhtmltopdf but proc_close() returns int 3
(testing command via ssh also shows success). Next $this->_exitCode
is checked and since it's !==0
command fails.
As there are issues with proc_open()
in Windows, we should provide a configuration option to use exec()
instead of proc_open()
.
Drawback: We can not reliably fetch stderr anymore.
This would also fix mikehaertl/phpwkhtmltopdf#56
$pipes
variable must be initalizated before to be passed as argument to proc_open
function.
Sorry for moving the thread around, I got really confused with all the code.
When I try passing this as an exaple:
'..\..\..\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe http://localhost/Ponudomat/printRdy.php print.pdf'
The program returns this:
'..\..\..\Program' is not recognized as an internal or external command, operable program or batch file.
It separates the DIR into different commands because of the spaces, to fix it you need to pass the DIR within the "" and that's where the problem kicks in.
When I try:
'"..\..\..\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe" http://localhost/Ponudomat/printRdy.php print.pdf'
I just keep getting the same error as the one above.
When I try adding "/ instead of " to try and force php into making a quote within a string.
This is the confusing part, when I put:
'"/..\..\..\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe"/ http://localhost/Ponudomat/printRdy.php print.pdf'
It actually runs the command and partially works. So now I got the .exe running but it gives me an error as it cannot read the parameters it needs because of the "/ so I get:
Loading pages (1/6) [> ] 0% [===> ] 5% [======> ] 10% Error: Failed loading page file:///C:/ (sometimes it will work just to ignore this error with --load-error-handling ignore) [========> ] 14% [=========> ] 15% [==========> ] 17% [============> ] 20% [=================================> ] 55% Exit with code 1 due to network error: ContentOperationNotPermittedError
Because the command is as follows getCommand:
/..\..\..\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe"/ http://localhost/Ponudomat/printRdy.php print.pdf
Case: $command->addArg('--arg', 123);
Generated command: cmd --arg '123'
But this makes no sense.
May be skip escapeshellarg if ctype_digit($value) === true
.
exec()
or proce_open()
or shell_exec()
every commands internally calls /bin/sh
(not /bin/bash
) with $PATH
as /usr/bin:/bin
/bin/bash
or other shells.$PATH
variable before running the command.Examples:
$path_to_add = '/usr/local/bin/';
$command = 'export PATH=$PATH:' . $path_to_add; mycommand';
and
exec("/bin/bash -c \"mycommand\"")
It seems that addArg is doing more than it should, which is causing the code to be a little complicated. In my use case, I'm doing one of a few things, which vary greatly on how the logic needs to behave:
--file, $tarball
--extract
$filename
-O > $destination
(If it wasn't obvious, my test case here is tar
.) Since there's a lot going on, but not a lot of flexibility, would it make sense to have a few different methods for addArg, for various use cases.
As a list critique point, passing in '--file=', $tarball
looks weird, I feel moving the option for a separator to an argument could make more sense.
Either way, I'm going to monkey patch a local copy I grabbed for a project I'm working on, and can submit my results if you're interested. Otherwise great looking class, the convenience of it handling the necessary proc_* operations is useful.
Case: $command->addArg('; ls -la', 'something');
Generated command: cmd ; ls -la 'something'
Сan execute any command on the server.
May be can do escapeshellarg for keys too.
The recent added check in the $descriptors array for windows has broken functionality for me. Running on a linux machine. Previously had the 'a' descriptor, but now with the getIsWindows() check on line 286, it's returning false, giving a 'w' descriptor. If I am building a small pdf this doesn't seem to be a problem, but for larger pdfs it just hangs. I have temporarily commented out the change here returning it to using the 'a', and everything seems to be in order again.
Hi!
This is probably something that I'm overlooking but I want to comment it if I can help someone else in my situation.
$pdf = new Pdf([
'command' => 'C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe',
'useExec' => true,
'A' => $path1,
'B' => $path2
]);
$pdf->shuffle(1, 'end', 'A')
->shuffle(1, 'end', 'B')
->send('informe.pdf');
I was creating a new PDF that way, using 'command' and 'useExec' parameters, with php-pdftk library because my server has Windows as its OS, but I was always receiving the same error:
Failed without error message: pdftk "command"="C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe" "useExec"="1" "A"="C:\Windows\Temp\phpC9C7.pdf" "B"="C:\Windows\Temp\phpC9D7.pdf" shuffle A1-end B1-end "output" "C:\Windows\Temp\tmpC9D8.tmp.pdf" (Exit code: 1)
To fix that error, I removed 'command' and 'useExec' parameters from the code shown above and changed the following condition in Command.php (line 378) from true to false because I noticed that, despite using 'useExec' parameter with true value, it wasn't changing it's value in that file and now there's no problem creating the PDF:
if ($this->useExec) {
Therefore, my doubt is, am I doing something wrong initializing the PDF or maybe is this a bug?
Thank you for your attention and for creating this awesome tools!
we have been using very old v0.4.4 for some years. We had previously fixed a bug in that version but never reported it here.
today I tested v0.13.1 which has the same bug. This is on Windows system. PHP 8.1.20 with pdftk 2.02
$command = ""C:\Program Files (x86)\PDFtk\bin\pdftk.exe""
above is taken from xdebug: notice the value of $command
has leading & trailing double-quote marks
see attached screenshot of debugger
patch:
--- /mikehaertl/shellcommand/Command-orig.php Wed Apr 19 04:25:22 2023
+++ /mikehaertl/shellcommand/Command.php Sun Nov 26 10:54:27 2023
@@ -195,9 +195,7 @@ class Command
// Could be a quoted absolute path because of spaces.
// i.e. "C:\Program Files (x86)\file.exe"
} elseif (isset($command[2]) && $command[2] === ':') {
- $position = 2;
+ // FIX THIS, it leaves a trailing quote
+ $command = trim($command, '"\'');
+ $position = 1;
} else {
$position = false;
}
without the change the executed command returns an error with no error-description. the problem is it leaves the trailing quote-mark.
the fix simply removes the quotes - this has always worked properly on Windows.
Hey,
first of all, i love this lib. It works great. But now i have to generate a command that contains a nested command and i am not sure what the best way is. The command i want to generate looks like that this:
rsync -av --delete -e "ssh -i /home/user/.ssh/sshkey" [email protected]:/home/ /mnt/server-mirror/home/
What would be the best way to generate the ssh -i /home/user/.ssh/sshkey part?
Thanks and greetings
Leo
Hi,
It looks like PHP compiled with '--enable-sigchild' will have issues with php-shellcommand
when proc_* functions are used (by default). php-shellcommand
will return an error when the command executes successfully.
The reason is discussed in the following PHP bug reports:
https://bugs.php.net/bug.php?id=61873
https://bugs.php.net/bug.php?id=71804
There is no issue when using useExec
option on a system with PHP compiled with '--enable-sigchild'.
There is not much that can be done other than detecting the '--enable-sigchild' configure command option and adding the useExec
option. Unfortunately, there doesn't appear to be any way to do this aside from parsing phpinfo().
Is there any other way to handle this issue?
Thanks.
I am looking at #36 and #31. I might be missing something but is there any technical reason why 5.4 is required? I know we should all be on 7.4
, but maybe this requirement is arbitrary and 5.3
works just fine. The reason why I say this is php-pdftk requires this package but requires 5.3
or above, which is kind of strange.
If I pass in a relative path on Windows to execute (assuming what I want to execute is already in the working directory or in my PATH environment variable), it produces incorrect output.
For example:
mysqldump --defaults-extra-file=C:\temp\my.cnf --add-drop-table --comments --create-options --dump-date --no-autocommit --routines --set-charset --triggers --result-file=C:\temp\backup.sql testdatabase
Will produce:
cd "mysqldump --defaults-extra-file=C:\temp\my.cnf --add-drop-table --comments --create-options --dump-date --no-autocommit --routines --set-charset --triggers --result-file=C:\temp" && backup.sql testdatabase
It looks like d9a0e8e is really only a fix for absolute paths.
If my php file is being executed from my E:\ drive to a command running on my C:\ drive, it doesn't work since the cd is happening on the wrong drive.
I fixed it by doing the following:
$drive = substr($command, 0, 2);
$command = sprintf($drive . ' && cd %s && %s', escapeshellarg(dirname($command)), basename($command));
So $command becomes:
C: && cd "C:\Program Files\wkhtmltopdf\bin" && wkhtmltopdf.exe
When i tried run this code:
$command = new Command(ROOT_PATH. '/composer/ composer update');
$result = $command->execute())
I get this result:
sh: /var/www/mysite.com/public_html/composer/: Is a directory
In our code that uses mikehaertl/phpwkhtmltopdf we have an option:
['window-status'=>'ready-to-print']
However on some occasions ready-to-print is not set in js. In this case wkhtmltopdf processes will never stop. And after some time we have hundreds of zombie-processes. Is it possible to set max execution time for wkhtmltopdf?
P.S.:
ini_set('max_execution_time', 15);
Should do the job to kill php process and wkhtmltopdf with it right? It does not.
28378 www 1 20 0 213M 31292K piperd 5 0:01 0.00% php-fpm
28588 www 1 20 0 213M 31304K piperd 0 0:01 0.00% php-fpm
It hangs on piperead. max_execution_time apperantly does not work for piperead.
wkhtmltopdf -V
wkhtmltopdf 0.12.4 (with patched qt)
mikehaertl/php-shellcommand 1.2.4
mikehaertl/php-tmpfile 1.1.0
mikehaertl/phpwkhtmltopdf 2.2.1
PHP 7.0.13 (php-fpm)
Hi,
I get a PHP warning that proc_get_status() has been disabled for security reasons
on this one system. Apparently, proc_open
is allowed, but not proc_get_status
. I looked at the code and it looks like the call to proc_get_status
was added to solve issue #20.
The thing is, I believe if proc_get_status
is disabled, you get the Command unexpectedly terminated without error message
even when the command is still running. I suggest changing the code at src/Command.php:431 to check if proc_get_status()
is usable and/or check the contents of $status before using it and doing something else if the contents of $status
are invalid.
while ($isRunning) {
$status = @proc_get_status($process);
if(!is_array($status))
// something...
I am sure there is a better solution that can be used. Maybe seeing if pipes are still open or setting useExec
to true in such cases. Maybe useExec
can be automatically set based on some checks?
Any thoughts?
Let me give you example. Say, you are executing command /abs/path/bin
/. You set arguments via addArg()
, then execute()
and finally getOutput()
or getError()
or getExitCode()
. All of these modifies internal state i.e. we remember output, exit code, etc.
Then, I reset args via setArgs()
to empty. Add new args and execute again. But now how can I know if say getOutput()
shows output of the first execution or second?
Currently, I am creating new instance of php-shellcommand
for each new execution. Is there a more elegant way?
$this->_stdErr = stream_get_contents($pipes[2]);
$this->_stdOut = stream_get_contents($pipes[1]);
Environment: Windows XAMPP
Have to swap these two lines,
https://stackoverflow.com/questions/31194152/proc-open-hangs-when-trying-to-read-from-a-stream
In mikehaertl/phpwkhtmltopdf#108 (comment) it was reported, that UTF-8 characters in arguments do not work.
Here they recommend to use the following environment variables:
// An environment variable
$env = array(
'LANG' => 'lv_LV.utf-8'
);
Issue mikehaertl/phpwkhtmltopdf#78 describes a problem that could be fixed with a different pipe configuration, as found here:
http://php.net/manual/en/function.proc-open.php#97012.
I've adopted this in release 1.0.5:
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'a') // stderr
But this caused new problems as now error capturing seemed to be broken as reported in issue mikehaertl/phpwkhtmltopdf#111.
So the change was undone in 1.0.6:
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
A workaround would be, to use one or the other, depending on the OS. But so far I have no information from mikehaertl/phpwkhtmltopdf#78 which OS is really affected. Windows was only my (unconfirmed) assumption.
It seems to be the case that the return value of proc_close()
is not really reliable. So sometimes it reports an error, even if everything went fine. We should improve this somehow.
Is there a way to show the realtime output of a long running command on the console?
Similar to what's described here.
php-shellcommand/src/Command.php
Lines 407 to 408 in 6c6f44c
php-shellcommand/src/Command.php
Lines 463 to 466 in 6c6f44c
after calling ajax req, i getting string of chars...
Do we need to set any headers?
Help needed.
Hi,
I'm trying your php-pdftk. I've been receiving some errors, and I found out the problem was related to this library. It took me some time to discover the problem was the lack of enabled functions. Perhaps having an error message notifying that the needed functions are not enabled may save some time. I had to scan the code to see which functions were necessary (exec, proc_open, proc_close, proc_get_status, proc_terminate, escapeshellarg, escapeshellcmd)
In general, I just use something like the next code to discover if a function is present:
if(!function_exists('exec')) {
echo "exec is not enabled"; // Do something relevant here
}
I'm just an occasional PHP developer, sorry if I'm suggesting something silly.
Thank a lot for all your libraries, you are amazing!
I was testing some html conversions using the mikehaertl/phpwkhtmltopdf, when I noticed one made PHP hang indefinitely.
Doing some debug, I found out that it was happening in the php-shellcommand/src/Command.php file at line number 317:
$this->_stdOut = stream_get_contents($pipes[1]);
After some research, I found a similar problem in this stackoverflow question and an apparent solution. http://stackoverflow.com/questions/31194152/proc-open-hangs-when-trying-to-read-from-a-stream
According to the stackoverflow answer, there is a possibility of an execution not outputting anything to stdout (pipe[1]) when an error occurs, so stream_get_contents waits for a stream that never comes.
As pointed out in the stackoverflow answer, reading the stderr (pipe[2]) before stdout (pipe[1]) did the trick for me, with the PHP finishing execution and the error message being outputted.
$this->_stdErr = stream_get_contents($pipes[2]);
$this->_stdOut = stream_get_contents($pipes[1]);
My system config:
In the Windows environment it appears that the stdOut is preceded by \x0C
(ASCII Form Feed character).
This is a problem when the expected stdOut is a JSON response string which will not parse using json_decode() because of the "invisible" form feed character.
The PHP trim() function does not strip these characters by default so the suggested change would be to replace the getOutput() function as follows
public function getOutput($trim = true)
{
return $trim ? trim($this->_stdOut, " \t\n\r\0\x0B\x0C") : $this->_stdOut;
}
And perhaps extend this change to getError() and getStdErr() as well.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.