Micro PHP's Missing Tenet

Ed Finkler's post on "MicroPHP Manifesto" made the the rounds last week. It expresses a widely felt desire lurking in the minds of PHP developers. Especially those who have spent time working with libraries from the Node.js or clojure communities. While the manifesto did a good job covering motivations and aspirations, it missed a key tenet of "micro", and stopped short of covering a few key practical opportunities and obstacles with PHP.

"I believe functions are the smallest unit of functionality"

The missing tenet from the Micro PHP Manifesto is: I believe functions are the smallest unit of functionality.

Not classes. Not methods. Functions. This belief fits in naturally with, and is a precursor to, most every other tenet of the manifesto.

"I like building small things with simple purposes." There is no smaller or simpler reusable unit of functionality than a function.

"I like building small things that work together to solve larger problems." Functions and their higher-order friends compose with mathematical simplicity and beauty.

"I want code that is easily verifiable." Pure functions are the easiest unit of functionality to test.

"I need to justify every piece of code I add to a project." Do we really need to subclass Controller to add a route to our app? Nope, not if you're using Slim, a great HTTP micro framework (full disclosure [1], my office mate wrote it). An anonymous function will do just fine, thank you. Senseless subclassing is what got us into this mess to begin with! [2]

When the traditional PHP framework programmer looks at a problem, he asks himself these questions, in order of preference:

  1. Can I add a method to an existing class?
  2. Can I add a subclass and override a method?
  3. Or should I start a new base class?

When the Micro PHP framework programmer looks at a problem, she asks herself these questions:

  1. Can I use an anonymous function?
  2. Can I use a named function?
  3. Can I add a method to an existing class?
  4. Can I add a subclass and override a method?
  5. Or should I start a new base class?

Namespaced functions and function values being legitimate options in 5.3 enables lighter weight libraries that play together nicely. Combined with jQuery pushing web developers' collective comfort with a more functional style of programming, the micro PHP movement is in a great position to take off.

But, for the love of Rasmus, PHP has avoided solving two fundamental problems that would radically inspire even smaller, even simpler, even more single-purposed, micro PHP libraries.

Damnit PHP, why can't you just...

1. Allow functions to be imported like classes

User defined functions are second rate compared to user defined classes in the PHP language. You can import classes from namespaces but you can't import functions. Read that again. You can import namespaced classes but not namespaced functions. PHP itself doesn't fully believe that "functions are the smallest unit of functionality". Oh, sweet irony. Most of PHP's core functionality (even array manipulation!) is exposed as functions (in a global namespace).

PHP discourages writing plain old functions by making them more painful to reuse than classes. Currently, the best way to write useable "plain old functions" is to write methods on a class you'll only ever invoke statically (i.e., underscore.php's __::each). If PHP respected functions they wouldn't need to be wrapped in bastardized classes to be useful.

2. Indoctrinate a symbol loading algorithm

So it isn't possible to import namespaced functions directly. But it gets worse. Even using what is available (use namespace; namespace\func();), if the function has not yet been defined, there is no way to autoload that function's source like PHP will do for classes. But it gets worse. The class loading algorithm is user defined. This is another historical impediment to "micro" component compatibility in PHP, because every framework has its own class loader [3]. There is hope in PSR-0, but it won't ship with 5.4.

PHP needs to plant a hard foot in the ground and indoctrinate one built-in, automatic symbol loading algorithm that will load classes and functions and constants. The chaos has gone on long enough. If Python and node.js can be bold enough to do it, so can PHP. The flexibility has stunted our growth.

Please, if you are listening PHP Core Team, for the love of the fat blue elephant in the room, do whatever it takes and break whatever you need to get these fundamental language utilities in the next major point release. Or even the mythical 6.0. The temporary pain will be worth the massive, revitalizing shock it will give to a community of PHP developers who would like to share components and code in a simple and standard way.

How should micro libraries share plain functions until PHP fixes importing and symbol loading?

Step 1. Use a PSR-0 class loader There are plenty. The Symfony2 ClassLoader's is good and PSR-0 compliant. It's MIT. Potencier writes great code. Use it.

Step 2. Write plain-old functions as static methods on a bastard, wrapper class. Ugh. Step 2 is the worst, but distributing plain-old functions puts consumers back in require_once hell. If all you are trying to write and share are plain-old functions, use static methods.

Step 3. Write to your local PHP Core politician. Demand importable functions and a native, standard symbol loader for PHP.next. Support this PHP RFC for function autoloading, and this one for PSR-0 inclusion.

Finally, will an isaacs please step up? PHP needs a modern package manager.

If someone really wants to blow oxygen on the Micro PHP fire they should start with a new package manager for PHP. PEAR is far too bureaucratic and, frankly, embarrassing in the face of a modern package manager like node.js's npm. PEAR has been around since before PHP5's release in 2004 and there are 179 modules hosted at pear.php.net for 5.0+. (And only 19 for poor PEAR2 and PHP 5.3) Contrast with npm which has been around since August 2010 and hosts 6,157 packages.

npm hosts over 30 times more packages than PEAR!?! How? You don't have to pass a bureaucratic, written proposal process to publish to npm. Anyone can publish any package they want in under 10 minutes. So do all npm packages have the high quality of PEAR packages? No. Do they all follow the same coding standards like PEAR packages? No. Are these real problems to fear in practice? No. The best packages are naturally high quality and obtain the most attention and dependencies. PEAR is selection by committee, npm is natural selection. The Micro PHP movement really needs a package manager with fewer, smaller rules for publishing and sharing. PEAR may be for sharing small packages with simple purposes, but it is certainly not Micro PHP.

(Update: some promising movements in this direction are Packagist and Phark.)

tl;dr

Functions are the smallest unit of functionality. This is a key missing tenet of the Micro PHP Manifesto. It's a practical precursor to many of the others. This isn't to say object-oriented libraries and designs don't have their place in PHP, they most certainly do, but that if you can solve your design problem with functions, choose functions. Prefer small, simple functions over complex, stateful class hierarchies.


Footnotes

[1] Even fuller disclosure: I've written a "Full Stack" PHP Framework. A lot of what Ed said is wrong with Zend and Symfony was wrong with Recess, which I wrote three years ago. It's intention was to be light weight. In terms of CLOC, it was, under 10k for a RESTful HTTP layer, ORM, "Views", & Annotations. In terms of code written by end-users, it was, comparatively, too. Recess was the first PHP framework to use Annotations. Declarative-style programming requires less code. I don't use it for new projects anymore. It's still too heavy. It's not line counts or ugly code, though. It caries the PHP 5.2 OOP-only baggage, too [2].

[2] Ed's post stops short of tracing the legacy leading to the current state of popular frameworks being un-micro. Here's the story. When all you know are objects...all you see are classes. And methods. And private variables. And design patterns. And Java. When all you know is Java all you see is Java.

PHP did not become be one of the most popular kids in school without having a "hey look, me too" mindset. PHP grew its popularity in the early 2000s by copying everything it could from Java's object model in the move from PHP4 to PHP5. From visibility controls to reflection.

When your language is inspired by Java, your libraries and frameworks will be too. You can't look at Recess' annotations without smelling Java. You can't look at Doctrine without smelling Java. You can't look at Symfony components without smelling Java. This isn't to say Symfony is bad, it's not. It's really damned good, but it's manifesto is different. It's manifesto was written by a Gang of Four. It's lineage predates PHP 5.3. Popular frameworks and libraries originating before PHP 5.3's release carry the burden of pursuing best practice object-oriented patterns at the cost of ignoring perfect opportunities where standalone or anonymous functions would suffice.

[3] PEAR/Zend-style class naming and directory structuring became somewhat of a norm before 5.3, but it sucked. Class names were combined with namespaces which lead to instantiating classes with names like Zend_Db_Table_Definition. Only in recent history, since 5.3 namespaces, has the community embraced a "standard" class loading algorithm with PSR-0. (Design by committee retained PEAR-style support, which is unfortunate, but I digress.) In theory this will make its way into the Standard PHP Library and be bundled with PHP, which will be great.