C3 method resolution order

25 November 2005


介绍 C3 方案之前,我们先看看 Perl 5 的多重继承方案。
package A;
sub hello { 'A::hello' }

package B;
use base 'A';

package C;
use base 'A';
sub hello { 'C::hello' }

package D;
use base ('B', 'C');

这是一个经典的多重继承,用图表表示如下:
    <A>(hello)
/ \
<B> <C>(hello)
\ /
<D>
在 Perl 5 中当你调用 D::hello 时会比较奇怪地发现它输出的是 A::hello.
这是因为在 Perl 5 中继承的顺序是 D->B->A->C
而按照我们一般的思路顺序则应当是 D->B->C->A 输出的应当为 C::hello

在 Perl6 中我们就采用了 C3 这种算法,这种算法首先是在 Dylan 语言中实现。而后被应用到 CLOS 还有 Python 2.3。
现在我们将它应用到了 Perl6 和 parrot. Perl6 的 Perl6-MetaModel 作者 Stevan Little 用 Perl5 写了个版本,就是 Class::C3

这种算法的思路就是父类一定在子类后面。
比如前面的多重继承可以这么写:D, B, A, C, A
在 Perl 5 中去掉后后面重复的,而 C3 算法是把前面的父类去掉。

详细的 C3 算法介绍等我看完那篇 Paper 后,如果有不吐不快之言,再写 journal.


modperl 的 PerlTransHandler 应用

23 November 2005


这次讲 modperl 的PerlTransHandler,主要的作用是 url rewrite

PerlTransHandler

前面 modperl 服务器的运行阶段和句柄 中讲过 PerlTransHandler 主要用于处理 URI
如果你用过 mod_rewrite 的话,发现这个 handler 比 mod_rewrite 更实用。

假设我们为了对搜索引擎友好,将动态的 URL 地址类如

http://www.fayland.org/cgi-bin/topic.cgi?id=44
http://www.fayland.org/cgi-bin/topic.cgi?id=44&type=print
分别转为对搜索引擎更友好的静态地址如
http://www.fayland.org/topic/44
http://www.fayland.org/topic/print/44
我没用 mod_rewrite 转过,但是试验过 PerlTransHandler:
package MyApache2::RewriteTopic;

use strict;
use warnings;

use Apache2::RequestRec ();

use Apache2::Const -compile => qw(DECLINED);

sub handler {
   my $r = shift;

   # for http://www.fayland.org/topic/44
   if ($r->uri =~ m|^/topic/(\d+)/?|) {
       $r->uri("/cgi-bin/topic.cgi");
       $r->args("id=$1");
   }
   # for http://www.fayland.org/topic/print/44
   if ($r->uri =~ m|^/topic/([a-z]+)/(\d+)/?|) {
       $r->uri("/cgi-bin/topic.cgi");
       $r->args("id=$2&type=$1");
   }

   return Apache2::Const::DECLINED;
}

1;

在 perl.conf 或 httpd.conf 增加:
PerlTransHandler +MyApache2::RewriteTopic
或者是
PerlModule MyApache2::RewriteTopic
PerlTransHandler MyApache2::RewriteTopic
两者是等同的。

    其间值得提提的有一下几点:
  • $r->uri 设定转向的 URI
  • $r->args 设定参数/parameters 这些参数可以用 CGI 的 param 来获取
  • Apache2::Const::DECLINED
    我在前面的那文章里讲过 PerlTransHandler 的类型是 RUN_FIRST,所以当我们返回 DECLINED 时,接下来的 hook 也能被执行。

    比如我们另外定义了一个 MyApache2::RewriteView 这个模块唯一不同的就是把 topic 改为 view, 且在 perl.conf 里增加 PerlTransHandler +MyApache2::RewriteTopic +MyApache2::RewriteView

    修改完后当我们碰到类如 http://www.fayland.org/view/77 时,虽然 MyApache2::RewriteTopic 不满足,但是由于它返回的是 Apache2::Const::DECLINED, 所以会接下来继续下面的 MyApache2::RewriteView。
    而如果 RewriteTopic 返回的是 Apache2::Const::OK, 那接下来的 MyApache2::RewriteView 就不会再继续了。

另外注意将 $r->uri("/cgi-bin/topic.cgi"); $r->args("id=$1"); 放到 if 里面,否则将 PerlTransHandler +MyApache2::RewriteTopic 放到某个 Location 里。
要不弄成全局,就将所有的 URI 都转向 topic.cgi 了。:)


modperl 服务器的运行阶段和句柄

22 November 2005


apache http cycle

上面这张图就是 modperl 的服务器循环。本文大致是 http://perl.apache.org/docs/2.0/user/handlers/http.html 的翻译或阅读随记。

modperl 可以控制所有的 Apache http 的阶段,每一个阶段都对应一个句柄,就是我以前提过的 Perl*Handler:


  1. PerlPostReadRequestHandler (PerlInitHandler)
  2. PerlTransHandler
  3. PerlMapToStorageHandler
  4. PerlHeaderParserHandler (PerlInitHandler)
  5. PerlAccessHandler
  6. PerlAuthenHandler
  7. PerlAuthzHandler
  8. PerlTypeHandler
  9. PerlFixupHandler
  10. PerlResponseHandler
  11. PerlLogHandler
  12. PerlCleanupHandler

图上右边 9 个阶段和下面的 Response 阶段都可能返回终止信号,终止后不继续运行下面的阶段而是跳转到 Log 阶段,而后 Cleanup, 最后等待下一次连接。

所有的 12 个阶段都有下面的结构:

  package MyApache2::MyHandlerName;

# load modules that are going to be used
use ...;

# compile (or import) constants
use Apache2::Const -compile => qw(OK);

sub handler {
my $r = shift;

# handler code comes here

return Apache2::Const::OK; # or another status constant
}
1;
其中 $r 还是 Apache2::RequestRec 的一个实例。如果你需要获得类的名字,则需要这么写:
sub handler : method {
my($class, $r) = @_;
这种写法与 modperl 1.x 中的 sub handler($$) { 大致上是一致的。

PerlResponseHandler 是由以前的 PerlHandler 更名而来的,而且它现在包括了 Filters.

Handler Type

在介绍每个阶段都是干什么的之前,我想先要弄清楚每个阶段的一些行为,或者称之为类型。
按我的暂时的理解,这种类型的意义在于处理一个阶段的多个句柄(为避免混淆,一般称它们为 hook)时,比如
Perl*Handler Apache::One Apache::Two Apache::Three
这里阶段是 *, 句柄是 Perl*Handler, 而 Apache::One Apache::Two 等则称为 hooks.
    我们总共有三个类型
  • VOID: 这种类型的句柄,不管 Apache::One Apache::Two 等返回什么,都按顺序执行下去
  • RUN_FIRST: 当遇到第一个 hook 不是返回 Apache2::Const::DECLINED 时停止。比如 Apache::One 返回 Apache2::Const::DECLINED, 就继续执行 Apache::Two, 直到某个 hook 不返回 Apache2::Const::DECLINED. 比如 Apache::Two 返回 Apache2::Const::OK 时就结束这个阶段跳转到下一个阶段,而不是继续执行 Apache::Three
  • RUN_ALL: 直到返回值不是 Apache2::Const::OK 或 Apache2::Const::DECLINED 时才结束这个阶段,否则按顺序执行多个 hooks.
下面是所有阶段的类型:
    Directive                   Type
--------------------------------------
PerlOpenLogsHandler RUN_ALL
PerlPostConfigHandler RUN_ALL
PerlChildInitHandler VOID
PerlChildExitHandler VOID

PerlPreConnectionHandler RUN_ALL
PerlProcessConnectionHandler RUN_FIRST

PerlPostReadRequestHandler RUN_ALL
PerlTransHandler RUN_FIRST
PerlMapToStorageHandler RUN_FIRST
PerlInitHandler RUN_ALL
PerlHeaderParserHandler RUN_ALL
PerlAccessHandler RUN_ALL
PerlAuthenHandler RUN_FIRST
PerlAuthzHandler RUN_FIRST
PerlTypeHandler RUN_FIRST
PerlFixupHandler RUN_ALL
PerlResponseHandler RUN_FIRST
PerlLogHandler RUN_ALL
PerlCleanupHandler RUN_ALL

PerlInputFilterHandler VOID
PerlOutputFilterHandler VOID

Handler Introduction

下面简单的介绍下各个阶段,而各阶段的运用代码以后我想我会都试验一下的。

PerlPostReadRequestHandler

这个阶段在请求被接受和 HTTP header 被解析后立即运行。这个阶段一般用于处理那些每个请求都只运行一次的事情。
比如 Apache2::Reload 一般就在这个阶段被执行。

PerlTransHandler

这个阶段一般对请求的 URI 进行操作处理。
如果没有自定义的句柄,则服务器的 translate 标准规则(如 Alias 指示符和 mod_rewrite 等)则被使用。
如果使用了字定义的句柄,它将修改默认的 translate 机制或覆盖它们。
它还可以为 PerlMapToStorageHandler 注册一些新的句柄。

PerlMapToStorageHandler

这个阶段用于将一个 URI 对应到一个真实的文件上。

PerlHeaderParserHandler

此阶段是请求/Request 对应到一个 在此阶段它能检查请求的 header 并对其进行操作。
这个阶段与最初的 PerlPostReadRequestHandler 阶段类似,唯一不同的就是它要对应到 PerlAccessHandler这是 AAA: Authentication and Authorization, and Access control 的第一个阶段。
这个阶段能控制访问者的 IP, 访问的时间段其他与用户有关的限制。

PerlAuthenHandler

该阶段当访问密码保护的文件或文件夹时发生。当然这些密码保护的文件或文件夹需要类如 AuthName, AuthType 这样的东西。(想想 .htaccess )
当验证通过时返回 Apache2::Const::OK,授权失败则是 Apache2::Const::HTTP_UNAUTHORIZED。这就是通常的 403.

PerlAuthzHandler

这个阶段和 PerlAuthenHandler 差不多的捆绑在一起的。我目前实在没什么心情继续看完这个阶段。以后再讨论。

PerlTypeHandler

这个阶段用于设置响应/Response 的 MIME type (Content-type) 和类如文档语言等之类的东西。

PerlFixupHandler

这个阶段是 PerlResponseHandler 前的最后一个阶段。一般用于处理一些响应前要做但可能还没做的事。
比如在此阶段 mod_env 导入了由 SetEnv 和 PassEnv 所设置的环境变量。

PerlResponseHandler

毫无疑问这是最最重要的阶段。它用于生成响应的内容(就是返回浏览器的内容)。

PerlLogHandler

这个阶段无论如果都是会被进行的,原因在文章的最初解释过了。用于纪录一些信息。

PerlCleanupHandler

这个是 modperl 所独有的阶段。用于处理清尾工作。比如移除临时文件等。

Have fun!

如果有什么错误之处,请务必务必发 email 给我: fayland[AT]gmail.com
Thanks.

自动代理配置

20 November 2005


因为中国 block 了好几个我经常访问的地址,比如 blogspot.com 中英文维基等。所以要使用到代理。
而一般的代理设置都是不管什么网址,要么都使用代理要么都不使用。
不过浏览器也提供了自动代理配置,不管是 Mozilla Firefox 还是 IE.

Mozilla Firefox 的配置在 Tools-> Options... -> General -> Connection Settings... -> automatic configuration proxy URL
(抱歉我使用英文版,不清楚中文该是如何,大致的翻译可能为:工具 -> 选项 -> 常规 -> 连接设置 -> 自动代理配置地址)

而 IE 的配置选项在 工具 -> Internet 选项 -> 连接 -> 局域网设置 -> 使用自动配置脚本

而这脚本怎么写,这里有详细介绍:http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html
我的脚本(是个 javascript 脚本)为(注意得保存为 pac 后缀):

function FindProxyForURL(url, host) {
   var proxy_yes = "PROXY 209.253.4.131:80; PROXY 203.199.178.78:80";
   var proxy_no = "DIRECT";
   if (shExpMatch(url, "*.blogspot.com*")) { return proxy_yes; }
   if (shExpMatch(url, "*.wikipedia.org*")) { return proxy_yes; }
   
   if (isResolvable(host))
       return proxy_no;
   else
       return proxy_yes;
}
Mozilla Firefox 比较复杂,要 URL 地址,还好的 Apache 常开着,就填了 http://localhost/proxy.pac
而 IE 的话可以是本地地址,可以填写 file://E:/Fayland/proxy.pac

这样你就能用代理访问类如 http://adsensemaster.blogspot.com/ 这样的网站了。
而直接访问你本来可以访问的地址,不能访问的交给代理。很完美的组合。:)
Thanks, enjoy!


针对汉字的 Lingua::Han::Utils

18 November 2005


这是一个我今天刚写的 module: Lingua::Han::Utils
主要用于封装一些我常用的与汉字处理有关的函数。目前封装了四个函数,分别介绍如下:

Unihan_value

返回 Unihan.txt 的第一个字段(出去+U)。 Unihan: http://www.unicode.org/Public/UNIDATA/
Unihan 的用处自然不用说,我写的拼音,比划还有广东化都来自这个文件。

use Lingua::Han::Utils qw/Unihan_value/;
# Unihan_value
# return the first field of Unihan.txt on unicode.org
my $word = "我";
my $unihan = Unihan_value($word); # return '6211'
my $words = "爱你";
my @unihan = Unihan_value($word); # return (7231, 4F60)
my $unihan = Unihan_value($word); # return 72314F60

cdecode

感谢 joe jiang 的帮助, Encode::Guess 正好满足要求。
一般来说,我们写的代码都有两种情况,一种是 ASCII 编辑模式,一种是在 Unicode 编辑模式下写的。
    而不同的模式要 decode 时是不一样的:
  • 在 ASCII 模式下,为 decode('euc-cn', $word) 或 decode('gb2312', $word)
  • 在 Unicode 编辑模式下,为 decode('utf8', $word)
此模块用 Guess 封装了两者,大家如果要 decode 的话直接使用 cdecode, 而不必考虑在什么模式下。

csplit

用于分割文字。可以是纯中文或中英文混合字。
use Lingua::Han::Utils qw/csplit/;
my $words = "我爱你";
my @words = csplit($words); # return ("我", "爱", "你")

csubstr

用于截取文字。可以是纯中文或中英文混合字。
use Lingua::Han::Utils qw/csubstr/;
my $words = "我爱你啊";
my @words = csubstr($words, 1, 2); # return ("爱", "你")
my @words = csubstr($words, 1); # return ("爱", "你", "啊")
my $words = csubstr($words, 1, 2); # 爱你

clength

将汉字对待成一个单词。
my $words = "我ya爱你";
print clength($words); # 5

结论

此模块在去往 CPAN 的路上。如果急着用可以从这下: http://www.fayland.org/CPAN/

汉字比划模块

18 November 2005


从新写了下比划模块,原始数据从 Unihan 解析出来。支持两万多个汉字。:)
是 Unihan 里的 kTotalStrokes 字段。

代码:

use Lingua::Han::Stroke;
my $stroke = Lingua::Han::Stroke->new();

print $stroke->("我"); # 7
print $stroke->("一"); # 1
guys, enjoy!


关于怎么创建新模块简单的纪录

17 November 2005


以下只是简单的纪录。详细的怎么弄有空会写。

module-starter --module=Lingua::Han::Cantonese --author="Fayland Lam" [email protected] --mb
cd Lingua-Han-Cantonese
# 写代码和测试文件
prove -l t/*
perl Build.pl
perl Build
# 删除 MANIFEST
perl Build manifest
perl Build dist
perl Build realclean

tips

  • 如果 lib 目录下有不是 pm 文件而是什么 data 文件的话,比如 Cantonese.dat, Module::Build 默认不会包括。你可以这么在 Build.PL 里加进这句:
    $builder->add_build_element('dat');

Lingua::Han::Cantonese for 广东话

17 November 2005


今天刚写了个模块 Lingua::Han::Cantonese 用以弄出广东话的语音。
这个我自己都不太懂广东话,如果有兴趣学广东话的话,这个倒是可以有点帮助。:)
传到 CPAN 上去了,也可以从我的 CPAN 目录下走。

这个模块我写的很简单,与 Lingua::Han::PinYin 的写法几乎没什么不同。
最近手头还有几个模块可能要写,不过人是挺忙的:

  • Business::CN::City 用于提供“中国城市资料数据库”的 Perl 接口
  • Lingua::Han::Utils 提供汉字处理一些常用的函数如 is_Chinese, 能处理中文的 length, split_words 等
  • 可能还要写一篇文章或幻灯片用以介绍“如果发布模块到 CPAN 上”的完整流程
至于什么时候会写还真是不能确定。上帝见证,最近考研把我忙得快脚不着地了。
God bless me.

modperl Apache2::HelloWorld

16 November 2005


一个最标准的 modperl 脚本可能是这样的:
package Apache2::F_H1;

use Apache2::RequestRec;
use Apache2::RequestIO;
use Apache2::Const -compile => qw(OK);

sub handler {
   my $r = shift;
   
   $r->content_type('text/html');
   $r->print('Hello World!');
   
   return Apache2::Const::OK;
}

1;

然后在 perl.conf 或 httpd.conf 里写:
<Perl>
   use lib 'E:/modperl';
</Perl>

PerlModule Apache2::F_H1
<Location /h1>
SetHandler perl-script
PerlResponseHandler Apache2::F_H1
</Location>

    简单的解释下代码:
  • Apache2::RequestRec 用于处理请求,这里用到了它的 content_type
  • Apache2::RequestIO 则用于输入输出,这里用到了它的 print
  • use Apache2::Const -compile => qw(OK); 则定义一些常量,这里是用到了 Apache2::Const::OK
  • my $r = shift; 这个和 Catalyst 中程序第一句 my ( $self, $c ) = @_; 的 $c 很类似。几乎所有的 modperl 提交给 PerlResponseHandler 或其他 Perl*Handler 时,子程序第一句就必定应当是这句。
  • 注意 1; 没有这个的话,Apache 很可能启动不起来。
而 sub handler 跟 Catalyst 中 View::TT 默认的 process 一样。如果没有指定其它的话,默认就是 handler.
当然我们也可以改变这个名字,如下面这样:
package Apache2::F_H2;

sub kissme {
   my $r = shift;
   
   $r->content_type('text/html');
   $r->print('Hello World!');
   
   return Apache2::Const::OK;
}

1;

这样我们可以在 perl.conf 里这么写:
PerlModule Apache2::F_H2
<Location /h2>
SetHandler modperl
PerlResponseHandler Apache2::F_H2::kissme
</Location>
Handler 时指定了 kissme.

而这里没有那三个 use Apache2::*; 也能正常运行是因为 perl.conf 里 PerlRequire "C:/Apache2/conf/startup.pl", 且 startup.pl 载入了这一系列模块(包括这三)。
而 SetHandler 为什么即可以用 perl-script 又可以用 modperl 则参考 mod_perl 配置的一些指令.

这就是我们最简单的 Hello, World! 至此,我们可以访问 http://localhost/h2 或 h1 来享受我们的成果。


mod_perl 配置的一些指令

13 November 2005


第一句见的最多的可能是 PerlModule

PerlModule

PerlModule Foo::Bar 就等同于 Perl 的 require Foo::Bar;
有点不同的可能是可以接受多个参数:PerlModule Apache::DBI CGI DBD::Mysql
如果你需要传递 qw/foo bar/ 给 Foo::Bar 的话,一般将它写入一个 pl 文件里,然后用 PerlRequire 载入它。如 startup.pl:
use Apache2::Const -compile => ':common';
use APR::Const -compile => ':common';
...

PerlRequire

在 1.0 中在这是常用句,用于载入一个 Perl 文件。由于它不能控制代码的运行时间,所以在 2.0 中被 PerlConfigRequire 和 PerlPostConfigRequire 所细分。
PerlConfigRequire 是碰到该语句立即运行该参数文件,而 PerlPostConfigRequire 是在服务器运行的最后阶段才运行。
一般而言,PerlPostConfigRequire 才是我们所要的。
PerlRequire "C:/Apache2/conf/startup.pl"

PerlOption

提供 mod_perl 的编译选项。
如果在运行时需要确定某些选项是否已经启用,可使用 $r->is_perl_option_enabled($option) 或 $s->is_perl_option_enabled($option) 来判断。
一般允许用 +, 禁止用 -

Enable

一般用于使某虚拟主机能用,而某主机不能使用 mod_perl. 如
<VirtualHost ...>
PerlOptions -Enable
</VirtualHost>

Perl*Handler

可以设置哪些 Perl*Handler 我们能使用而哪些不能使用。如
<VirtualHost ...>
PerlOptions -Authen -Authz -Access -Sections
</VirtualHost>
就禁止使用 PerlAuthenHandler, PerlAuthzHandler, PerlAccessHandler, 和 Sections/段。而
<VirtualHost ...>
PerlOptions None +Response
</VirtualHost>
就只能使用 PerlResponseHandler

AutoLoad

大致是自动加载的意思。比如开启 PerlOptions +Autoload 后,当使用 PerlResponseHandler Apache::Magick 时前面就不需要先明显的加载 PerlModule Apache::Magick 模块。而是碰到后会自动加载。

ParseHeaders

它与 mod_perl 1.0 中的 PerlSendHeader On 功能一样。
如果你在代码中使用 print "Content-type: text/html\n\n"; 的话将这个 PerlOption 开起来。它会将这句话自动转为调用 send_http_header
这一般在 ModPerl::Registry 中用得比较多。因为 ModPerl::Registry 使用的旧代码都是这么输出的。
<Location /perl>
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
Options +ExecCGI
PerlOptions +ParseHeaders
PerlOptions +SetupEnv
</Location>

Others etc.

还有一些其他比较复杂的涉及到编译池或环境变量什么的。如果以后有机会用到再介绍。

PerlSwitches

Switch 的意思是开关。如我们在命令行时会使用 perl -e 这里的 -e 就是一个 switch.
一般而言它最大的用途就是修改 @INC, (我们也可以在 startup.pl 里修改,然后 PerlRequire)用 PerlSwitches 的好处就是黏合力更强一些。如:
PerlSwitches -I/var/www/MyApp/lib
它的作用还类似于这么写:
<Perl>
use lib qw(/var/www/MyApp/lib);
</Perl>
这三种办法都是可以的。

SetHandler

有两种类型,一种是 modperl, 另一种是 perl-script

perl-script

这是更为常见的类型的。它的默认配置为:

  • 如果没有 PerlOptions -GlobalRequest 的话,PerlOptions +GlobalRequest 将在 PerlResponseHandler 阶段起作用。
  • 如果没有 PerlOptions -SetupEnv 的话,默认将是 PerlOptions +SetupEnv
  • 将 STDIN 和 STDOUT 与 $r 绑定,这样就能使用 CORE::print() 来输出到 STDOUT 或从 STDIN 中读取。而 modperl 类型必须使用 $r->puts() 这样的函数来输出。
  • %ENV, @INC, $/, STDOUT 的 $| 和 END 块这种特殊变量将在调用 response handler 之前保存而在之后将恢复。
  • 任何加到 %ENV 的变量将传递给 subprocess_env 表,而后我们可以在接下来的 PerlLogHandler 和 PerlCleanupHandler 阶段中通过 r->subprocess_env 来获取。

modperl

这种类型下你只能调用 Perl*Handler 里的函数。如果你不需要以上 perl-script 的用途的话,使用 modperl 会带来更好的性能。
当配置成 PerlOptions +SetupEnv 时, modperl 类型只配置如下环境变量:$ENV{MOD_PERL}(总是存在) $ENV{PATH} 和 $ENV{TZ} (如果你在命令行/shell 或 httpd.conf 里设置了它们)。这时候你如果想传递一些配置变量的话一定要使用 PerlSetVar 和 PerlAddVar 而不是 PerlSetEnv 和 PerlPassEnv. 举一个例子:
 #httpd.conf
<Location /print_env2>
SetHandler modperl
PerlResponseHandler Apache2::VarTest
PerlSetVar VarTest VarTestValue
</Location>
接下来在 Apache2::VarTest::handler() 里你可以通过
$r->dir_config('VarTest');

例子

file:MyApache2/PrintEnv1.pm
-----------------------
package Apache::PrintEnv1;
use strict;

use Apache::RequestRec ( ); # for $r->content_type
use Apache2::RequestIO (); # for print
use Apache2::Const -compile => ':common';

sub handler {
my $r = shift;

$r->content_type('text/plain');
for (sort keys %ENV){
print "$_ => $ENV{$_}\n";
}

return Apache2::Const::OK;
}
1;

这个需要配置成
PerlModule MyApache2::PrintEnv1
<Location /print_env1>
SetHandler perl-script
PerlResponseHandler MyApache2::PrintEnv1
</Location>
file:MyApache2/PrintEnv2.pm
------------------------
package MyApache2::PrintEnv2;
use strict;

use Apache2::RequestRec (); # for $r->content_type
use Apache2::RequestIO (); # for $r->print

use Apache2::Const -compile => ':common';

sub handler {
my $r = shift;

$r->content_type('text/plain');
$r->subprocess_env;
for (sort keys %ENV){
$r->print("$_ => $ENV{$_}\n");
}

return Apache2::Const::OK;
}
1;
这个可以配置成
PerlModule MyApache2::PrintEnv2
<Location /print_env2>
SetHandler modperl
PerlResponseHandler MyApache2::PrintEnv2
</Location>
    不同点:
  • perl-script 可以用 print, 而 modperl 只能用 $r->print
  • perl-script 默认就用 PerlOptions +SetupEnv, 而 modperl 下一定要显性调用 $r->subprocess_env; 或开启 PerlOptions +SetupEnv

PerlResponseHandler

这个阶段是用于创建回复的。这毫无疑问是 mod_perl 中最最重要的阶段。
而且这个指令是 mod_perl 中必须的两个指令之一。
<Location /hello>
SetHandler perl-script
PerlResponseHandler Apache2::Hello
</Location>
哪个指令都能少,就这两个是必须的。它指出了在 ResponseHandler 阶段所委托的模块。

Reference

* http://perl.apache.org/docs/2.0/user/config/config.html