This document is http://tnx.nl/php/ - If you replicate it, please do link. This page was written in 2003 (I think), is very out of date, and was never updated to reflect changes in PHP.

Despair-like poster: PHP, Training Wheels without the Bike

PHP is a nice tool for adding dynamic parts to a small website. It is popular because it integrates very well with the popular Apache webserver, but also because the basics of PHP are very easy to learn. But is it wise to build applications or large websites with PHP? Perhaps Perl is a better solution.

This document compares PHP to Perl, because its author is very familiar with these two languages. It would be nice if others could write comparisons to Ruby, Python, and other dynamic programming languages.

A follow-up document is available, at http://tnx.nl/php5.

Table of contents

Arguments and return values are extremely inconsistent

To show this problem, here's a nice table of the functions that match a user defined thing: (with something inconsistent like this, it's amazing to find that the PHP documentation doesn't have such a table. Maybe even PHP people will use this document, just to find out what function to use :P)

                         replaces case                 gives   s/m/x offset
                 matches with     insens number arrays matches flags (-1=end)
ereg             ereg             no     all    no     array   no    0
ereg_replace     ereg    str      no     all    no     no      no    0
eregi            ereg             yes    all    no     array   no    0
eregi_replace    ereg    str      yes    all    no     no      no    0
mb_ereg          ereg[1]          no     all    no     array   no    0
mb_ereg_replace  ereg[1] str/expr no     all    no     no      yes   0
mb_eregi         ereg[1]          yes    all    no     array   no    0
mb_eregi_replace ereg[1] str      yes    all    no     no      no    0
preg_match       preg[2]          yes/no one    no     array   yes   0
preg_match_all   preg             yes/no all    no     array   yes   0
preg_replace     preg    str/expr yes/no n/all  yes    no      yes   0
str_replace      str     str      no     all    yes    number  no    0
str_ireplace     str     str      yes    all    yes    number  no    0
strstr, strchr   str/char         no     one    no     substr  no    0
stristr          str/char         yes    one    no     substr  no    0
strrchr          char             no     one    no     substr  no    -1
strpos           str/char         no     one    no     index   no    n
stripos          str/char         yes    one    no     index   no    n
strrpos          char[3]          no     one    no     index   no    n
strripos         str              yes    one    no     index   no    -1
mb_strpos        str[1]           no     one    no     index   no    n
mb_strrpos       str[1]           yes    one    no     index   no    -1

The problem exists for other function groups too, not just for matching.

(In Perl, all the functionality provided by the functions in this table is available through a simple set of 4 operators.)

[1] Handles multi-byte characters
[2] PCRE regex: so-called "Perl compatible" regular expressions.
[3] Also does strings in PHP 5

PHP has separate functions for case insensitive operations

(This can be argued both ways. Some think it's good to have functions that combine functions, even if that means having dozens of extra names to remember)

In Perl, you use a double lc() (lowercase) or the /i flag where PHP usually provides a case insensitive variant. The case-insensitive versions have very inconsistent naming.

Perl: $foo cmp $bar                            lc $foo cmp lc $bar
PHP:  strcmp($foo, $bar)                       strcasecmp($foo, $bar)

Perl: index($foo, $bar)                        index(lc $foo, lc $bar)
PHP:  strpos($foo, $bar)                       stripos($foo, $bar)

Perl: $foo =~ s/foo/bar/                       $foo =~ s/foo/bar/i
PHP:  $foo = str_replace('foo', 'bar', $foo)   $foo = str_ireplace(...)
PHP:  $foo = ereg_replace('foo', 'bar' ,$foo)  $foo = eregi_replace(...)

PHP has inconsistent function naming

PHP has no lexical scope

Perl has lexical scope and dynamic scope. PHP doesn't have these.

For an explanation of why lexical scope is important, see Coping with Scoping.

                       PHP  Perl
Superglobal            Yes  Yes [1]
Global                 Yes  Yes
Function local         Yes  Yes [2]
Lexical (block local)  No   Yes
Dynamic                No   Yes

[1] Perl has variables that are always in the main:: namespace. These are like PHP's superglobals.
[2] Using a lexical variable in a sub routine's block, you get a function local variable.

PHP has too many functions in the main namespace

(Using the core binaries compiled with all possible extensions in the core distribution, using recent versions in November 2003.)

Number of PHP  main functions: 3079 [1]
Number of Perl main functions:  206 [2]
Median PHP  function name length: 13
Mean   PHP  function name length: 13.67
Median Perl function name length:  6
Mean   Perl function name length:  6.22

Note that Perl has short syntax equivalents for some functions:

readpipe('ls -l') ==> `ls -l`
glob('*.txt')     ==> <*.txt>
readline($fh)     ==> <$fh>
quotemeta($foo)   ==> "\Q$foo"
lcfirst($foo)     ==> "\l$foo"  (lc is \L)
ucfirst($foo)     ==> "\u$foo"  (uc is \U)

[1] Source: PHP Quick Reference
[2] Source: perldoc perlfunc

PHP lacks abstraction and takes TIMTOWTDI to bad extremes

Why has PHP got 3079 functions while Perl does with only 206? In PHP, there are usually several functions that are very similar. In Perl, you have to know and remember less.

Another important factor is the use of modules, especially the DBI module which provides support for SQL databases, instead of bloating the core with lots of features that occupy memory but are rarely used.

(Modules that are more often not used than used don't count (This rules out PEAR for PHP and IO::File for Perl). Modules may be pulled in when the core provides no similar functionality. For simplicity, internal working is left out of this comparison.)

* Escaping:

    * PHP: (14)
    dbx_escape_string, escapeshellarg, escapeshellcmd, pg_escape_bytea,
    pg_escape_string, pg_unescape_bytea, addslashes, addcslashes, preg_quote,
    quotemeta, mysql_escape_string, mysql_real_escape_string,
    mysqli_real_escape_string, sqlite_escape_string

    * Perl: (2) [1]
    quotemeta, $dbh->quote

* Sorting:

    * PHP: (16)
    sort, arsort, asort, krsort, ksort, natsort, natcasesort, rsort, usort,
    array_multisort, uasort, uksort, dbx_sort, imap_sort, ldap_sort, yaz_sort

    * Perl: (1)

* Walking a list

    * PHP: (10)
    array_filter, preg_grep, array_search, array_unique, in_array, array_map,
    array_walk, array_count_values, array_change_key_case, array_sum

    * Perl: (2)
    map, grep

* Splitting:

    * PHP: (8)
    split, explode, strtok, spliti, chunk_split, mb_split, preg_split,

    * Perl: (1)

* Matching:

    * Strings:

        * PHP: (11)
        strstr, strchr, stristr, strpos, strrchr, stripos, mb_strpos,
        mb_strrpos, strrpos, strripos, substr

        * Perl: (3)
        index, rindex, substr

    * Regular expressions:

        * PHP: (6)
        ereg, eregi, mb_ereg, mb_eregi, preg_match, preg_match_all

        * Perl: (1)

* Substituting a matched part:

    * PHP: (12)
    ereg_replace, eregi_replace, mb_ereg_replace, mb_eregi_replace,
    preg_replace, str_ireplace, str_replace, ltrim, rtrim, trim, nl2br

    * Perl: (1)

* Connecting to an SQL database:

    * PHP: (17)
    dbx_connect, fbsql_connect, ibase_connect, msql_connect, msql_pconnect,
    mssql_connect, mysql_connect, odbc_connect, pg_connect, pg_pconnect,
    sesam_connect, ifx_pconnect, ifx_connect, sqlite_open, sqlite_popen,
    mysqli_connect, mysqli_pconnect

    * Perl: (2)
    DBI->connect, DBI->connect_cached

* Opening:

    * PHP: (5)
    dio_open, fopen, proc_open, popen, gzopen[2]

    * Perl: (2)
    open, sysopen

* Reading/receiving:

    * PHP: (12)
    dio_read, fread, gzread[2], socket_read, socket_recv, socket_recvfrom,
    socket_recvmsg, readline, fgetc, fgets, stream_get_line, file

    * Perl: (5)
    read, readline, sysread, recv, getc

* Printing/writing:

    * PHP: (14)
    print, echo, printf, fprintf, vprintf, dio_write, fwrite, fputs,
    gzwrite[2], socket_send, socket_sendmsg, socket_sendto, socket_write,

    * Perl: (5)
    print, printf, syswrite, send, write

* Closing:

    * PHP: (7)
    closelog, dio_close, fclose, gzclose[2], pclose, socket_close,

    * Perl: (1)

* Miscellaneous:

    * PHP:
    array_combine, array_fill, array_merge, list, range, count,
    create_function, strtr, pow, putenv, getenv, getmygid, getmypid, getmyuid

    * Perl:
    syntax or magic variables

[1] Because of system LIST syntax and DBI's placeholders, explicit escaping is usually not even needed.
[2] Handled in Perl by a PerlIO layer


Slashdot, BBC creates 'Perl on Rails': I am going to create "PHP off the Rails" for developers of PHP websites. PHP developers will need no training, as most of them are off the rails already! -- Anne Thwacks

EFnet #php: 19:45 <+Dragnslcr> Comparing PHP to Perl is like comparing pears to newspapers

Perl Monks: PHP - it's "training wheels without the bike" -- Randal L. Schwartz

lesscode.org: PHP has a very low barrier to entry; consequently, a lot of eye-wateringly bad programmers are writing code in it; hence, a lot of code written in PHP is awful. This is arguably a good thing. -- Aristotle Pagaltzis

use Perl;: We all know that, due to the poor design of the language, security and PHP applications don't mix well, because PHP makes writing secure code difficult; apparently security doesn't mix well either with the mindset of the PHP core developers. -- Rafael Garcia-Suarez

Perl Buzz: Sure, PHP 6 may have a shorter release cycle than Perl 6 has, but at the end of it all, we'll have Perl 6, and you'll still have PHP. Just sayin'. -- Andy Lester

FOSDEM: :I chose PHP because I thought it would be a better fit for the community I was targeting, which tended to have a lot of non-programmers in it. -- Patrick Michaud