выполнение php-скрипта из C-программы и сохранение результатов в переменной - PullRequest
3 голосов
/ 26 июня 2009

Я хотел бы выполнить скрипт PHP из программы на C и сохранить возвращаемый контент в переменную C.

Я пробовал как следует, но это не работает:

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";
?>

Окружающая среда:

  • PHP 5.2.6
  • Apache 2.0
  • Fedora Core 10

Также предложите любой другой лучший способ сделать это.

Ответы [ 3 ]

13 голосов
/ 26 июня 2009

Короткий ответ здесь - использовать system() или popen() вместо execl(). Поскольку Джейсон уже опубликовал хороший ответ об использовании popen(), я пропущу это и объясню, как использовать execl() на тот случай, если вам действительно все равно. Скорее всего, это все ненужный технический тупик - но, черт возьми, я уже набрал большую часть этого текста как длинную прелюдию, прежде чем обсуждать popen(), и сейчас я не буду его выбрасывать!

Во-первых ...

При вызове execl() все аргументы командной строки необходимо передавать отдельно. Кроме того, первый аргумент должен быть повторен как argv[0] в любой программе main() традиционно является названием программы. Таким образом, фиксированный вызов должен выглядеть так:

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

(Я добавил приведение к (char *), чтобы гарантировать, что нулевой указатель передается в качестве конечного аргумента, а не целое число 0, если NULL определено как 0, а не (void *) 0, что законны.)

Однако ...

Это правильно воспринимает вызов execl(), но есть большая проблема. Семейство функций exec почти всегда используется в сочетании с fork() и некоторым сложным pipe() жонглированием. Это связано с тем, что функции exec не запускают программу в отдельном процессе; они фактически заменяют текущий процесс! Поэтому, как только вы позвоните execl(), ваш код готов. Законченный. execl() никогда не возвращается. Если вы просто назовете это так, как сделали, вы никогда не увидите, что произойдет, поскольку ваша программа волшебным образом превратится в /usr/bin/php процесс.

ОК, так что это за fork() и pipe()? На высоком уровне вам нужно разделить ваш процесс на два процесса. Родительский процесс останется «вашим» процессом, в то время как дочерний процесс немедленно вызовет execl() и преобразуется в /usr/bin/php. Тогда, если вы правильно соединили родительский и дочерний процессы, они смогут общаться друг с другом.

Короче говоря, если вы все еще здесь и не заснули, вам следует обратиться к мудрому оракулу в Google за более подробной информацией обо всем этом. Существует множество веб-сайтов, дающих еще (!) Подробную информацию о том, как танцевать fork / exec.

Я не оставлю тебя в покое. Вот функция, которую я использую для своих собственных программ, которая делает именно то, что я обрисовал в общих чертах. На самом деле он очень похож на popen(), с той лишь разницей, что вызывающий абонент может получить доступ к потоку stderr ребенка в дополнение к stdin и stdout.

код ...

#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;
    }
}
7 голосов
/ 26 июня 2009

Вероятно, самый простой способ - использовать функцию popen : (выдержка из связанной страницы):

В следующем примере демонстрируется использование popen () и pclose () для выполнения команды ls * для получения списка файлов в текущем каталоге:

#include <stdio.h>
...


FILE *fp;
int status;
char path[PATH_MAX];


fp = popen("ls *", "r");
if (fp == NULL)
    /* Handle error */;


while (fgets(path, PATH_MAX, fp) != NULL)
    printf("%s", path);


status = pclose(fp);
if (status == -1) {
    /* Error reported by pclose() */
    ...
} else {
    /* Use macros described under wait() to inspect `status' in order
       to determine success/failure of command executed by popen() */
    ...
}

Тем не менее, желание прочитать вывод PHP-скрипта из программы на C является своего рода красным флагом, и я подозреваю, что, возможно, есть более чистое общее решение для этого, но трудно сказать больше без описания более высокого уровня что ты пытаешься сделать.

1 голос
/ 29 декабря 2015

Помните, что когда вы используете execl(), он «завершится успешно». Таким образом, вы никогда не увидите «Завершить функцию php», потому что она никогда не попадет в эту строку кода.

Чтобы решить эту проблему, вы можете использовать system или fork() для создания дочернего процесса, а в своем дочернем процессе - execl или любую другую функцию из семейства exec.

...