altre destinazioni

ultimi post

ultimi commenti

tag principali

categorie

powered by

  • WPFrontman + WP

friends

copyright

  • © 2004-2011
    Ludovico Magnocavallo
    tutti i diritti riservati

Python never had a chance against PHP?

30 marzo 2005

As much as I usually like John Lim’s excellent PHP Everywhere, I don’t particularly agree with his comments on Ian Bicking’s Why Web Programming Matters Most. Being a longtime PHP programmer (since when it was called PHP/FI) and PEAR contributor, and having heavily used (and loved) Python for all my projects in the past couple of years, both Ian’s

resolving Python’s problems with web programming is the most important thing we can do to market Python

and John’s

what made PHP successful is not what PHP is lacking but the features that PHP has that are superior to Python

ring true to my experience.

What I don’t particularly agree with is John’s list of things PHP “does better” than Python:

questo post continua, leggi il resto

HTML Dynamic TestRunner for PHPUnit

17 settembre 2003

Yesterday I set up to write unit tests for a few PHP classes I’m refactoring. Given my usual tendency to set aside serious tasks and concentrate on eye candy, after a few tests I decided to write my own test runner (something I’ve done at least twice before, never storing away the code for later use).

My test runner builds on a few assumptions:

  • the tests live in a web accessible directory
  • every php file in the directory with a name starting with test is a test case
  • every test file contains one case class extending PHPUnit_TestCase
  • every test class has the same name as the file where it resides
  • test cases are all the methods of the class whose names start with test
  • you run tests and see results with a web browser

The main test runner features are:

  • conditional execution of one or more test cases based on an HTML form
  • run only/exclude a test with a single click of the mouse
  • display aggregate test results by suite, showing counts for successes/failures and a warning indicator if any have been raised
  • optionally expand test results to show results/failures for single test cases
  • remember each suite visibility status (collapsed/expanded) for subsequent runs
  • collect PHP warnings emitted during each test case run and display them under the appropriate test suite

I made a few screenshots, showing a collapsed view of all available suites, the same for a single suite, with a warning indicator, and one suite expanded, showing test cases results and warnings.

The DynamicTestRunner code only needs to include the PEAR version of PHPUnit. Using a templating engine would have made the code more understandable, but at the cost of introducing a dependency.

If you want to preserve the option of being able to run each test file on its own, you can test if it has been called by testRunner with a simple if statement at the end of each test file (substituing of course testEntities with the name of the class declared in the file):

if (!isset($_SERVER['SCRIPT_FILENAME']) ||
    $_SERVER['SCRIPT_FILENAME'] == __FILE__) {
    $suite = new PHPUnit_TestSuite('testEntities');
    $result = PHPUnit::run($suite);
    echo $result->toString();
}

Dumping Vars the PEAR way

4 settembre 2003

Since it was announced on the PEAR newsgroup a few years ago, I have been using the Var_Dump class by Frederic Poeydomenge as one of my favourite PHP development tools. Dumping variables at strategic places has always been a practical and effective (if a bit simple) method of debugging in PHP and other languages. Unfortunately, when you’re working with complex data or with class instances, using the echo construct or the var_dump function is not very useful, unless you go to great lengths to extract what you really need to display from your data.

Var_Dump is a class that assists you in exactly this task, extracting all available information from your data and displaying it in a variety of ways. It is the PHP analogous to the Python pprint module, and one could spend an interesting few minutes comparing the two very different PHP and Python solutions to dumping (or pretty printing, which is more what’s happening here) data.

In its simplest form, the Var_Dump class can be used through a single static method, and a few optional constants. At the other end of the scale, a Var_Dump instance can be customized through skins, color sets, and display options to tailor its output to your exact needs. In this brief document, we will show examples of its use through a static method, since it’s the one you most probably will resort to during development. More complex examples can be found on the author’s site.

We start by importing the class code, and setting up a few variables and a class instance, that we will feed to Var_Dump.

require_once 'Var_Dump.php';

$a = array(0,1,2,34);
$b = array('a'=>'something', 'b'=>0, 0=>'b', 'c'=>$a);

class B {}

class A extends B {
    var $a;
    var $b = 1;
    function a($a) {
        $this->a = $a;
    }
}

$a_inst = new A(2);

Once we have that out of the way, we start feeding our data to Var_Dump by calling its display method as a static class method. First the simple array, without passing any extra argument to display.

Var_Dump::display($a);

Our friendly class spits out the following output:

0 Long (0)
1 Long (1)
2 Long (2)
3 Long (34)

Nice, isn’t it? Ok, let’s start using the optional arguments to display, and feed it something a bit more complex.

Var_Dump::display($b, 'b');

The second argument we passed to display in this example is the name of a key not to display, mainly used to avoid printing deeply nested data structures. As we can see from the following output, Var_Dump does a remarkable job of printing multidimensional arrays.

a String[9]  (something)
b Not parsed ()
0 String[1]  (b)
c Array(4)

    + 0 Long (0)
    | 1 Long (1)
    | 2 Long (2)
    + 3 Long (34)

On to the class instance, and the third argument you can pass to display.

Var_Dump::display($a_inst, null, VAR_DUMP_DISPLAY_MODE_HTML_TABLE);

As you have probably guessed, the third argument controls the output generated by Var_Dump through a few self-explanatory constants:

  • VAR_DUMP_DISPLAY_MODE_TEXT
  • VAR_DUMP_DISPLAY_MODE_HTML_TEXT
  • VAR_DUMP_DISPLAY_MODE_HTML_TABLE

The output of our instance is pretty interesting, as Var_Dump squeezes all available bits of information from it, including its class and its base classes, if any.

Class name String[1] (a)
Parent class String[1] (b)
Class vars (2)
a Null (Null)
b Long (1)
Class methods (1)
0 String[1] (a)
Object vars (2)
a Long (2)
b Long (1)

The fourth, and last of the arguments you can pass to display is again a constant from a self-explanatory set:

  • VAR_DUMP_NO_CLASS_VARS
  • VAR_DUMP_NO_CLASS_METHODS
  • VAR_DUMP_NO_CLASS_INFO

Let’s see what happens if we feed our instance to display, by using the VAR_DUMP_NO_CLASS_INFO constant, and reverting to the simpler output.

Var_Dump::display($a_inst, null,
    VAR_DUMP_DISPLAY_MODE_HTML, VAR_DUMP_NO_CLASS_INFO);
Class name   String[1] (a)
Parent class String[1] (b)
Object vars  Array(2) 
           
    + a Long (2)
    + b Long (1)

That’s all for tonight, if you have suggestions for PHP topics you would like to read here, drop me a message.

Feed On Feeds

3 settembre 2003

my feedonfeeds hack

I noticed from my logs that somebody using feedonfeeds is subscribed to my blog. Feedonfeeds is a nice PHP aggregator/reader, written by Steve Minutillo.

After shopping around for a while for a news aggregator, I settled on feedonfeeds a couple of months ago for a couple of reasons:

  • it has very few dependencies (any version of MySQL is ok, and a sufficiently recent PHP)
  • it is a server-side aggregator, allowing me to read news from home/office/wherever
  • it installs in a couple of minutes anywhere you can put a few PHP files
  • it is easily customizable
  • it works =)

So I installed feedonfeeds, and staying up to date has become a joy.

Never satisfied, after a couple of days of use I decided I absolutely needed a few features that feedonfeeds was lacking. So I set about refactoring it, and the project turned out to be a full rewrite. As usual with these things, the result is a half-completed app, so that I now have the (minor) features I wanted, and lack most basic ones feedonfeeds provides out of the box. =)

My rewrite is template-based, and factors out most common operations (eg SQL statements etc) in a few classes, instead of using lots of functions. So far, I have:

  • a fully functional news reading page with save/read/unread flagging, paging by page and by date, etc. (the one you see in the picture above)
  • a half-working feeds page, showing subscribed feeds and their new/read/saved news counts, and controls to add/edit/remove a feed that get you nowhere =)
  • an improved SQL schema, using InnoDB tables support for foreign keys
  • a new Python backend based on Mark Pilgrim’s ultra-liberal feed parser, supporting the parser’s use of HTTP features (If-None-Match and If- Modified-Since headers when requesting an RSS feed, and parsing the ETag and Last-Modified headers) to limit transfers; the uhm “backend” is an amazing 75 lines of code, including a 20 or 30 lines string containing the SQL schema =)

update: I finally managed to add the most important missing functionality to my feedonfeeds rewrite, and package it.

PHPTAL (re)visited -- moved

20 agosto 2003

As promised in a previous entry, I managed to give a look at PHPTAL. Since the entry is a bit long, I copied it as a standalone article, and left only this brief notice on the blog.

PHPTAL (re)visited

20 agosto 2003

As promised in a previous entry, I managed to give a look at PHPTAL. Well, not a serious, in-depth look at the implementation, more like a sort of I like TAL let’s see if this is good enough look. Okay, okay, I can see your noses wrinkling, let me explain this a bit.

My involvement with PHP, which led me to develop the PEAR DB LDAP driver and maintaining for a while the Interbase driver, plus using it for lots of personal and work projects, came to an abrupt halt last year for two reasons: I got a new job for a major Italian bank managing a group of consultants, so less or no development; I fell in love with Python, so I began using it more and more for my personal projects.

Lately I started developing again at work, partly because I like it more than managing people (managing a development team is better, but unfortunately we delegate this sort of stuff to consultants), partly because the current economic climate (and plenty of available spare time at work) lets me convince the bosses from time to time to try and develop some of our projects in-house.

Thus my involvement with PHP began again, since some of the stuff we’re rewriting or refactoring is already done in PHP, and very few things (none that I know of) can beat PHP web applications in speed of development/deployment and performance. Performance is in fact one of our major issues, since the applications I am working on are used daily by a good part of our 60.000 internal users. The other major issue related to PHP and its ancillary libraries/classes is reliability. We’re not working on financial applications but on simple intranet stuff like our internal telephone directory. Simple stuff on which however some of our business processes depend, since the only way of timely reaching somebody is often to call him on his mobile phone, be it for troubleshooting an “important” application or process or to schedule a meeting, and one of the few ways to know who deals with what (if the what is something you don’t know anything about in your usual tasks) is resorting to the telephone/organizational units directory.

Thus my good enough criteria means good enough for our loads, and reliable enough not to expose strange or random behaviour.

For templating we currently use a mix of the old PHPlib template class (used by our consultants), and my rewrite of it (in the new projects). It’s simple, it’s fast, it allows you to separate the logic from the presentation pretty well, although not as much as TAL does. So yesterday morning I spent a few minutes with a colleague trying to benchmark PHTAL and see if it is fast enough to try and develop something with it.

The results were pretty much what I expected: PHPTAL is too slow to be used for our applications. What I did not expect, however, was the order of magnitude of its slowness compared to what we are using (more on that later). Not satisfied with the (basic, but sufficient for our needs) performance tests, I did a quick and dirty reliability test by comparing PHPTAL with my reference TAL implementation, the Python library simpleTAL. I was pretty surprised to discover that simpleTAL is slightly faster than PHPTAL, and that it spits out warnings if you try to use the TAL templates used in the PHPTAL examples. This did not sound good for PHPTAL’s quality.

So tonight I did a bit of reading around the PHPTAL documentation, and was pretty surprised to learn that PHPTAL requires a separate Types library that define new data types on top of the (perfectly complete, in PHP’s context) native PHP ones. Urgh! I am allergic to too many abstractions (what Joel of Joel On Software fame calls leaky abstractions), and this looks definitely like a bad case of abstractitis. What’s the need of a reference helper? Every decent PHP developer should know his way around references, they’re not so hard (well, after you bang your head against the wall a few times in frustration but decide to stick with it). What’s the need of creating an Iterator interface on top of PHP’s very good, feature-rich, and fast arrays? Something like this does not look like sensible PHP code to me:

require_once 'Types/Iterator.php';
$i = $iterable->getNewIterator();
while ($i->isValid()) {
    $value =& $i->value();
    $i->next();
}

It is suspiciously similar in functionality to this:

$i = array('a'=>'b', 'c'=>'d');
foreach ($i as $k => $v)
    $value =& $v;

Two lines less, more clarity, more speed, less abstractions. Of course, this is only my opinion. Well, back to the original topic of this entry, testing.

For the tests, I tried to use one of the templates used in the PHPTAL documentation. Since it sports invalid TAL syntax according to simpleTAL (the Python library), I had to drop a row which was used only as a placeholder anyway. The resulting template looks like this:

<?xml version="1.0"?> 
<html>
<head>
    <title tal:content="title">place for the page title</title>
</head>
<body>
<h1 tal:content="title">sample title</h1>
<table>
<tr>
    <td>name</td>
    <td>phone</td>
</tr>
<tr tal:repeat="item users"> 
    <td tal:content="item/matricola">matricola</td>
    <td tal:content="string: ${item/cognome} ${item/nome}">item name</td>
    <td tal:content="item/telefono">item phone</td>
</tr>
</table>
</body> 
</html>

The data is in a separate php file that simply declares a “$users” array composed of 200 elements, each an associative array with the required fields. To time results, I used the very good PEAR Benchmark_Timer class by Sebstian Bergmann.

The sample PHPTAL code looks like this:

#!/usr/bin/php -q
<?php
require_once 'users.php';
require_once 'Benchmark/Timer.php';

$t =& new Benchmark_Timer(true);

require_once "HTML/Template/PHPTAL.php";
$t->setMarker('post-require');

$tpl =& new PHPTAL("tal_template.xml");
$t->setMarker('post-template');

$tpl->set("title", "Test Page");
$t->setMarker('post-set-title');

$tpl->setRef("users", $users);
$t->setMarker('post-set-users');

$res = $tpl->execute();
$t->setMarker('post-execute');

if (PEAR::isError($res))
    echo $res->toString(), "n";
?>

Running this test the first time gives:

----------------------------------------------------------------------
marker             time index            ex time               perct
----------------------------------------------------------------------
Start              1061421217.91528700   -                       0.00%
----------------------------------------------------------------------
post-require       1061421217.93816100   0.022874                5.79%
----------------------------------------------------------------------
post-template      1061421217.93849100   0.000330                0.08%
----------------------------------------------------------------------
post-set-title     1061421217.93855700   0.000066                0.02%
----------------------------------------------------------------------
post-set-users     1061421217.93861100   0.000054                0.01%
----------------------------------------------------------------------
post-execute       1061421218.30995300   0.371342               94.04%
----------------------------------------------------------------------
Stop               1061421218.31017400   0.000221                0.06%
----------------------------------------------------------------------
total              -                     0.394887              100.00%
----------------------------------------------------------------------

subsequent runs use a cached version of the parsed template (something I don’t like too much, it should be an option to cache, not an option not to cache) and give:

----------------------------------------------------------------------
marker             time index            ex time               perct
----------------------------------------------------------------------
Start              1061421220.91106600   -                       0.00%
----------------------------------------------------------------------
post-require       1061421220.93519000   0.024124               15.37%
----------------------------------------------------------------------
post-template      1061421220.93551900   0.000329                0.21%
----------------------------------------------------------------------
post-set-title     1061421220.93558300   0.000064                0.04%
----------------------------------------------------------------------
post-set-users     1061421220.93563700   0.000054                0.03%
----------------------------------------------------------------------
post-execute       1061421221.06779900   0.132162               84.20%
----------------------------------------------------------------------
Stop               1061421221.06802400   0.000225                0.14%
----------------------------------------------------------------------
total              -                     0.156958              100.00%
----------------------------------------------------------------------

Pretty slow, considering it’s running on a sufficiently fast machine, and it’s practically doing nothing. Our real application on top of that has lots of templating operations, LDAP searches, etc.

My second test tried to replicate the same functionality using my Template class. The template looks like this:

<html>
<head>
<title>{title}</title>
</head>
<body>
  <h1>{title}</h1>
  <table>
  <tr>
    <td>name</td>
    <td>phone</td>
  </tr>
  <!-- BEGIN row -->
  <tr> 
    <td>{row_matricola}</td>
    <td>{row_cognome} {row_nome}</td>
    <td>{row_telefono}</td>
  </tr>
  <!-- END row -->
  <!-- BEGIN dummy -->
  <tr> 
   <td>sample name</td>
   <td>sample phone</td>
  </tr>
  <tr>
    <td>sample name</td>
    <td>sample phone</td>
  </tr>
  <!-- END dummy -->
</table>
</body> 
</html>

The dummy row is there to serve the same purpose of the rows I removed from the TAL template after trying it with Python. I left them there since the speed difference is already enough. The code used for the second test is:

#!/usr/bin/php -q
<?php
require_once 'users.php';
require_once 'Benchmark/Timer.php';

$t =& new Benchmark_Timer(true);

require_once "Template.php";
$t->setMarker('post-require');

$tpl =& new Template('/home/ludo/tests');
$tpl->setFile('main', 'template.html');
$t->setMarker('post-template');

$tpl->setVar("title", "Test Page");
$t->setMarker('post-set-title');

$tpl->parseBlock('ROW', 'row', $users, 'main');
$t->setMarker('post-set-users');

$tpl->setBlock('main', 'dummy', 'DUMMY');
$tpl->setVar('DUMMY', '');

$res = $tpl->parse('MAIN', 'main');
$t->setMarker('post-execute');

// result may be an error
if (PEAR::isError($res))
    echo $res->toString(), "n";
?>

Running this test gives:

----------------------------------------------------------------------
marker             time index            ex time               perct
----------------------------------------------------------------------
Start              1061421680.50660500   -                       0.00%
----------------------------------------------------------------------
post-require       1061421680.50944200   0.002837               22.93%
----------------------------------------------------------------------
post-template      1061421680.50970300   0.000261                2.11%
----------------------------------------------------------------------
post-set-title     1061421680.50974900   0.000046                0.37%
----------------------------------------------------------------------
post-set-users     1061421680.51790200   0.008153               65.88%
----------------------------------------------------------------------
post-execute       1061421680.51878400   0.000882                7.13%
----------------------------------------------------------------------
Stop               1061421680.51898000   0.000196                1.58%
----------------------------------------------------------------------
total              -                     0.012375              100.00%
----------------------------------------------------------------------

More than 10 times faster, and that’s without caching anything. More than 30 times faster against the uncached version of PHPTAL.

To compare with Python, I timed the execution of the three scripts with the shell time command, since I was just interested in a rough overview of the relative speed.

Python:

/-(ludo@pippozzo)-(53/pts)-(01:24:37:Thu Aug 21)--
-($:~/tests)-- time ./test.py
 
real    0m0.591s
user    0m0.580s
sys     0m0.010s

PHPTAL:

/-(ludo@pippozzo)-(54/pts)-(01:24:38:Thu Aug 21)--
-($:~/tests)-- time ./test_tal.php >/dev/null

real    0m0.421s
user    0m0.310s
sys     0m0.110s

Template:

/-(ludo@pippozzo)-(56/pts)-(01:24:53:Thu Aug 21)--
-($:~/tests)-- time ./test_tpl.php >/dev/null

real    0m0.268s
user    0m0.190s
sys     0m0.080s

To sum it up, we’re going to keep using our Template class. PHPTAL is a commendable effort, and something that I would definitely use in most of my PHP development, but it needs to be more lightweight, reliable and fast.

If you’re interested in the Python script, here it is:

#!/usr/bin/env python
def test():
   from simpletal import simpleTAL, simpleTALES
    import sys, cPickle

    users = cPickle.load(file('users.pickle', 'r'))

    context = simpleTALES.Context()
    context.addGlobal("title", "Hello World")
    context.addGlobal("users", users)

    template = simpleTAL.compileHTMLTemplate (file("tal_template.xml", 'r'))

    template.expand(context, file('/dev/null', 'w'))

if __name__ == '__main__':
    test()

PHP 101 - PEAR error handling

18 agosto 2003

Read with interest today A Few Tips for Writing Useful Libraries in PHP (via PHP Everywhere), a nice article by the author of the MagpieRSS parser.

Posted a couple comments to the site, to which the author kindly replied by email. Nothing much, the usual “don’t name your php files with an extension other than the one defined in your webserver”, and an observation about error handling.

As all PHP developers know, PHP has no exceptions (and thus no try/something clauses), errors are either stored in special variables or accessed by calling functions, depending on what extension you’re working with (PHP5 will introduce exceptions). All pretty messy, as the article author correctly points out.

One thing most developers don’t know is that PEAR (the default PHP library bundled with the PHP source code) has a nice PEAR base class with great error handling features (why a good number of PHP developers stay away from PEAR is another matter entirely from the topic of this entry).

Basically, if your classes (you use PHP’s OO features when writing reusable code, don’t you?) inherit the PEAR base class, you get error handling for free (and a few other things, like destructors).

Using PEAR error handling is very easy, a quick example will suffice:

// our imaginary library (not very useful, is it?)

define('MYLIB_ERROR', 100);

require_once 'PEAR.php';

class myClass extends PEAR {

    function myClass($myargs = null) {
            $this->PEAR();
    }

    function &raiseError($message, $method, $line) {
        $error = PEAR::raiseError(sprintf("%s.%s() line %d: %s",
            get_class($this), $method, $line, $message),
            MYLIB_ERROR);
    }

    function myMethod() {
        $this->raiseError("hmmm something went wrong.....", 'myMethod', __LINE__);
    }

}

For our libray developer, that’s all there is to it. Basically you import the base PEAR class, and use it as a base class for your library classes. Whenever you need to “throw” an exception, just raise a PEAR error. The raiseError method is what I use in my classes, makes much easier debugging what went wrong by appending the method name and the line number where the error occurred, and a constant I use to trap errors depending on the originating classes.

Let’s see the user part of PEAR’s error handling routines:

// user code accessing our library

PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'errorHandler');
function errorHandler($err) {
    echo("<b>PEAR error</b><br>message: <i>"
        . $err->getMessage()
        . "</i><br>user info: <i>"
        . $err->getUserInfo()
        . "</i><br>");
}

// or use any of the standard PEAR error messages, eg
// PEAR::setErrorHandling(PEAR_ERROR_PRINT);
// for development or a more sophisticated handler for production,
// eg mailing a copy of the error or saving it in a log

$myinst =& new myClass();
$res = $myinst->myMethod();
if (PEAR::isError($res)) {
    // do something, the error is handled
    // (in our case printed) by the handler routine
} else {
    // do something else
}

The user only needs to import the PEAR base class (or alternatively use $myinst->isError() since it extends PEAR) and check method return values for possible errors. An added benefit of using PEAR’s error handling routines is that by changing the function used to handle errors you can switch from debugging (ie print verbose error messages) to production (ie don’t let the user see errors, handle them in your application) just by changing the error handling routine. A common practice is to automate this sort of things by checking the environment of the running server (eg hostname, etc.).

PHPTAL, ZPT for PHP

17 agosto 2003

I’ve been thinking of writing a TAL|METAL package for PHP for some time, to replace my own version of the aging PHPlib Template class (no I don’t like Smarty or other tamplating packages for PHP).

Yesterday I stumbled upon PHPTAL, which looks good and is even a PEAR package. Will try it soon.