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 /**#@-*/
46 /**
47 * Error messages
48 * @global array $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS']
49 */
50 $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'] =
51 array(
52 'en' =>
53 array(
54 PEAR_PACKAGEFILEMANAGER_NOSTATE =>
55 'Release State (option \'state\' must by specified in PEAR_PackageFileManager constructor (alpha|beta|stable)',
56 PEAR_PACKAGEFILEMANAGER_NOVERSION =>
57 'Release Version (option \'version\') must be specified in PEAR_PackageFileManager constructor',
58 PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
59 'Package source base directory (option \'packagedirectory\') must be specified in PEAR_PackageFileManager constructor',
60 PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
61 'Package install base directory (option \'baseinstalldir\') must be specified in PEAR_PackageFileManager constructor',
62 PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND =>
63 'Base class "%s" can\'t be located',
64 PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE =>
65 'Base class "%s" can\'t be located in default or user-specified directories',
66 PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE =>
67 'Failed to write package.xml file to destination directory',
68 PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE =>
69 'Destination directory "%s" is unwritable',
70 PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE =>
71 'Failed to copy package.xml.tmp file to package.xml',
72 PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE =>
73 'Failed to open temporary file "%s" for writing',
74 PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST =>
75 'package.xml file path "%s" doesn\'t exist or isn\'t a directory',
76 PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES =>
77 'Directory "%s" is not a CVS directory (it must have the CVS/Entries file)',
78 PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST =>
79 'Package source base directory "%s" doesn\'t exist or isn\'t a directory',
80 PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS =>
81 'Run $managerclass->setOptions() before any other methods',
82 ),
83 // other language translations go here
84 );
85 /**
86 * PEAR :: PackageGenerate updates the <filelist></filelist> section
87 * of a PEAR package.xml file to reflect the current files in
88 * preparation for a release.
89 *
90 * The PEAR_PackageGenerate class uses a plugin system to generate the
91 * list of files in a package. This allows both standard recursive
92 * directory parsing (plugin type file) and more intelligent options
93 * such as the CVS browser {@link PEAR_PackageFileManager_Cvs}, which
94 * grabs all files in a local CVS checkout to create the list, ignoring
95 * any other local files.
96 *
97 * Other options include specifying roles for file extensions (all .php
98 * files are role="php", for example), roles for directories (all directories
99 * named "tests" are given role="tests" by default), and exceptions.
100 * Exceptions are specific pathnames with * and ? wildcards that match
101 * a default role, but should have another. For example, perhaps
102 * a debug.tpl template would normally be data, but should be included
103 * in the docs role. Along these lines, to exclude files entirely,
104 * use the ignore option.
105 *
106 * Required options for a release include version, baseinstalldir, state,
107 * and packagedirectory (the full path to the local location of the
108 * package to create a package.xml file for)
109 *
110 * Example usage:
111 * <code>
112 * <?php
113 * require_once('PEAR/PackageFileManager.php');
114 * $packagexml = new PEAR_PackageFileManager;
115 * $e = $packagexml->setOptions(
116 * array('baseinstalldir' => 'PhpDocumentor',
117 * 'version' => '1.2.1',
118 * 'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/',
119 * 'state' => 'stable',
120 * 'filelistgenerator' => 'cvs', // generate from cvs, use file for directory
121 * 'notes' => 'We\'ve implemented many new and exciting features',
122 * 'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/
123 * 'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc
124 * 'dir_roles' => array('tutorials' => 'doc'),
125 * 'exceptions' => array('README' => 'doc', // README would be data, now is doc
126 * 'PHPLICENSE.txt' => 'doc'))); // same for the license
127 * if (PEAR::isError($e)) {
128 * echo $e->getMessage();
129 * die();
130 * }
131 * $packagexml->addRole('pkg', 'doc'); // add a new role mapping
132 * $e = $packagexml->writePackageFile();
133 * if (PEAR::isError($e)) {
134 * echo $e->getMessage();
135 * die();
136 * }
137 * ?>
138 * </code>
139 * @package PEAR_PackageFileManager
140 */
141 class PEAR_PackageFileManager
142 {
143 /**
144 * Format: array(array(regexp-ready string to search for whole path,
145 * regexp-ready string to search for basename of ignore strings),...)
146 * @var false|array
147 * @access private
148 */
149 var $_ignore = false;
150
151 /**
152 * Contents of the package.xml file
153 * @var string
154 * @access private
155 */
156 var $_packageXml = false;
157
158 /**
159 * @access private
160 * @var string
161 */
162 var $_options = array(
163 'packagefile' => 'package.xml',
164 'filelistgenerator' => 'file',
165 'license' => 'PHP License',
166 'roles' =>
167 array(
168 'php' => 'php',
169 'html' => 'doc',
170 '*' => 'data',
171 ),
172 'dir_roles' =>
173 array(
174 'docs' => 'doc',
175 'examples' => 'doc',
176 'tests' => 'tests',
177 ),
178 'exceptions' => array(),
179 'installexceptions' => array(),
180 'ignore' => array(),
181 'deps' => array(),
182 'notes' => '',
183 'changelognotes' => false,
184 'outputdirectory' => false,
185 'pathtopackagefile' => false,
186 'lang' => 'en',
187 );
188
189 function PEAR_PackageFileManager()
190 {
191 }
192
193 /**
194 *
195 * @param array
196 */
197 function setOptions($options = array())
198 {
199 if (!isset($options['state'])) {
200 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSTATE);
201 }
202 if (!isset($options['version'])) {
203 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOVERSION);
204 }
205 if (!isset($options['packagedirectory'])) {
206 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPKGDIR);
207 } else {
208 $options['packagedirectory'] = str_replace(DIRECTORY_SEPARATOR,
209 '/',
210 realpath($options['packagedirectory']));
211 if ($options['packagedirectory']{strlen($options['packagedirectory']) - 1} != '/') {
212 $options['packagedirectory'] .= '/';
213 }
214 }
215 if (!isset($options['baseinstalldir'])) {
216 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOBASEDIR);
217 }
218 $this->_options = array_merge($this->_options, $options);
219
220 $path = ($this->_options['pathtopackagefile'] ?
221 $this->_options['pathtopackagefile'] : $this->_options['packagedirectory']);
222 $this->_options['filelistgenerator'] = ucfirst(strtolower($this->_options['filelistgenerator']));
223 if (PEAR::isError($res = $this->_getExistingPackageXML($path, $this->_options['packagefile']))) {
224 return $res;
225 }
226 // attempt to load the interface from the standard PEAR location
227 @include_once('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php');
228 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
229 if (isset($this->_options['usergeneratordir'])) {
230 // attempt to load from a user-specified directory
231 $this->_options['usergeneratordir'] = str_replace(DIRECTORY_SEPARATOR,
232 '/',
233 realpath($this->_options['usergeneratordir']));
234 if ($this->_options['usergeneratordir']{strlen($this->_options['usergeneratordir']) - 1} != '/') {
235 $this->_options['usergeneratordir'] .= '/';
236 }
237 @include_once($this->_options['usergeneratordir'] . $this->_options['filelistgenerator'] . '.php');
238 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
239 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE,
240 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
241 }
242 } else {
243 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND,
244 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
245 }
246 }
247 }
248
249 /**
250 * Add an extension/role mapping to the role mapping option
251 * @param string file extension
252 * @param string role
253 */
254 function addRole($extension, $role)
255 {
256 $this->_options['roles'][$extension] = $role;
257 }
258
259 /**
260 * Add a dependency on another package, or an extension/php
261 *
262 * This will overwrite an existing dependency if it is found. In
263 * other words, if a dependency on PHP 4.1.0 exists, and
264 * addDependency('php', '4.3.0', 'ge', 'php') is called, the existing
265 * dependency on PHP 4.1.0 will be overwritten with the new one on PHP 4.3.0
266 * @param string Dependency element name
267 * @param string Dependency version
268 * @param string A specific operator for the version, this can be one of:
269 * 'has', 'not', 'lt', 'le', 'eq', 'ne', 'ge', or 'gt'
270 * @param string Dependency type. This can be one of:
271 * 'pkg', 'ext', 'php', 'prog', 'os', 'sapi', or 'zend'
272 */
273 function addDependency($name, $version = false, $operator = 'ge', $type = 'pkg')
274 {
275 if (!$this->_packageXml) {
276 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
277 }
278 $found = false;
279 foreach($this->_options['deps'] as $index => $dep) {
280 if ($dep['name'] == $name && $deps['type'] == $type) {
281 $found = $index;
282 break;
283 }
284 }
285 $dep =
286 array(
287 'name' => $name,
288 'type' => $type);
289 if ($version) {
290 $dep['version'] = $version;
291 if ($operator) {
292 $dep['rel'] = $operator;
293 }
294 }
295
296 if ($found !== false) {
297 $this->_options['deps'][$found] = $dep; // overwrite existing dependency
298 } else {
299 $this->_options['deps'][] = $dep; // add new dependency
300 }
301 }
302
303 /**
304 * Writes the package.xml file out with the newly created <release></release> tag
305 * @param boolean null if no debugging, true if web interface, false if command-line
306 */
307 function writePackageFile($debuginterface = null)
308 {
309 if (!$this->_packageXml) {
310 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
311 }
312 extract($this->_options);
313 $date = date('Y-m-d');
314 $this->_packageXml['release_date'] = $date;
315 $this->_packageXml['release_version'] = $version;
316 $this->_packageXml['release_state'] = $state;
317 $this->_packageXml['release_notes'] = $notes;
318 $this->_packageXml['filelist'] = $this->_getFileList();
319 if (PEAR::isError($this->_packageXml['filelist'])) {
320 return $this->_packageXml['filelist'];
321 }
322 $this->_packageXml['release_deps'] = $this->_getDependencies();
323 $this->_updateChangeLog();
324 $common = new PEAR_Common;
325 $packagexml = $common->xmlFromInfo($this->_packageXml);
326 if (isset($debuginterface)) {
327 if ($debuginterface) {
328 echo '<pre>' . htmlentities($packagexml) . '</pre>';
329 } else {
330 echo $packagexml;
331 }
332 return true;
333 }
334 $outputdir = ($this->_options['outputdirectory'] ?
335 $this->_options['outputdirectory'] : $this->_options['packagedirectory']);
336 if (is_writable($this->_options['packagedirectory'] . $this->_options['packagefile'])) {
337 if ($fp = @fopen($outputdir . $this->_options['packagefile'] . '.tmp', "w")) {
338 $written = @fwrite($fp, $packagexml);
339 @fclose($fp);
340 if ($written === false) {
341 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE);
342 }
343 if (!@copy($outputdir . $this->_options['packagefile'] . '.tmp',
344 $outputdir . $this->_options['packagefile'])) {
345 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE);
346 } else {
347 @unlink($outputdir . $this->_options['packagefile'] . '.tmp');
348 return true;
349 }
350 } else {
351 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE,
352 $outputdir . $this->_options['packagefile'] . '.tmp');
353 }
354 } else {
355 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE, $outputdir);
356 }
357 }
358
359 /**
360 * ALWAYS use this to test output before overwriting your package.xml!!
361 * @uses writePackageFile() calls with the debug parameter set based on
362 * whether it is called from the command-line or web interface
363 */
364 function debugPackageFile()
365 {
366 $webinterface = isset($_SERVER['PATH_TRANSLATED']);
367 return $this->writePackageFile($webinterface);
368 }
369
370 /**
371 * Utility function to shorten error generation code
372 * @static
373 */
374 function raiseError($code, $i1 = '', $i2 = '')
375 {
376 return PEAR::raiseError('PEAR_PackageFileManager Error: ' .
377 sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code],
378 $i1, $i2));
379 }
380
381 /**
382 * @uses getDirTag() generate the xml from the array
383 * @return string
384 * @access private
385 */
386 function _getFileList()
387 {
388 $generatorclass = 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator'];
389 $generator = new $generatorclass($this, $this->_options);
390 return $this->_getDirTag($generator->getFileList());
391 }
392
393 /**
394 * Recursively generate the <filelist> section's <dir> and <file> tags
395 * @param array the sorted directory structure
396 * @param false|stringwhether the parent directory has a role this should
397 * inherit
398 * @param integer indentation level
399 * @access private
400 */
401 function _getDirTag($struc, $role=false)
402 {
403 if (PEAR::isError($struc)) {
404 return $struc;
405 }
406 extract($this->_options);
407 $ret = array();
408 foreach($struc as $dir => $files) {
409 if ($dir === '/') {
410 return $this->_getDirTag($struc[$dir], $role);
411 } else {
412 if (!isset($files['file'])) {
413 $myrole = '';
414 if ($role) {
415 $myrole = $role;
416 } elseif (isset($dir_roles[$dir])) {
417 $myrole = $dir_roles[$dir];
418 }
419 $ret = array_merge($ret, $this->_getDirTag($files, $myrole));
420 } else {
421 $myrole = '';
422 if (!$role)
423 {
424 $myrole = false;
425 if (isset($exceptions[$files['file']])) {
426 $myrole = $exceptions[$files['file']];
427 } elseif (isset($roles[$files['ext']])) {
428 $myrole = $roles[$files['ext']];
429 } else {
430 $myrole = $roles['*'];
431 }
432 } else {
433 $myrole = $role;
434 }
435 if (isset($installexceptions[$files['file']])) {
436 $bi = $installexceptions[$files['file']];
437 } else {
438 $bi = $baseinstalldir;
439 }
440 $ret[$files['path']] = array('role' => $myrole, 'baseinstalldir' => $bi);
441 }
442 }
443 }
444 return $ret;
445 }
446
447 /**
448 * Retrieve the 'deps' option passed to the constructor
449 * @access private
450 * @return string
451 */
452 function _getDependencies()
453 {
454 if (isset($this->_options['deps'])) {
455 return $this->_options['deps'];
456 } else {
457 return array();
458 }
459 }
460
461 /**
462 * Creates a changelog entry with the current release
463 * notes and dates, or overwrites a previous creation
464 * @access private
465 */
466 function _updateChangeLog()
467 {
468 $curlog = false;
469 foreach($this->_packageXml['changelog'] as $index => $changelog) {
470 if ($changelog['version'] == $this->_options['version']) {
471 $curlog = $index;
472 }
473 }
474 $notes = ($this->_options['changelognotes'] ?
475 $this->_options['changelognotes'] : $this->_options['notes']);
476 $changelog = array('version' => $this->_options['version'],
477 'release_date' => date('Y-m-d'),
478 'release_license' => $this->_options['license'],
479 'release_state' => $this->_options['state'],
480 'release_notes' => $notes,
481 );
482 if ($curlog !== false) {
483 $this->_packageXml['changelog'][$curlog] = $changelog;
484 } else {
485 $this->_packageXml['changelog'][] = $changelog;
486 }
487 }
488
489 /**
490 * @access private
491 */
492 function _getExistingPackageXML($path, $packagefile = 'package.xml')
493 {
494 if (@is_dir($path)) {
495 $contents = @file_get_contents($path . $packagefile);
496 if (!$contents) {
497 return false;
498 } else {
499 $common = new PEAR_Common;
500 $this->_packageXml = $common->infoFromString($contents);
501 unset($this->_packageXml['filelist']);
502 }
503 return true;
504 } else {
505 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST,
506 $path);
507 }
508 }
509 }
510 if (!function_exists('file_get_contents')) {
511 function file_get_contents($path, $use_include_path = null, $context = null)
512 {
513 $a = @file($path, $use_include_path, $context);
514 if (is_array($a)) {
515 return implode('', $a);
516 } else {
517 return false;
518 }
519 }
520 }
521 ?>

Documentation generated on Mon, 21 Jul 2003 17:14:19 -0400 by phpDocumentor 1.2.1