lighttpd与“php fastcgi”将输出发送到控制台而不是浏览器

时间:2011-06-20 09:03:55

标签: php fastcgi lighttpd

我有一个php脚本作为fastcgi服务器运行,执行浏览器请求的php文件(为什么不使用php / php-cgi?因为performance issue,我们也使用了APC,但仍然不够好):< / p>

# 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_once)。

脚本由以下人员启动:

# /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,
                      )
                    )
                 )

...

但是当浏览器要求a.php如下:

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

cgi脚本将“hahahehe”输出到系统控制台而不是浏览器。

# hahahehe

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

我也用Apache测试过,输出只是去了apache的error_log。也许是 dup2()将解决问题,但我没有找到FastCGI devkit使用的fd。

P.S。 __autoload不会节省我们的时间,因为大多数包含的是函数和常量。

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时,字符串无处可去,有趣......

2 个答案:

答案 0 :(得分:0)

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

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


而不是eval,只需包含文件:

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

这可能确保require_once按预期工作。

答案 1 :(得分:0)

@hakre最后通过黑客攻击phpsrc / sapi / cgi / cgi_main.c解决了这个问题。 原因是php的echo和printf函数的行为受到控制 通过不同的sapis,如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”只会加载一次。