HOWTO: Deploy Your Application Using PEAR

March 14, 2007

At the State of Iowa we recently published our own PEAR channel (sorry inaccessible to the Internet). Our intentions are that all our custom PHP libraries and applications will be managed through that channel. Better yet, we are even considering using PEAR for managing non-PHP libraries and applications…hey, PEAR doesn’t care it’s not PHP!

As a result I have produced two HOWTOs on our intranet: one for our system administrators and the other for our developers. I’ve condensed those down into this one HOWTO which is a bit more generic but still useful to anybody looking to establish their own PEAR Channel for the same purpose. My example hereon assumes that your PEAR channel is at pear.example.com.

Come along on the journey of a developer making their very first PEAR package and deploying it to a PEAR channel using a real-word example. Seasoned PHP developers will be packaging and deploying PEAR compatible packages in no time.So you’ve finally finished creating a wonderful application…probably in PHP but for the purpose of this discussion it can be coded in literally anything (hint: PEAR itself requires PHP but can be used to package system written in almost any language). In the days of old, you’d tag the code in CVS, create a tarball and ship it over to the system administrator. Easy enough, right? But what if?

What if your software requires it be run under Linux? What if you require a specific PHP version? What if you require a specific PEAR version? What if you accidentally ship the system administrator the wrong version? See, the beauty of PEAR is it alleviates all of these concerns by making the packaging very regimented and the installation and upgrade process as easy as running one command-line program.

  1. While optional it’s generally a good idea to keep the core PEAR package up-to-date. You may be wondering how you keep something up-to-date that you don’t remember installing. It’s important to remember that some PEAR packages are included in all PHP installations.
    #>pear upgrade pear
  2. As I eluded to, the key to making all this work is generating a valid PEAR XML build script. That process is simplified by using the PEAR Package File Manager which, odds are, you need to install:
    #>pear install pear_packagefilemanager

    Which should give you output similar to the following:

    downloading PEAR_PackageFileManager-1.6.0.tgz ... Starting to download PEAR_PackageFileManager-1.6.0.tgz (81,626 bytes) ...................done: 81,626 bytes install ok: channel://pear.php.net/PEAR_PackageFileManager-1.6.0 
  3. With PEAR_PackageFileManager installed we can now write the script that builds the PEAR Package XML file. Before we do that you need to have an idea of how the project is laid out:
    /path/to/app    /commands    /CVS    /public_html    /sql    /system    /templates    /views    Config.php    config.php.dist    mvcconfig.xml

    So in the root folder (i.e. /path/to/app) we need to create a new file called buildPackage.php that has this content. Yes, indeed there is a lot going on in that file so let’s cover it in detail. Note that the file is fairly well commented so we’ll only cover the most critical parts.

    $channel     = 'pear.example.com';

    The above bit of code assigns the channel we will deploy the application to. Channels are independently managed PEAR repositories each with their own unique set of packages and applications. The most obvious PEAR channel you already know about is pear.php.net but with PEAR any person or organization can create and manage their own PEAR channel.

    $category    = 'Applications';$package     = 'app';

    Every application will need to be assigned a category which is just a simple taxonomy used to organized all PEAR packages in the channel. The package you choose is important is and must be unique. For applications we will use the convention of [customer]_[application].

    $version     = '1.2.1';

    PEAR is very version aware so all packages and applications must have defined version numbers in the format of [major release].[minor release].[build]. You must include all three numbers and they must increment up with each subsequent release. If your application doesn’t have a defined version number and is already in production we’d suggest simply starting off with 2.0.0 as a logical baseline.

    require_once 'PackageFileManager2.php';$pkg = new PEAR_PackageFileManager2();    

    As mentioned earlier, we are using the PEAR Package File Manager to provide us an object-oriented way to create our PEAR build XML. There are two valid formats for the XML called version 1 and version 2. You’ll notice we are creating an instance of PEAR_PackageFileManager2 which produces valid version 2 XML. That will always be the version we use but for thoroughness if you wanted to use the deprecated version 1 format you could have used PEAR_PackageFileManager.

    $strConfigDistFile = $packagedir . '/config.php.dist';$bConfigCopy = !file_exists($strConfigDistFile);if ($bConfigCopy) {    copy(        $packagedir . '/config.php',        $strConfigDistFile    );}

    All applications have one or more configuration files. An often used convention is to use config.php for application settings. During the build process we do not want to include any of the actual configuration files because they will overwrite the settings when you install or upgrade the package making things a bit more painful for the system administrators. To get around this you should copy all configuration files to have a .dist extension. So in our example we copied config.php to config.php.dist and then edited out any sensitive information in config.php.dist (e.g. hostnames, database usernames, database passwords, etc). Now remember that any file you want to include in your release needs to be in CVS so be sure you add all your .dist files to CVS. The above bit of code will automagically create a .dist equivalent for config.php if one doesn’t already exist but until you add it to CVS it will not be included in the release.

    $arignores = array(    $packagedir . 'config.php',    'templates/compiled_templates/',    'compiled_mvcConfig.php',    'sql/compiledQueries.php',    '*.tgz');

    With many package there are some files and/or directories you will want to explicitly exclude Things like unit tests, configuration files, etc are good examples. The above code defines an array of the things we want to explicitly exclude as, by default all files found in the project directory /path/to/app are included. Keep in mind above we are only putting the files and directories we want to ignore into an array. Later on we’ll have to tell PEAR to actually ignore them.

    $e = $pkg->setOptions(    array(        // Where are our package files.        'packagedirectory'  => $packagedir,        // Where will package files be installed in        // the local PEAR repository?        'baseinstalldir'    => 'customer/appname',        // Where should the package file be generated        'pathtopackagefile' => dirname(__FILE__),        // Just simple output, no MD5 sums and  tags        'simpleoutput'      => true,        // Use standard file list generator, choose CVS, if you        // have your code in CVS        'filelistgenerator' => 'CVS',
    
            // List of files to ignore and put not explicitly into the package        'ignore'            => $arignores,
    
            // Global mapping of directories to file roles.        // @see http://pear.php.net/manual/en/guide.migrating.customroles.defining.php        'dir_roles'         => array(            'commands'                 => 'web',            'models'                   => 'web',            'public_html'              => 'web',            'sql'                      => 'web',            'system'                   => 'web',            'templates'                => 'web',            'views'                     => 'web'        ),        'roles' => array('*' => 'web'),    ));

    The above bit of code does a lot of heavy lifting for us. It is specifying the options we send to our instance of PEAR_PackageFileManager ($pkg). One of the more important settings is the ‘baseinstalldir’ as that specifies where to install the package on the remote servers relative to the webroot. "What webroot?", you ask. Since we are using pearified/Role_Web system administrators will be required to specify the web server’s webroot which is the folder where web content starts. So, if the system administrator specifies /var/www as their webroot then the baseinstalldir is relative to that. So in our case the full path would be /var/www/customer/app. Why is this setting so important? We want to keep customer applications organized logically with each agency using the basic layout of [customer]/[application].

    The next important setting is the filelistgenerator. This determines how we pick the set of files to include in the package by default. There are a few options that can be used but at ITE we will always use CVS since all our code had best be in CVS before packaging it.

    Note the above section of code is also where we specify the explicit ignores we defined (see the ‘ignore’ option). The dir_roles and roles option specifies the roles we are assigning to the various directies and files respectively. Roles, unlike the name suggests, don’t have anything to do with security, rather, they are used to determine where to put the various files and folders in our project on the remote servers. PEAR ships with a number of roles by default e.g. php, data, doc. Those default PEAR roles will cause files to be put in. These roles all map to php_dir, data_dir and doc_dir respectively. Generally those directories are in /path/to/pear, /path/to/pear/data and /path/to/pear/docs respectively but to be sure you should run:

    #>pear config-show

    One role not included by default is the web role that we specified for our application. Later we’ll get that role installed but for now just ensure that all of your applications files and folders have the web role.

    $pkg->setReleaseStability('stable');$pkg->setAPIStability('stable');$pkg->setReleaseVersion($version);$pkg->setAPIVersion($version);

    Above we specify the stability and version for both the release and the API. Why two settings? Simple, you can have a stable, well-tested, release that has an underlying API that is subject to change. In fact, the inverse can be true so be sure to choose your settings wisely. For stability the options available are alpha, beta and stable. It is important you use the right stability, particularly for the release as if you tag a release as alpha or beta on accident the remote installations will ignore it unless they explicitly say they are OK with using alpha or beta quality releases.

    $pkg->setPhpDep('5.2.0');$pkg->setPearinstallerDep('1.4.11');

    Above we begin building in dependencies that are enforced by the PEAR installer. In our case we require PHP 5.2.0 or better and the PEAR package 1.4.11 or better.

    pkg->addPackageDepWithChannel('required', 'Role_Web', 'pearified.com');

    One of the most powerful things about PEAR is that packages can depend on other PEAR packages in any other channel. In our case we require that pearified/Role_Web be installed on the remote installations before installing our application. You can even add optional dependencies should your application be able to provide advanced functionality with the addition of optional packages.

    $pkg->addUsesRole('web', 'Webfiles');

    If you recall the setOptions() call we made earlier we assigned all our applications files and directories to use the ‘web’ role. The above snippet tells our package that our self-assigned alias of ‘web’ maps to the ‘Webfiles’ role provided by pearified/Role_Web.

    $pkg->addMaintainer('lead', 'tony.bibbs@example.com', 'Tony Bibbs', 'tony.bibbs@example.com');

    Before we complete our build script we need to assign one or more maintainers by specifying their role, channel username, full name and email address.

    if (isset($argv[1]) && $argv[1] === 'make') {    $pkg->writePackageFile();} else {    $pkg->debugPackageFile();}

    Finally we need to take all the information we put in $pkg in our build script and have it generate the XML. As you can see you can call our script with no argument and it will simply call debugPackageFile() which will write the XML to the screen which is really useful for debugging your XML. Once you feel you have debugged the script you can call the script with the ‘make’ argument which will write the XML file. In our example it will write to /path/to/app/package.xml.

  4. So now you’ve built the script that generates your XML file! Before we can actually package our application we need to install that infamous pearified/Role_Web:
    #>pear channel-discover pearified.com#>pear install pearified/Role_Web

    Note the above will notify you there is a post install script. That script will ask you for the top level directory where all web documents go (e.g. /var/www). You must run this to be able to test out the package we end up creating below.

  5. Because we are publishing to pear.example.com you need to discover the channel otherwise your build will not work:
    #>pear channel-discover pear.example.com 
  6. Excellent, now we have everything we need to package up our application!
    #>php buildPackage.php make

    The above won’t produce any output so when it is done simply ensure you have a brand-new, sparkley package.xml.

  7. With our package.xml in hand we can now do the build:
    #>pear package package.xml

    The above will produce a lot of output as it scans the project directory. It may even produce warnings…particularly about classes not having specific prefixes. You can ignore those as PEAR tries to ensure developers adhere to their standard for class naming which, for applications, makes no sense (at least not in my opinion). If it works you’ll see a file like [package_name]-[version].tgz. In our case: app-1.2.1.tgz. This is your PEAR compatible package of your application!

  8. You need to verify your PEAR package installs properly. To do this you will need to simply install the software:
    #>pear install example.com/app

    The above should give output similar to the above “pear install” calls we made. If you get a message the install was OK then you should go to your web server’s top level folder (/var/www in our case) and ensure you see the folder for your application.

  9. If you verified all is good then you simply need to upload your releae to your PEAR Channel.

That’s it. You’ve successfully used PEAR as a way to deploy full fledged applications not just the PHP package PEAR has come to be known for! Note that all this assumes you have a PEAR Channel setup. If you need one I’d recommend using Chiara_PEAR_Server .

Leave a Reply

Your email address will not be published. Required fields are marked *

*