PHP gets lambda methods, closures

Posted on September 26th, 2008 in Code, PHP | 2 Comments »

Not one month after I wrote about the future of PHP (June 2008), I was quite happily proven wrong.

For my part, I’d like to see first-class functions and closures included in the language. [...]

But none of that will happen, because PHP is a language in decline. Not a decline in usage—it will only continue to expand its reach—but in the addition of innovative features from other languages. There will be no need to evolve; most of the agitators for change will have moved on.
Me

It’s always been in my nature to own up to it when I’m proven wrong, so consider this my mea culpa. PHP has always been a klugey language, borrowing from other languages and implementing those ideas in somewhat endearingly clunky ways. I naively believed that that dynamism was coming to an end, but as you can see from the link above, that’s demonstrably not the case. Despite that, I still think there is a slow but steady “brain drain” from the ranks of the top tier of PHP developers—I’ve seen it first hand in the last few years and the overall trend should make PHP developers at least a little uncomfortable.

Anyway, the closure implementation coming in PHP 5.3 is, like namespaces, a little clunky.

function replace_in_array($search, $replacement, $array) {
    $map = function ($text) use ($search, $replacement) {
        if (strpos($text, $search) > 50) {
            return str_replace($search, $replacement, $text);
        }
        return $text;
    };
    return array_map ($map, $array);
}

Yeah, you have to manually link the variables to make them available to the closure. Not ideal, but neither is having to manually specify your scope in JavaScript (via Function.apply()).

There are a few of other differences in PHP 5.3’s implementation of closures and other languages:

  • First, like other functions, they have access to the global scope with the global keyword. Do yourself a great big favor and just avoid doing that.
  • Second, you can choose which variables are linked by reference and which are not.
  • Finally, you can declare an anonymous function static if it’s declared in a class but doesn’t use an instance of that class for anything. If you have a large object, this would prevent the closure from retaining a reference to that instance (and therefore, its memory footprint) after it has outlived its usefulness. This will probably be the least understood aspect of PHP closures for most developers.

Currying is now possible as well. Ryan Timmons wasted no time in writing a method for doing just that:

function curry($function, $argument) {
    return function() use ($function, $argument) {
        $arguments = func_get_args();
        array_unshift($arguments, $argument);
        return call_user_func_array($function, $arguments);
    };
}

Between this, namespaces, late static binding, and a bundled packaging method (ext/phar), the next version of PHP is looking more like a major release instead of a minor one.

PHP 5.3 is scheduled for final release in October.

Zend_Paginator

Posted on September 3rd, 2008 in Code, Frameworks, PHP, Zend Framework | 3 Comments »

On and off for the last two or three months, Jurriën Stutterheim and I wrote Zend_Paginator, the pagination component for Zend Framework. Yesterday it was officially released as part of Zend Framework 1.6.

This was our first contribution to the framework, and was very much a collaborative working relationship. I first created a proposal in about half an hour on Christmas Eve 2007 for how I thought a pagination component should work after being dissatisfied with existing solutions. It contained some good ideas about flexibility, but on its own it was half an idea, at best. So I promptly forgot about it.

Then Jurriën came along and created a proposal based on his Zym_Paginate component, which was part of Zym Framework, itself an extension of Zend Framework.

At the urging of Zend, we merged our proposals. We refined our ideas, incorporating aspects from each of our proposals in addition to brand new thoughts. Then I wrote a new component from scratch based in part on Jurriën’s existing work. He wrote the excellent unit tests (with 100% code coverage, not always easy to achieve!), and I wrote the DocBook documentation. I also can’t overstate the positive impact the community had, particularly from Bryce Lohr who prompted us to decouple and reorganize major portions of the component.

Anyway, I’m very happy with how it turned out. There are still a few issues, most of them minor—and because of the component’s flexible design, there are almost always workarounds. They should be fixed in time for 1.6.1.

Here’s some praise for the component:

Upcoming Zend_Paginator in 1.6 is absolutely brilliant. Wasted time be gone.
Joakim Nygård

The new Zend Framework 1.6 release candidate includes a Zend_Paginator class, which is an excellent thing to have around because I know I’ve re-invented that wheel on every site I’ve developed. My only criticism of the new Zend_Paginator is it offers a daunting amount of possibilities.
Chris Beer

This really is a beautifully crafted component, very elegant, very classy.
—David Mintz, via e-mail

I’ve seen dozens of different paginators in the last couple of years, and to be honest, this one is by far the best. You got everything right! I can’t wait for this component to hit the incubator.

It’s great, well done guys.
Federico Cargnelutti

PHP’s create_function() and closures

Posted on May 17th, 2008 in Code, JavaScript, PHP | No Comments »

A coworker recently asked me what the difference was, functionally, between PHP’s create_function() function and traditional closures that you might find in languages with first-class functions, like Ruby or JavaScript. You can pretty easily illustrate this with a couple of examples.

First, a bit about closures. The idea with closures is that you can cleanly and readably pass around a bit of logic as an object, and any references that that object makes to variables in the surrounding scope must persist until that object is done with them.

So here’s an example in JavaScript:

<script type="text/javascript">
function getGreeter(name)
{
    return function(salutation) {
        alert(salutation + ', ' + name);
    }
}

var greeter = getGreeter('Eddy');
greeter('Hello');   // Hello, Eddy
greeter('Howdy');   // Howdy, Eddy
greeter('Bonjour'); // Bonjour, Eddy
</script>

Here’s the closest equivalent in PHP:

<?php
$code = '$name, $salutation', 'print $salutation . ', ' . $name;';
$greeter = create_function($code);
$greeter('Eddy', 'Hello');
// etc.

And that’s a callback, not a closure. In JavaScript the garbage collector reclaims the memory used by the anonymous “greeter” function… but in PHP functions get declared and stay declared, so every time you call create_function(), you increase the memory usage.

It gets worse. This is basically what PHP does internally:

<?php
function create_function($args, $code)
{
    // create a random $functionName
    eval('function ' . $functionName . '($args){$code}');
    return $functionName;
}

Yeah, the entire thing is evaluated. So not only does it not get garbage collected, but it has all the problems of eval()—it’s slow, difficult to debug, and uncacheable by bytecode caches like APC. Problems that closures don’t have in other languages.

It’s why you can do something like this…

<?php
$code = 'print "I print repeatedly.\n"; } print "I print once.\n"; if (false) {';
$function = create_function('', $code);
call_user_func($function);
call_user_func($function);
call_user_func($function);

// I print once.
// I print repeatedly.
// I print repeatedly.
// I print repeatedly.

…and why you should never use create_function().

Converting string literals in PHP

Posted on May 17th, 2008 in Code, PHP | 1 Comment »

In PHP (and most languages), this is false:

'\143\141\164' == "\143\141\164"

No surprise there. One is a 12-byte string of backslashes and numbers, and the other is a 3-byte string of octal values spelling “cat”. When you use double quotes, PHP transparently converts the string.

Sometimes it’s convenient to write values in files as string literals that represent characters. Some values simply don’t translate well in their native form, and it’s more explicit to write them out “long hand” in octal or hexadecimal. This is useful if you have to match, say, an exotic series of characters with 100% accuracy.

But what happens when you need to clue PHP in that the string “\143\141\164″ (as read from a file) should equal “cat”? As far as I know, there’s no easy way to do this. Presumably, there should be a function—something like str_convert_literals()—which would accept a string and do the conversion itself. But there isn’t, so you must rely on regular expressions.

Here’s the solution I found after some trying various other methods (like tokenizing the string):

$string = preg_replace_callback('/\\\\([0-7]{1,3})/', 'convertOctalToCharacter',
                                $string);

function convertOctalToCharacter($octal)
{
    return chr(octdec($octal[1]));
}

I’ll run through what’s going on briefly. The regular expression matches anything following a backslash that is a series of up to three digits, 0-7 (octal is base 8, after all). It passes that match to the convertOctalToCharacter() function, which converts the value to decimal and then feeds it to the chr() function (which only accepts decimal values). That in turn converts the integer to its corresponding character value, which is then substituted into the string.

Based on this, the hexadecimal conversion function isn’t very difficult to guess. To get you started, I’ll give you a not-so-subtle hint: the regular expression is “/\\\\x([0-9A-F]{1,2})/i”.

One more thing: if you also translate special characters like “\r”, consider using lookbehinds in your expression to ensure that valid sequences like \\r aren’t converted twice.

Detecting plugins in Internet Explorer (and a few hints for all the others)

Posted on June 26th, 2007 in Code, Firefox, Internet Explorer, JavaScript, Safari | 16 Comments »

There’s a lot of bad information out there about detecting plugins in Internet Explorer. I know; I spent days crawling through it all in order to create a plugin detection tool for a client. It’s not that the code you’ll find in the crevices of those forgotten pages doesn’t work. It does. The problem is that virtually all of it is grossly inefficient and almost immediately outdated because it’s version-based and has to be updated with each new plugin release. On top of that, some of it still uses VBScript, or even worse, JavaScript that writes VBScript. Since plugin detection itself continues to be necessary in some unique situations, I was determined to find a way to do it that didn’t require constant maintenance.

The problem is that no one quite knows what to look for when dealing with Internet Explorer’s plugins. Plugins in IE are ActiveX-based, so there’s no single API for them all—each has its own method of returning, for example, version information. What this basically means is that while Firefox and other browsers are putting all of their plugin information in one handy place, Internet Explorer is jealously guarding its plugin-related secrets. It’s like the Dick Cheney of browsers. And we are, um, the Information Security Oversight Office in that analogy.

Er… let’s just jump right in with some working detection code.

 

Adobe Reader

Detecting Adobe Reader (formerly Acrobat Reader) is pretty straightforward, except for the hitch that the ActiveX control was renamed in version 7. The key for version detection here is GetVersions(). Unfortunately, no version information is available for Adobe Reader 8 and later for non-IE browsers.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var control = null;
    try {
        // AcroPDF.PDF is used by version 7 and later
        control = new ActiveXObject('AcroPDF.PDF');
    } catch (e) {
        // Do nothing
    }
    if (!control) {
        try {
            // PDF.PdfCtrl is used by version 6 and earlier
            control = new ActiveXObject('PDF.PdfCtrl');
        } catch (e) {
            return;
        }
    }
    if (control) {
        isInstalled = true;
        version = control.GetVersions().split(',');
        version = version[0].split('=');
        version = parseFloat(version[1]);
    }
} else {
    // Check navigator.plugins for "Adobe Acrobat" or "Adobe PDF Plug-in"*
}

* Nicole Lucas adds that “Adobe Acrobat” won’t work with Adobe Reader 8 because Adobe changed the name to “Adobe PDF Plug-In for Firefox and Netscape”. Thanks, Nicole.


Flash Player

Flash is even easier. The version method in this case is GetVariable(’$version’).

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var control = null;
    try {
        control = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
    } catch (e) {
        return;
    }
    if (control) {
        isInstalled = true;
        version = control.GetVariable('$version').substring(4);
        version = version.split(',');
        version = parseFloat(version[0] + '.' + version[1]);
    }
} else {
    // Check navigator.plugins for "Shockwave Flash"
}


Java Runtime Environment

The JRE (formerly Java Virtual Machine, or JVM) is actually more difficult to handle than you would think. Determining if Java is installed is easy—a quick call to navigator.javaEnabled() returns a simple Boolean.* The problem is detecting the version and provider (Microsoft or Sun).

I never found a satisfactory solution to this. The gist is this: to get this information, you must load a Java applet. To load the Java applet, you must do it externally. But when an applet is loaded externally, it doesn’t load right away, so any programmatic calls to the exposed class methods must be delayed. And dealing with this with setTimeout() is awkward.

Nevertheless, here’s what I ended up with:

var isInstalled = navigator.javaEnabled();
if (!isInstalled) {
    return;
}

// Get version
var version = null;
if (/*@cc_on ! @*/ false) { // This JRE check does not depend on ActiveX, only IE
    // IE requires the 'classid' attribute upon object creation.
    var element = '<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">';
    var applet  = document.createElement(element);
    applet.id = 'applet';

    // IE requires that objects be displayed in order to interact with them
    applet.style.width  = '1px';
    applet.style.height = '1px';

    // Create and append the object's 'code' parameter
    var param = document.createElement('param');
    param.name  = 'code';
    param.value = 'JavaDetector.class';
    applet.appendChild(param);

    // Append the applet to 
    document.body.appendChild(applet);

    // Attempt to get the version
    var window.java = new Object();
    /*
     * Synchronous calls to getVendor(), etc. result in an exception, but despite
     * the timeout of 0 this call will not execute immediately
     */
    setTimeout(function() {
        try {
            var applet = document.getElementById('applet');
            window.java.vendor  = applet.getVendor();
            window.java.version = parseFloat(applet.getVersion());
        } catch (e) {
            // Do nothing
        }
    }, 0);
} else {
    if (typeof java != 'undefined') {
        version = java.lang.System.getProperty('java.version');
    } else {
        // Look for "Java Plug-in", "Java (version) Plug-in", and "Java Plug-in for Cocoa"
    }
    version = parseFloat(version);
}

And your Java class looks something like this:

public class JavaDetector extends java.applet.Applet
{
    public static String getVendor()
    {
        String vendor = null;
        try {
            vendor = System.getProperty("java.vendor");
        } catch (Exception e) {
            vendor = "Microsoft Corp.";
        }
        return vendor;
    }

    public static String getVersion()
    {
        return System.getProperty("java.version");
    }
}

* Eric Gerds adds that “navigator.javaEnabled() does not reveal whether Java is installed or not in IE.” Apparently disabling Java in IE merely disables the applet tag. Applets can still be loaded with the object tag despite javaEnabled() returning false. As such, you may want to modify this code. See his complete comment below.


QuickTime Player

QuickTime is another one that poses difficulty for IE. Detecting it is simple enough: just check for QuickTime.QuickTime. But detecting the version poses other problems. The ActiveX control that returns version information for QuickTime must be enabled manually in Internet Explorer 7. When called, the user is prompted. Although it saves that information for all future visits to that page, the code that follows will typically return null for the version number the first time around. Unfortunately, I’ve found no good way to reliably detect the version through ActiveX alone.

The version number that you do receive isn’t immediately useful. The QuickTimeVersion property is in hexadecimal (for whatever reason), so you’ll need to convert it by calling QuickTimeVersion.toString(16) and then manually assembling the number.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var control = null;
    try {
        control = new ActiveXObject('QuickTime.QuickTime');
    } catch (e) {
        // Do nothing
    }
    if (control) {
        // In case QuickTimeCheckObject.QuickTimeCheck does not exist
        isInstalled = true;
    }

    try {
        // This generates a user prompt in Internet Explorer 7
        control = new ActiveXObject('QuickTimeCheckObject.QuickTimeCheck');
    } catch (e) {
        return;
    }
    if (control) {
        // In case QuickTime.QuickTime does not exist
        isInstalled = true;

        // Get version
        version = control.QuickTimeVersion.toString(16); // Convert to hex
        version = version.substring(0, 1) + '.' + version.substring(1, 3);
        version = parseFloat(version);
    }
} else {
    // Check navigator.plugins for "QuickTime Plug-in"
}


RealPlayer

Because it’s had so many names (five, that I can count), RealPlayer is still one of the more inefficient plugins to check for. Thankfully, they’ve kept the API the same, however, so checking for the version is still just GetVersionInfo().

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var definedControls = [
        'rmocx.RealPlayer G2 Control',
        'rmocx.RealPlayer G2 Control.1',
        'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)',
        'RealVideo.RealVideo(tm) ActiveX Control (32-bit)',
        'RealPlayer'
    ];

    var control = null;
    for (var i = 0; i < definedControls.length; i++) {
        try {
            control = new ActiveXObject(definedControls[i]);
        } catch (e) {
            continue;
        }
        if (control) {
            break;
        }
    }
    if (control) {
        isInstalled = true;
        version = control.GetVersionInfo();
        version = parseFloat(version);
    }
} else {
    // Check navigator.plugins for "RealPlayer" and "RealPlayer Version"
}


Shockwave Player

Thankfully, Macromedia always made an effort to remain consistent with their naming. Thus, one simple call can detect any version of Shockwave. The version check here is ShockwaveVersion(”)—yes, you must pass it an empty string.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var control = null;
    try {
        control = new ActiveXObject('SWCtl.SWCtl');
    } catch (e) {
        return;
    }
    if (control) {
        isInstalled = true;
        version = control.ShockwaveVersion('').split('r');
        version = parseFloat(version[0]);
    }
} else {
    // Check navigator.plugins for "Shockwave for Director"
}


Windows Media Player

Windows Media Player is probably the easiest one to detect of them all, but also the most unreliable on non-Internet Explorer browsers. Firefox users will typically be missed here unless they’ve installed the WMP for Firefox plugin. The relevant version property is versionInfo.

var isInstalled = false;
var version = null;
if (window.ActiveXObject) {
    var control = null;
    try {
        control = new ActiveXObject('WMPlayer.OCX');
    } catch (e) {
        return;
    }
    if (control) {
        isInstalled = true;
        version = parseFloat(control.versionInfo);
    }
} else {
    // Check navigator.plugins for "Windows Media"--this also detects the Flip4Mac plugin
}

Phew! That’s that. I can’t guarantee these are the best methods of detecting plugins in Internet Explorer, but they’re the best I came up with. If you’re aware of easier solutions, let me know and I’ll update this post.

Incidentally, if you need a comprehensive solution, I recommend putting these into a class structure—for my project, I used an abstract base class that each plugin class extended, and an overall plugin detection class that called each class’s self-detection method. Extensible, self-contained, and unobtrusive.