* @copyright 2005-2009 The PEAR Group * @license New BSD, Revised * @version CVS: $Id: PackageFileManager2.php 309758 2011-03-28 01:47:53Z dufuz $ * @link http://pear.php.net/package/PEAR_PackageFileManager2 * @since File available since Release 1.0.0alpha1 */ /** * PEAR Packagefile parser */ require_once 'PEAR/PackageFile.php'; /** * PEAR Packagefile version 2.0 */ require_once 'PEAR/PackageFile/v2/rw.php'; /**#@+ * Error Codes */ define('PEAR_PACKAGEFILEMANAGER2_NOPKGDIR', 3); define('PEAR_PACKAGEFILEMANAGER2_NOBASEDIR', 4); define('PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND', 5); define('PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND_ANYWHERE', 6); define('PEAR_PACKAGEFILEMANAGER2_CANTWRITE_PKGFILE', 7); define('PEAR_PACKAGEFILEMANAGER2_DEST_UNWRITABLE', 8); define('PEAR_PACKAGEFILEMANAGER2_CANTCOPY_PKGFILE', 9); define('PEAR_PACKAGEFILEMANAGER2_CANTOPEN_TMPPKGFILE', 10); define('PEAR_PACKAGEFILEMANAGER2_PATH_DOESNT_EXIST', 11); define('PEAR_PACKAGEFILEMANAGER2_DIR_DOESNT_EXIST', 13); define('PEAR_PACKAGEFILEMANAGER2_RUN_SETOPTIONS', 14); define('PEAR_PACKAGEFILEMANAGER2_NO_FILES', 20); define('PEAR_PACKAGEFILEMANAGER2_IGNORED_EVERYTHING', 21); define('PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE', 22); define('PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE', 23); define('PEAR_PACKAGEFILEMANAGER2_INVALID_ROLE', 24); define('PEAR_PACKAGEFILEMANAGER2_CVS_PACKAGED', 26); define('PEAR_PACKAGEFILEMANAGER2_NO_PHPCOMPATINFO', 27); define('PEAR_PACKAGEFILEMANAGER2_INVALID_POSTINSTALLSCRIPT', 28); define('PEAR_PACKAGEFILEMANAGER2_PKGDIR_NOTREAL', 29); define('PEAR_PACKAGEFILEMANAGER2_OUTPUTDIR_NOTREAL', 30); define('PEAR_PACKAGEFILEMANAGER2_PATHTOPKGDIR_NOTREAL', 31); /**#@-*/ /** * Error messages * @global array $GLOBALS['_PEAR_PACKAGEFILEMANAGER2_ERRORS'] * @access private */ $GLOBALS['_PEAR_PACKAGEFILEMANAGER2_ERRORS'] = array( 'en' => array( PEAR_PACKAGEFILEMANAGER2_NOPKGDIR => 'Package source base directory (option \'packagedirectory\') must be ' . 'specified in PEAR_PackageFileManager2 setOptions', PEAR_PACKAGEFILEMANAGER2_PKGDIR_NOTREAL => 'Package source base directory (option \'packagedirectory\') must be ' . 'an existing directory (was "%s")', PEAR_PACKAGEFILEMANAGER2_PATHTOPKGDIR_NOTREAL => 'Path to a Package file to read in (option \'pathtopackagefile\') must be ' . 'an existing directory (was "%s")', PEAR_PACKAGEFILEMANAGER2_OUTPUTDIR_NOTREAL => 'output directory (option \'outputdirectory\') must be ' . 'an existing directory (was "%s")', PEAR_PACKAGEFILEMANAGER2_NOBASEDIR => 'Package install base directory (option \'baseinstalldir\') must be ' . 'specified in PEAR_PackageFileManager2 setOptions', PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND => 'Base class "%s" can\'t be located', PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND_ANYWHERE => 'Base class "%s" can\'t be located in default or user-specified directories', PEAR_PACKAGEFILEMANAGER2_CANTWRITE_PKGFILE => 'Failed to write package.xml file to destination directory', PEAR_PACKAGEFILEMANAGER2_DEST_UNWRITABLE => 'Destination directory "%s" is unwritable', PEAR_PACKAGEFILEMANAGER2_CANTCOPY_PKGFILE => 'Failed to copy package.xml.tmp file to package.xml', PEAR_PACKAGEFILEMANAGER2_CANTOPEN_TMPPKGFILE => 'Failed to open temporary file "%s" for writing', PEAR_PACKAGEFILEMANAGER2_PATH_DOESNT_EXIST => 'package.xml file path "%s" doesn\'t exist or isn\'t a directory', PEAR_PACKAGEFILEMANAGER2_DIR_DOESNT_EXIST => 'Package source base directory "%s" doesn\'t exist or isn\'t a directory', PEAR_PACKAGEFILEMANAGER2_RUN_SETOPTIONS => 'Run $managerclass->setOptions() before any other methods', PEAR_PACKAGEFILEMANAGER2_NO_FILES => 'No files found, check the path "%s"', PEAR_PACKAGEFILEMANAGER2_IGNORED_EVERYTHING => 'No files left, check the path "%s" and ignore option "%s"', PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE => 'Package validation failed:%s%s', PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE => 'Replacement Type must be one of "%s", was passed "%s"', PEAR_PACKAGEFILEMANAGER2_INVALID_POSTINSTALLSCRIPT => 'Invalid post-install script task: %s', PEAR_PACKAGEFILEMANAGER2_INVALID_ROLE => 'Invalid file role passed to addRole, must be one of "%s", was passed "%s"', PEAR_PACKAGEFILEMANAGER2_CVS_PACKAGED => 'path "%path%" contains CVS directory', PEAR_PACKAGEFILEMANAGER2_NO_PHPCOMPATINFO => 'pear/PHP_CompatInfo is not installed, cannot detect dependencies', ), // other language translations go here ); /** * PEAR_PackageFileManager2, like PEAR_PackageFileManager, is designed to * create and manipulate package.xml version 2.0. * * The PEAR_PackageFileManager2 class can work directly with PEAR_PackageFileManager * to create parallel package.xml files, version 1.0 and 2.0, that represent the * same project, but take advantage of package.xml 2.0-specific features. * * Like PEAR_PackageFileManager, The PEAR_PackageFileManager2 class uses a plugin system * to generate the list of files in a package. This allows both standard recursive * directory parsing (plugin type file) and more intelligent options * such as the CVS browser {@link PEAR_PackageFileManager_Cvs}, which * grabs all files in a local CVS checkout to create the list, ignoring * any other local files. * * Example usage is similar to PEAR_PackageFileManager: * * setOptions( * array('baseinstalldir' => 'PhpDocumentor', * 'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/', * 'filelistgenerator' => 'cvs', // generate from cvs, use file for directory * 'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/ * 'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc * 'dir_roles' => array('tutorials' => 'doc'), * 'exceptions' => array('README' => 'doc', // README would be data, now is doc * 'PHPLICENSE.txt' => 'doc'))); // same for the license * $pfm->setPackage('MyPackage'); * $pfm->setSummary('this is my package'); * $pfm->setDescription('this is my package description'); * $pfm->setChannel('mychannel.example.com'); * $pfm->setAPIVersion('1.0.0'); * $pfm->setReleaseVersion('1.2.1'); * $pfm->setReleaseStability('stable'); * $pfm->setAPIStability('stable'); * $pfm->setNotes("We've implemented many new and exciting features"); * $pfm->setPackageType('php'); // this is a PEAR-style php script package * $pfm->addRelease(); // set up a release section * $pfm->setOSInstallCondition('windows'); * $pfm->addInstallAs('pear-phpdoc.bat', 'phpdoc.bat'); * $pfm->addIgnoreToRelease('pear-phpdoc'); * $pfm->addRelease(); // add another release section for all other OSes * $pfm->addInstallAs('pear-phpdoc', 'phpdoc'); * $pfm->addIgnoreToRelease('pear-phpdoc.bat'); * $pfm->addRole('pkg', 'doc'); // add a new role mapping * $pfm->setPhpDep('4.2.0'); * $pfm->setPearinstallerDep('1.4.0a12'); * $pfm->addMaintainer('lead', 'cellog', 'Greg Beaver', 'cellog@php.net'); * $pfm->setLicense('PHP License', 'http://www.php.net/license'); * $pfm->generateContents(); // create the tag * // replace @PHP-BIN@ in this file with the path to php executable! pretty neat * $test->addReplacement('pear-phpdoc', 'pear-config', '@PHP-BIN@', 'php_bin'); * $test->addReplacement('pear-phpdoc.bat', 'pear-config', '@PHP-BIN@', 'php_bin'); * $pkg = &$pfm->exportCompatiblePackageFile1(); // get a PEAR_PackageFile object * // note use of {@link debugPackageFile()} - this is VERY important * if (isset($_GET['make']) || (isset($_SERVER['argv']) && @$_SERVER['argv'][1] == 'make')) { * $pkg->writePackageFile(); * $pfm->writePackageFile(); * } else { * $pkg->debugPackageFile(); * $pfm->debugPackageFile(); * } * ?> * * * In addition, a package.xml file can now be generated from * scratch, with the usage of new options package, summary, description, and * the use of the {@link addLead(), addDeveloper(), addContributor(), addHelper()} methods * * @category PEAR * @package PEAR_PackageFileManager2 * @author Greg Beaver * @copyright 2005-2009 The PEAR Group * @license New BSD, Revised * @version Release: 1.0.2 * @link http://pear.php.net/package/PEAR_PackageFileManager2 * @since Class available since Release 1.0.0alpha1 */ class PEAR_PackageFileManager2 extends PEAR_PackageFile_v2_rw { /** * Format: array(array(regexp-ready string to search for whole path, * regexp-ready string to search for basename of ignore strings),...) * @var false|array * @access private * @since 1.0.0a1 */ var $_ignore = false; /** * Contents of the package.xml file * @var PEAR_PackageFile_v2 * @access private * @since 1.0.0a1 */ var $_packageXml = false; /** * List of warnings * @var array * @access private * @since 1.0.0a1 */ var $_warningStack = array(); /** * flag used to determine whether to use PHP_CompatInfo to detect deps * @var boolean * @access private * @since 1.0.0a1 */ var $_detectDependencies = false; /** * The original contents of the old package.xml, if any * @var PEAR_PackageFile_v2|false * @access private * @since 1.0.0a1 */ var $_oldPackageFile = false; /** * Collection of subpackages * * This collection is used to handle coordination between the contents of * related packages whose files reside in the same development directory * @var array * @access private * @since 1.0.0a1 */ var $_subpackages = array(); /** * List of package.xml generation options * @var string * @access private * @since 1.0.0a1 */ var $_options = array( 'packagefile' => 'package.xml', 'filelistgenerator' => 'file', 'license' => 'New BSD License', 'baseinstalldir' => '', 'changelogoldtonew' => true, 'roles' => array( 'h' => 'src', 'c' => 'src', 'cpp' => 'src', 'in' => 'src', 'm4' => 'src', 'w32' => 'src', 'dll' => 'ext', 'php' => 'php', 'html' => 'doc', '*' => 'data', ), 'dir_roles' => array( 'docs' => 'doc', 'examples' => 'doc', 'tests' => 'test', 'scripts' => 'script', ), 'exceptions' => array(), 'installexceptions' => array(), 'ignore' => array(), 'include' => false, 'notes' => '', 'changelognotes' => false, 'outputdirectory' => false, 'pathtopackagefile' => false, 'lang' => 'en', 'configure_options' => array(), 'replacements' => array(), 'globalreplacements' => array(), 'globalreplaceexceptions' => array(), 'simpleoutput' => false, 'addhiddenfiles' => false, 'cleardependencies' => false, 'clearcontents' => true, 'clearchangelog' => false, ); /** * Does nothing, use factory * * The constructor is not used in order to be able to * return a PEAR_Error from setOptions * * @see setOptions() * @access public * @since 1.0.0a1 */ function PEAR_PackageFileManager2() { parent::PEAR_PackageFile_v2(); $config = &PEAR_Config::singleton(); $this->setConfig($config); } /** * Add a pattern to include when generating the file list. * * If any include options are specified, all files that do not match the * inclusion patterns will be ignored * * Note that to match partial path entries, you must start with a *, * so to match "data/README" you need to use "*data/README" * * @param string|array $include file pattern to include * @param bool $clear (optional) if true, the include array will be reset * (useful for cloned packagefiles) * * @return void * @access public * @since 1.6.0a2 */ function addInclude($include, $clear = false) { if ($clear) { $this->_options['include'] = array(); } if (is_array($include)) { foreach ($include as $fn) { if (is_string($fn)) { $this->_options['include'][] = $fn; } } return; } $this->_options['include'][] = $include; } /** * Add an tag to a tag * * @param string $path full path to filename to ignore * * @return void * @access public * @see PEAR_PackageFile_v2_rw::addIgnore() * @since 1.6.0a3 */ function addIgnoreToRelease($path) { return parent::addIgnore($path); } /** * Add a pattern or patterns to ignore when generating the file list * * @param string|array $ignore file pattern to ignore * @param bool $clear (optional) if true, the include array will be reset * (useful for cloned packagefiles) * * @return void * @access public * @since 1.6.0a2 */ function addIgnore($ignore, $clear = false) { if ($clear) { $this->_options['ignore'] = array(); } if (is_array($ignore)) { foreach ($ignore as $fn) { if (is_string($fn)) { $this->_options['ignore'][] = $fn; } } return; } $this->_options['ignore'][] = $ignore; } /** * Set package.xml generation options * * The options array is indexed as follows: * * $options = array('option_name' => ); * * * The documentation below simplifies this description through * the use of option_name without quotes * * Configuration options: * - lang: lang controls the language in which error messages are * displayed. There are currently only English error messages, * but any contributed will be added over time.
* Possible values: en (default) * - packagefile: the name of the packagefile, defaults to package.xml * - pathtopackagefile: the path to an existing package file to read in, * if different from the packagedirectory * - packagedirectory: the path to the base directory of the package. For * package PEAR_PackageFileManager, this path is * /path/to/pearcvs/pear/PEAR_PackageFileManager where * /path/to/pearcvs is a local path on your hard drive * - outputdirectory: the path in which to place the generated package.xml * by default, this is ignored, and the package.xml is * created in the packagedirectory * - filelistgenerator: the section plugin which will be used. * In this release, there are two generator plugins, * file and cvs. For details, see the docs for these * plugins * - usergeneratordir: For advanced users. If you write your own filelist * generator plugin, use this option to tell * PEAR_PackageFileManager where to find the file that * contains it. If the plugin is named foo, the class * must be named PEAR_PackageFileManager_Foo * no matter where it is located. By default, the Foo * plugin is located in PEAR/PackageFileManager/Foo.php. * If you pass /path/to/foo in this option, setOptions * will look for PEAR_PackageFileManager_Foo in * /path/to/foo/Foo.php * - changelogoldtonew: True if the ChangeLog should list from oldest entry to * newest. Set to false if you would like new entries first * - simpleoutput: True if the package.xml should be human-readable * - clearchangelog: True if change log should not be generated/updated * - addhiddenfiles: True if you wish to add hidden files/directories that begin with . * like .bashrc. This is only used by the File generator. The CVS * generator will use all files in CVS regardless of format * * package.xml simple options: * - baseinstalldir: The base directory to install this package in. For * package PEAR_PackageFileManager, this is "PEAR", for * package PEAR, this is "/" * - changelognotes: notes for the changelog, this should be more detailed than * the release notes. By default, PEAR_PackageFileManager uses * the notes option for the changelog as well * * WARNING: all complex options that require a file path are case-sensitive * * package.xml complex options: * - ignore: an array of filenames, directory names, or wildcard expressions specifying * files to exclude entirely from the package.xml. Wildcards are operating system * wildcards * and ?. file*foo.php will exclude filefoo.php, fileabrfoo.php and * filewho_is_thisfoo.php. file?foo.php will exclude fileafoo.php and will not * exclude fileaafoo.php. test/ will exclude all directories and subdirectories of * ANY directory named test encountered in directory parsing. *test* will exclude * all files and directories that contain test in their name * - include: an array of filenames, directory names, or wildcard expressions specifying * files to include in the listing. All other files will be ignored. * Wildcards are in the same format as ignore * - roles: this is an array mapping file extension to install role. This * specifies default behavior that can be overridden by the exceptions * option and dir_roles option. use {@link addRole()} to add a new * role to the pre-existing array * - dir_roles: this is an array mapping directory name to install role. All * files in a directory whose name matches the directory will be * given the install role specified. Single files can be excluded * from this using the exceptions option. The directory should be * a relative path from the baseinstalldir, or "/" for the baseinstalldir * - exceptions: specify file role for specific files. This array maps all files * matching the exact name of a file to a role as in "file.ext" => "role" * - globalreplacements: a list of replacements that should be performed on every single file. * The format is the same as replacements * - globalreplaceexceptions: a list of exact filenames that should not have global * replacements performed (useful for images and large files) * note that this is not exported to package.xml 1.0!! * * @param array $options (optional) list of generation options * @param boolean $internal (optional) private function call * * @see PEAR_PackageFileManager_File * @see PEAR_PackageFileManager_CVS * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_NOPKGDIR * @throws PEAR_PACKAGEFILEMANAGER2_PKGDIR_NOTREAL * @throws PEAR_PACKAGEFILEMANAGER2_PATHTOPKGDIR_NOTREAL * @throws PEAR_PACKAGEFILEMANAGER2_OUTPUTDIR_NOTREAL * @throws PEAR_PACKAGEFILEMANAGER2_NOBASEDIR * @throws PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND_ANYWHERE * @throws PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND * @access public * @since 1.0.0a1 */ function setOptions($options = array(), $internal = false) { if (!isset($options['packagedirectory']) || !$options['packagedirectory']) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_NOPKGDIR); } if (!file_exists($options['packagedirectory'])) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_PKGDIR_NOTREAL, $options['packagedirectory']); } $options['packagedirectory'] = str_replace(DIRECTORY_SEPARATOR, '/', realpath($options['packagedirectory'])); if ($options['packagedirectory']{strlen($options['packagedirectory']) - 1} != '/') { $options['packagedirectory'] .= '/'; } if (isset($options['pathtopackagefile']) && $options['pathtopackagefile']) { if (!file_exists($options['pathtopackagefile'])) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_PATHTOPKGDIR_NOTREAL, $options['pathtopackagefile']); } $options['pathtopackagefile'] = str_replace(DIRECTORY_SEPARATOR, '/', realpath($options['pathtopackagefile'])); if ($options['pathtopackagefile']{strlen($options['pathtopackagefile']) - 1} != '/') { $options['pathtopackagefile'] .= '/'; } } if (isset($options['outputdirectory']) && $options['outputdirectory']) { if (!file_exists($options['outputdirectory'])) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_OUTPUTDIR_NOTREAL, $options['outputdirectory']); } $options['outputdirectory'] = str_replace(DIRECTORY_SEPARATOR, '/', realpath($options['outputdirectory'])); if ($options['outputdirectory']{strlen($options['outputdirectory']) - 1} != '/') { $options['outputdirectory'] .= '/'; } } if (!isset($options['baseinstalldir']) || !$options['baseinstalldir']) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_NOBASEDIR); } $this->_options = array_merge($this->_options, $options); if (!isset($this->_options['roles']['*'])) { $this->_options['roles']['*'] = 'data'; } $path = ($this->_options['pathtopackagefile'] ? $this->_options['pathtopackagefile'] : $this->_options['packagedirectory']); $this->_options['filelistgenerator'] = ucfirst(strtolower($this->_options['filelistgenerator'])); if (!$internal) { if (PEAR::isError($res = PEAR_PackageFileManager2::_getExistingPackageXML($path, $this->_options['packagefile'], array('cleardependencies' => true)))) { return $res; } $this->_oldPackageFile = $res; } // file generator resource to load $resource = 'PEAR/PackageFileManager/' . ucfirst(strtolower($this->_options['filelistgenerator'])) . '.php'; // file generator class name $className = substr($resource, 0, -4); $className = str_replace('/', '_', $className); if (class_exists($className)) { return; } // attempt to load the interface from the standard PEAR location if ($this->isIncludeable($resource)) { include_once $resource; } elseif (isset($this->_options['usergeneratordir'])) { // attempt to load from a user-specified directory if (is_dir(realpath($this->_options['usergeneratordir']))) { $this->_options['usergeneratordir'] = str_replace(DIRECTORY_SEPARATOR, '/', realpath($this->_options['usergeneratordir'])); if ($this->_options['usergeneratordir']{strlen($this->_options['usergeneratordir']) - 1} != '/') { $this->_options['usergeneratordir'] .= '/'; } } else { $this->_options['usergeneratordir'] = '////'; } $generator = $this->_options['usergeneratordir'] . ucfirst(strtolower($this->_options['filelistgenerator'])) . '.php'; if (file_exists($generator) && is_readable($generator)) { include_once $generator; } if (!class_exists($className)) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_GENERATOR_NOTFOUND_ANYWHERE, $className); } } } /** * Define a link between a subpackage and the parent package * * In many cases, a subpackage is developed in the same directory * as the parent package, and the files should be excluded from the package.xml * version 2.0. * * @param object &$pm PEAR_PackageFileManager2 object representing the subpackage's package.xml * @param boolean $dependency dependency type to add, use true for a package dependency, * false for a subpackage dependency * @param boolean $required (optional) whether the dependency should be required or optional * * @return void|false * @access public * @since 1.0.0a1 */ function specifySubpackage(&$pm, $dependency = null, $required = false) { if (!$pm->getDate()) { $pm->setDate(date('Y-m-d')); } if (!$pm->validate(PEAR_VALIDATE_NORMAL)) { return false; } $this->_subpackages[] = &$pm; if ($dependency !== null) { $type = $required ? 'required' : 'optional'; if ($pm->getChannel()) { if ($dependency) { $this->addPackageDepWithChannel($type, $pm->getPackage(), $pm->getChannel(), $pm->getVersion(), false, false, false, $pm->getProvidesExtension()); } else { $this->addSubPackageDepWithChannel($type, $pm->getPackage(), $pm->getChannel(), $pm->getVersion(), false, false, false, $pm->getProvidesExtension()); } } else { if ($dependency) { $this->addPackageDepWithUri($type, $pm->getPackage(), $pm->getUri(), $this->getProvidesExtension()); } else { $this->addSubpackageDepWithUri($type, $pm->getPackage(), $pm->getUri(), $this->getProvidesExtension()); } } } } /** * Convert a package xml 1.0 to 2.0 with user and default options * * @param string $packagefile name of package file * @param array $options (optional) list of generation options * * @return PEAR_PackageFileManager2|PEAR_Error * @static * @access public * @since 1.0.0a1 */ function &importFromPackageFile1($packagefile, $options = array()) { $z = &PEAR_Config::singleton(); $pkg = new PEAR_PackageFile($z); $pf = $pkg->fromPackageFile($packagefile, PEAR_VALIDATE_NORMAL); if (PEAR::isError($pf)) { return $pf; } if ($pf->getPackagexmlVersion() == '1.0') { $packagefile = &$pf; } $a = &PEAR_PackageFileManager2::importOptions($packagefile, $options); return $a; } /** * Import options from an existing package.xml * * @param string $packagefile name of package file * @param array $options (optional) list of generation options * * @return PEAR_PackageFileManager2|PEAR_Error * @static * @access public * @since 1.0.0a1 */ function &importOptions($packagefile, $options = array()) { if (is_a($packagefile, 'PEAR_PackageFile_v1')) { $gen = &$packagefile->getDefaultGenerator(); $res = $gen->toV2('PEAR_PackageFileManager2'); if (PEAR::isError($res)) { return $res; } $res->setOld(); if (isset($options['cleardependencies']) && $options['cleardependencies']) { $res->clearDeps(); } if (!isset($options['clearcontents']) || $options['clearcontents']) { $res->clearContents(); } else { $res->_importTasks($options); } $packagefile = $packagefile->getPackageFile(); } if (!isset($res)) { $res = &PEAR_PackageFileManager2::_getExistingPackageXML(dirname($packagefile) . DIRECTORY_SEPARATOR, basename($packagefile), $options); if (PEAR::isError($res)) { return $res; } } if (PEAR::isError($ret = $res->_importOptions($packagefile, $options))) { return $ret; } return $res; } /** * Import options from an existing package.xml 2.0 * * @param string $packagefile name of package file * @param array $options list of generation options * * @return void|PEAR_Error * @access private * @since 1.0.0a1 */ function _importOptions($packagefile, $options) { $this->_options['packagedirectory'] = dirname($packagefile); $this->_options['pathtopackagefile'] = dirname($packagefile); $this->_options['baseinstalldir'] = '/'; return $this->setOptions(array_merge($this->_options, $options), true); } /** * Get the existing options * * @param bool $withTasks (optional) Returns full options (=false) * or without replacements (=true) * * @return array * @access public * @since 1.0.0a1 */ function getOptions($withTasks = false) { if ($withTasks === false) { return $this->_options; } $opt = $this->_options; unset($opt['replacements']); return $opt; } /** * Add an extension/role mapping to the role mapping option * * Roles influence both where a file is installed and how it is installed. * Files with role="data" are in a completely different directory hierarchy * from the program files of role="php" * * In PEAR 1.3b2, these roles are * - php (most common) * - data * - doc * - test * - script (gives the file an executable attribute) * - src * * @param string $extension file extension * @param string $role file role * * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_ROLE * @access public * @since 1.0.0a1 */ function addRole($extension, $role) { include_once 'PEAR/Installer/Role.php'; $roles = PEAR_Installer_Role::getValidRoles($this->getPackageType()); if (!in_array($role, $roles)) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_ROLE, implode($roles, ', '), $role); } $this->_options['roles'][$extension] = $role; } /** * Add a replacement option for all files * * This sets an install-time complex search-and-replace function * allowing the setting of platform-specific variables in all * installed files. * * if $type is php-const, then $to must be the name of a PHP Constant. * If $type is pear-config, then $to must be the name of a PEAR config * variable accessible through a {@link PEAR_Config::get()} method. If * type is package-info, then $to must be the name of a section from * the package.xml file used to install this file. * * @param string $type variable type, either php-const, pear-config or package-info * @param string $from text to replace in the source file * @param string $to variable name to use for replacement * * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE * @access public * @since 1.0.0a1 */ function addGlobalReplacement($type, $from, $to) { include_once 'PEAR/Task/Replace/rw.php'; if (!isset($this->_options['globalreplacements'])) { $this->_options['globalreplacements'] = array(); } $l = null; $task = new PEAR_Task_Replace_rw($this, $this->_config, $l, ''); $task->setInfo($from, $to, $type); if (is_array($res = $task->validate())) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE, implode(', ', $res[3]), $res[1] . ': ' . $res[2]); } $this->_options['globalreplacements'][] = $task; } /** * Add a replacement option for a file, or files matching the glob pattern * * This sets an install-time complex search-and-replace function * allowing the setting of platform-specific variables in an * installed file. * * if $type is php-const, then $to must be the name of a PHP Constant. * If $type is pear-config, then $to must be the name of a PEAR config * variable accessible through a {@link PEAR_Config::get()} method. If * type is package-info, then $to must be the name of a section from * the package.xml file used to install this file. * * @param string $path relative path of file (relative to packagedirectory option) * glob patterns are allowed (eg. {Dir1,Dir2}/*.php) * @param string $type variable type, either php-const, pear-config or package-info * @param string $from text to replace in the source file * @param string $to variable name to use for replacement * * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE * @access public * @since 1.0.0a1 */ function addReplacement($path, $type, $from, $to) { if (!isset($this->_options['replacements'])) { $this->_options['replacements'] = array(); } include_once 'PEAR/Task/Replace/rw.php'; $l = null; $task = new PEAR_Task_Replace_rw($this, $this->_config, $l, ''); $task->setInfo($from, $to, $type); if (is_array($res = $task->validate())) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_REPLACETYPE, implode(', ', $res[3]), $res[1] . ': ' . $res[2]); } $current_dir = getcwd(); chdir($this->_options['packagedirectory']); $glob = defined('GLOB_BRACE') ? glob($path, GLOB_BRACE) : glob($path); chdir($current_dir); if (false !== $glob) { foreach ($glob as $pathItem) { $this->_options['replacements'][$pathItem][] = $task; } } } /** * Convert a file to windows line endings on installation * * @param string $path relative path of file (relative to packagedirectory option) * * @return void * @access public * @since 1.0.0a1 */ function addWindowsEol($path) { if (!isset($this->_options['replacements'])) { $this->_options['replacements'] = array(); } include_once 'PEAR/Task/Windowseol/rw.php'; $l = null; $task = new PEAR_Task_Windowseol_rw($this, $this->_config, $l, ''); // we'll use this because it will still work $this->_options['replacements'][$path][] = $task; } /** * Convert a file to unix line endings on installation * * @param string $path relative path of file (relative to packagedirectory option) * * @return void * @access public * @since 1.0.0a1 */ function addUnixEol($path) { if (!isset($this->_options['replacements'])) { $this->_options['replacements'] = array(); } include_once 'PEAR/Task/Unixeol/rw.php'; $l = null; $task = new PEAR_Task_Unixeol_rw($this, $this->_config, $l, ''); // we'll use this because it will still work $this->_options['replacements'][$path][] = $task; } /** * Get a post-installation task object for manipulation prior to adding it * * @param string $path relative path of file (relative to packagedirectory option) * * @return PEAR_Task_Postinstallscript_rw * @access public * @since 1.0.0a1 */ function &initPostinstallScript($path) { include_once 'PEAR/Task/Postinstallscript/rw.php'; $options = array('name' => $path, 'role' => 'php'); $task = new PEAR_Task_Postinstallscript_rw($this, $this->_config, $l, $options); return $task; } /** * Add post-installation script task to a post-install script. * * The script must have been created with {@link initPostinstallScript()} and * then populated using the API of PEAR_Task_Postinstallscript_rw. * * @param object $task PEAR_Task_Postinstallscript_rw * @param string $path relative path of file (relative to packagedirectory option) * * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_POSTINSTALLSCRIPT * @access public * @since 1.0.0a1 */ function addPostinstallTask($task, $path) { if (!is_a($task, 'PEAR_Task_Postinstallscript')) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_POSTINSTALLSCRIPT, 'Task passed in is not a PEAR_Task_Postinstallscript task'); } // necessary for validation $this->addFile('', $path, array('role' => 'php', 'name' => $path)); $this->setPackagefile($this->_options['packagedirectory'] . DIRECTORY_SEPARATOR . $this->_options['packagefile']); if (is_array($res = $task->validate())) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_POSTINSTALLSCRIPT, $res[1]); } if (!isset($this->_options['replacements'])) { $this->_options['replacements'] = array(); } $this->_options['replacements'][$path][] = $task; } /** * Uses PEAR::PHP_CompatInfo package to detect dependencies (extensions, php version) * * @param array $options (optional) parser options for PHP_CompatInfo * * @return void|PEAR_Error * @throws PEAR_PACKAGEFILEMANAGER2_RUN_SETOPTIONS * @throws PEAR_PACKAGEFILEMANAGER2_NO_PHPCOMPATINFO * @access public * @since 1.0.0a1 */ function detectDependencies($options = array()) { if (!$this->isIncludeable('PHP/CompatInfo.php')) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_NO_PHPCOMPATINFO); } include_once 'PHP/CompatInfo.php'; if (!is_array($options)) { $options = array(); } $this->_detectDependencies = $options; } /** * Returns whether or not a file is in the include path. * * @param string $file path to filename * * @return boolean true if the file is in the include path, false otherwise * @access public * @since 1.0.0a1 */ function isIncludeable($file) { foreach (explode(PATH_SEPARATOR, ini_get('include_path')) as $path) { $p = $path . DIRECTORY_SEPARATOR . $file; if (file_exists($p) && is_readable($p)) { return true; } } return false; } /** * Writes the package.xml file out with the newly created tag * * ALWAYS use {@link debugPackageFile} to verify that output is correct before * overwriting your package.xml * * @param boolean $debuginterface null if no debugging, true if web interface, false if command-line * * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE * @throws PEAR_PACKAGEFILEMANAGER2_CANTWRITE_PKGFILE * @throws PEAR_PACKAGEFILEMANAGER2_CANTCOPY_PKGFILE * @throws PEAR_PACKAGEFILEMANAGER2_CANTOPEN_TMPPKGFILE * @throws PEAR_PACKAGEFILEMANAGER2_DEST_UNWRITABLE * @return true|PEAR_Error * @access public * @since 1.0.0a1 */ function writePackageFile($debuginterface = null) { $warnings = $this->_stack->getErrors(true); $this->setDate(date('Y-m-d')); if (count($warnings)) { $nl = (isset($debuginterface) && $debuginterface ? '
' : "\n"); foreach ($warnings as $errmsg) { echo 'WARNING: ' . $errmsg['message'] . $nl; } } if ($this->_options['simpleoutput']) { $state = PEAR_VALIDATE_NORMAL; } else { $state = PEAR_VALIDATE_PACKAGING; } $this->_getDependencies(); if ($this->_options['clearchangelog']) { $this->clearChangeLog(); } else { $this->_updateChangeLog(); } $outputdir = ($this->_options['outputdirectory'] ? $this->_options['outputdirectory'] : $this->_options['packagedirectory']); $this->setPackagefile($this->_options['packagedirectory'] . $this->_options['packagefile']); if (!$this->validate($state)) { $errors = $this->getValidationWarnings(); $ret = ''; $nl = (isset($debuginterface) && $debuginterface ? '
' : "\n"); $haserror = false; foreach ($errors as $err) { if (!$haserror && $err['level'] == 'error') { $haserror = true; } if (isset($debuginterface) && $debuginterface) { $msg = htmlspecialchars($err['message']); } else { $msg = $err['message']; } $ret .= ucfirst($err['level']) . ': ' . $msg . $nl; } if ($haserror) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE, $nl, $ret); } } $gen = &$this->getDefaultGenerator(); $pfm = $gen->toXml($state); if (isset($debuginterface)) { if ($debuginterface) { echo '
' . htmlentities($pfm) . '
'; } else { echo $pfm; } return true; } $file = $outputdir . $this->_options['packagefile']; if ((file_exists($file) && is_writable($file)) || @touch($file)) { if ($fp = @fopen($file . '.tmp', "w")) { $written = @fwrite($fp, $pfm); @fclose($fp); if ($written === false) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_CANTWRITE_PKGFILE); } if (!@copy($file . '.tmp', $file)) { return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_CANTCOPY_PKGFILE); } @unlink($file . '.tmp'); return true; } return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_CANTOPEN_TMPPKGFILE, $outputdir . $this->_options['packagefile'] . '.tmp'); } return $this->raiseError(PEAR_PACKAGEFILEMANAGER2_DEST_UNWRITABLE, $outputdir); } /** * ALWAYS use this to test output before overwriting your package.xml!! * * This method instructs writePackageFile() to simply print the package.xml * to output, either command-line or web-friendly (this is automatic * based on the existence of $_SERVER['PATH_TRANSLATED'] * * @uses writePackageFile() calls with the debug parameter set based on * whether it is called from the command-line or web interface * @return true|PEAR_Error * @access public * @since 1.0.0a1 */ function debugPackageFile() { $webinterface = (php_sapi_name() != 'cli'); return $this->writePackageFile($webinterface); } /** * Store a warning on the warning stack * * @param integer $code error code * @param array $info additional specific error info * * @return void * @access public * @since 1.0.0a1 */ function pushWarning($code, $info) { $this->_warningStack[] = array('code' => $code, 'message' => $this->_getMessage($code, $info)); } /** * Retrieve the list of warnings * * @return array * @access public * @since 1.0.0a1 */ function getWarnings() { $a = $this->_warningStack; $this->_warningStack = array(); return $a; } /** * Retrieve an error message from a code * * @param integer $code error code * @param array $info additional specific error info * * @return string Error message * @access private * @since 1.0.0a1 */ function _getMessage($code, $info) { $msg = $GLOBALS['_PEAR_PACKAGEFILEMANAGER2_ERRORS'][$this->_options['lang']][$code]; foreach ($info as $name => $value) { $msg = str_replace('%' . $name . '%', $value, $msg); } return $msg; } /** * Utility function to shorten error generation code * * {@source} * * @param integer $code error code * @param string $i1 (optional) additional specific error info #1 * @param string $i2 (optional) additional specific error info #2 * * @return PEAR_Error * @static * @access public * @since 1.0.0a1 */ function raiseError($code, $i1 = '', $i2 = '') { return PEAR::raiseError('PEAR_PackageFileManager2 Error: ' . sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER2_ERRORS'][$this->_options['lang']][$code], $i1, $i2), $code); } /** * Generates file list contents of package.xml * * @uses _getDirTag() generate the xml from the array * @uses _getSimpleDirTag generate the xml from the array for human reading * @return void|PEAR_Error * @access private * @since 1.0.0a1 */ function generateContents() { $this->addIgnore(array('package.xml', 'package2.xml')); $options = $this->_options; if (count($this->_subpackages)) { if (!is_array($options['ignore'])) { $options['ignore'] = array(); } $subp = count($this->_subpackages); for ($i = 0; $i < $subp; $i++) { $save = $this->_subpackages[$i]->getArray(); $filelist = $this->_subpackages[$i]->getFileList(); foreach ($filelist as $file => $atts) { $options['ignore'][] = '*' . $file; // ignore all subpackage files } $this->_subpackages[$i]->fromArray($save); } } $generatorclass = 'PEAR_PackageFileManager_' . ucfirst(strtolower($this->_options['filelistgenerator'])); $generator = new $generatorclass($options); $this->clearContents($this->_options['baseinstalldir']); $this->_struc = $generator->getFileList(); if ($this->_options['simpleoutput']) { return $this->_getSimpleDirTag($this->_struc); } return $this->_getDirTag($this->_struc); } /** * Recursively generate the section's and tags, but with * simple human-readable output * * @param array|PEAR_Error $struc the sorted directory structure, or an error * from filelist generation * @param false|string $role (optional) whether the parent directory has a role this should * inherit * @param string $_curdir (optional) indentation level * * @return array|PEAR_Error * @access private * @since 1.0.0a1 */ function _getSimpleDirTag($struc, $role = false, $_curdir = '') { if (PEAR::isError($struc)) { return $struc; } extract($this->_options); $ret = array(); foreach ($struc as $dir => $files) { if (false && $dir === '/') { // global directory role? overrides all exceptions except file exceptions if (isset($dir_roles['/'])) { $role = $dir_roles['/']; } return $this->_getSimpleDirTag($struc[$dir], $role, ''); } // directory if (!isset($files['file']) || is_array($files['file'])) { // contains only directories if (isset($dir_roles[$_curdir . $dir])) { $myrole = $dir_roles[$_curdir . $dir]; } else { $myrole = $role; } $recurdir = ($_curdir == '') ? $dir . '/' : $_curdir . $dir . '/'; if ($recurdir == '//') { $recurdir = ''; } $this->_getSimpleDirTag($files, $myrole, $recurdir); } else { // contains files $myrole = ''; if (!$role) { $myrole = false; if (isset($exceptions[$files['path']])) { $myrole = $exceptions[$files['path']]; } elseif (isset($roles[$files['ext']])) { $myrole = $roles[$files['ext']]; } else { $myrole = $roles['*']; } } else { $myrole = $role; if (isset($exceptions[$files['path']])) { $myrole = $exceptions[$files['path']]; } } $test = explode('/', $files['path']); foreach ($test as $subpath) { if ($subpath == 'CVS') { $this->pushWarning(PEAR_PACKAGEFILEMANAGER2_CVS_PACKAGED, array('path' => $files['path'])); } } $atts = array('role' => $myrole); if (isset($installexceptions[$files['path']])) { $atts['baseinstalldir'] = $installexceptions[$files['path']]; } $diradd = dirname($files['path']); $this->addFile($diradd == '.' ? '/' : $diradd, $files['file'], $atts); if (isset($globalreplacements) && !in_array($files['path'], $globalreplaceexceptions, true)) { foreach ($globalreplacements as $task) { $this->addTaskToFile($files['path'], $task); } } if (isset($replacements[$files['path']])) { foreach ($replacements[$files['path']] as $task) { $this->addTaskToFile($files['path'], $task); } } } } return; } /** * Recursively generate the section's and tags * * @param array|PEAR_Error $struc the sorted directory structure, or an error * from filelist generation * @param false|string $role (optional) whether the parent directory has a role this should * inherit * @param string $_curdir (optional) indentation level * * @return array|PEAR_Error * @access private * @since 1.0.0a1 */ function _getDirTag($struc, $role = false, $_curdir = '') { if (PEAR::isError($struc)) { return $struc; } extract($this->_options); foreach ($struc as $dir => $files) { if ($dir === '/') { // global directory role? overrides all exceptions except file exceptions if (isset($dir_roles['/'])) { $role = $dir_roles['/']; } return $this->_getDirTag($struc[$dir], $role, ''); } // non-global directory if (!isset($files['file']) || is_array($files['file'])) { // contains only other directories $myrole = ''; if (isset($dir_roles[$_curdir . $dir])) { $myrole = $dir_roles[$_curdir . $dir]; } elseif ($role) { $myrole = $role; } $this->_getDirTag($files, $myrole, $_curdir . $dir . '/'); } else { // contains files $myrole = ''; if (!$role) { $myrole = false; if (isset($exceptions[$files['path']])) { $myrole = $exceptions[$files['path']]; } elseif (isset($roles[$files['ext']])) { $myrole = $roles[$files['ext']]; } else { $myrole = $roles['*']; } } else { $myrole = $role; if (isset($exceptions[$files['path']])) { $myrole = $exceptions[$files['path']]; } } if (isset($installexceptions[$files['path']])) { $bi = $installexceptions[$files['path']]; } else { $bi = $this->_options['baseinstalldir']; } $test = explode('/', $files['path']); foreach ($test as $subpath) { if ($subpath == 'CVS') { $this->pushWarning(PEAR_PACKAGEFILEMANAGER2_CVS_PACKAGED, array('path' => $files['path'])); } } $atts = array('role' => $myrole, 'baseinstalldir' => $bi, ); if (!isset($this->_options['simpleoutput']) || !$this->_options['simpleoutput']) { $md5sum = @md5_file($this->_options['packagedirectory'] . $files['path']); if (!empty($md5sum)) { $atts['md5sum'] = $md5sum; } } $diradd = dirname($files['path']); $this->addFile($diradd == '.' ? '/' : $diradd, $files['file'], $atts); if (isset($globalreplacements) && !in_array($files['path'], $globalreplaceexceptions, true)) { foreach ($globalreplacements as $task) { $this->addTaskToFile($files['path'], $task); } } if (isset($replacements[$files['path']])) { foreach ($replacements[$files['path']] as $task) { $this->addTaskToFile($files['path'], $task); } } } } return; } /** * @param array $files * @param array &$ret * * @return array * @access private * @since 1.0.0a1 */ function _traverseFileArray($files, &$ret) { foreach ($files as $file) { if (!isset($file['fullpath'])) { $this->_traverseFileArray($file, $ret); } else { $ret[] = $file['fullpath']; } } } /** * Retrieve the 'deps' option passed to the constructor * * @access private * @return void|PEAR_Error * @since 1.0.0a1 */ function _getDependencies() { if ($this->_detectDependencies) { $this->_traverseFileArray($this->_struc, $ret); $compatinfo = new PHP_CompatInfo(); $info = $compatinfo->parseArray($ret, $this->_detectDependencies); $max_version = (empty($info['max_version'])) ? false : $info['max_version']; $ret = $this->setPhpDep($info['version'], $max_version); if (is_a($ret, 'PEAR_Error')) { return $ret; } foreach ($info['extensions'] as $ext) { $this->addExtensionDep('required', $ext); } } return; } /** * Creates a changelog entry with the current release * notes and dates, or overwrites a previous creation * * @return void * @access private * @since 1.0.0a1 */ function _updateChangeLog() { $changelog = $this->_oldPackageFile ? $this->_oldPackageFile->getChangelog() : false; $notes = $this->_options['changelognotes']; if (!$changelog) { $this->setChangelogEntry($this->getVersion(), $this->generateChangeLogEntry($notes)); return; } if (!isset($changelog['release'][0])) { $changelog['release'] = array($changelog['release']); } $found = false; foreach ($changelog['release'] as $i => $centry) { $changelog['release'][$i]['notes'] = trim($changelog['release'][$i]['notes']); if ($centry['version']['release'] == $this->getVersion()) { $changelog['release'][$i] = $this->generateChangeLogEntry($notes); $found = true; } } if (!$found) { $changelog['release'][] = $this->generateChangeLogEntry($notes); } usort($changelog['release'], array($this, '_changelogsort')); $this->clearChangeLog(); foreach ($changelog['release'] as $entry) { $this->setChangelogEntry($entry['version']['release'], $entry); } } /** * User-defined comparison function to sort changelog array * * @param array $a first array to compare items * @param array $b second array to compare items * * @return integer sort comparaison result (-1, 0, +1) of two elements $a and $b * @access private * @since 1.0.0a1 */ function _changelogsort($a, $b) { if (isset($a['date']) && isset($b['date'])) { if ($this->_options['changelogoldtonew']) { $c = strtotime($a['date']); $d = strtotime($b['date']); } else { $d = strtotime($a['date']); $c = strtotime($b['date']); } if ($c - $d > 0) { return 1; } elseif ($c - $d < 0) { return -1; } } if (isset($a['version']['release']) && isset($b['version']['release'])) { if ($this->_options['changelogoldtonew']) { $v1 = $a['version']['release']; $v2 = $b['version']['release']; } else { $v2 = $a['version']['release']; $v1 = $b['version']['release']; } return version_compare($v1, $v2); } return 0; } /** * @return void * @since 1.0.0a1 */ function setOld() { $this->_oldPackageFile = new PEAR_PackageFile_v2_rw(); $this->_oldPackageFile->fromArray($this->getArray()); } /** * Import tasks options and files roles (if exceptions) * from an existing package.xml * * @param array $options list of generation options * * @return void|PEAR_Error * @access private * @since 1.6.0b5 */ function _importTasks($options) { $filelist = $this->getFilelist(true); $vroles = array_values($this->_options['roles']); foreach ($filelist as $file => $contents) { $atts = $contents['attribs']; unset($contents['attribs']); // check for tasks replacement, eol if (count($contents)) { foreach ($contents as $tag => $raw) { $taskNs = $this->getTasksNs(); $task = str_replace("$taskNs:", '', $tag); if ($task == 'replace') { if (!isset($raw[0])) { $raw = array($raw); } foreach ($raw as $attrs) { $a = $attrs['attribs']; $this->addReplacement($file, $a['type'], $a['from'], $a['to']); } } elseif ($task == 'windowseol') { $this->addWindowsEol($file); } elseif ($task == 'unixeol') { $this->addUnixEol($file); } elseif ($task == 'postinstallscript') { $script = &$this->initPostinstallScript($file); $raw = $this->_stripNamespace($raw); foreach ($raw['paramgroup'] as $paramgroup) { if (isset($paramgroup['instructions'])) { $instructions = $paramgroup['instructions']; } else { $instructions = false; } if (isset($paramgroup['param'][0])) { $params = $paramgroup['param']; } else { $params = array($paramgroup['param']); } $param = array(); foreach ($params as $p) { $default = isset($p['default']) ? $p['default'] : null; $param[] = $script->getParam($p['name'], $p['prompt'], $p['type'], $default); } $script->addParamGroup($paramgroup['id'], $param, $instructions); } $ret = $this->addPostinstallTask($script, $file); if (PEAR::isError($ret)) { return $ret; } } } } // check for role attribute if (isset($atts['role'])) { $myrole = $atts['role']; if (!in_array($myrole, $vroles)) { $this->_options['exceptions'][$file] = $myrole; } else { $inf = pathinfo($file); if (isset($inf['extension'])) { if (isset($this->_options['roles'][$inf['extension']])) { $role = $this->_options['roles'][$inf['extension']]; } else { $role = $this->_options['roles']['*']; } if ($role != $myrole) { $this->_options['exceptions'][$file] = $myrole; } } else { $this->_options['exceptions'][$file] = $myrole; } } } // check for baseinstalldir attribute if (isset($options['baseinstalldir']) && isset($atts['baseinstalldir']) && $atts['baseinstalldir'] != $options['baseinstalldir'] ) { $this->_options['installexceptions'][$file] = $atts['baseinstalldir']; } } } /** * Strip namespace from postinstallscript task array * * @param array $params tasks options * * @return array * @access private * @since 1.6.0b5 */ function _stripNamespace($params) { $newparams = array(); foreach ($params as $i => $param) { if (is_array($param)) { $param = $this->_stripNamespace($param); } $newparams[str_replace($this->getTasksNs() . ':', '', $i)] = $param; } return $newparams; } /** * @param string $path full path to package file * @param string $packagefile (optional) name of package file * @param array $options (optional) list of generation options * * @throws PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE * @throws PEAR_PACKAGEFILEMANAGER2_PATH_DOESNT_EXIST * @return true|PEAR_Error * @uses _generateNewPackageXML() if no package.xml is found, it * calls this to create a new one * @access private * @static * @since 1.0.0a1 */ function &_getExistingPackageXML($path, $packagefile = 'package.xml', $options = array()) { if (is_string($path) && is_dir($path)) { $contents = false; if (file_exists($path . $packagefile)) { $contents = file_get_contents($path . $packagefile); } if (!$contents) { $a = PEAR_PackageFileManager2::_generateNewPackageXML(); return $a; } include_once 'PEAR/PackageFile/Parser/v2.php'; $pkg = &new PEAR_PackageFile_Parser_v2(); $z = &PEAR_Config::singleton(); $pkg->setConfig($z); $pf = &$pkg->parse($contents, $path . $packagefile, false, 'PEAR_PackageFileManager2'); if (PEAR::isError($pf)) { return $pf; } if (!$pf->validate(PEAR_VALIDATE_DOWNLOADING)) { $errors = ''; foreach ($pf->getValidationWarnings() as $warning) { $errors .= "\n" . ucfirst($warning['level']) . ': ' . $warning['message']; } if (php_sapi_name() != 'cli') { $errors = nl2br(htmlspecialchars($errors)); } $a = $pf->raiseError(PEAR_PACKAGEFILEMANAGER2_INVALID_PACKAGE, $errors); return $a; } $pf->setOld(); if (isset($options['cleardependencies']) && $options['cleardependencies']) { $pf->clearDeps(); } if (!isset($options['clearcontents']) || $options['clearcontents']) { $pf->clearContents(); } else { // merge options is required to use PEAR_PackageFileManager2::addPostinstallTask() $ret = $pf->_importOptions($packagefile, $options); if (PEAR::isError($ret)) { return $ret; } $pf->_importTasks($options); } return $pf; } if (!is_string($path)) { $path = gettype($path); } include_once 'PEAR.php'; $a = PEAR::raiseError('Path does not exist: ' . $path, PEAR_PACKAGEFILEMANAGER2_PATH_DOESNT_EXIST); return $a; } /** * Create the structure for a new package.xml * * @uses $_packageXml emulates reading in a package.xml * by using the package, summary and description * options * @return PEAR_PackageFileManager2 * @access private * @static * @since 1.0.0a1 */ function &_generateNewPackageXML() { $pf = &new PEAR_PackageFileManager2(); $pf->_oldPackageFile = false; return $pf; } }