Call a C Program from PHP and Read Program Output

Call a C program from php and read program output

You'll want to use the shell_exec() function (quoting) :

Execute command via shell and return
the complete output as a string

Which means something that will look like this :

$output = shell_exec('/path/to/your/program');


Or, you could use the backtick operator -- which would do exactly the same thing (quoting) :

PHP will attempt to execute the
contents of the backticks as a shell
command; the output will be returned

And, in code :

$output = `/path/to/your/program`;

executing php script from C program and store the results in to a variable

Short answer here is to use system() or popen() rather than execl(). Seeing as Jason has already posted a good answer about using popen(), I'll skip that and explain how to use execl() just in case you actually care. Most likely, this is all unnecessary technical mumbo jumbo--but dammit, I had most of this typed out already as a long prelude before discussing popen() and I'm not throwing it away now!

Firstly...

When calling execl() all of the command-line arguments need to be passed separately. Also, the first argument must be repeated as argv[0] in any program's main() is traditionally the name of the program. So the fixed call should look like:

execl("/usr/bin/php", "/usr/bin/php", "-q",
"/var/www/html/phpinfo.php", (char *) NULL);

(I added the cast to (char *) to ensure that a null pointer is passed as the final argument rather than the integer 0, if NULL happens to be defined as 0 and not (void *) 0, which is legal.)

However...

This gets the execl() call right, but there's a bigger problem. The exec family of functions are almost always used in combination with fork() and some complicated pipe() juggling. This is because the exec functions do not run the program in a separate process; they actually replace the current process! So once you call execl(), your code is done. Finished. execl() never returns. If you just call it like you've done you'll never get to see what happens as your program will magically transform into a /usr/bin/php process.

OK, so what's this about fork() and pipe()? At a high level, what you've got to do is split your process into two processes. The parent process will continue to be "your" process, while the child process will immediately call execl() and transform itself into /usr/bin/php. Then if you've wired the parent and child processes together correctly they'll be able to communicate with each other.

To make a long story short, if you're still here and haven't nodded off you should consult the wise oracle Google for way more details about all of this. There are plenty of web sites out there giving even more (!) in-depth details about how to do the fork/exec dance.

I won't leave you hanging though. Here's a function I use for my own programs that does exactly what I've outlined. It is very similar to popen() in fact, the only difference being that the caller can access the child's stderr stream in addition to stdin and stdout.

Code...


#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

pid_t execute(const char *command, FILE **in, FILE **out, FILE **err)
{
pid_t pid;
int fd[6];

pipe(&fd[0]);
pipe(&fd[2]);
pipe(&fd[4]);

switch (pid = fork()) {
case -1:
perror("unable to fork()");
exit(1);

case 0:
close(fd[1]); // Close write end of stdin.
close(fd[2]); // Close read end of stdout.
close(fd[4]); // Close read end of stderr.

dup2(fd[0], STDIN_FILENO); // Have stdin read from the first pipe.
dup2(fd[3], STDOUT_FILENO); // Have stdout write to the second pipe.
dup2(fd[5], STDERR_FILENO); // Have stderr write to the third pipe.

execlp("/bin/sh", "/bin/sh", "-c", command, (char *) NULL);

perror("execlp() failed");
_exit(1);

default:
close(fd[0]); // Close read end of stdin.
close(fd[3]); // Close write end of stdout.
close(fd[5]); // Close write end of stderr.

if (in) *in = fdopen(fd[1], "wb"); else close(fd[1]);
if (out) *out = fdopen(fd[2], "rb"); else close(fd[2]);
if (err) *err = fdopen(fd[4], "rb"); else close(fd[4]);

return pid;
}
}

Calling C program with PHP

You say you want the PHP to send and receive messages to your program without restarting the compiled program.

So I don't think using shell_exec or proc_open will work how you want, since these commands both load a fresh instance of the compiled program.

Instead, I suggest you look into sockets, and how you would rewrite your database engine to use those instead of STDIN/STDOUT. Then you can use PHP's socket functions to communicate between your applications. And you'll have just one instance of your compiled program running in the background, even with multiple hits to your PHP script.

Is it possible to call C code from php

There is a way to do it, but it's probably not a very good idea. One could write a small native program around the library (an executable for your system, that is). Then, you could use the php system() function to call that program. Then, read the output you need (if any) off the standard output?

(there may be a better way, I'm not super-familliar with PHP, but at least this should work.)

Reading data from STDOUT of C application using PHP

You miss the moment when the hello program stops its output and terminates (and its process becomes "defunct"). Generally, fgets( $some_file ) returns a string if there is output and returns false if $some_file has reached its end. Thus, if you add this check:

$s = fgets($pipes[1]);
if( $s === false ) {
// Hello program has finished.
echo 'Finished', PHP_EOL;
// Close all descriptors and return...
}

you should terminate successfully.

C Program to Expand and Run PHP

I want to give props to @Suroot and also this great article: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

However, the entire process is like so at Linux command line:

...first, you need to be on Linux, PHP for command line (CLI) needs to be enabled, and PHP at command line should not have any errors when you do "php -v" or pass it a test script that you know typically would work at command line.

...second, you need to have GCC installed for compiling C programs.

...create a working directory and 'cd' into it.

...create a file script.php that has this test code inside:

<?php

$s1 = @ $argv[1];
$s2 = @ $argv[2];
$s3 = @ $argv[3];

for($i=0;$i<=10;$i++){
echo "($s1) ($s2) ($s3)\n";
}

...note, this is only our test. You can put your own PHP code in there later on once you've proven that the test works.

...note that "script.php" is critical to be named that or the main.c won't work when it hits the _binary_script_php_start line. That through me for a loop at first because I didn't make the mental connection that the variable name is glued in by this process.

...create a C code file main.c that has this code inside:

#include <stdio.h>
#include <stdlib.h>

extern char _binary_script_php_start;
extern char _binary_script_php_end;

int main(int argc, char *argv[]) {
// EXTRACT OUR RESOURCE OBJECT INTO /tmp/test.php
char *p = &_binary_script_php_start;
FILE *fp = fopen("/tmp/test.php","wb");
while ( p != &_binary_script_php_end ) {
fputc(*p++,fp);
}
fclose(fp);
// NOW READ IN OUR STANDARD ARGUMENTS AND LAUNCH OUR COMMAND
int i = 1;
char *cmd = "php /tmp/test.php";
char *s = NULL;
asprintf(&s, "%s",cmd);
for(i = 1; i < argc; i++) {
asprintf(&s, "%s \"%s\"",s,argv[i]);
}
system(s);
free(s);
unlink("/tmp/test.php"); // comment me out for debugging if you want
}

...note, I'm sort of rusty with C (haven't used it since the 1980s), and strcpy() is not advised because it's unsafe. So, I read up on StackOverflow that we should use asprintf() on GNU Linux systems. Evidently asprintf() handles buffer overflow and malloc issues? I sure hope so -- I don't want to lead you wrong here. Please research that!

...and now the magic begins!

...we need to embed the script.php into an object file "data.o" with this command-line command (thus starting with the $ prompt):

$ objcopy -I binary -O elf32-i386 -B i386 script.php data.o

...now we build our command "runme" where we link in "data.o" and "main.c" together:

$ gcc main.c data.o -o runme

...this then made a compiled C program for us called "runme". We can then execute it to test it:

$ ./runme "test1" "test2 test3" "test4"

...You should see an output like the following:

(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)
(test1) (test2 test3) (test4)

...as you can see, the doublequotes actually are important -- they tell the C, and therefore the PHP, that the parameters should be grouped together. That's why test3 doesn't appear in the last parentheses. Without the doublequotes, you'll see a completely different result.

At this point, you see how we can make a C program that has an embedded PHP script inside and which runs on Linux. (Mac, Windows -- you'll likely need to tweak this code somewhat.) The C program expands our PHP script into /tmp, executes it, and passes it parameters that we can read in and use. From here on out, you can revise the script.php to do as you need, and you can see about using a unique filename so that you don't clobber someone else's /tmp/test.php file, just in case.



Related Topics



Leave a reply



Submit