集成 PHP 和 Perl

作者:Irfan Habib

Perl 是一种常与文本处理和 CGI 关联的语言。PHP 是一种常与动态网页关联的语言。两者都在 Web 开发者中非常流行。通常,这两种语言中的一种会被使用而牺牲另一种。硬核 Perl 开发者会喜欢用 Perl 开发一切,而 PHP 开发者则倾向于坚持使用 PHP。

像往常一样,在开源世界中,每种语言的用户之间都存在很多狂热。如果您认为其中一种语言是完美的,而另一种是蹩脚的,那么本文不适合您!本文是为那些采取更务实的方法并使用最适合他们的方法的人而写的。每种语言都有其优点和局限性。就我个人而言,我在工作和家庭中都使用这两种语言。随着时间的推移,我发现哪种语言最适合哪些任务,并尽可能地整合每种语言的优势,以快速完成我的工作。

Perl 非常擅长系统管理和大量数据处理等。这意味着,如果您想对文本报告进行一些广泛的处理,Perl 将是更可取的,因为它提供了方便的、支持正则表达式的文本比较,这使得搜索报告变得容易得多。Perl 还具有广泛的字符串操作功能。Perl 因为比 PHP 更早出现,并且拥有庞大的社区,所以在 CPAN 中存档了数千个扩展,这使得人们可以使用该语言方便地完成几乎任何事情。从 XML 处理到写入并行端口设备,CPAN 几乎包含了所有内容。CPAN 是 Perl 至今仍然对大量开发者有用的原因。虽然用 PHP 和其他语言的混合来完成这里描述的所有事情并非不可能,但用 Perl 只是更方便。

PHP 非常擅长与网页和数据库集成。PHP 与静态 HTML 网页集成得很好。这就是为什么它如此受欢迎,并且近年来比 Perl 有更高的知名度。它对众多流行的免费或非免费数据库有成熟的支持,并且比任何其他开源语言都更好地支持 MS SQL (MSSQL) 服务器。根据个人经验,我至少尝试过两个用于 Perl 的 CPAN 扩展,以使其与 MSSQL 安装一起工作,但成功有限。然而,PHP 对 MSSQL 有无缝支持,并且像使用 MySQL 一样本地化地使用它。

我最近参与了一个项目,其中几乎整个项目都是用 Perl 编写的。然而,一小部分代码需要访问 MSSQL 服务器。我知道在 PHP 中使用 MSSQL 是多么简单,而且我不想经历为 MSSQL 设置我的 Perl 安装的痛苦。这就是为什么我在互联网上搜索一种方法来集成这两种语言,以便我可以使用每种语言的最佳部分并生成一个连贯的解决方案。而且,我找到了 PHP::Interpreter CPAN 模块。PHP::Interpreter 非常完美。它使两种语言完全集成,以至于人们开始相信两者只是彼此的扩展。正如本文所示,PHP::Interpreter 允许您在 Perl 中本地使用 PHP 对数据库和其他功能的成熟支持,并且还可以使用 Perl 大量的 CPAN 模块来扩展您的 PHP 程序。

根据 AnnoCPAN,该模块的主要功能是封装一个嵌入式 PHP5 解释器。它为 PHP 解释器中声明的所有函数提供代理方法(通过 AUTOLOAD),透明地转换 Perl 数据类型为 PHP(反之亦然),以及 PHP 调用 Perl 子例程并访问 Perl 符号表的能力。这个包的目标是构建一个透明的桥梁,用于并排运行 PHP 代码和 Perl 代码。

为了演示这个模块的强大功能,我们编写了两个示例来展示 PHP::Interpreter 的每一面,将 Perl 与 PHP 集成,以及将 PHP 与 Perl 集成。每个示例都展示了两种语言在哪些方面可以很好地互补,从而产生强大的代码。

示例 1:将 PHP 与 Perl 集成

在第一个示例中,我们创建一个应用程序来监控通过 SSH 对我们系统的失败登录尝试。SSH 经常成为脚本小子和恶意用户的目标,以入侵系统并获得访问权限。该脚本识别攻击者的 IP,阻止所有来自使用 iptables 的传入数据包,最后将它们记录到 MS SQL 服务器数据库中。我们使用 Perl 来做它最擅长的事情——处理日志文件。它将持续监控 /var/log/messages 文件,SSH 守护程序使用该文件来记录失败的登录尝试。为了持续监控日志文件,我们使用 CPAN 扩展 File::Tail。为了透明地支持写入 MS SQL Server,我们在 PHP 中实现这部分,并展示如何无缝地集成这两种语言,并在两者互补的场景中使用它们。

设置 PHP::Interpreter

设置 PHP::Interpreter 基本上是一个标准的 Perl 模块安装过程。您可以从 search.cpan.org/dist/PHP-Interpreter 获取它。解压它,并创建 Makefile

perl Makefile.PL

编译它

make

然后,安装它

make install

您可以执行额外的

pod2html interpreter.pm > interpreter.html

并保留文档文件以供将来参考。

我们还使用了 CPAN 模块 File::Tail,它允许我们持续监控日志文件。您可以从 search.cpan.org/dist/File-Tail 获取此模块。

解压它,并创建 Makefile

perl MakeFile.PL
make
make install

现在,启动一个文本编辑器,开始编码

1. use PHP::Interpreter;
2. use File::Tail;
3. use threads ('yield', 'stack_size' =>64 * 4096, 'exit'
   =>'threads_only');
4. use Thread;
5. my $php = PHP::Interpreter->new;
6. my $ref=tie *FH,"File::Tail",(name=>'/var/log/messages');
7. while (<FH>)
8. {
9. if($_=~/sshd/) #checks for message from sshd
10. {
11. if($_=!/Failed password for/) #check for a failed password attempt
12. {
13. $ind = rindex($str,'from');
14. $rind = rindex($str,'port');
15. $ip = substr($str,$ind+4,$rind-$ind-4);
16. $thr = new Thread \&writems, $ip;
17. $thr->join();
18. }
19. }
20. }
21. sub writems
22. {
23. `iptables -I INPUT -s $ip -j DROP`
24. $php->include(*"*writems.php*"*);
25. $php->writeIP('ssqlserver','sshwatch','sshusr','sshpass',$_[0]);
26. print $php->eval("echo Succeeded!");
27. }

在一个单独的文件中,编写以下脚本(该文件应命名为 writems.php)

1. <?php
2. function writeIP($dbhost,$dbname,$dbuser,$dbpass,$ip)
3. {
4. $conn = mssql_connect($dbhost,$dbuser,$dbpass)
5. or die("Couldn't connect to SQL Server on $dbhost");
6. $db = mssql_select_db($dbname, $s)
7. or die("Couldn't open database $myDB");
8. set_time_limit(0);
9. $squery="insert into sshwatch(currentdate,ip)
10. values('".date('Y/m/d')."','".$ip."')"; mssql_query($squery);
11. }
12. ?>

要运行该应用程序,只需运行 Perl 脚本

Perl scriptname

在第 25 行,您需要填写 MSSQL 服务器安装的正确设置。您还需要安装支持 MSSQL 的 PHP。这通常通过在 PHP 编译期间传递开关 -with-mssql 来完成。一些发行版也可能要求您安装 FreeTDS,PHP 使用它来访问 MSSQL。

现在,让我们回顾一下代码的一些特定部分。要在您的代码中使用 PHP::Interpreter,请声明它的用法,如第 1 行所示。要创建 PHP 解释器的新实例,请按照第 5 行所示进行操作

my $php = PHP::Interpreter->new;

与面向对象编程一样,您现在可以调用 $php 对象上的方法来实现与 PHP 的互操作性。上面的代码显示了 PHP::Interpreter 提供的两个用于互操作性的函数。在第 24 行,我们调用 include() 函数,该函数将 PHP 脚本文件包含到环境中,您可以从对象本地调用文件中定义的函数。我们在第 25 行对 writeIP 执行相同的操作,writeIP 是在 writems.php 列表的第 2 行的 writems.php 中声明的 PHP 函数。$php 对象的 Eval 函数允许您执行特定的 PHP 指令,就像使用实时解释器一样。指令被解释,返回值可以存储到变量中或直接使用,如第 26 行所示。正如您在上面的程序中看到的,PHP::Interpreter 提供了一种面向对象的机制,用于完全集成这两种语言。这种集成仅通过两行代码实现:初始 use 语句和对象的实例化。PHP::Interpreter 不仅关于调用函数和过程式编程,它也适用于面向对象的 PHP。这就是您如何实例化在 PHP 中定义的类的对象

my $instance = $PHP->instantiate('PHPclass', @args);

实例存储在 $instance 中,任何参数都传递给类的构造函数。

示例 2:将 Perl 与 PHP 集成

Perl/PHP 集成的最大优势是 PHP 访问 Perl CPAN 模块的能力。几乎可以通过软件完成的所有事情都有 CPAN 模块;您可以在 PHP 中使用 PHP::Interpreter 调用 CPAN 模块来扩展 PHP 应用程序以执行任何非 PHP 原生的事情——例如,它使您能够写入 IO 端口。写入 IO 端口一直是 C/C++ 程序的专属领域,但有了 PHP::Interpreter,即使是仅仅一种脚本语言也可以具有写入 IO 端口的能力。以下示例展示了如何在 PHP 中使用 Perl 代码,但首先,我们讨论 PHP::Interpreter 的特性,这些特性允许 PHP/Perl 集成。

通过 PHP::Interpreter 调用的 PHP 解释器有一个特殊的类,允许 PHP 与 Perl 通信。通过 PHP 中的以下调用创建该类的实例

1. <?php
2. $perl = Perl::getInstance();
3. ?>

新的 $perl 对象允许您在 PHP 中评估特定的 Perl 指令,例如

1. <?php
2. $perl = Perl::getInstance();
3. $perl->eval(q^
4. print "Executing Perl code in PHP\n";
5. ^);
6. ?>

与示例 1 类似,我们在 Perl 中调用了一个 PHP 函数,您可以在 PHP 中调用 Perl 子例程。在实例化 PHP::Interpreter 实例的 Perl 程序中定义的所有子例程都可以像这样调用(我稍后将提供更详细的示例)

1. <?php
2. $perl = Perl::getInstance();
3. $return = $perl->call('sub', @args);
4. ?>

当然,您可以从实例化 PHP::Interpreter 的 Perl 文件中获取和设置变量;但是,仅支持包变量,而不支持词法变量。

让我们看看 PHP/Perl 集成的实际应用——例如,一段使用 Babel Fish CPAN 模块的 Perl 代码。(Babel Fish 是一种允许您在不同语言之间翻译文本的软件。要了解更多关于 Babel Fish 的信息,请访问 babel.altavista.com。)PHP 程序调用 translate 函数,该函数将在 Perl 中实现,以将英语字符串翻译成德语并检索输出。

要安装 Babel Fish CPAN,请访问 search.cpan.org/CPAN/authors/id/D/DM/DMUEY/AltaVista-BabelFish-v42.0.1.tar.gz,并使用本文前面所示的标准安装过程进行安装。

AltaVista::BabelFish 也有一些先决条件,例如 Class::Std 和 Class::Std::Util。这些需要下载并安装才能使 Babel Fish 工作

1. use AltaVista::BabelFish;
2. use PHP::Interpreter;
3. my $p = PHP::Interpreter->new();
4. $p->include("phpscript.php");
5. my $val = $p->invoke();

6. sub translate
7. {
8. my $phish = AltaVista::BabelFish->new({ source => $_[0], target =>
   $_[1] });
9. return $phish->translate($_[2]) or die $phish->get_errstr();
10. }

phpscript.php 文件包含以下内容

1. <?php
2. function invoke()
3. {
4. $perl = Perl::getInstance();
5. $string = $perl->call('translate', 'en','de','Translate this for me');
6. print "Translated string: $string\n";|
7. }
8. ?>

让我们更详细地看一下这段代码。在 PHP 程序的第 4 行,我们使用 Perl::getInstance() 创建 Perl 类的实例。这是由 PHP::Interpreter 动态插入到环境中的特殊类,以实现 PHP 到 Perl 的集成。

在第 5 行,我们然后使用类对象 $perl 来调用一个名为 translate 的函数,该函数在 Perl 程序的第 6 行中定义,并且我们相应地传递参数。子例程 translate 从 Perl 脚本中调用,翻译通过 Babel Fish 模块完成。翻译后的字符串返回到 PHP 并通过 print 语句打印出来。虽然这是一个基本的示例,但可以扩展整个脚本,为从 PHP 生成的动态网页的查看者提供运行时翻译。有了 CPAN 和 PHP::Interpreter,PHP 中可以实现的可能性的界限仅受开发人员的想象力限制。

您也可以将 PHP Perl 类用于面向对象的 Perl。通过 new() 函数调用 Perl 对象,如下所示

1. <?php
2. $perl = Perl::getInstance();
3. $instance = $perl->new('perlclass', @args);
4. ?>

第 3 行中 new() 方法的第一个参数是类的名称,其他参数传递给类的构造函数。

结论

本文展示了 PHP::Interpreter 的两个方面:在 Perl 中使用 PHP 和在 PHP 中使用 Perl。该模块本质上允许 PHP 程序员扩展 PHP 的功能,使其能够执行 CPAN 允许 Perl 执行的任何操作。它还允许 Perl 程序员在 PHP 中使用那些在 Perl 中尚未成熟或尚未实现的功能。我绝没有涵盖 PHP::Interpreter 的所有内容,鼓励读者探索 PHP::Interpreter 的官方 CPAN 文档。

Irfan Habib 是国立科技大学软件工程专业的本科生。多年来,他对自由和开源软件非常感兴趣。他经常遇到需要快速组合解决方案的任务,而 Perl 和 PHP 通常允许他做到这一点。可以通过 irfan.habib@niit.edu.pk 联系他。

加载 Disqus 评论