phpDocumentor PEAR_PackageFileManager
[ class tree: PEAR_PackageFileManager ] [ index: PEAR_PackageFileManager ] [ all elements ]

Source for file PackageFileManager.php

Documentation is available at PackageFileManager.php


1 <?php
2 //
3 // +------------------------------------------------------------------------+
4 // | PEAR :: Package File Manager |
5 // +------------------------------------------------------------------------+
6 // | Copyright (c) 2003 Gregory Beaver |
7 // | Email cellog@phpdoc.org |
8 // +------------------------------------------------------------------------+
9 // | This source file is subject to version 3.00 of the PHP License, |
10 // | that is available at http://www.php.net/license/3_0.txt. |
11 // | If you did not receive a copy of the PHP license and are unable to |
12 // | obtain it through the world-wide-web, please send a note to |
13 // | license@php.net so we can mail you a copy immediately. |
14 // +------------------------------------------------------------------------+
15 // | Portions of this code based on phpDocumentor |
16 // | Web http://www.phpdoc.org |
17 // | Mirror http://phpdocu.sourceforge.net/ |
18 // +------------------------------------------------------------------------+
19 //
20
21 /**
22 * @package PEAR_PackageFileManager
23 */
24 /**
25 * PEAR installer
26 */
27 require_once 'PEAR/Common.php';
28 /**#@+
29 * Error Codes
30 */
31 define('PEAR_PACKAGEFILEMANAGER_NOSTATE', 1);
32 define('PEAR_PACKAGEFILEMANAGER_NOVERSION', 2);
33 define('PEAR_PACKAGEFILEMANAGER_NOPKGDIR', 3);
34 define('PEAR_PACKAGEFILEMANAGER_NOBASEDIR', 3);
35 define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND', 4);
36 define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE', 5);
37 define('PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE', 6);
38 define('PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE', 7);
39 define('PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE', 8);
40 define('PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE', 9);
41 define('PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST', 10);
42 define('PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES', 11);
43 define('PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST', 12);
44 define('PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS', 13);
45 define('PEAR_PACKAGEFILEMANAGER_NOPACKAGE', 14);
46 define('PEAR_PACKAGEFILEMANAGER_WRONG_MROLE', 15);
47 define('PEAR_PACKAGEFILEMANAGER_NOSUMMARY', 16);
48 define('PEAR_PACKAGEFILEMANAGER_NODESC', 17);
49 define('PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS', 18);
50 define('PEAR_PACKAGEFILEMANAGER_NO_FILES', 19);
51 define('PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING', 20);
52 define('PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE', 21);
53 define('PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE', 22);
54 define('PEAR_PACKAGEFILEMANAGER_INVALID_ROLE', 23);
55 /**#@-*/
56 /**
57 * Error messages
58 * @global array $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS']
59 */
60 $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'] =
61 array(
62 'en' =>
63 array(
64 PEAR_PACKAGEFILEMANAGER_NOSTATE =>
65 'Release State (option \'state\') must by specified in PEAR_PackageFileManager setOptions (alpha|beta|stable)',
66 PEAR_PACKAGEFILEMANAGER_NOVERSION =>
67 'Release Version (option \'version\') must be specified in PEAR_PackageFileManager setOptions',
68 PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
69 'Package source base directory (option \'packagedirectory\') must be ' .
70 'specified in PEAR_PackageFileManager setOptions',
71 PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
72 'Package install base directory (option \'baseinstalldir\') must be ' .
73 'specified in PEAR_PackageFileManager setOptions',
74 PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND =>
75 'Base class "%s" can\'t be located',
76 PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE =>
77 'Base class "%s" can\'t be located in default or user-specified directories',
78 PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE =>
79 'Failed to write package.xml file to destination directory',
80 PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE =>
81 'Destination directory "%s" is unwritable',
82 PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE =>
83 'Failed to copy package.xml.tmp file to package.xml',
84 PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE =>
85 'Failed to open temporary file "%s" for writing',
86 PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST =>
87 'package.xml file path "%s" doesn\'t exist or isn\'t a directory',
88 PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES =>
89 'Directory "%s" is not a CVS directory (it must have the CVS/Entries file)',
90 PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST =>
91 'Package source base directory "%s" doesn\'t exist or isn\'t a directory',
92 PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS =>
93 'Run $managerclass->setOptions() before any other methods',
94 PEAR_PACKAGEFILEMANAGER_NOPACKAGE =>
95 'Package Name (option \'package\') must by specified in PEAR_PackageFileManager '.
96 'setOptions to create a new package.xml',
97 PEAR_PACKAGEFILEMANAGER_NOSUMMARY =>
98 'Package Summary (option \'summary\') must by specified in PEAR_PackageFileManager' .
99 ' setOptions to create a new package.xml',
100 PEAR_PACKAGEFILEMANAGER_NODESC =>
101 'Detailed Package Description (option \'description\') must be' .
102 ' specified in PEAR_PackageFileManager constructor to create a new package.xml',
103 PEAR_PACKAGEFILEMANAGER_WRONG_MROLE =>
104 'Maintainer role must be one of "%s", was "%s"',
105 PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS =>
106 'Add maintainers to a package before generating the package.xml',
107 PEAR_PACKAGEFILEMANAGER_NO_FILES =>
108 'No files found, check the path "%s"',
109 PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING =>
110 'No files left, check the path "%s" and ignore option "%s"',
111 PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE =>
112 'Package validation failed:%s%s',
113 PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE =>
114 'Replacement Type must be one of "%s", was passed "%s"',
115 PEAR_PACKAGEFILEMANAGER_INVALID_ROLE =>
116 'Invalid file role passed to addRole, must be one of "%s", was passed "%s"',
117 ),
118 // other language translations go here
119 );
120 /**
121 * PEAR :: PackageGenerate updates the <filelist></filelist> section
122 * of a PEAR package.xml file to reflect the current files in
123 * preparation for a release.
124 *
125 * The PEAR_PackageGenerate class uses a plugin system to generate the
126 * list of files in a package. This allows both standard recursive
127 * directory parsing (plugin type file) and more intelligent options
128 * such as the CVS browser {@link PEAR_PackageFileManager_Cvs}, which
129 * grabs all files in a local CVS checkout to create the list, ignoring
130 * any other local files.
131 *
132 * Other options include specifying roles for file extensions (all .php
133 * files are role="php", for example), roles for directories (all directories
134 * named "tests" are given role="tests" by default), and exceptions.
135 * Exceptions are specific pathnames with * and ? wildcards that match
136 * a default role, but should have another. For example, perhaps
137 * a debug.tpl template would normally be data, but should be included
138 * in the docs role. Along these lines, to exclude files entirely,
139 * use the ignore option.
140 *
141 * Required options for a release include version, baseinstalldir, state,
142 * and packagedirectory (the full path to the local location of the
143 * package to create a package.xml file for)
144 *
145 * Example usage:
146 * <code>
147 * <?php
148 * require_once('PEAR/PackageFileManager.php');
149 * $packagexml = new PEAR_PackageFileManager;
150 * $e = $packagexml->setOptions(
151 * array('baseinstalldir' => 'PhpDocumentor',
152 * 'version' => '1.2.1',
153 * 'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/',
154 * 'state' => 'stable',
155 * 'filelistgenerator' => 'cvs', // generate from cvs, use file for directory
156 * 'notes' => 'We\'ve implemented many new and exciting features',
157 * 'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/
158 * 'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc
159 * 'dir_roles' => array('tutorials' => 'doc'),
160 * 'exceptions' => array('README' => 'doc', // README would be data, now is doc
161 * 'PHPLICENSE.txt' => 'doc'))); // same for the license
162 * if (PEAR::isError($e)) {
163 * echo $e->getMessage();
164 * die();
165 * }
166 * $packagexml->addRole('pkg', 'doc'); // add a new role mapping
167 * $e = $packagexml->writePackageFile();
168 * if (PEAR::isError($e)) {
169 * echo $e->getMessage();
170 * die();
171 * }
172 * ?>
173 *
174 * In addition, a package.xml file can now be generated from
175 * scratch, with the usage of new options package, summary, description, and
176 * the use of the {@link addMaintainer()} method
177 * </code>
178 * @package PEAR_PackageFileManager
179 */
180 class PEAR_PackageFileManager
181 {
182 /**
183 * Format: array(array(regexp-ready string to search for whole path,
184 * regexp-ready string to search for basename of ignore strings),...)
185 * @var false|array
186 * @access private
187 */
188 var $_ignore = false;
189
190 /**
191 * Contents of the package.xml file
192 * @var string
193 * @access private
194 */
195 var $_packageXml = false;
196
197 /**
198 * Contents of the original package.xml file, if any
199 * @var string
200 * @access private
201 */
202 var $_oldPackageXml = false;
203
204 /**
205 * @access private
206 * @var PEAR_Common
207 */
208 var $_pear;
209
210 /**
211 * @access private
212 * @var string
213 */
214 var $_options = array(
215 'packagefile' => 'package.xml',
216 'doctype' => 'http://pear.php.net/dtd/package-1.0',
217 'filelistgenerator' => 'file',
218 'license' => 'PHP License',
219 'roles' =>
220 array(
221 'php' => 'php',
222 'html' => 'doc',
223 '*' => 'data',
224 ),
225 'dir_roles' =>
226 array(
227 'docs' => 'doc',
228 'examples' => 'doc',
229 'tests' => 'tests',
230 ),
231 'exceptions' => array(),
232 'installexceptions' => array(),
233 'installas' => array(),
234 'platformexceptions' => array(),
235 'ignore' => array(),
236 'deps' => false,
237 'maintainers' => false,
238 'notes' => '',
239 'changelognotes' => false,
240 'outputdirectory' => false,
241 'pathtopackagefile' => false,
242 'lang' => 'en',
243 'configure_options' => array(),
244 'replacements' => array(),
245 'pearcommonclass' => 'PEAR_Common',
246 );
247
248 /**
249 * Does nothing, use setOptions
250 * @see setOptions
251 */
252 function PEAR_PackageFileManager()
253 {
254 }
255
256 /**
257 * Set package.xml generation options
258 *
259 * The options array is indexed as follows:
260 * <code>
261 * $options = array('option_name' => <optionvalue>);
262 * </code>
263 *
264 * The documentation below simplifies this description through
265 * the use of option_name without quotes
266 *
267 * Configuration options:
268 * - lang: lang controls the language in which error messages are
269 * displayed. There are currently only English error messages,
270 * but any contributed will be added over time.<br />
271 * Possible values: en (default)
272 * - packagefile: the name of the packagefile, defaults to package.xml
273 * - pathtopackagefile: the path to an existing package file to read in,
274 * if different from the packagedirectory
275 * - packagedirectory: the path to the base directory of the package. For
276 * package PEAR_PackageFileManager, this path is
277 * /path/to/pearcvs/pear/PEAR_PackageFileManager where
278 * /path/to/pearcvs is a local path on your hard drive
279 * - outputdirectory: the path in which to place the generated package.xml
280 * by default, this is ignored, and the package.xml is
281 * created in the packagedirectory
282 * - filelistgenerator: the <filelist> section plugin which will be used.
283 * In this release, there are two generator plugins,
284 * file and cvs. For details, see the docs for these
285 * plugins
286 * - usergeneratordir: For advanced users. If you write your own filelist
287 * generator plugin, use this option to tell
288 * PEAR_PackageFileManager where to find the file that
289 * contains it. If the plugin is named foo, the class
290 * must be named PEAR_PackageFileManager_Foo
291 * no matter where it is located. By default, the Foo
292 * plugin is located in PEAR/PackageFileManager/Foo.php.
293 * If you pass /path/to/foo in this option, setOptions
294 * will look for PEAR_PackageFileManager_Foo in
295 * /path/to/foo/Foo.php
296 * - doctype: Specifies the DTD of the package.xml file. Default is
297 * http://pear.php.net/dtd/package-1.0
298 * - pearcommonclass: Specifies the name of the class to instantiate, default
299 * is PEAR_Common, but users can override this with a custom
300 * class that implements PEAR_Common's method interface
301 *
302 * package.xml simple options:
303 * - baseinstalldir: The base directory to install this package in. For
304 * package PEAR_PackageFileManager, this is "PEAR", for
305 * package PEAR, this is "/"
306 * - license: The license this release is released under. Default is
307 * PHP License if left unspecified
308 * - notes: Release notes, any text describing what makes this release unique
309 * - changelognotes: notes for the changelog, this should be more detailed than
310 * the release notes. By default, PEAR_PackageFileManager uses
311 * the notes option for the changelog as well
312 * - version: The version number for this release. Remember the convention for
313 * numbering: initial alpha is between 0 and 1, add b<beta number> for
314 * beta as in 1.0b1, the integer portion of the version should specify
315 * backwards compatibility, as in 1.1 is backwards compatible with 1.0,
316 * but 2.0 is not backwards compatible with 1.10. Also note that 1.10
317 * is a greater release version than 1.1 (think of it as "one point ten"
318 * and "one point one"). Bugfix releases should be a third decimal as in
319 * 1.0.1, 1.0.2
320 * - package: [optional] Package name. Use this to create a new package.xml, or
321 * overwrite an existing one from another package used as a template
322 * - summary: [optional] Summary of package purpose
323 * - description: [optional] Description of package purpose. Note that the above
324 * three options are not optional when creating a new package.xml
325 * from scratch
326 *
327 * <b>WARNING</b>: all complex options that require a file path are case-sensitive
328 *
329 * package.xml complex options:
330 * - ignore: an array of filenames, directory names, or wildcard expressions specifying
331 * files to exclude entirely from the package.xml. Wildcards are operating system
332 * wildcards * and ?. file*foo.php will exclude filefoo.php, fileabrfoo.php and
333 * filewho_is_thisfoo.php. file?foo.php will exclude fileafoo.php and will not
334 * exclude fileaafoo.php. test/ will exclude all directories and subdirectories of
335 * ANY directory named test encountered in directory parsing. *test* will exclude
336 * all files and directories that contain test in their name
337 * - roles: this is an array mapping file extension to install role. This
338 * specifies default behavior that can be overridden by the exceptions
339 * option and dir_roles option. use {@link addRole()} to add a new
340 * role to the pre-existing array
341 * - dir_roles: this is an array mapping directory name to install role. All
342 * files in a directory whose name matches the directory will be
343 * given the install role specified. Single files can be excluded
344 * from this using the exceptions option. The directory should be
345 * a relative path from the baseinstalldir, or "/" for the baseinstalldir
346 * - exceptions: specify file role for specific files. This array maps all files
347 * matching the exact name of a file to a role as in "file.ext" => "role"
348 * - deps: dependency array. Pass in an empty array to clear all dependencies, and use
349 * {@link addDependency()} to add new ones/replace existing ones
350 * - maintainers: maintainers array. Pass in an empty array to clear all maintainers, and
351 * use {@link addMaintainer()} to add a new maintainer/replace existing maintainer
352 * - installexceptions: array mapping of specific filenames to baseinstalldir values. Use
353 * this to force the installation of a file into another directory,
354 * such as forcing a script to be in the root scripts directory so that
355 * it will be in the path. The filename must be a relative path to the
356 * packagedirectory
357 * - platformexceptions: array mapping of specific filenames to the platform they should be
358 * installed on. Use this to specify unix-only files or windows-only
359 * files. The format of the platform string must be
360 * OS-version-cpu-extra if any more specific information is needed,
361 * and the OS must be in lower case as in "windows." The match is
362 * performed using a regular expression, but uses * and ? wildcards
363 * instead of .* and .?. Note that hpux/aix/irix/linux are all
364 * exclusive. To select non-windows, use (*ix|*ux)
365 * - installas: array mapping of specific filenames to the filename they should be installed as.
366 * Use this to specify new filenames for files that should be installed. This will
367 * often be used in conjunction with platformexceptions if there are two files for
368 * different OSes that must have the same name when installed.
369 * - replacements: array mapping of specific filenames to complex text search-and-replace that
370 * should be performed upon install. The format is:
371 * <pre>
372 * filename => array('type' => php-const|pear-config|package-info
373 * 'from' => text in file
374 * 'to' => name of variable)
375 * </pre>
376 * if type is php-const, then 'to' must be the name of a PHP Constant.
377 * If type is pear-config, then 'to' must be the name of a PEAR config
378 * variable accessible through a PEAR_Config class->get() method. If
379 * type is package-info, then 'to' must be the name of a section from
380 * the package.xml file used to install this file.
381 * - configure_options: array specifies build options for PECL packages (you should probably
382 * use PECL_Gen instead, but it's here for completeness)
383 * @see PEAR_PackageFileManager_File
384 * @see PEAR_PackageFileManager_CVS
385 * @return void|PEAR_Error
386 * @throws PEAR_PACKAGEFILEMANAGER_NOSTATE
387 * @throws PEAR_PACKAGEFILEMANAGER_NOVERSION
388 * @throws PEAR_PACKAGEFILEMANAGER_NOPKGDIR
389 * @throws PEAR_PACKAGEFILEMANAGER_NOBASEDIR
390 * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE
391 * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND
392 * @param array
393 */
394 function setOptions($options = array())
395 {
396 if (!isset($options['state'])) {
397 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSTATE);
398 }
399 if (!isset($options['version'])) {
400 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOVERSION);
401 }
402 if (!isset($options['packagedirectory'])) {
403 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPKGDIR);
404 } else {
405 $options['packagedirectory'] = str_replace(DIRECTORY_SEPARATOR,
406 '/',
407 realpath($options['packagedirectory']));
408 if ($options['packagedirectory']{strlen($options['packagedirectory']) - 1} != '/') {
409 $options['packagedirectory'] .= '/';
410 }
411 }
412 if (!isset($options['baseinstalldir'])) {
413 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOBASEDIR);
414 }
415 $this->_options = array_merge($this->_options, $options);
416
417 if (!class_exists($this->_options['pearcommonclass'])) {
418 $this->_options['pearcommonclass'] = 'PEAR_Common';
419 }
420 $path = ($this->_options['pathtopackagefile'] ?
421 $this->_options['pathtopackagefile'] : $this->_options['packagedirectory']);
422 $this->_options['filelistgenerator'] = ucfirst(strtolower($this->_options['filelistgenerator']));
423 if (PEAR::isError($res = $this->_getExistingPackageXML($path, $this->_options['packagefile']))) {
424 return $res;
425 }
426 // attempt to load the interface from the standard PEAR location
427 @include_once('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php');
428 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
429 if (isset($this->_options['usergeneratordir'])) {
430 // attempt to load from a user-specified directory
431 $this->_options['usergeneratordir'] = str_replace(DIRECTORY_SEPARATOR,
432 '/',
433 realpath($this->_options['usergeneratordir']));
434 if ($this->_options['usergeneratordir']{strlen($this->_options['usergeneratordir']) - 1} != '/') {
435 $this->_options['usergeneratordir'] .= '/';
436 }
437 @include_once($this->_options['usergeneratordir'] . $this->_options['filelistgenerator'] . '.php');
438 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
439 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE,
440 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
441 }
442 } else {
443 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND,
444 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
445 }
446 }
447 }
448
449 /**
450 * Add an extension/role mapping to the role mapping option
451 * @param string file extension
452 * @param string role
453 * @throws PEAR_PACKAGEFILEMANAGER_INVALID_ROLE
454 */
455 function addRole($extension, $role)
456 {
457 $roles = call_user_func(array($this->_options['pearcommonclass'], 'getfileroles'));
458 if (!in_array($role, $roles)) {
459 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_ROLE, implode($roles, ', '), $role);
460 }
461 $this->_options['roles'][$extension] = $role;
462 }
463
464 /**
465 * Add an install-time platform conditional install for a file
466 *
467 * The format of the platform string must be
468 * OS-version-cpu-extra if any more specific information is needed,
469 * and the OS must be in lower case as in "windows." The match is
470 * performed using a regular expression, but uses * and ? wildcards
471 * instead of .* and .?. Note that hpux/aix/irix/linux are all
472 * exclusive. To select non-windows, use (*ix|*ux)
473 *
474 * This information is based on eyeing the source for OS/Guess.php, so
475 * if you are unsure of what to do, read that file.
476 * @param string relative path of file (relative to packagedirectory option)
477 * @param string platform descriptor string
478 */
479 function addPlatformException($path, $platform)
480 {
481 if (!isset($this->_options['platformexceptions'])) {
482 $this->_options['platformexceptions'] = array();
483 }
484 $this->_options['platformexceptions'][$path] = $platform;
485 }
486
487 /**
488 * Add a replacement option for a file
489 *
490 * This sets an install-time complex search-and-replace function
491 * allowing the setting of platform-specific variables in an
492 * installed file.
493 *
494 * if $type is php-const, then $to must be the name of a PHP Constant.
495 * If $type is pear-config, then $to must be the name of a PEAR config
496 * variable accessible through a {@link PEAR_Config::get()} method. If
497 * type is package-info, then $to must be the name of a section from
498 * the package.xml file used to install this file.
499 * @param string relative path of file (relative to packagedirectory option)
500 * @param string variable type, either php-const, pear-config or package-info
501 * @param string text to replace in the source file
502 * @param string variable name to use for replacement
503 * @throws PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE
504 */
505 function addReplacement($path, $type, $from, $to)
506 {
507 if (!isset($this->_options['replacements'])) {
508 $this->_options['replacements'] = array();
509 }
510 $types = call_user_func(array($this->_options['pearcommonclass'], 'getreplacementtypes'));
511 if (!in_array($type, $types)) {
512 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE,
513 implode($types, ', '), $type);
514 }
515 $this->_options['replacements'][$path][] = array('type' => $type, 'from' => $from, 'to' => $to);
516 }
517
518 /**
519 * Add a maintainer to the list of maintainers.
520 *
521 * Every maintainer must have a valid account at pear.php.net. The
522 * first parameter is the account name (for instance, cellog is the
523 * handle for Greg Beaver at pear.php.net). Every maintainer has
524 * one of four possible roles:
525 * - lead: the primary maintainer
526 * - developer: an important developer on the project
527 * - contributor: self-explanatory
528 * - helper: ditto
529 *
530 * Finally, specify the name and email of the maintainer
531 * @param string username on pear.php.net of maintainer
532 * @param lead|developer|contributor|helperrole of maintainer
533 * @param string full name of maintainer
534 * @param string email address of maintainer
535 */
536 function addMaintainer($handle, $role, $name, $email)
537 {
538 if (!$this->_packageXml) {
539 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
540 }
541 if (!in_array($role, $GLOBALS['_PEAR_Common_maintainer_roles'])) {
542 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_WRONG_MROLE,
543 implode(', ', $GLOBALS['_PEAR_Common_maintainer_roles']),
544 $role);
545 }
546 if (!isset($this->_packageXml['maintainers'])) {
547 $this->_packageXml['maintainers'] = array();
548 }
549 $found = false;
550 foreach($this->_packageXml['maintainers'] as $index => $maintainer) {
551 if ($maintainer['handle'] == $handle) {
552 $found = $index;
553 break;
554 }
555 }
556 $maintainer =
557 array('handle' => $handle, 'role' => $role, 'name' => $name, 'email' => $email);
558 if ($found !== false) {
559 $this->_packageXml['maintainers'][$found] = $maintainer;
560 } else {
561 $this->_packageXml['maintainers'][] = $maintainer;
562 }
563 }
564
565 /**
566 * Add an install-time configuration option for building of source
567 *
568 * This option is only useful to PECL projects that are built upon
569 * installation
570 * @param string name of the option
571 * @param string prompt to display to the user
572 * @param string default value
573 * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
574 * @return void|PEAR_Error
575 */
576 function addConfigureOption($name, $prompt, $default = null)
577 {
578 if (!$this->_packageXml) {
579 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
580 }
581 if (!isset($this->_packageXml['configure_options'])) {
582 $this->_packageXml['configure_options'] = array();
583 }
584 $found = false;
585 foreach($this->_packageXml['configure_options'] as $index => $option) {
586 if ($option['name'] == $name) {
587 $found = $index;
588 break;
589 }
590 }
591 $option = array('name' => $name, 'prompt' => $prompt);
592 if (isset($default)) {
593 $option['default'] = $default;
594 }
595 if ($found !== false) {
596 $this->_packageXml['configure_options'][$found] = $option;
597 } else {
598 $this->_packageXml['configure_options'][] = $option;
599 }
600 }
601
602 /**
603 * Add a dependency on another package, or an extension/php
604 *
605 * This will overwrite an existing dependency if it is found. In
606 * other words, if a dependency on PHP 4.1.0 exists, and
607 * addDependency('php', '4.3.0', 'ge', 'php') is called, the existing
608 * dependency on PHP 4.1.0 will be overwritten with the new one on PHP 4.3.0
609 * @param string Dependency element name
610 * @param string Dependency version
611 * @param string A specific operator for the version, this can be one of:
612 * 'has', 'not', 'lt', 'le', 'eq', 'ne', 'ge', or 'gt'
613 * @param string Dependency type. This can be one of:
614 * 'pkg', 'ext', 'php', 'prog', 'os', 'sapi', or 'zend'
615 * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
616 * @return void|PEAR_Error
617 */
618 function addDependency($name, $version = false, $operator = 'ge', $type = 'pkg')
619 {
620 if (!$this->_packageXml) {
621 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
622 }
623 if (!isset($this->_packageXml['release_deps'])) {
624 $this->_packageXml['release_deps'] = array();
625 }
626 $found = false;
627 foreach($this->_packageXml['release_deps'] as $index => $dep) {
628 if ($type == 'php') {
629 if ($dep['type'] == 'php') {
630 $found = $index;
631 break;
632 }
633 } else {
634 if (isset($dep['name']) && $dep['name'] == $name && $dep['type'] == $type) {
635 $found = $index;
636 break;
637 }
638 }
639 }
640 $dep =
641 array(
642 'name' => $name,
643 'type' => $type);
644 if ($type == 'php') {
645 unset($dep['name']);
646 }
647 if ($version) {
648 $dep['version'] = $version;
649 if ($operator) {
650 $dep['rel'] = $operator;
651 }
652 }
653
654 if ($found !== false) {
655 $this->_packageXml['release_deps'][$found] = $dep; // overwrite existing dependency
656 } else {
657 $this->_packageXml['release_deps'][] = $dep; // add new dependency
658 }
659 }
660
661 /**
662 * Writes the package.xml file out with the newly created <release></release> tag
663 *
664 * ALWAYS use {@link debugPackageFile} to verify that output is correct before
665 * overwriting your package.xml
666 * @param boolean null if no debugging, true if web interface, false if command-line
667 * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
668 * @throws PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS
669 * @throws PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE
670 * @throws PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE
671 * @throws PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE
672 * @throws PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE
673 * @return void|PEAR_Error
674 */
675 function writePackageFile($debuginterface = null)
676 {
677 if (!$this->_packageXml) {
678 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
679 }
680 if (!isset($this->_packageXml['maintainers']) || empty($this->_packageXml['maintainers'])) {
681 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS);
682 }
683 extract($this->_options);
684 $date = date('Y-m-d');
685 if (isset($package)) {
686 $this->_packageXml['package'] = $package;
687 }
688 if (isset($summary)) {
689 $this->_packageXml['summary'] = $summary;
690 }
691 if (isset($description)) {
692 $this->_packageXml['description'] = $description;
693 }
694 $this->_packageXml['release_date'] = $date;
695 $this->_packageXml['version'] = $version;
696 $this->_packageXml['release_license'] = $license;
697 $this->_packageXml['release_state'] = $state;
698 $this->_packageXml['release_notes'] = $notes;
699 $PEAR_Common = $this->_options['pearcommonclass'];
700 $this->_pear = new $PEAR_Common;
701 $this->_packageXml['filelist'] = $this->_getFileList();
702 if (PEAR::isError($this->_packageXml['filelist'])) {
703 return $this->_packageXml['filelist'];
704 }
705 if (isset($this->_pear->pkginfo['provides'])) {
706 $this->_packageXml['provides'] = $this->_pear->pkginfo['provides'];
707 }
708 $this->_packageXml['release_deps'] = $this->_getDependencies();
709 $this->_updateChangeLog();
710
711 $common = &$this->_pear;
712 $warnings = $errors = array();
713 $packagexml = $common->xmlFromInfo($this->_packageXml);
714 $common->validatePackageInfo($packagexml, $warnings, $errors, $this->_options['packagedirectory']);
715 if (count($errors)) {
716 $ret = '';
717 $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
718 foreach($errors as $errmsg) {
719 $ret .= $errmsg . $nl;
720 }
721 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE, $nl, $ret);
722 }
723 if (count($warnings)) {
724 $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
725 foreach($warnings as $errmsg) {
726 echo $errmsg . $nl;
727 }
728 }
729 if (!strpos($packagexml, '<!DOCTYPE')) {
730 // hack to fix pear
731 $packagexml = str_replace('<package version="1.0">',
732 '<!DOCTYPE package SYSTEM "' . $this->_options['doctype'] .
733 "\">\n<package version=\"1.0\">",
734 $packagexml);
735 }
736 if (isset($debuginterface)) {
737 if ($debuginterface) {
738 echo '<pre>' . htmlentities($packagexml) . '</pre>';
739 } else {
740 echo $packagexml;
741 }
742 return true;
743 }
744 $outputdir = ($this->_options['outputdirectory'] ?
745 $this->_options['outputdirectory'] : $this->_options['packagedirectory']);
746 if ((file_exists($outputdir . $this->_options['packagefile']) &&
747 is_writable($outputdir . $this->_options['packagefile']))
748 ||
749 @touch($outputdir . $this->_options['packagefile'])) {
750 if ($fp = @fopen($outputdir . $this->_options['packagefile'] . '.tmp', "w")) {
751 $written = @fwrite($fp, $packagexml);
752 @fclose($fp);
753 if ($written === false) {
754 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE);
755 }
756 if (!@copy($outputdir . $this->_options['packagefile'] . '.tmp',
757 $outputdir . $this->_options['packagefile'])) {
758 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE);
759 } else {
760 @unlink($outputdir . $this->_options['packagefile'] . '.tmp');
761 return true;
762 }
763 } else {
764 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE,
765 $outputdir . $this->_options['packagefile'] . '.tmp');
766 }
767 } else {
768 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE, $outputdir);
769 }
770 }
771
772 /**
773 * ALWAYS use this to test output before overwriting your package.xml!!
774 * @uses writePackageFile() calls with the debug parameter set based on
775 * whether it is called from the command-line or web interface
776 */
777 function debugPackageFile()
778 {
779 $webinterface = isset($_SERVER['PATH_TRANSLATED']);
780 return $this->writePackageFile($webinterface);
781 }
782
783 /**
784 * Utility function to shorten error generation code
785 *
786 * {@source }
787 * @return PEAR_Error
788 * @static
789 */
790 function raiseError($code, $i1 = '', $i2 = '')
791 {
792 return PEAR::raiseError('PEAR_PackageFileManager Error: ' .
793 sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code],
794 $i1, $i2));
795 }
796
797 /**
798 * Uses {@link PEAR_Common::analyzeSourceCode()} and {@link PEAR_Common::buildProvidesArray()}
799 * to create the <provides></provides> section of the package.xml
800 * @param PEAR_Common
801 * @param string path to source file
802 * @access private
803 */
804 function _addProvides(&$pear, $file)
805 {
806 if (!($a = $pear->analyzeSourceCode($file))) {
807 return;
808 } else {
809 $pear->buildProvidesArray($a);
810 }
811 }
812
813 /**
814 * @uses getDirTag() generate the xml from the array
815 * @return string
816 * @access private
817 */
818 function _getFileList()
819 {
820 $generatorclass = 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator'];
821 $generator = new $generatorclass($this, $this->_options);
822 return $this->_getDirTag($generator->getFileList());
823 }
824
825 /**
826 * Recursively generate the <filelist> section's <dir> and <file> tags
827 * @param array|PEAR_Errorthe sorted directory structure, or an error
828 * from filelist generation
829 * @param false|stringwhether the parent directory has a role this should
830 * inherit
831 * @param integer indentation level
832 * @return array|PEAR_Error
833 * @access private
834 */
835 function _getDirTag($struc, $role=false, $_curdir = '')
836 {
837 if (PEAR::isError($struc)) {
838 return $struc;
839 }
840 extract($this->_options);
841 $ret = array();
842 foreach($struc as $dir => $files) {
843 if ($dir === '/') {
844 // global directory role? overrides all exceptions except file exceptions
845 if (isset($dir_roles['/'])) {
846 $role = $dir_roles['/'];
847 }
848 return $this->_getDirTag($struc[$dir], $role, '');
849 } else {
850 if (!isset($files['file'])) {
851 $myrole = '';
852 if (isset($dir_roles[$_curdir . $dir])) {
853 $myrole = $dir_roles[$_curdir . $dir];
854 } elseif ($role) {
855 $myrole = $role;
856 }
857 $ret = array_merge($ret, $this->_getDirTag($files, $myrole, $_curdir . $dir . '/'));
858 } else {
859 $myrole = '';
860 if (!$role)
861 {
862 $myrole = false;
863 if (isset($exceptions[$files['path']])) {
864 $myrole = $exceptions[$files['path']];
865 } elseif (isset($roles[$files['ext']])) {
866 $myrole = $roles[$files['ext']];
867 } else {
868 $myrole = $roles['*'];
869 }
870 } else {
871 $myrole = $role;
872 if (isset($exceptions[$files['path']])) {
873 $myrole = $exceptions[$files['path']];
874 }
875 }
876 if (isset($installexceptions[$files['path']])) {
877 $bi = $installexceptions[$files['path']];
878 } else {
879 $bi = $baseinstalldir;
880 }
881 $ret[$files['path']] =
882 array('role' => $myrole,
883 'baseinstalldir' => $bi,
884 );
885 $md5sum = @md5_file($this->_options['packagedirectory'] . $files['path']);
886 if (!empty($md5sum)) {
887 $ret[$files['path']]['md5sum'] = $md5sum;
888 }
889 if (isset($platformexceptions[$files['path']])) {
890 $ret[$files['path']]['platform'] = $platformexceptions[$files['path']];
891 }
892 if (isset($installas[$files['path']])) {
893 $ret[$files['path']]['install-as'] = $installas[$files['path']];
894 }
895 if (isset($replacements[$files['path']])) {
896 $ret[$files['path']]['replacements'] = $replacements[$files['path']];
897 }
898 if ($myrole == 'php') {
899 $this->_addProvides($this->_pear, $files['fullpath']);
900 }
901 }
902 }
903 }
904 return $ret;
905 }
906
907 /**
908 * Retrieve the 'deps' option passed to the constructor
909 * @access private
910 * @return array
911 */
912 function _getDependencies()
913 {
914 if ($this->_packageXml['release_deps']) {
915 return $this->_packageXml['release_deps'];
916 } else {
917 return array();
918 }
919 }
920
921 /**
922 * Creates a changelog entry with the current release
923 * notes and dates, or overwrites a previous creation
924 * @access private
925 */
926 function _updateChangeLog()
927 {
928 $curlog = false;
929 if (!isset($this->_packageXml['changelog'])) {
930 $changelog = array();
931 if (isset($this->_oldPackageXml['release_notes'])) {
932 $changelog['release_notes'] = $this->_oldPackageXml['release_notes'];
933 }
934 if (isset($this->_oldPackageXml['version'])) {
935 $changelog['version'] = $this->_oldPackageXml['version'];
936 }
937 if (isset($this->_oldPackageXml['release_date'])) {
938 $changelog['release_date'] = $this->_oldPackageXml['release_date'];
939 }
940 if (isset($this->_oldPackageXml['release_license'])) {
941 $changelog['release_license'] = $this->_oldPackageXml['release_license'];
942 }
943 if (isset($this->_oldPackageXml['release_state'])) {
944 $changelog['release_state'] = $this->_oldPackageXml['release_state'];
945 }
946 $this->_packageXml['changelog'] = array($changelog);
947 }
948 foreach($this->_packageXml['changelog'] as $index => $changelog) {
949 if (isset($changelog['version']) && strnatcasecmp($changelog['version'], $this->_options['version']) == 0) {
950 $curlog = $index;
951 }
952 if (isset($this->_packageXml['changelog'][$index]['release_notes'])) {
953 $this->_packageXml['changelog'][$index]['release_notes'] = trim($changelog['release_notes']);
954 }
955 // the parsing of the release notes adds a \n for some reason
956 }
957 $notes = ($this->_options['changelognotes'] ?
958 $this->_options['changelognotes'] : $this->_options['notes']);
959 $changelog = array('version' => $this->_options['version'],
960 'release_date' => date('Y-m-d'),
961 'release_license' => $this->_options['license'],
962 'release_state' => $this->_options['state'],
963 'release_notes' => $notes,
964 );
965 if ($curlog !== false) {
966 $this->_packageXml['changelog'][$curlog] = $changelog;
967 } else {
968 $this->_packageXml['changelog'][] = $changelog;
969 }
970 }
971
972 /**
973 * @return true|PEAR_Error
974 * @uses _generateNewPackageXML() if no package.xml is found, it
975 * calls this to create a new one
976 * @param string full path to package file
977 * @param string name of package file
978 * @throws PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST
979 * @access private
980 */
981 function _getExistingPackageXML($path, $packagefile = 'package.xml')
982 {
983 if (@is_dir($path)) {
984 $contents = @file_get_contents($path . $packagefile);
985 if (!$contents) {
986 return $this->_generateNewPackageXML();
987 } else {
988 $PEAR_Common = $this->_options['pearcommonclass'];
989 $common = new $PEAR_Common;
990 $this->_oldPackageXml =
991 $this->_packageXml = $common->infoFromString($contents);
992 if (PEAR::isError($this->_packageXml)) {
993 return $this->_packageXml;
994 }
995 if ($this->_options['deps'] !== false) {
996 $this->_packageXml['release_deps'] = $this->_options['deps'];
997 } else {
998 $this->_options['deps'] = $this->_packageXml['release_deps'];
999 }
1000 if ($this->_options['maintainers'] !== false) {
1001 $this->_packageXml['maintainers'] = $this->_options['maintainers'];
1002 } else {
1003 $this->_options['maintainers'] = $this->_packageXml['maintainers'];
1004 }
1005 unset($this->_packageXml['filelist']);
1006 unset($this->_packageXml['provides']);
1007 }
1008 return true;
1009 } else {
1010 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST,
1011 $path);
1012 }
1013 }
1014
1015 /**
1016 * Create the structure for a new package.xml
1017 *
1018 * @uses $_packageXml emulates reading in a package.xml
1019 * by using the package, summary and description
1020 * options
1021 * @return true|PEAR_Error
1022 * @access private
1023 */
1024 function _generateNewPackageXML()
1025 {
1026 $this->_oldPackageXml = false;
1027 if (!isset($this->_options['package'])) {
1028 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPACKAGE);
1029 }
1030 if (!isset($this->_options['summary'])) {
1031 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSUMMARY);
1032 }
1033 if (!isset($this->_options['description'])) {
1034 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NODESC);
1035 }
1036 $this->_packageXml = array();
1037 $this->_packageXml['package'] = $this->_options['package'];
1038 $this->_packageXml['summary'] = $this->_options['summary'];
1039 $this->_packageXml['description'] = $this->_options['description'];
1040 $this->_packageXml['changelog'] = array();
1041 if ($this->_options['deps'] !== false) {
1042 $this->_packageXml['release_deps'] = $this->_options['deps'];
1043 } else {
1044 $this->_packageXml['release_deps'] = $this->_options['deps'] = array();
1045 }
1046 if ($this->_options['maintainers'] !== false) {
1047 $this->_packageXml['maintainers'] = $this->_options['maintainers'];
1048 } else {
1049 $this->_packageXml['maintainers'] = $this->_options['maintainers'] = array();
1050 }
1051 return true;
1052 }
1053 }
1054 if (!function_exists('file_get_contents')) {
1055 /**
1056 * @ignore
1057 */
1058 function file_get_contents($path, $use_include_path = null, $context = null)
1059 {
1060 $a = @file($path, $use_include_path, $context);
1061 if (is_array($a)) {
1062 return implode('', $a);
1063 } else {
1064 return false;
1065 }
1066 }
1067 }
1068 ?>

Documentation generated on Fri, 25 Jul 2003 16:26:20 -0400 by phpDocumentor 1.2.2