我有一个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时,字符串无处可去,有趣......
答案 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”只会加载一次。