我有一个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"将只加载一次。