Using Perl Code from PHP

April 14, 2004

Uncategorized

Intended Audience
Introduction
Perl Interpreter in PHP
Using Perl Objects
Error Handling
Summary


Intended Audience

This article describes the perl extension for PHP. It is intended for developers who use both languages in their projects, or who are migrating from one language to the other. It could be of general interest to other developers.


Introduction

PHP and perl are two very popular Web programming languages. They both have many libraries and extensions that can simplify the process of development, but often you can find a perl library you want, and not the corresponding library in PHP. (Perl is older then PHP, so naturally it has a larger selection of libraries and extensions.) This was the main reason that the perl extension for PHP was written.

Many large projects use both PHP and perl, with some subsystems implemented in PHP, and others in perl. Often these subsystems need to communicate with each other, and some perl modules – such as PHP::Include and PHP::Session – have been implemented to achieve this (see http://www.cpan.org/modules/by-module/PHP/ for more information). However, although they allow PHP session files to be read, PHP variables to be serialized and simple PHP code to be executed from within perl, there is no real communication between the two interpreters.

The PHP perl extension was implemented to allow the usage of perl code from within PHP. It is a wrapper that embeds the perl interpreter and converts data from PHP to perl and back. At the time of writing it only provides a one-way interface from PHP to perl, but in the future it could be extended to implement a full two-way interface. The perl extension allows the programmer to do the following from a PHP script:

  • load and execute perl files
  • evaluate perl code
  • access perl variables
  • call perl functions
  • instantiate perl objects
  • access properties of perl objects
  • call methods of perl objects

All these features are accessible through a single API class called Perl.

PHP’s perl extension is available from the PECL web site at http://pecl.php.net/package/perl. The latest development version can be obtained with the following CVS command:

$ cvs -d :pserver:cvs.php.net:/repository co pecl/perl

If you have a full perl installation, the extension will work with it. If you don’t have perl on board, you can still communicate with the perl interpreter through PHP by putting a copy of perl58.so or perl58.dll somewhere PHP can find it (in the PHP directory or in your system path).


Perl Interpreter in PHP

To access the perl interpreter from PHP, you must first create an instance of the Perl class. Its constructor can receive some parameters, but we will omit them at this point. They are necessary for working with perl objects, but not for working with the interpreter itself.


$perl = new Perl();

This line of code creates an instance of the perl interpreter. It is possible to create several instances of the interpreter, but all of them will use the same one internally, so that all code and variables will be shared across instances. The object $perl can be used to execute external perl files, evaluate inline perl code, access perl variables and call perl functions.

External perl files can be loaded using the Perl::require() method. Take a look at the following example:

Example 1 (test1.pl)

print "Hello from perl!
"

Example 1 (test1.php)

<?php

print "Hello from PHP!
"
;
$perl = new Perl();
$perl->require("test1.pl");
print
"Bye!
"
;

?>

In this example perl outputs a string directly to the PHP output stream, but in some cases you will want to grab the output as a string and process it with your PHP code. This can be done using the PHP output buffering API:

Example 2 (test2.php)

<?php

ob_start();
$perl = new Perl();
$perl->require("test1.pl");
$out = ob_get_contents();
ob_end_clean();
print
"Perl: $out";

?>

As you can see, it works fine. Of course the same can be done with PHP’s system call, but less efficiently. The system() function will start the interpreter each time it’s called, whereas $perl->eval() uses the embedded interpreter in the same address space and doesn’t need to create a new process.

As was said earlier, the PHP perl extension can evaluate inline perl code. This method is more useful if you want to execute a small piece of code. With the Perl::eval() method you don’t need to create several small perl files, but can instead simply embed perl code into PHP.

Example 3 (test3.php)

<?php

print "Hello from PHP!
"
;
$perl = new Perl();
$perl->eval('print "Hello from perl!
"'
);
print
"Bye!
"
;

?>

Perl::eval() accepts only one argument – the perl code to execute, in string format. PHP allows three different ways of writing string literals; single quoted, double quoted or heredoc. Note that PHP will act on the content of literal strings in the usual way before they are passed to perl (see http://www.php.net/manual/language.types.string.php for details).

In the previous example we didn’t receive any unexpected results from Perl::eval(), but we could do so. The perl interpreter can run the same code in different contexts, and the result can be very different in those different contexts. For example, a list returned in a scalar context would be received as the final element of that list.

A Perl object uses the scalar context by default, but evaluating in an array or hash context[1] requires the use of special tricks. The method eval() should not be called directly on the perl interpreter object, but on the appropriate property (array or hash).

Example 4 (test4.php)

<?php

$perl = new Perl();
var_dump($perl->eval('("a","b","c")'));        // eval in scalar context
var_dump($perl->array->eval('("a","b","c")')); // eval in array context
var_dump($perl->hash->eval('("a","b","c")'));  // eval in hash context

/* output:

string(1) "c"
array(3) {
      [0]=>
      string(1) "a"
      [1]=>
      string(1) "b"
      [2]=>
      string(1) "c"
    }
array(2) {
      ["a"]=>
      string(1) "b"
      ["c"]=>
      NULL
    }
*/

?>

This example evaluates the same data – a list – in different contexts, and as you can see, there are three different results from that data.

Perl has several scopes of global variables (scalars $x, arrays @x, hashes %x and code &x). PHP’s perl extension allows you to access global scalar, array and hash variables. To access scalar perl variables, just use the property with the same name. To access array and hash variables, use the same trick as for selecting the evaluation context.

Example 5 (test5.php)

<?php

$perl = new Perl();
$perl->eval('$x = 1; @x  = 1..4; %x  = 1..4');
var_dump($perl->x);
var_dump($perl->array->x);
var_dump($perl->hash->x);

/* output:

int(1)
array(4) {
      [0]=>
      int(1)
      [1]=>
      int(2)
      [2]=>
      int(3)
      [3]=>
      int(4)
    }
array(2) {
      [1]=>
      int(2)
      [3]=>
      int(4)
    }
*/

?>

As you see, here we have three variables with the same name but in different scopes, and of course all three have different values.

The perl extension allows not only reading but also writing to perl variables. You can do this by assigning a new value to the corresponding property; however, you cannot modify part of a perl array or hash. (The modification will not take effect.)

Evaluating perl variables is just one simple interaction between PHP and perl. More often we need to call a single perl function. PHP’s perl extension provides a simple and elegant way to do this. You just need to call the method with the corresponding name via the interpreter, and pass parameters to it:

Example 6 (test6.php)

<?php

$perl = new Perl();
$perl->eval('
        sub sum {
            my $x = shift(@_);
            foreach my $y (@_) {
                $x += $y;
            }
        return $x;
        }
    '
);

print $perl->eval("sum(1, 2, 3, 4, 5, 6, 7, 8, 9)")."
"
;
print
$perl->sum(1, 2, 3, 4, 5, 6, 7, 8, 9)."
"
;

?>

The function can receive parameters and return a scalar or complex value. As with eval(), any function call can be made in one of three different contexts (scalar, array or hash), and the context can be specified in the same way.

Perl uses packages to limit the name scope of variables and functions, and sometimes we need to call functions or access variables from these packages using qualified names. Such names doesn’t confirm to PHP syntax, but they can be used with the special construct ->{}.

Example 7 (test7.php)

<?php

$perl = new Perl();
$perl->eval('use Digest::MD5 qw(md5_hex);');

echo "PHP:
"
;
var_dump(md5('Hello'));

echo "perl:
"
;
var_dump($perl->eval('md5_hex("Hello");'));
var_dump($perl->md5_hex("Hello"));
var_dump($perl->{"Digest::MD5::md5_hex"}("Hello"));

?>

This example uses the external perl module md5, which is loaded during runtime. If you have a full perl installation, downloading and installing CPAN modules is as easy as downloading a tarball, extracting it to the perl top-level directory and typing:


cd perl
perl makefile.pl
make
make test
make install

at the command prompt. On Windows systems you will need to replace make with nmake; however, this will only work if you have MSVS tools installed.

If you don’t have a full perl installation – i.e. you’re running perl as a shared object – it is still possible to run perl modules once they have been built elsewhere. The minimal environment necessary for running them consists of the entire set of perl package management (*.pm) files from perllib, and the full perllibauto and perllibDigest directories.

The PHP Perl extension cannot call internal perl functions (print, read, etc); you will need to use Perl::eval() in order to access these. For example, in order to include the minimal module environment outlined above you would need to call:

$perl->eval("BEGIN {unshift( @INC, 'perlenv_dirpath'); }");


Using Perl Objects

In common with many other programming languages, perl uses an object-oriented approach, but it has no special syntax for classes. Classes are simply packages that happen to provide methods to deal with objects; methods are simply package functions that expect an object reference as the first argument; objects are simply references that ‘know’ which class they belong to.

The perl extension allows you to instantiate perl objects from a PHP script, and to access their properties and methods. The same Perl class is used for this, but this time you need to pass arguments to its constructor. The first argument is a perl class (package) name, the second is the optional constructor name, and any remaining arguments are constructor-specific. If the constructor name is omitted then the default constructor new is used.

new Perl(string class_name [, string constructor_name [, mixed args]]);

The following example defines the perl class Point with two constructors, new and Point. The first constructor doesn’t accept any special arguments; the second receives initial coordinate values. The class has two properties, x and y, and two methods, move() and get(). As you can see, the method move() moves a point to the offset specified in the arguments passed to it. The method get() is more interesting. Depending on the context, it returns the current coordinates as a string (in the scalar context) or as an array (in the array context).

As with native PHP objects, you can call the methods of instantiated perl objects and access their properties. The only difference is the calling context. By default, methods are called in the scalar context. To call them in an array or hash context, use the same trick as before: the method should not be called directly on the perl object, but on a special property (array or hash).

All properties can be accessed directly (without the array or hash property). This is because perl objects can contain only scalar properties. Arrays and hashes are represented as references to them, and references are scalars in perl. Object properties can be read, written, modified and even undefined, but parts of a property cannot be modified. For example, we can’t modify an element of an array property.

Example 8 (test8.php)

<?php

$perl = new Perl();
$perl->eval('
package Point;
sub new {
        my $this = shift;
        my $type = ref($this) || $this;
        my $self = {};
        $self->{x} = 0;
        $self->{y} = 0;
        bless $self, $type;
        return $self;
        }
sub Point {
        my $this = shift;
        my $type = ref($this) || $this;
        my $self = {};
        $self->{x} = shift;
        $self->{y} = shift;
        bless $self, $type;
        return $self;
        }
sub move {
        my $self = shift;
        $self->{x} += shift;
        $self->{y} += shift;
        }
sub get {
        my $self = shift;
        if (wantarray) {
            return ($self->{x}, $self->{y});
        } else {
            return $self->{x} . "x" . $self->{y};
            }
        }
    '
);

// create perl object "Point" with constructor "new"
$p1 = new Perl("Point");
var_dump($p1);
$p1->x += 100;                     // modify property
unset($p1->y);                     // undefine property
var_dump($p1);

// create perl object "Point" with constructor "Point"
$p2 = new Perl("Point", "Point", 100, 200);
var_dump($p2);
$p2->move(200,200);                // call method "move"
var_dump($p2->get());              // call method "get" in scalar context
var_dump($p2->array->get());       // call method "get" in array context
echo $p2->x . "." . $p2->y . "
"
; // print perl object properties

?>


Error handling

The perl extension uses the PHP exception mechanism to report perl errors. A special exception class, PerlException, is used for this. The following example tries to evaluate invalid perl code, and as a result Perl::eval() throws a PerlException:

Example 9 (test9.php)

<?php

$perl = new Perl();
try {
        
var_dump($perl->eval('$a = $s{$d}.'));
        echo
"ok
"
;
    }
catch (
PerlException $exception) {
        echo
"Perl error: " . $exception->getMessage() . "
"
;
    }

?>

Exceptions can be thrown by Perl::eval(), Perl::require(), or a call to a perl function, object method or constructor.


Summary

The PHP perl extension is a simple one-way binding from PHP to perl. It allows you to execute perl code from PHP, and has the ability to access perl variables, call perl functions, and instantiate perl objects.

Note that the extension is still marked EXPERIMENTAL, so your ideas and suggestions can help to make it stable and usable. Please report any problems you find with the extension to http://bugs.php.net/.


[1] – Perl defines the scalar and array (list) contexts. The hash context is the same as the array (list) context, but the result is converted to an associative PHP array (hash array).

8 Responses to “Using Perl Code from PHP”

  1. _____anonymous_____ Says:

    Hello,
    I notice that doing a $perl->require() or $perl->eval(), will send the header even if using output buffering. Once that happens, I can no longer set cookies, etc. Has anyone been able to use perl without it sending a header?

    Thanks!

  2. ssg0 Says:

    Do somebody know how to fix this bug?

    http://www.pecl.php.net/bugs/bug.php?id=14644

  3. strahinja Says:

    I`m trying to pass variable from PHP file to perl script in code bellow. If someone has an example to post that would be great, btw. everything else works great.

  4. _____anonymous_____ Says:

    $perl = New Perl() points to /usr/bin/perl or what default perl is.
    What if I want to use /usr/local/bin/perl?

    Do I have to decide this at pecl compile time?

  5. _____anonymous_____ Says:

    add
    extention=perl.so to your php.ini file

  6. _____anonymous_____ Says:

    I installed PHP’s perl extension on debian. Everything installed without any errors. I checked whether PERL is properly installed by doing ‘apt-get install perl’. To be completely sure, I copied the perl.so file in the usr/lib/php5 directory. When I try to use the extension within a php script ($perl = new Perl();),I get a php error that the class cannot be found. Any ideas?

  7. _____anonymous_____ Says:

    You upgrade to php5. Its been out for 4 years, its time for an upgrade.

  8. omnoviatech Says:

    This PECL extension seems really cool, but its for PHP5 only. How can I do this php->perl stuff in php4?