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

Documentation generated on Fri, 25 Jul 2003 02:16:03 -0400 by phpDocumentor 1.2.2