3

我想从 C 程序执行 PHP 脚本并将返回的内容存储到 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
  • 阿帕奇 2.0
  • Fedora 核心 10

还建议任何其他更好的方法来做到这一点。

4

3 回答 3

14

这里的简短回答是使用system()orpopen()而不是execl(). 鉴于 Jason 已经发布了一个关于 using 的好答案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除了stdinand之外还可以访问子流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;
    }
}
于 2009-06-26T05:09:43.840 回答
7

可能最简单的方法是使用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() */
    ...
}

但是,想要从 C 程序中读取 PHP 脚本的输出是一种危险信号,我怀疑可能有一些更清晰的整体解决方案,但是如果没有对您的内容进行更高级别的描述,就很难说更多。重新尝试做。

于 2009-06-26T04:56:03.950 回答
1

请记住,当您使用execl()它时,它将“成功退出”。因此,您将永远不会看到“结束 php 函数”,因为它永远不会到达那行代码。

为了克服这个问题,您可以使用system或使用fork()来创建子进程,并在您的子进程中使用execl或任何其他exec家庭功能。

于 2015-12-29T17:28:46.797 回答