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

c++fedora10php

I would like to execute a PHP script from a C program and store the returning content in to a C variable.

I tried like following but it doesn't work:

C:

printf("calling php function\n");
execl("/usr/bin/php -q", "/var/www/html/phpinfo.php", NULL);
printf("End php function\n");

PHP:

<?php
echo "hello";
?>

Environment:

  • PHP 5.2.6
  • Apache 2.0
  • Fedora Core 10

Also suggest any other better way to do this.

Best Solution

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;
    }
}