Integrating PHP and Perl

Irfan Habib

Issue #154, February 2007

PHP and Perl are both so powerful, they can even run each other.

Perl is a language often associated with text processing and CGI. PHP is a language often associated with dynamic Web pages. Both are very popular with Web developers. Often, each of these languages is used at the expense of the other. Hard-core Perl developers would love to develop everything in Perl, and PHP developers tend to stick with PHP.

As usual in the Open Source world, there is a lot of zealotry between users of each language. If you think that one of these languages is perfect and the other is lame, this article is not for you! This article is for those who take a more pragmatic approach and use what works best for them. Each language has its strengths and limitations. Personally, I use both languages at work and at home. With time, I have discovered which language is best for which tasks and try to integrate the strengths of each language as much as possible to complete my work quickly.

Perl is extremely good at system administration and extensive data processing, among other things. This means, if you want to do some extensive processing on a text report, Perl would be preferable, as it provides handy regular-expression-enabled text comparisons, which make it so much easier to search through a report. Perl also has extensive string manipulation features. Perl, by virtue of being older than PHP and having an extensive community, has thousands of extensions archived in CPAN, which allow one to do virtually anything with the language, conveniently. From XML processing to writing to parallel port devices, CPAN includes everything. CPAN is the reason Perl continues to be useful to a large number of developers to date. Although it is not impossible to do everything described here with PHP and a mixture of other languages, it's simply more convenient with Perl.

PHP is extremely good at integration with Web pages and databases. PHP integrates nicely with static HTML Web pages. That's why it's so popular and has had more visibility than Perl in recent years. It has mature support for numerous popular free or non-free databases and supports MS SQL (MSSQL) server better than any other open-source language. From personal experience, I have tried at least two CPAN extensions for Perl to get it to work with an MSSQL installation, but with limited success. However, PHP has seamless support for MSSQL and uses it as natively as MySQL.

I was recently involved in a project in which nearly the entire project was in Perl. However, a tiny bit of code needed access to an MSSQL server. I knew how simple it was in PHP to work with MSSQL, and I did not want to go through the pain of setting up my Perl installation for MSSQL. That's why I searched the Internet for a way to integrate both languages in a manner that would allow me to use the best parts of each language and produce a coherent solution. And, I found the PHP::Interpreter CPAN module. PHP::Interpreter was perfect. It enables the complete integration of the two languages to an extent that one starts to believe that both are mere extensions of each other. PHP::Interpreter, as this article shows, allows you to use PHP's mature support for databases and other features natively in Perl, and also to use Perl's vast number of CPAN modules to extend your PHP programs.

According to AnnoCPAN, the module's main function is to encapsulate an embedded PHP5 interpreter. It provides proxy methods (via AUTOLOAD) to all the functions declared in the PHP interpreter, transparent conversion of Perl data types to PHP (and vice versa), and the ability for PHP to call Perl subroutines similarly and access the Perl symbol table. The goal of this package is to construct a transparent bridge for running PHP code and Perl code side by side.

To demonstrate the power of this module, we code two examples to show each side of the PHP::Interpreter, integrating Perl with PHP and integrating PHP with Perl. Each example shows areas in which both languages complement each other nicely to produce powerful code.

Example 1: Integrating PHP with Perl

In the first example, we create an application to monitor failed logins through SSH to our system. SSH often is targeted by script kiddies and malicious users to compromise a system and gain access to it. The script identifies the IPs of the offenders, blocks all incoming packets from using iptables and, finally, logs them in to an MS SQL server database. We use Perl to do what it's best at—processing log files. It will continuously monitor the /var/log/messages file, which the SSH dæmon uses to log failed login attempts. To monitor a log file continuously, we use the CPAN extension File::Tail. To support writing to MS SQL Server transparently, we implement this portion in PHP and show how the two languages can be integrated seamlessly and used in scenarios where both complement each other.

Setting Up PHP::Interpreter

Setting up PHP::Interpreter is basically a standard Perl module installation procedure. You can get it from search.cpan.org/dist/PHP-Interpreter. Unpack it, and create the Makefile:

perl Makefile.PL

Compile it:

make

And, install it:

make install

You can do an additional:

pod2html interpreter.pm > interpreter.html

and keep the documentation file for future reference.

We also use the CPAN module File::Tail, which allows us to monitor a log file continuously. You can get this module from search.cpan.org/dist/File-Tail.

Unpack it, and create the Makefile:

perl MakeFile.PL
make
make install

Now, fire up a text editor, and start coding:

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. }

In a separate file, write the following script (the file should be named 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. ?>

To run the application, simply run the Perl script:

Perl scriptname

In Line 25, you need to fill in the correct settings for your MSSQL server installation. You also need to have a PHP installation with support for MSSQL. This is usually done by passing the switch -with-mssql during the compilation of PHP. Some distributions also require you to install FreeTDS, which is used by PHP to access MSSQL.

Now, let's review some specific portions of the code. To use the PHP::Interpreter in your code, declare its use, as in line 1. To create a new instance of PHP interpreter, do as is shown in line 5:

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

As with object-oriented programming, you now can invoke methods on the $php object to achieve interoperability with PHP. The above code shows two functions provided by the PHP::Interpreter for interoperability. In line 24, we are calling the include() function, which includes a PHP script file to the environment, and you can call functions defined in the file natively from the object. We do the same with writeIP in line 25, which is a PHP function declared in writems.php on line 2 of the writems.php listing. The Eval function of the $php object allows you to execute a specific PHP instruction, as with a live interpreter. The instruction is interpreted, and the return may be stored into a variable or used directly, as in line 26. As you can see in the above program, PHP::Interpreter provides an object-oriented mechanism for completely integrating the two languages. This integration is achieved with only two lines of code: the initial use statement and the instantiation of the object. PHP::Interpreter is not only about calling functions and procedural programming, it also works with object-oriented PHP. This is how you can instantiate an object of class defined in a PHP:

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

The instance is stored in $instance, and any arguments are passed to the class' constructor.

Example 2: Integrating Perl with PHP

The biggest advantage of Perl/PHP integration is PHP's ability to access Perl CPAN modules. There are CPAN modules for almost everything that can be done via software; you can use PHP::Interpreter in PHP to call CPAN modules to extend a PHP application to do anything, which is not native to PHP—for example, it enables you to write to IO ports. Writing to IO ports has been the exclusive domain of C/C++ programs, but with PHP::Interpreter, even a mere scripting language can have the capability to write to IO ports. The example that follows shows how to use Perl code with PHP, but first, we discuss the features of PHP::Interpreter that allow PHP/Perl integration.

The PHP interpreter, invoked via PHP::Interpreter, has a special class that allows PHP to Perl communication. Create an instance of the class via this call in PHP:

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

The new $perl object allows you to evaluate specific Perl instructions in PHP, such as:

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

Similar to Example 1, where we called a PHP function in Perl, you can call Perl subroutines in PHP. All subroutines defined in the Perl program, which instantiated the PHP::Interpreter instance, can be invoked like this (I will provide a more detailed example shortly):

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

And, of course, you can get and set variables from the Perl file that instantiated the PHP::Interpreter; however, only package variables, not lexical variables, are supported.

Let's look at a practical application of PHP/Perl integration—for example, a snippet of Perl code that uses the Babel Fish CPAN module. (Babel Fish is a piece of software that allows you to translate text between different languages. To learn more about Babel Fish, go to babel.altavista.com.) The PHP program calls the translate function, which will be implemented in Perl, to translate a string in English to German and retrieve the output.

To install the Babel Fish CPAN, go to search.cpan.org/CPAN/authors/id/D/DM/DMUEY/AltaVista-BabelFish-v42.0.1.tar.gz, and install it with the standard installation procedure, as shown previously in this article.

AltaVista::BabelFish also has some prerequisites, such as Class::Std and Class::Std::Util. These need to be downloaded and installed for Babel Fish to work:

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. }

The phpscript.php file contains the following:

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. ?>

Let's look at this piece of code in more detail. In line 4 of the PHP program, we are creating an instance of the Perl class using Perl::getInstance(). This is the special class inserted by the PHP::Interpreter dynamically into the environment to achieve PHP to Perl integration.

In line 5, we then use the class object, $perl, to invoke a function called translate, which is defined in line 6 of the Perl program, and we pass the arguments accordingly. The subroutine translate is invoked from the Perl script, and the translation is done via the Babel Fish module. The translated string is returned to PHP and printed via the print statement. Although this is a rudimentary example, the entire script can be extended to provide runtime translation for viewers of a dynamic Web page generated from PHP. With CPAN and the PHP::Interpreter, the possibilities of what can be achieved in PHP are bounded only by the developer's imagination.

You can use the PHP Perl class for object-oriented Perl as well. Invoke a Perl object via the new() function, as follows:

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

The first argument to the new() method, in line 3, is the name of the class, and additional arguments are passed to the constructor of the class.

Conclusion

This article shows both sides of the PHP::Interpreter: using PHP in Perl and Perl in PHP. The module essentially allows a PHP programmer to extend the capabilities of PHP to enable it to do anything that CPAN allows Perl to do. It also allows a Perl programmer to use those features in PHP that are not yet mature or not implemented in Perl. By no means have I covered all of the PHP::Interpreter, and readers are encouraged to explore the official CPAN documentation of PHP::Interpreter.

Irfan Habib is an undergraduate student of software engineering at the National University of Sciences Technology. He has been deeply interested in Free and Open Source Software for years. He often comes across tasks for which he needs to pull together a solution really quickly, and Perl and PHP usually allow him to do that. He can be reached at irfan.habib@niit.edu.pk.