In my Beyond Frameworks talk, I explained how a component-based architecture can help answer some of the important (i.e. expensive!) questions you might face when creating long-lived apps that rely on a PHP framework. In this series of blog posts, I’m going to look at how to go about creating and working with components.
Last week, I began work on a simple component to talk to the Repustate semantic analysis API. To save myself a bit of effort, I thought it would make sense to make my API client reuse PEAR’s existing HTTP_Request2 component. No sense in re-inventing the wheel, I thought. But that’s where my troubles began.
There are a few quirks in the way that the PEAR installer handles version numbers, and you need to know how to deal with them if you’re going to re-use PEAR project components in your own apps.
Adding A Dependency On HTTP_Request2
HTTP_Request2 is PEAR’s very useful wrapper around making HTTP requests from your app. At the time of writing, there are 22 PEAR packages that rely on it: a good indicator that this component is doing something right.
Making the repustateApi component rely on PEAR’s HTTP_Request2 should be as simple as adding the following to the %lt;required> section in package.xml:
<package> <name>HTTP_Request2</name> <channel>pear.php.net</channel> <min>2.0.0</min> <max>2.999.9999</max> </package>
But, when I try to rebuild the vendor/ folder (this is the sandbox where all of the component’s dependencies are installed, so that we can run our unit tests), using the command phing build-vendor, I get an error:
[echo] Populating vendor/ with dependencies [exec] Executing command: phix pear:register-channels 2>&1 [exec] Registering PEAR channel pear.gradwell.com [exec] Adding Channel "pear.gradwell.com" succeeded [exec] Discovery of channel "pear.gradwell.com" succeeded [exec] Registering PEAR channel pear.php.net [exec] Channel "pear.php.net" is already initialized [exec] Executing command: pear -c /home/stuarth/Devel/sental/repustateApi/.tm p/pear-config install --alldeps /home/stuarth/Devel/sental/repustateApi/.buil d/package.xml 2>&1 [exec] Failed to download pear/HTTP_Request2 (version >= 2.0.0, version = 2.0.0, version <= 2.999.9999) [exec] downloading Autoloader-2.0.1.tgz ... [exec] Starting to download Autoloader-2.0.1.tgz (2,080 bytes) [exec] ....done: 2,080 bytes [exec] install ok: channel://pear.gradwell.com/Autoloader-2.0.1
So what’s going on? And what do we need to change to make this dependency work?
PEAR Version Numbers and Stability
There are two things preventing the PEAR installer from downloading the package:
- The package’s version number is 2.0.0beta2, not plain old 2.0.0
- The package has been marked as beta quality, rather than as a stable release.
From HTTP_Request2’s point of view, this is all perfectly reasonable. The package authors are currently transitioning from the older PHP4-based HTTP_Request, and they’re not yet ready to unleash HTTP_Request2 on the world.
As a result, the PEAR installer is (rightly) looking at the dependency information I’ve set, and telling me that HTTP_Request2 is not yet at version 2.0.0.
The PEAR installer doesn’t just use version numbers when handling dependencies. If you dig deep into package.xml, you’ll find not only a <version> tag, but also a <stability> tag. Here’s the tags from PEAR’s HTTP_Request2’s package.xml at the time of writing:
<version> <release>2.0.0beta2</release> <api>2.0.0</api> </version> <stability> <release>beta</release> <api>beta</api> </stability>
How To Depend On A Beta PEAR Component
The ‘fix’ we need to make to package.xml is very simple; simply depend on the beta version:
<package> <name>HTTP_Request2</name> <channel>pear.php.net</channel> <min>2.0.0beta1</min> <max>2.999.9999</max> </package>
With this change made, the PEAR installer does the right thing, and installs HTTP_Request2 just like I want it to
It remains to be seen what will happen when HTTP_Request-2.0.0 and beyond are released. Will the PEAR installer download it, or will I have to come back and edit the dependency in package.xml? I’ll post news on that when the time comes.
With the right dependency information in package.xml, I can now get back to cranking out the code for the repustateApi component.