![]() |
|
URL of the article:
Issue:
02.2003
PEAR based Deployment
This article will help you to provide an infrastructure for deploying your applications using the PEAR Package Manager API and Server-Tools.
Alexander Merz
Every popular PHP program is distributed with an installation routine. They simplify the installation of large programs but this comfort does not often continue for the subsequent administration and updating programs and their plug-ins. The open PEAR infrastructure is an ideal basis to provide this comfort.
You can use the PEAR tools and server back ends independently of the official PHP Extension and Application Repository. Originally the PEAR core developers knew that it is not possible to add every interesting program or class into PEAR-CVS. For this reason all standards and tools are public and Open Source - but the PHP License also allows for commercial use. For a program to be fully compatible with PEAR three elements are required:
A PEAR package is a Tar archive, optionally compressed with the ZIP algorithm; which combines an arbitrary amount of program specific files and a package definition file package.xml - which contains the required data for the package and its installation. The number of packages you make from your program depends more on maintenance and handling and less on the source code size. In the most cases, for modular programs, you can create a Package for every module and one for the base functions. For more monolithic programs, you should form your Packages based on the program's processes. For example, based on input, processing and output functions; think in tasks, functions and classes while splitting the program and do not orientate yourself on existing files and directories. Packages contain PHP source code, documentation, tests and other kind of files; e.g. a guestbook-Package for a CMS can include graphics. If you know which Packages you want to create, it is recommended to write a list for every Package with entries for all related files. For each file you should note its type (role), where it is placed while package building and where to install it on the client computer. The role determines the destination of the file. Also, write down the dependencies between the packages. See the PEAR manual for more information about types of roles and dependencies[3]. Now we are equipped for the hardest part of the package creation: to write the package.xml file. The file in XML format contains the list of related files, information about the maintainers and the change log of a package. In practice: we want to create the package My_Dinner. It contains the files meal.php and wine.php. Furthermore, the files table.php and chair.php in the sub directory Room. Our package definition file package.xml is located in the same directory as meal.php - all path definitions about their origin are relative to the package.xml; additionally our package depends on the package Kitchen. As the first element of the file, the name of the package contains a short and an extended description. <?xml version="1.0" encoding="ISO-8859-1" ?> <package version="1.0"> <name>My_Dinner</name> <summary>Provides an API for a meal at 6 o'clock</summary> <description> This package was created after the explosion of the microwave oven </description> The <package>-tag's attribute identifies the version of the format - version 1.0 is supported in versions upward of PHP 4.2 of the PEAR installer; earlier versions should not be used. The next section lists the participating programmers, divisions or companies, the content of the tags can be chosen freely. <maintainers> <maintainer> <user>alexmerz</user> <name>Alexander merz</name> <email>alex@example.com</email> <role>lead</role> </maintainer> ... </maintainers> There is one limitation: if you want to deploy the package with the PEAR package server, the <user>-tag entry must correspond to a user account on the server; you will find more information in the part about the package server. The third XML section holds the actual information for the current version: <release> <version>1.0.1</version> <date>2003-02-12</date> <license>lgpl</license> <state>stable</state> <deps> <dep type=pkg version=1.2>Kitchen</dep> </deps> <notes> add new butler method </notes> <filelist> <dir name="/" baseinstalldir="Dinner"> <file role="php">meal.php</file> <file role="php">wine.php</file> <dir name="Room"> <file role="php">table.php</file> <file role="php">chair.php</file> </dir> </dir> </filelist> </release> The meaning of the entries, step by step: the first four lines should be self explanatory. The tag content can be chosen freely, only the date should follow the format yyyy-mm-dd. The <deps>-segment covers information about dependencies of the package for certain requirements, like other packages, libraries or operation systems - in our case the package Kitchen, version 1.2. The <notes> entry contains information about new releases and changes. The last section lists all files in the package. The first <dir> tag expresses the link between the position of the package.xml file and all inner <file> and <dir> tags. The name-attribute value '/' means these entries are on the same level as package.xml. The baseinstalldir-attribute refers to the destination directory for the inner entries; all listed files and directories will be installed in the directory Dinner on the client. The first two <file>-tags point to the PHP files in the same directory, the inner <dir>-tag to the sub directory Room and both its files - they will be installed in the directory Dinner and Room on the client. The fourth and last section contains the change log, which includes the history of the package. Built up like the release tag, you can simply copy and paste the original <release>-tag of the third sections and remove the filelist part. <changelog> <release> <version>1.0</version> <date>2002-11-11</date> <license>lgpl</license> <state>stable</state> <notes> This is the first stable release. Changes : - fix chair </notes> </release> .... </changelog> Now we have a complete package definition file; the whole Document Type Definition (DTD) for the XML format is available in the PEAR CVS-Repository[2]. At the end, we can build the package archive from the package.xml file and the package related files with the PEAR Installer on the command line: pear package path_to_package/package.xml. If this introduction was a little bit too short for you: just play with it! Take an existing package.xml from a PEAR package and adjust it to your requirements - the PEAR installer prints out understandable error messages if your package.xml is not correct. Setting up a package Server After we are able to build packages, we can start with the deployment. In the easiest case you offer the package as an FTP or HTTP download on a web page and the user can install the package archive with pear install packagename.tgz. But to provide a simple pear install packagename or all the features of package administration you need your own package server. Basically to setup a package server is not difficult, assuming you use Apache and MySQL - other combinations may work, but they have not been tested. The required scripts are available under the PHP license. First step: download the scripts and database dumps from the CVS and install the required PEAR packages - see the next paragraphs Required Server files. Additionally, and read General Server and PHP set up notes. Required Server files You need the following directories from the pearweb-CVS-Repository pearweb/doc pearweb/include pearweb/sql pearweb/public_html And the following PEAR packages need to be installed: Cache DB HTTP HTTP_Upload HTML_Common HTML_Form HTML_Table HTML_TreeMenu HTTP_Request Net_Url General Server and PHP set up notes The PEAR Website requires a virtual host or dedicated server. Set the public_html directory as document root; disable error_reporting in your php.ini or set it with a php directive in the .htaccess file/http.conf. You have also to edit the auto_prepend_file directive in the same way, for example in an .htaccess file in the public_html directory: php_value auto_prepend_file /path/include/pear-prepend.php It is a good idea to place the include directory above the document root - it contains the database password. The PEAR directory and the include directory must be present in the include_path. For the client tools and APIs it is essential to force the webserver to parse the get script in the public_html directory with PHP; write the following in the .httaccess: ForceType application/x-httpd-php Required on client side a PEAR base installation, the XML_RPC PEAR package In the directory sql is all the data required to set up the database. The tables contain all data to administrate the packages. First define a correct data source name (DSN), either set the environment variable PEAR_DATABASE_DSN with the DSN as the value on the computer or change the assignment in the file include/pear-config.php, currently on line 34. The DSN for a MySQL database with the database user pear, the password secret and the database peardb is mysql:://pear:secret@localhost/peardb. For other options see the PEAR-Manual[1]. Go to the sql directory, call make create on the command line, the database peardb and all tables are generated automatically; with make destroy all files are deleted completely. The newly generated database already contains some example entries which can be deleted. Add a new user to the database as described in Adding a new user. In the file include/pear-config.php you have to adjust the constants PEAR_TEMPDIR and PEAR_TARBALL_DIR in the same way. Do the first test - call index.php from your browser and you should see the PEAR-Homepage. If this works you can start by creating a category and uploading a release. Add an new User The package Server includes an authorization mechanism for administering packages. You need to create a user account on the package server. This can be done by adding a new record in the user table, in the most cases only the fields handle, password (as an md5-hash!), name and registered must be filled in, registered must be set to 1. At least one user should have the admin-field set to 1, this means that this user has extended rights, e.g. to add new categories. The <user>-tag in the maintainer section of package.xml must correspond to a handle in the user table. Only an existing user can upload a new package to the package server. Package administration with the Web-interface First login on your PEAR homepage with your user name (user handle) and password. Then call the Category manager by clicking on Categories in the administrator section on the left hand side. Add a category using the given form - it is good practice to have a category for each program or project. Note: you can only access the administrator section if the admin-flag in the user table is set to true, although you need no administrator rights to register and upload a new package. Go to New package and fill in the form. If you do this successfully, you will get the message, that you - the user who filled in the register form - is now the lead developer. As lead developer, you have the right to upload a new release of the registered package in the Upload package section. Check in the package Browser section of your package - if you are still logged in, you can edit the package information by clicking on the small pencil icon in the lower right corner of the information box; and with the X icon you can delete the package. Is not all this fine and easy? Take care if you want to publish your PEAR site, first it may irritate customers and users seeing the PEAR homepage; second you may be in conflict with local laws by leaving the site as is. Unfortunately the whole PEAR website is not based on templates - the template directory contains only some JavaScript code and templates for text boxes. The starting point for customizing is the layout.php file in the include directory. Cleaning up: the sql and doc directories are not required for running the system, the manual directory in public_html makes only sense for the PEAR Manual - you can delete them without worry; the same with public_html/downloads, the go-pear script and all the CVS directories. Depending on your purposes you can delete the files in the public_html directory, see the Important public_html files table for an overview. Important public_html files
PEAR_Registry methods for information retrieval
Functions provided by XML-RPC interface
Now you have an working PEAR infrastructure! It is no problem to do this now: pear config-set master_server pear.example.com pear install Dinner But this is bad for the customer! Why? Firstly, the customer has to set the server name manually (and to reset it to pear.php.net afterwards); secondly, it installs the package in the PEAR directory - and may overwrite an existing official PEAR package; thirdly, it looks ugly - not corperate identity-compatible. In this section I will describe a script which installs a set of predefined packages into the working directory, which includes checks for the availability of newer versions on the server. Due to the PEAR package Manager API, this is easier than you think - and if your customer already has a working PEAR Installer, this API is already installed. The required files reside in the pear/ directory of the PEAR directory. Listing 1 shows the script. The require_once() commands include the necessary class files. In the first section two objects are created: the $ui-object refers to a user front end and the $config object allows for a PEAR configuration which is independent of the customer's settings. Listing 1 <?php require_once 'PEAR.php'; require_once 'PEAR/Config.php'; require_once 'PEAR/Command.php'; require_once 'PEAR/Registry.php'; require_once 'PEAR/Remote.php'; PEAR_Command::setFrontendType("CLI"); $ui = &PEAR_Command::getFrontendObject(); $config = &PEAR_Config::singleton(); $config->set("master_server", "pear.example.com"); $config->set("php_dir", "."); $config->set("verbose", 0); PEAR::setErrorHandling(PEAR_ERROR_RETURN); echo "start installation\n"; $files = array("My_Dinner-1.0.tgz"); install($files, $config); upgradeAll($config); echo "\ninstallation finished\n"; function install($files, $config) { $cmd = &PEAR_Command::factory("install", $config); foreach($files as $file) { echo "install $file ... "; $ok = $cmd->run("install", array(), array($file)); echo (PEAR::isError($ok))? "failed\n" : "ok\n"; } } function upgradeAll($config) { $registry = &new PEAR_Registry('.'); $remote = &new PEAR_Remote($config); $cmd = &PEAR_Command::factory("upgrade", $config); $pkgs = $registry->listPackages(); foreach($pkgs as $pkg) { $remoteInfo = $remote->call('package.info', $pkg); $versions = array_keys($remoteInfo['releases']); $last = $versions[0]; $info = $registry->packageInfo($pkg); $current = $info['version']; if($current < $last) { echo "update $pkg to version $last... "; $ok = $cmd->run("upgrade", array(), $pkgs); echo (PEAR::isError($ok))? "failed\n" : "ok\n"; } } } ?> You can define different types of user front ends - CLI comes with the PEAR base installation, others are GTK and Web, but they need to be explicitly installed. The front end type has no influence on the script directly; it affects the kind of rendering information only - uninteresting for us, we render the output independently. The settings for the PEAR_Config object are identical to the the pear config-set command - it accepts the same configuration keys and values. Here we set the master server entry to our package server; the installation directory for the packages to the place where the install script resides; and the last set directive forces the PEAR package Manager API to print no messages. Also, the error handling is set to return the error object only. In the $files array, we list the names of the supplied package files; the first function installs them, the second checks for upgrades. The install function shows you how to call a command - the same commands you use with the PEAR Installer. Then execute a command, create an command specific PEAR_Command instance and then call the run as often as necessary: $cmd = &PEAR_Command::factory($command, $config); $ok = $cmd->run($command, $options, $parameter); The command pear install --force My_Dinner corresponds to: $cmd = &PEAR_Command::factory('install', $config); $ok = $cmd->run('install', array('force'=>true), array('My_Dinner')); The upgradeAll function is more complex - an overview: first we ask the PEAR registry which packages are installed, next, for every package we compare the version number of the installed with the current version number on the package Server. If the current version is newer than the installed version, the package will be upgraded. The PEAR Registry is the central storage for information about installed packages; if the install script runs the first time, a .registry directory and the registry files will created in the directory defined in the constructor - here in the current directory. Without a directory declaration the native PEAR registry will be used. With listpackages() we retrieve an array of all installed packages in our installation directory; in the foreach loop we ask for information for every package with packageInfo() - from there we extract the version number. Note: the PEAR registry has nothing to do with the Windows Registry. To get the latest version number of a package, we have to talk to the package server. The PEAR_Remote class reduces the effort in accessing the XML-RPC interface on the package server to two commands: create an instance and execute the call method. The call method expects the name of the remote function and its parameters. If the latest version is newer than that installed, we issue an upgrade command to install the new version. Take a look to the tables which show the methods and functions of PEAR_Registry and the XML-RPC interface. Summary Now it is done! You have the basic knowledge to use an excellent and heavily tested system to deploy your programs. I hope you are curious enough to spend some time to dive into the system. The package Manager API is well documented in the source and you can adapt the install script to your needs. Have fun! Alexander Merz (alexmerz@php.net) is the editor of the PEAR manual and works as a freelancer. Links
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|