带有“;php-fastcgi”;将输出发送到控制台而不是浏览器


lighttpd with "php fastcgi" send output to console instead of browser

我有一个php脚本作为fastcgi服务器运行,它执行浏览器请求的php文件(为什么不使用php/php-cgi?由于性能问题,我们也使用了APC,但仍然不够好):

# cat myphpcgi
#!/usr/local/php5/bin/php
<?php
while (Accept() >= 0) {     # simple wrapper of FCGI_Accept from FastCGI dev-kit
    $file = GetRequestFile();   # get requested .php file name(like /var/www/htdocs/a.php)
    if ($file == FALSE)
    continue;
    $contents = file_get_contents($file);
    if ($contents == FALSE)
        continue;
    # remove heading <?php and trailing ?>
    $contents = ltrim($contents);
    $contents = rtrim($contents);
    $start = strpos($contents, "<?php");
    $start += 5;
    $end = strrpos($contents, "?>");
    $end -= 2;
    $ct = substr($contents, $start, $end);
    # execute the requested file
    eval($ct);
}
?>

通过使用上面的cgi脚本,函数/类库将只加载一次(使用require_one)。

脚本由以下人员启动:

# /usr/local/lighttpd/bin/spawn-fcgi -f /var/www/cgi-bin/myphpcgi -a 127.0.0.1 -p 8888

lighttpd的配置如下:…

fastcgi.server = ( ".php" =>
                    ("localhost" =>
                      ( "host" => "127.0.0.1",
                        "port" => 8888,
                      )
                    )
                 )

但当浏览器要求提供以下php时:

<?php
require_once "bigfunction.php"
echo "haha";
printf("hehe");
?>

cgi脚本将"哈哈哈"输出到系统控制台,而不是浏览器。

# hahahehe

如果我包装FCGI_printf函数(也来自FastCGI devkit)并使用它打印出来字符串,结果将发送到浏览器,这是一个解决方案,但现有代码需要进行更改。

我还使用Apache进行了测试,输出只是转到Apache的error_log。也许是dup2()将解决这个问题,但我没有找到FastCGI devkit使用的fd。

附言__自动加载不会节省我们的时间,因为大多数包含的都是函数和常量。

p.S.2 dup2()无法解决问题:来自cgi脚本的strace的输出:

write(1, "haha", 4haha)                     = 4
write(1, "hehe'n", 5hehe)                   = 5
write(3, "'1'6'0'1'0'0'0'0'1'3'0'1'0'10'0'0'0'0'0'0'0'0'0'0", 24) = 24
shutdown(3, 1 /* send */)  

其中fd 3是与lighttpd的连接,当我添加dup2(3,1)时,浏览器上出现500错误,lighttpd日志显示:

2011-05-24 13:09:54: (mod_fastcgi.c.2443) unexpected end-of-file (perhaps the fastcgi process died): pid: 0 socket: tcp:127.0.0.1:8888
2011-05-24 13:09:54: (mod_fastcgi.c.3237) response not received, request sent: 834 on socket: tcp:127.0.0.1:8888 for /large.php , closing connection

p.S.3:Accept()代码:

PHP_FUNCTION(Accept)
{
    int n = FCGI_Accept();
    RETVAL_LONG(n);
    return;
}

GetRequestFile()的代码:

PHP_FUNCTION(GetRequestFile)
{
    char buf[1024];
    char *p = NULL;
    int ret;
    // apache and lighttpd has different name
    p = getenv("PATH_TRANSLATED");
    if (p == NULL) {
        p = getenv("SCRIPT_FILENAME");
        if (p == NULL) {
            RETVAL_BOOL(0);
            return;
        }
    }
    memcpy(buf, p, strlen(p));
    buf[strlen(p)] = ''0';
    RETVAL_STRINGL(buf, strlen(buf), 1);
    //dup2(3, 1);
    return;
}

p.S.4当为FCGI_printf添加包装时,如下所示:

PHP_FUNCTION(Myprintf)
{
    char *str;
    int str_len;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
        return;
    }
    char *p = emalloc(str_len + 1);
    memcpy(p, str, str_len);
    p[str_len] = ''0';
    FCGI_printf("%s", p);
    efree(p);
    return;
}

使用Myprintf()打印字符串,当myphpcgi与apache一起工作时,它工作得很好,字符串会显示在浏览器上。但当使用lighttpd时,字符串将不起作用,很有趣。。。

您不需要自己编写FCGI包装来为PHP提供FCGI。

相反,您应该使用现有的包装器作为FCGI来执行PHP脚本。它可以与任何现有的PHP代码配合使用。


不进行eval,只需包含以下文件:

# remove heading <?php and trailing ?>
include($file);

这可能会确保require_one按预期工作。

@hakre最后通过破解phpsrc/sapi/cgi/cgi_main.c解决了这个问题。原因是php的echo和printf函数的行为受到控制通过不同的sapi,如cgi、cli,sapi确定目的地是。模块无法更改目的地。

在cgi_main.c中,原始工作流程为:

listen();
while (req = accept()) {
    php_request_start();
    php_execute_script();
    php_request_shutdown();
}

我所做的只是将请求的启动和关闭从while循环中移出,

listen();
php_request_start();
while (req = accept()) {
    php_execute_script();
}
php_request_shutdown();

它运行良好,并且"require_onced-files"将只加载一次。