PHP, Zend Framework and Other Crazy Stuff
Archive for October, 2007
Doing that thing called PEAR: Packaging Source Code for PEAR Distribution
Oct 25th
All right then! You read the last blog entry advocating PEAR (or found it during the week ) and you want to know all about packaging code so your users can install your library or application using the PEAR installer. A few things first:
1. You don’t need to propose a package to PEAR to do this.
2. You don’t need to create a PEAR “channel” although it’s very much recommended (another future blog post no doubt).
3. You don’t need a whole lot of effort.
Taking your source code, and generating a PEAR package is a relatively simple task.
A PEAR package is a gzip tarball using the .tgz extension. It contains the source code to be installed, and a package definition file, package.xml. The package.xml file is how the PEAR installer knows which files to copy where, among other things like package details, installation tasks and post-installation scripts. I’d post a simple package.xml file to illustrate but that would be a bit dull (horrible XML!) and we’re not going to edit any XML anyway (who does anymore?). Anyway, inside the tarball will be the source code inside a directory reflecting your library/app name and a package.xml definition.
This all sounds fun already!
So let’s get dug in. Since editing XML is a dirty business, we’re going to let PEAR do all the hard work. All we really need to is setup a PHP script to generate the package.xml dynamically. This is made easier with the availability of the PEAR_PackageFileManager (which includes version 2 of the manager for the more modern v2.0 package.xml files). You can install this manager using:
pear install PEAR_PackageFileManager
I’m going to assume you’ve installed PEAR. If you have it, and it’s an old copy run this first :
pear upgrade PEAR
Now, we’ll use the Manager the script a PHP file to generate our package.xml. At the outset this will look complex, but it’s actually not difficult. Most of the stuff should be pretty obvious. Follow the comments for more information and save the file in the root directory of your source code. We’ll assume a directory layout (in cvs/svn) of:
trunk |- tests |- docs |- examples |- MyCoolApp |- library files/classes under top-level namespace |- MyCoolApp.php |- generate_package_xml.php
If your source code does not match this exactly, you can amend the dir_roles option below,
File: generate_package_xml.php
[geshi lang=php]
require_once('PEAR/PackageFileManager2.php');
PEAR::setErrorHandling(PEAR_ERROR_DIE);
$options = array(
'filelistgenerator' => ‘cvs’, // this copy of our source code is a CVS checkout
‘simpleoutput’ => true,
‘baseinstalldir’ => ‘/’, // The PEAR directory install location
‘packagedirectory’ => dirname(___FILE___), // We’ve put this file in the root source code dir
‘clearcontents’ => true, // dump any old package.xml content (set false to append release)
// no bundling of cvs/svn files or this generator file
‘ignore’ => array(‘generate_package_xml.php’, ‘.svn’, ‘.cvs*’),
‘dir_roles’ => array( // set up roles for some directories; the default is php
‘docs’ => ‘doc’,
‘examples’ => ‘doc’,
‘tests’ => ‘test’,
),
);
// Oddly enough, this is a PHP source code package…
$packagexml = &PEAR_PackageFileManager2::importOptions($packagefile, $options);
$packagexml->setPackageType(‘php’);
// Package name, summary and longer description
$packagexml->setPackage(‘MyCoolLibrary’);
$packagexml->setSummary(‘MyCoolLibrary does x, y and z’);
$packagexml->setDescription(“MyCoolLibrary does x, y and z. It follows Specification W 1.0.”);
// The channel where this package is hosted. Since we’re installing from a local
// downloaded file rather than a channel we’ll pretend it’s from PEAR.
$packagexml->setChannel(‘pear.php.net’);
// Add some release notes!
$notes = <<
- Added documentation
- Implemented directory and file filtering with SPL FilterIterator
EOT;
$packagexml->setNotes($notes);
// Add any known dependencies such as PHP version, extensions, PEAR installer
$packagexml->setPhpDep(’5.1.4′);
$packagexml->setPearinstallerDep(’1.4.0′);
$packagexml->addPackageDepWithChannel(‘required’, ‘PEAR’, ‘pear.php.net’, ’1.4.0′);
// Other info, like the Lead Developers. license, version details and stability type
$packagexml->addMaintainer(‘lead’, ‘padraic’, ‘Pádraic Brady’, ‘[email protected]’);
$packagexml->setLicense(‘New BSD License’, ‘http://opensource.org/licenses/bsd-license.php’);
$packagexml->setAPIVersion(’0.0.1a’);
$packagexml->setReleaseVersion(’0.0.1a’);
$packagexml->setReleaseStability(‘alpha’);
$packagexml->setAPIStability(‘alpha’);
// Add this as a release, and generate XML content
$packagexml->addRelease();
$packagexml->generateContents();
// Pass a “make” flag from the command line or browser address to actually write
// package.xml to disk, otherwise just debug it for any errors
if (isset($_GET['make']) || (isset($_SERVER['argv']) && @$_SERVER['argv'][1] == ‘make’)) {
$packagexml->writePackageFile();
} else {
$packagexml->debugPackageFile();
}[/geshi]
Ye gods! This stuff is just too complicated! Yes, it’s really that simple . There is other stuff you could add depending on your library/app. Maybe you need to install command scripts, or run specific post-installation PHP scripts? All possible with a few extra easy lines. But this is a simple form with no such complications. We’ll do a more complicated one another time.
Now comes the really hard part. Visit this file in a browser adding “?make=1″ to the URL, or with the command line:
php generate_package_xml.php make
If you recheck the directory (the output will give it away) you’ll see a new package.xml file. You can open it up to see why hand editing XML is such a bore. The PEAR_PackageFileManager2 is a massive time saver, and adds automation (secret ingredient to avoiding complex repetitive tasks).
The next step is generating the PEAR installable package. This requires three steps:
1. Validate package.xml
2. Generate the package tarball
3. Test install the package
Or, from the command line:
pear package-validate package.xml
pear package
pear install -force MyCoolLibrary-1.0.0a.tgz [use --force if similar version is already installed]
If you can follow these simple steps, you would be able to generate a PEAR package in seconds. Then the whole world can download your package file, and run “pear install” on it.
In my next visit to the world of PEAR, I’ll add a little more automation (yes, more) using PHP’s answer to Java Ant or the GNU’s infamous make - Phing.
To PEAR or not to PEAR? And how to PEAR anyway?
Oct 19th
The title looked better without the verb form . But really, over the last few months after finally getting over my ignorance of PEAR beyond it being a hodge podge of packages of dubious quality I’ve been questioning whether pearifying my future and past code is worthwhile. The answer is a resounding YES.
The problem, if you’re not familiar with PEAR you probably understand this, is that PEAR does not have all the coolest libraries. Swiftmailer is not in PEAR. Neither is Smarty. And look, HTMLPurifier is not there either. You can throw in others - like the mind numbing lack of a Zend Framework PEAR channel.
Since PEAR only has a few really well publicised packages (like MDB2 for example) it’s rarely noted as a first-stop in a search for the ultimate library for your needs. The problem with this lack of attention, after invading the PEAR fortress myself with OpenID For PHP, is that PEAR is actually quite a cool piece of architecture which is about to get even better when PEAR2 goes primetime. Why libraries are not listed there seems to largely be down to perceptions of PEAR having a high barrier to entry. I don’t use the Economics term lightly .
The perceived barriers to entry include:
1. PEAR now only allows PHP5 in new packages
While this might look bad to some, let’s face it. PHP4 is ancient news. If you are a developer, and still using PHP4 only for new code then you have lost the plot. I’d only forgive you if you used PHP4 expressly for the purposes of supporting down on their luck users on shared hosting not offering PHP5. But that excuse is getting thinner every year at this stage.
2. PEAR will require large scale changes to my shiny new cool code
PEAR encourages two development goals. Adherence to a popular recognised coding standard - predictably called The “PEAR Coding Standard”, and adherence to sound design principals. Members will quickly find and expose design flaws, criticise your cumbersome API, and flambast your lack of a maintainable design. On the otherhand they will readily praise the presence of tests and docs, and the sight of a well designed library. PEAR is like the ultimate QA Process - and nothing to be afraid of if you are already producing good source code. Besides, if you can’t accept simple criticism you should stop developing because it’s unavoidable in open source. Criticism in PEAR is a healthy constructive slice of feedback which should be welcomed with open arms. It only requires huge time consuming changes if your code, is arguably, a pile of spaghetti to start with.
3. PEAR only allows proposals for complete functional code
PEAR2 will soon be here which means all you happy XP/Agile folk can eat all the cake you wish. I’m ambiguous here myself as regards to the old steadfast PEAR. Maybe it’s the legacy of an existence before Agile practices became popular, and the code was all important, but I believe most PEAR members are not living in the dark ages . The Proposal system is not Agile oriented at present, so if you have a new idea visit the mailing list and ask about proposing before code exists for better advice than you’ll get from the official manual. PEAR2 sounds like it will formalise more Agile friendly means of gaining proposal acceptance and support for developing from scratch.
Of course “complete” is an exaggeration. Proposing an alpha or devel package to PEAR as it stands seems pretty straight forward. Just do the startup TDD and coding to get to some level of functionality before proposing. Then improve over subsequent iterations as usual to reach a beta.
4. PEAR is elitist
PEAR’s social norms are often criticised. However few people seem aware that PEAR has suffered a recent revolution and is now governed by a democratically elected PEAR Group and President. Vive la résistance, mon amis! As for the elitist front, I’ve never seen any sign of it. The only thing I’ve experienced from the mailing list and private emails/IMs is a sense of a helpful friendly community of developers who are more than aware of the past problems of PEAR and who are motivated to fix them. If anything folk seem to be curious about my experience in PEAR and are readily open to feedback on how PEAR works. Seeing as how I’m a total n00b to PEAR that speaks for itself about how non-elitist it really is.
5. PEAR code sucks!
Indeed. It’s hard to argue that some proportion of PEAR packages are plain awful. But given how many packages PEAR has (my first CVS checkout was an interesting experience for a novice), and their age, that’s inevitable. I like to think of PEAR as a function of software development progress in general. As development practices, PHP tooling, and PHP features progressed, so has the code on PEAR. With PEAR2 around the corner (even more progression!), the opportunity for renewal is literally almost upon us.
6. PEAR is fossilised
See 5. I haven’t been in PEAR long enough to know how fossilisation is viewed by the community. But I think it’s obvious you can’t pin this completely on PEAR. Any library of sufficient age and penetration is going to fossilise. I think what’s important is that a path for proposing alternatives with duplicate goals is allowed by some set of criteria. Over time any fantastic library which doesn’t innovate will die and shrivel as a younger innovative library stamps all over it gleefully with a fresh approach and superior mindshare. I look at PHP Mailer for example, and then Swiftmailer, and wonder when PHP Mailer lost its will to survive.
7. I’m not going to contribute to PEAR, ergo it’s useless
So wrong. I too was so ignorant. Then I met this weird thing called “pear package”. Creating public or private PEAR packages of your own libraries is such a gift to PHP. No more complex copy and text replace tasks, no more fiddling with include_paths (whose every extension slows down your application), no more fizzy installation instructions. If you have never created a PEAR package, or don’t know how to install a PEAR package you (or PEAR) can offer, then you are missing a treat.
For those with an affection for build tools, like Java ant or PHP’s own Phing, you can easily script a PEAR package task. I’ve started using the d51PearPkg2Task for Phing from Travis Swicegood and it’s such a simple piece of automation to have on hand. PHPSpec will now be distributed as a normal tarball download and as a PEAR package from some channel when released. I just run “phing dist” from a command line and it magically appears .
Okay, so maybe you won’t contribute to PEAR. But PEAR’s tooling is hard to ignore. They have the only decent distribution method supporting automated installation in PHP. If you don’t take advantage of that then it’s your loss. And your users’ loss.
8. I can’t use PEAR on a shared host!!!
Yes, you can. The secret is in setting up your include_path, or using __autoload, or finding one of the tutorials for doing it. Any shared host can support a custom PEAR collection with a small amount of effort which will override the default (out of date) version your shared host currently provides.
I’ll answer the “How to PEAR?” question in another entry soon. If you feel one of the 8 (undoubtably there are others) barriers above personally, I suggest you look a bit more closely at PEAR. Evolution is in play.