Skip to content


php|tropics coming up

tropics_logo I'm making some final preparations this weekend for my first journey to Mexico and the PHP|Tropics conference.

I'll be speaking on two topics: PDO (of course) and something of an odd-ball talk on PHP's Streams--basically a collection of various handy code snippets and best/worst practices for using streams.

I'm looking forward to the trip; I'll have my electrovaya fully charged for some plane hacking, and be accompanied by my friend, colleague and boss George Schlossnagle--it should be a lot of fun meeting up with the PHP gang again, as well as meeting with the attendees: it's always good meet people that are seriously using PHP.

The location is also going to be nice ;-)

Hope to see you there :)

OSEvents - spammers?

It saddens (and annoys) me that someone promoting PHP conferences has subscribed me (and presumably other php core developers) to mailing lists containing more information than I ever wanted to know about their events--without my permission.

I don't mind the odd useful email here or there, but I've had enough of them recently to prompt me to take the time to unsubscribe and to blog about it. The chances of me associating myself with them or their conferences, in either attendee or speaker capacity are now pretty damned slim.

Am I just being touchy, or feeling overly self important? I don't think so. It's my inbox; don't sign me up for stuff that I didn't ask for. I consider that really bad behaviour. OS Events--consider your wrists slapped.


   foreach (file('myfile.txt') as $line) {
       echo $line;

How sexy is that? ;-) (yes, I know, you can't aggregate it)


A few people have reported problems with trying to run PDO on OSX; I believe them to be general dynamic loading issues in the PHP build system itself. We'll try to resolve them for PHP 5.1. In the meantime, if you're on OSX and want to try PDO, then you can try building it statically; here's how:

Now we need to integrate the PDO extensions into the PHP source tree. Open up a terminal window and cd to where you've downloaded the sources.

   tar xjf php5-STABLE-latest.tar.bz2
   tar xzf PDO-0.2.2.tgz
   tar xzf PDO_SQLITE-0.2.2.tgz
   mv PDO-0.2.2 php5-STABLE-200502120730/ext/pdo
   mv PDO_SQLITE-0.2.2 php5-STABLE-200502120730/ext/pdo_sqlite
   cd php5-STABLE-200502120730
   rm configure
   ./buildconf --force

At this point, you're ready to build configure PHP. As I'm writing this, I'm testing things out on a G4 450MHz running OSX.2; it's not quite as fast as I'd like, so I'm going for a fairly minimal PHP install, skipping all the xml stuff and using only pdo and pdo sqlite. You should probably leave out the "--disable-all" option when you build it:

   ./configure --disable-all --enable-debug \\
          --prefix=/usr/local/php-5.0.4 \\
          --enable-cli --enable-pdo \\

Now, here's a tricky part. The build system in PHP 5.0.x doesn't know that PDO should be initialized before the PDO driver(s), so we need to edit two files to make that happen. For PHP 5.1, you won't need to do this step. Use your favourite editor to open up main/internal_functions.c. Find the part that looks like this:

    zend_module_entry *php_builtin_extensions[] = {

You need to change it so it looks like this instead:

    zend_module_entry *php_builtin_extensions[] = {

In other words, you need to make sure that the pdo_ptr line is listed before any other pdo_XXX_ptr line. Note that you will probably have a bunch of other extensions listed here; leave the order of those as they are; the important thing is that pdo comes before the other pdo lines.

Repeat this step for main/internal_functions_cli.c

Now you're ready to build:


And you're done. You can install this if you like, by running "make install"; it will land in the prefix you specified (if you copied me, that will be /usr/local/php-5.0.4), but for the sake of testing, you don't need to do that.

I quickly tested that my build worked by running:

    ./sapi/cli/php -m

and I saw:

    [PHP Modules]
    [Zend Modules]

And a really quick test to make sure that it doesn't blow up straight-away:

    ./sapi/cli/php -r '$d = new PDO("sqlite::memory:"); debug_zval_dump($d);'


    object(PDO)#1 (0) refcount(2){


It took a long time to compose this post, mostly due to the processor speed on that box; please put the info to good use!

I'm not an OSX expert, so I can't tell you how to set up your build environment (because I don't remember!), and it's not convenient for me to check things out. If you have OSX specific problems please ask around on the mailing list. If you run into a crash, use gdb to generate a backtrace and report the bug using the PECL bug tracker.

Good luck!

It's the weekend: test PDO

It's the weekend, time for relaxation and recreational hacking. The perfect opportunity to give PDO a whirl :-)

Please do try it out soon as you can; with PHP 5.1 beta due on the first of March, it's really important to make sure that we don't have any "brown-paper-bag" bugs sooner rather than later.

If you pass up on this round of testing, and then you find a bug in PDO in PHP 5.1, it's your fault. :-)

Let's try to make 5.1 as "gold" as we can!

PDO: Iterators

[Updated to fix missing execute()]

Louis-Philippe Huberdeau posted this comment to an earlier post, asking:

I have been playing with PDO this morning, looks great! I just wonder if it could use a few more PHP5 features and SPL kind of thing.

The gist of what he was suggesting is already present. His first request was to be able to access the columns as properties of the statement handle. While you can't do that exactly, you can do something similar. His second request was being able to iterate the rows using foreach(). Here are two ways to acheive that, using alternative syntax:

   $stmt = $db->prepare("select foo from bar");
   foreach ($stmt as $row) {
       echo $row->foo;

and the less verbose version:

    foreach ($db->query("select foo from bar", PDO_FETCH_LAZY) as $row) {
        echo $row->foo;

PDO_FETCH_LAZY is almost exactly the same thing as having the properties show up on the statement handle; the "lazy object" you get on each iteration is internally aliased to the statement handle. If you're familiar with the concept of interfaces from other languages, the lazy object is actually the same object instance, it's just a different interface.

The implication of this is that you can't simply copy a $row into another variable and expect its contents to remain as they were at that point; $row still references the current active row of the statement, so if you fetch() another row (either direct or by advancing the iteration with foreach) $row will refer to the new row.

PDO_FETCH_LAZY doesn't mean that you can be lazy, it means that PDO can be lazy. :-)

If you want to be lazy, and still want $row->foo, use PDO_FETCH_OBJ instead; the sample above will work the same.

PDO beta releases

[Update: I forgot to mention that windows binaries are availaing from snaps; all PDO drivers are present, including MS-SQL, which doesn't show up in the PECL package list yet. Note that the sqlite driver has a bug that has already been fixed in CVS; wait a couple of hours for the next snapshot build before trying to use it on windows. ]

I posted this message to the PHP internals and PECL development lists just now. I'm soliciting beta testers for PDO. If you want to help QA PDO ready for its PHP 5.1 debut, please read on.

You may have noticed my "drive-by release" of various different PDO packages tonight/this morning.

This is stage one of the "PDO push".

I'd like to encourage you all to try it out; as you should know, we're going to be releasing PDO as part of PHP 5.1, so we need as many people as possible to play around with it to discover any remaining issues.

Some drivers have had more testing and support than others, simply because they are easier or more convenient to develop and test against.

If you find any missing features, inconsistencies between drivers, bugs or crashes, please report them using the PECL bug tracking system. Please categorize bugs correctly, and, if possible, try out more than one driver. If you have the same problem for more than one driver, report the bug using the PDO bug reporting page, otherwise use the bug reporting page for your driver. The bug reporting pages can be found by visiting<extensionname> and clicking on the "Package Bugs" link. Please don't submit the same problem more than once; add your feedback to existing bugs if it is appropriate.

During the PDO push, I want to release updated PDO packages at the end of each day in which a bug is found and fixed. Please help me with this task by reporting any issues as clearly and completely as possible. Now is the time to test and evaluate PDO; if we don't get enough feedback over the next couple of weeks, it won't make it into PHP 5.1 and you'll have to wait until the next major PHP release.

You'll no doubt be wondering how to use PDO, so here are some useful resources:

The PHP manual

Various posts from my blog; some information is slightly outdated, one of the links includes slides from one of my talks on PDO at the php|works conference last year:

PDO semi-toy example

php|works slides

More on PDO

First steps

My article on the Oracle Technology Network giving an overview of PDO, again, some information my be slightly outdated.

Also during the PDO push, I'll be working with Dan to help flesh out the official PHP documentation for PDO, and quite probably blogging little handy snippets for some of the newer/more advanced features.

Thanks for your attention, and looking forward to your feedback.


50 gmail accounts up for grabs

[Update: 35 remaining]

Yes, 50.

If you're smart enough to figure out how to email me (NOT post a comment on the blog; I need a functional email address from you) to ask for one, you're welcome to it.

blog spam - a solution

Today, this blog got its first ever spam, via the trackback interface. How annoying. Here's how I've stopped it (yes, the regexes could be better, and the parse_url() call eliminated, but its late and this is a quick hack):

function ne_rbl_check($ip) {
   static $lists = array('');
   $ip = gethostbyname($ip);
   foreach ($lists as $bl) {
      $octets = explode('.', $ip);
      $octets = array_reverse($octets);
      $h = implode('.', $octets) . $bl;
      $x = gethostbyname($h);
      if ($h != $x) {
         return false;
   return true;
function ne_surbl_checks()
   $things = func_get_args();
   foreach ($things as $thing) {
      if (preg_match('/^\\d+\\.\\d+\\.\\d+\\.\\d+$/', $thing)) {
         if (!ne_rbl_check($thing)) return false;
      if (preg_match_all('~(http|https|ftp|news|gopher)://([^ ]+)~si',
            $thing, $m = array(), PREG_SET_ORDER)) {
         foreach ($m as $match) {
            $url = parse_url($match[0]);
            if (!ne_rbl_check($url['host'])) return false;
   return true;

These two functions implement RBL and SURBL checks. RBLs, as you probably already know, are real-time block lists; you can look up an IP address in a block list using DNS, and if you get a record back, that address is in the block list. The first of the two functions implements this, in a bit of a lame hackish way.

The second function implements content-based checks, commonly known as SURBL; the text is scanned for things that look like IP addresses or URLs; those IP addresses or host names are extracted from the content and then looked up in the RBL using the first function.

Why is this good? A comment spammer will typically want to inject a link to their site onto your blog, and you can be fairly sure that their site is listed in a good RBL. The RBL used in my sample above is an aggregation of the SBL and XBL lists which contain known spammers and known zombie/exploited machines, so it should do the job perfectly.

Now to hook it up to the blog; this snippet is taken from my trackback interface:

if (!ne_surbl_checks(get_ip(), $_REQUEST['excerpt'], $_REQUEST['url'], $_REQUEST['blog_name'])) {
   respond('you appear to be on SBL/XBL, or referring to content that is', 1);

get_ip() is a function to determine the IP address of the person submitting the page; I haven't included it here for the sake of brevity; it's fairly simple to code one, but keep in mind that it needs to be aware of http proxies. respond() returns an appropriate error message to the person making the trackback and exits the script.

And that's all there is to it; you can do similar things with your comments submission and pingback interfaces.