3 * PEAR_PackageFile_v1, package.xml version 1.0
9 * @author Greg Beaver <cellog@php.net>
10 * @copyright 1997-2009 The Authors
11 * @license http://opensource.org/licenses/bsd-license.php New BSD License
12 * @version CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
13 * @link http://pear.php.net/package/PEAR
14 * @since File available since Release 1.4.0a1
19 require_once 'PEAR/ErrorStack.php';
22 * Error code if parsing is attempted with no xml extension
24 define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
27 * Error code if creating the xml parser resource fails
29 define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
32 * Error code used for all sax xml parsing errors
34 define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
37 * Error code used when there is no name
39 define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
42 * Error code when a package name is not valid
44 define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
47 * Error code used when no summary is parsed
49 define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
52 * Error code for summaries that are more than 1 line
54 define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
57 * Error code used when no description is present
59 define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
62 * Error code used when no license is present
64 define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
67 * Error code used when a <version> version number is not present
69 define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
72 * Error code used when a <version> version number is invalid
74 define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
77 * Error code when release state is missing
79 define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
82 * Error code when release state is invalid
84 define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
87 * Error code when release state is missing
89 define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
92 * Error code when release state is invalid
94 define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
97 * Error code when no release notes are found
99 define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
102 * Error code when no maintainers are found
104 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
107 * Error code when a maintainer has no handle
109 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
112 * Error code when a maintainer has no handle
114 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
117 * Error code when a maintainer has no name
119 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
122 * Error code when a maintainer has no email
124 define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
127 * Error code when a maintainer has no handle
129 define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
132 * Error code when a dependency is not a PHP dependency, but has no name
134 define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
137 * Error code when a dependency has no type (pkg, php, etc.)
139 define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
142 * Error code when a dependency has no relation (lt, ge, has, etc.)
144 define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
147 * Error code when a dependency is not a 'has' relation, but has no version
149 define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
152 * Error code when a dependency has an invalid relation
154 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
157 * Error code when a dependency has an invalid type
159 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
162 * Error code when a dependency has an invalid optional option
164 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
167 * Error code when a dependency is a pkg dependency, and has an invalid package name
169 define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
172 * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
174 define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
177 * Error code when rel="has" and version attribute is present.
179 define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
182 * Error code when type="php" and dependency name is present
184 define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
187 * Error code when a configure option has no name
189 define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
192 * Error code when a configure option has no name
194 define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
197 * Error code when a file in the filelist has an invalid role
199 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
202 * Error code when a file in the filelist has no role
204 define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
207 * Error code when analyzing a php source file that has parse errors
209 define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
212 * Error code when analyzing a php source file reveals a source element
213 * without a package name prefix
215 define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
218 * Error code when an unknown channel is specified
220 define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
223 * Error code when no files are found in the filelist
225 define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
228 * Error code when a file is not valid php according to _analyzeSourceCode()
230 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
233 * Error code when the channel validator returns an error or warning
235 define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
238 * Error code when a php5 package is packaged in php4 (analysis doesn't work)
240 define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
243 * Error code when a file is listed in package.xml but does not exist
245 define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
248 * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
250 define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
253 * Error code when a package.xml contains non-ISO-8859-1 characters
255 define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
258 * Error code when a dependency is not a 'has' relation, but has no version
260 define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
263 * Error code when a package has no lead developer
265 define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
268 * Error code when a filename begins with "."
270 define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
272 * package.xml encapsulator
275 * @author Greg Beaver <cellog@php.net>
276 * @copyright 1997-2009 The Authors
277 * @license http://opensource.org/licenses/bsd-license.php New BSD License
278 * @version Release: 1.9.4
279 * @link http://pear.php.net/package/PEAR
280 * @since Class available since Release 1.4.0a1
282 class PEAR_PackageFile_v1
286 * @var PEAR_ErrorStack
292 * A registry object, used to access the package name validation regex for non-standard channels
299 * An object that contains a log method that matches PEAR_Common::log's signature
306 * Parsed package information
313 * path to package.xml
320 * path to package .tgz or false if this is a local/extracted package.xml
333 * Determines whether this packagefile was initialized only with partial package info
335 * If this package file was constructed via parsing REST, it will only contain
343 var $_incomplete = true;
346 * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
347 * @param string Name of Error Stack class to use.
349 function PEAR_PackageFile_v1()
351 $this->_stack = &new PEAR_ErrorStack('PEAR_PackageFile_v1');
352 $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
356 function installBinary($installer)
361 function isExtension($name)
366 function setConfig(&$config)
368 $this->_config = &$config;
369 $this->_registry = &$config->getRegistry();
372 function setRequestedGroup()
378 * For saving in the registry.
380 * Set the last version that was installed
383 function setLastInstalledVersion($version)
385 $this->_packageInfo['_lastversion'] = $version;
389 * @return string|false
391 function getLastInstalledVersion()
393 if (isset($this->_packageInfo['_lastversion'])) {
394 return $this->_packageInfo['_lastversion'];
399 function getInstalledBinary()
404 function listPostinstallScripts()
409 function initPostinstallScripts()
414 function setLogger(&$logger)
416 if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
417 return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
419 $this->_logger = &$logger;
422 function setPackagefile($file, $archive = false)
424 $this->_packageFile = $file;
425 $this->_archiveFile = $archive ? $archive : $file;
428 function getPackageFile()
430 return isset($this->_packageFile) ? $this->_packageFile : false;
433 function getPackageType()
438 function getArchiveFile()
440 return $this->_archiveFile;
443 function packageInfo($field)
445 if (!is_string($field) || empty($field) ||
446 !isset($this->_packageInfo[$field])) {
449 return $this->_packageInfo[$field];
452 function setDirtree($path)
454 if (!isset($this->_packageInfo['dirtree'])) {
455 $this->_packageInfo['dirtree'] = array();
457 $this->_packageInfo['dirtree'][$path] = true;
460 function getDirtree()
462 if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
463 return $this->_packageInfo['dirtree'];
468 function resetDirtree()
470 unset($this->_packageInfo['dirtree']);
473 function fromArray($pinfo)
475 $this->_incomplete = false;
476 $this->_packageInfo = $pinfo;
479 function isIncomplete()
481 return $this->_incomplete;
484 function getChannel()
486 return 'pear.php.net';
499 function getExtends()
501 if (isset($this->_packageInfo['extends'])) {
502 return $this->_packageInfo['extends'];
512 if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
515 return $this->getArray();
520 return $this->_packageInfo;
525 return $this->getPackage();
528 function getPackage()
530 if (isset($this->_packageInfo['package'])) {
531 return $this->_packageInfo['package'];
537 * WARNING - don't use this unless you know what you are doing
539 function setRawPackage($package)
541 $this->_packageInfo['package'] = $package;
544 function setPackage($package)
546 $this->_packageInfo['package'] = $package;
547 $this->_isValid = false;
550 function getVersion()
552 if (isset($this->_packageInfo['version'])) {
553 return $this->_packageInfo['version'];
558 function setVersion($version)
560 $this->_packageInfo['version'] = $version;
561 $this->_isValid = false;
564 function clearMaintainers()
566 unset($this->_packageInfo['maintainers']);
569 function getMaintainers()
571 if (isset($this->_packageInfo['maintainers'])) {
572 return $this->_packageInfo['maintainers'];
578 * Adds a new maintainer - no checking of duplicates is performed, use
579 * updatemaintainer for that purpose.
581 function addMaintainer($role, $handle, $name, $email)
583 $this->_packageInfo['maintainers'][] =
584 array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
585 $this->_isValid = false;
588 function updateMaintainer($role, $handle, $name, $email)
591 if (!isset($this->_packageInfo['maintainers']) ||
592 !is_array($this->_packageInfo['maintainers'])) {
593 return $this->addMaintainer($role, $handle, $name, $email);
595 foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
596 if ($maintainer['handle'] == $handle) {
601 if ($found !== false) {
602 unset($this->_packageInfo['maintainers'][$found]);
603 $this->_packageInfo['maintainers'] =
604 array_values($this->_packageInfo['maintainers']);
606 $this->addMaintainer($role, $handle, $name, $email);
609 function deleteMaintainer($handle)
612 foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
613 if ($maintainer['handle'] == $handle) {
618 if ($found !== false) {
619 unset($this->_packageInfo['maintainers'][$found]);
620 $this->_packageInfo['maintainers'] =
621 array_values($this->_packageInfo['maintainers']);
629 if (isset($this->_packageInfo['release_state'])) {
630 return $this->_packageInfo['release_state'];
635 function setRawState($state)
637 $this->_packageInfo['release_state'] = $state;
640 function setState($state)
642 $this->_packageInfo['release_state'] = $state;
643 $this->_isValid = false;
648 if (isset($this->_packageInfo['release_date'])) {
649 return $this->_packageInfo['release_date'];
654 function setDate($date)
656 $this->_packageInfo['release_date'] = $date;
657 $this->_isValid = false;
660 function getLicense()
662 if (isset($this->_packageInfo['release_license'])) {
663 return $this->_packageInfo['release_license'];
668 function setLicense($date)
670 $this->_packageInfo['release_license'] = $date;
671 $this->_isValid = false;
674 function getSummary()
676 if (isset($this->_packageInfo['summary'])) {
677 return $this->_packageInfo['summary'];
682 function setSummary($summary)
684 $this->_packageInfo['summary'] = $summary;
685 $this->_isValid = false;
688 function getDescription()
690 if (isset($this->_packageInfo['description'])) {
691 return $this->_packageInfo['description'];
696 function setDescription($desc)
698 $this->_packageInfo['description'] = $desc;
699 $this->_isValid = false;
704 if (isset($this->_packageInfo['release_notes'])) {
705 return $this->_packageInfo['release_notes'];
710 function setNotes($notes)
712 $this->_packageInfo['release_notes'] = $notes;
713 $this->_isValid = false;
718 if (isset($this->_packageInfo['release_deps'])) {
719 return $this->_packageInfo['release_deps'];
725 * Reset dependencies prior to adding new ones
729 unset($this->_packageInfo['release_deps']);
732 function addPhpDep($version, $rel)
734 $this->_isValid = false;
735 $this->_packageInfo['release_deps'][] =
736 array('type' => 'php',
738 'version' => $version);
741 function addPackageDep($name, $version, $rel, $optional = 'no')
743 $this->_isValid = false;
745 array('type' => 'pkg',
748 'optional' => $optional);
749 if ($rel != 'has' && $rel != 'not') {
750 $dep['version'] = $version;
752 $this->_packageInfo['release_deps'][] = $dep;
755 function addExtensionDep($name, $version, $rel, $optional = 'no')
757 $this->_isValid = false;
758 $this->_packageInfo['release_deps'][] =
759 array('type' => 'ext',
762 'version' => $version,
763 'optional' => $optional);
767 * WARNING - do not use this function directly unless you know what you're doing
769 function setDeps($deps)
771 $this->_packageInfo['release_deps'] = $deps;
776 return isset($this->_packageInfo['release_deps']) &&
777 count($this->_packageInfo['release_deps']);
780 function getDependencyGroup($group)
785 function isCompatible($pf)
790 function isSubpackageOf($p)
792 return $p->isSubpackage($this);
795 function isSubpackage($p)
800 function dependsOn($package, $channel)
802 if (strtolower($channel) != 'pear.php.net') {
805 if (!($deps = $this->getDeps())) {
808 foreach ($deps as $dep) {
809 if ($dep['type'] != 'pkg') {
812 if (strtolower($dep['name']) == strtolower($package)) {
819 function getConfigureOptions()
821 if (isset($this->_packageInfo['configure_options'])) {
822 return $this->_packageInfo['configure_options'];
827 function hasConfigureOptions()
829 return isset($this->_packageInfo['configure_options']) &&
830 count($this->_packageInfo['configure_options']);
833 function addConfigureOption($name, $prompt, $default = false)
835 $o = array('name' => $name, 'prompt' => $prompt);
836 if ($default !== false) {
837 $o['default'] = $default;
839 if (!isset($this->_packageInfo['configure_options'])) {
840 $this->_packageInfo['configure_options'] = array();
842 $this->_packageInfo['configure_options'][] = $o;
845 function clearConfigureOptions()
847 unset($this->_packageInfo['configure_options']);
850 function getProvides()
852 if (isset($this->_packageInfo['provides'])) {
853 return $this->_packageInfo['provides'];
858 function getProvidesExtension()
863 function addFile($dir, $file, $attrs)
865 $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
866 if ($dir == '/' || $dir == '') {
871 $file = $dir . $file;
872 $file = preg_replace('![\\/]+!', '/', $file);
873 $this->_packageInfo['filelist'][$file] = $attrs;
876 function getInstallationFilelist()
878 return $this->getFilelist();
881 function getFilelist()
883 if (isset($this->_packageInfo['filelist'])) {
884 return $this->_packageInfo['filelist'];
889 function setFileAttribute($file, $attr, $value)
891 $this->_packageInfo['filelist'][$file][$attr] = $value;
894 function resetFilelist()
896 $this->_packageInfo['filelist'] = array();
899 function setInstalledAs($file, $path)
902 return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
904 unset($this->_packageInfo['filelist'][$file]['installed_as']);
907 function installedFile($file, $atts)
909 if (isset($this->_packageInfo['filelist'][$file])) {
910 $this->_packageInfo['filelist'][$file] =
911 array_merge($this->_packageInfo['filelist'][$file], $atts);
913 $this->_packageInfo['filelist'][$file] = $atts;
917 function getChangelog()
919 if (isset($this->_packageInfo['changelog'])) {
920 return $this->_packageInfo['changelog'];
925 function getPackagexmlVersion()
931 * Wrapper to {@link PEAR_ErrorStack::getErrors()}
932 * @param boolean determines whether to purge the error stack after retrieving
935 function getValidationWarnings($purge = true)
937 return $this->_stack->getErrors($purge);
942 * Validation error. Also marks the object contents as invalid
944 * @param array error information
947 function _validateError($code, $params = array())
949 $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
950 $this->_isValid = false;
954 * Validation warning. Does not mark the object contents invalid.
956 * @param array error information
959 function _validateWarning($code, $params = array())
961 $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
965 * @param integer error code
968 function _getErrorMessage()
971 PEAR_PACKAGEFILE_ERROR_NO_NAME =>
972 'Missing Package Name',
973 PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
975 PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
976 'Summary should be on one line',
977 PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
978 'Missing description',
979 PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
981 PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
982 'No release version found',
983 PEAR_PACKAGEFILE_ERROR_NO_STATE =>
984 'No release state found',
985 PEAR_PACKAGEFILE_ERROR_NO_DATE =>
986 'No release date found',
987 PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
988 'No release notes found',
989 PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
990 'Package must have at least one lead maintainer',
991 PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
992 'No maintainers found, at least one must be defined',
993 PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
994 'Maintainer %index% has no handle (user ID at channel server)',
995 PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
996 'Maintainer %index% has no role',
997 PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
998 'Maintainer %index% has no name',
999 PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
1000 'Maintainer %index% has no email',
1001 PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
1002 'Dependency %index% is not a php dependency, and has no name',
1003 PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
1004 'Dependency %index% has no relation (rel)',
1005 PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
1006 'Dependency %index% has no type',
1007 PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
1008 'PHP Dependency %index% has a name attribute of "%name%" which will be' .
1010 PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
1011 'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
1012 'and has no version',
1013 PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
1014 'Dependency %index% is a type="php" dependency, ' .
1015 'and has no version',
1016 PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
1017 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
1018 PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
1019 'Dependency %index% has invalid optional value "%opt%", should be yes or no',
1020 PEAR_PACKAGEFILE_PHP_NO_NOT =>
1021 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
1022 ' to exclude specific versions',
1023 PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
1024 'Configure Option %index% has no name',
1025 PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
1026 'Configure Option %index% has no prompt',
1027 PEAR_PACKAGEFILE_ERROR_NO_FILES =>
1028 'No files in <filelist> section of package.xml',
1029 PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
1030 'File "%file%" has no role, expecting one of "%roles%"',
1031 PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
1032 'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
1033 PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
1034 'File "%file%" cannot start with ".", cannot package or install',
1035 PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
1036 'Parser error: invalid PHP found in file "%file%"',
1037 PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
1038 'in %file%: %type% "%name%" not prefixed with package name "%package%"',
1039 PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
1040 'Parser error: invalid PHP file "%file%"',
1041 PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
1042 'Channel validator error: field "%field%" - %reason%',
1043 PEAR_PACKAGEFILE_ERROR_PHP5 =>
1044 'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
1045 PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
1046 'File "%file%" in package.xml does not exist',
1047 PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
1048 'Package.xml contains non-ISO-8859-1 characters, and may not validate',
1053 * Validate XML package definition file.
1058 function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
1060 if (($this->_isValid & $state) == $state) {
1063 $this->_isValid = true;
1064 $info = $this->_packageInfo;
1065 if (empty($info['package'])) {
1066 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
1067 $this->_packageName = $pn = 'unknown';
1069 $this->_packageName = $pn = $info['package'];
1072 if (empty($info['summary'])) {
1073 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
1074 } elseif (strpos(trim($info['summary']), "\n") !== false) {
1075 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
1076 array('summary' => $info['summary']));
1078 if (empty($info['description'])) {
1079 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
1081 if (empty($info['release_license'])) {
1082 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
1084 if (empty($info['version'])) {
1085 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
1087 if (empty($info['release_state'])) {
1088 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
1090 if (empty($info['release_date'])) {
1091 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
1093 if (empty($info['release_notes'])) {
1094 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
1096 if (empty($info['maintainers'])) {
1097 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
1101 foreach ($info['maintainers'] as $m) {
1102 if (empty($m['handle'])) {
1103 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
1104 array('index' => $i));
1106 if (empty($m['role'])) {
1107 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
1108 array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
1109 } elseif ($m['role'] == 'lead') {
1112 if (empty($m['name'])) {
1113 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
1114 array('index' => $i));
1116 if (empty($m['email'])) {
1117 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
1118 array('index' => $i));
1123 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
1126 if (!empty($info['release_deps'])) {
1128 foreach ($info['release_deps'] as $d) {
1129 if (!isset($d['type']) || empty($d['type'])) {
1130 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
1131 array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
1134 if (!isset($d['rel']) || empty($d['rel'])) {
1135 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
1136 array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
1139 if (!empty($d['optional'])) {
1140 if (!in_array($d['optional'], array('yes', 'no'))) {
1141 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
1142 array('index' => $i, 'opt' => $d['optional']));
1145 if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
1146 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
1147 array('index' => $i));
1148 } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
1149 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
1150 array('index' => $i, 'rel' => $d['rel']));
1152 if ($d['type'] == 'php' && !empty($d['name'])) {
1153 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
1154 array('index' => $i, 'name' => $d['name']));
1155 } elseif ($d['type'] != 'php' && empty($d['name'])) {
1156 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
1157 array('index' => $i));
1159 if ($d['type'] == 'php' && empty($d['version'])) {
1160 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
1161 array('index' => $i));
1163 if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
1164 $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
1165 array('index' => $i));
1170 if (!empty($info['configure_options'])) {
1172 foreach ($info['configure_options'] as $c) {
1173 if (empty($c['name'])) {
1174 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
1175 array('index' => $i));
1177 if (empty($c['prompt'])) {
1178 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
1179 array('index' => $i));
1184 if (empty($info['filelist'])) {
1185 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
1186 $errors[] = 'no files';
1188 foreach ($info['filelist'] as $file => $fa) {
1189 if (empty($fa['role'])) {
1190 $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
1191 array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
1193 } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
1194 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
1195 array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
1197 if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
1198 // file contains .. parent directory or . cur directory references
1199 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1200 array('file' => $file));
1202 if (isset($fa['install-as']) &&
1203 preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
1204 str_replace('\\', '/', $fa['install-as']))) {
1205 // install-as contains .. parent directory or . cur directory references
1206 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1207 array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
1209 if (isset($fa['baseinstalldir']) &&
1210 preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
1211 str_replace('\\', '/', $fa['baseinstalldir']))) {
1212 // install-as contains .. parent directory or . cur directory references
1213 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
1214 array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
1218 if (isset($this->_registry) && $this->_isValid) {
1219 $chan = $this->_registry->getChannel('pear.php.net');
1220 if (PEAR::isError($chan)) {
1221 $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
1222 return $this->_isValid = 0;
1224 $validator = $chan->getValidationObject();
1225 $validator->setPackageFile($this);
1226 $validator->validate($state);
1227 $failures = $validator->getFailures();
1228 foreach ($failures['errors'] as $error) {
1229 $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
1231 foreach ($failures['warnings'] as $warning) {
1232 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
1235 if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
1236 if ($this->_analyzePhpFiles()) {
1237 $this->_isValid = true;
1240 if ($this->_isValid) {
1241 return $this->_isValid = $state;
1243 return $this->_isValid = 0;
1246 function _analyzePhpFiles()
1248 if (!$this->_isValid) {
1251 if (!isset($this->_packageFile)) {
1254 $dir_prefix = dirname($this->_packageFile);
1255 $common = new PEAR_Common;
1256 $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
1257 array($common, 'log');
1258 $info = $this->getFilelist();
1259 foreach ($info as $file => $fa) {
1260 if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
1261 $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
1262 array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
1265 if ($fa['role'] == 'php' && $dir_prefix) {
1266 call_user_func_array($log, array(1, "Analyzing $file"));
1267 $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
1269 $this->_buildProvidesArray($srcinfo);
1273 $this->_packageName = $pn = $this->getPackage();
1275 if (isset($this->_packageInfo['provides'])) {
1276 foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
1277 if (isset($what['explicit'])) {
1278 // skip conformance checks if the provides entry is
1279 // specified in the package.xml file
1283 if ($type == 'class') {
1284 if (!strncasecmp($name, $pn, $pnl)) {
1287 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
1288 array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
1289 } elseif ($type == 'function') {
1290 if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
1293 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
1294 array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
1298 return $this->_isValid;
1302 * Get the default xml generator object
1304 * @return PEAR_PackageFile_Generator_v1
1306 function &getDefaultGenerator()
1308 if (!class_exists('PEAR_PackageFile_Generator_v1')) {
1309 require_once 'PEAR/PackageFile/Generator/v1.php';
1311 $a = &new PEAR_PackageFile_Generator_v1($this);
1316 * Get the contents of a file listed within the package.xml
1320 function getFileContents($file)
1322 if ($this->_archiveFile == $this->_packageFile) { // unpacked
1323 $dir = dirname($this->_packageFile);
1324 $file = $dir . DIRECTORY_SEPARATOR . $file;
1325 $file = str_replace(array('/', '\\'),
1326 array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
1327 if (file_exists($file) && is_readable($file)) {
1328 return implode('', file($file));
1331 if (!class_exists('Archive_Tar')) {
1332 require_once 'Archive/Tar.php';
1334 $tar = &new Archive_Tar($this->_archiveFile);
1335 $tar->pushErrorHandling(PEAR_ERROR_RETURN);
1336 if ($file != 'package.xml' && $file != 'package2.xml') {
1337 $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
1339 $file = $tar->extractInString($file);
1340 $tar->popErrorHandling();
1341 if (PEAR::isError($file)) {
1342 return PEAR::raiseError("Cannot locate file '$file' in archive");
1348 // {{{ analyzeSourceCode()
1350 * Analyze the source code of the given PHP file
1352 * @param string Filename of the PHP file
1356 function _analyzeSourceCode($file)
1358 if (!function_exists("token_get_all")) {
1361 if (!defined('T_DOC_COMMENT')) {
1362 define('T_DOC_COMMENT', T_COMMENT);
1364 if (!defined('T_INTERFACE')) {
1365 define('T_INTERFACE', -1);
1367 if (!defined('T_IMPLEMENTS')) {
1368 define('T_IMPLEMENTS', -1);
1370 if (!$fp = @fopen($file, "r")) {
1374 $contents = file_get_contents($file);
1375 $tokens = token_get_all($contents);
1377 for ($i = 0; $i < sizeof($tokens); $i++) {
1378 @list($token, $data) = $tokens[$i];
1379 if (is_string($token)) {
1382 print token_name($token) . ' ';
1383 var_dump(rtrim($data));
1392 $current_class = '';
1393 $current_interface = '';
1394 $current_class_level = -1;
1395 $current_function = '';
1396 $current_function_level = -1;
1397 $declared_classes = array();
1398 $declared_interfaces = array();
1399 $declared_functions = array();
1400 $declared_methods = array();
1401 $used_classes = array();
1402 $used_functions = array();
1404 $implements = array();
1408 for ($i = 0; $i < sizeof($tokens); $i++) {
1409 if (is_array($tokens[$i])) {
1410 list($token, $data) = $tokens[$i];
1412 $token = $tokens[$i];
1416 if ($token != '"' && $token != T_END_HEREDOC) {
1428 $current_function = '';
1429 $current_function_level = -1;
1433 case T_START_HEREDOC:
1437 case T_DOLLAR_OPEN_CURLY_BRACES:
1438 case '{': $brace_level++; continue 2;
1441 if ($current_class_level == $brace_level) {
1442 $current_class = '';
1443 $current_class_level = -1;
1445 if ($current_function_level == $brace_level) {
1446 $current_function = '';
1447 $current_function_level = -1;
1450 case '[': $bracket_level++; continue 2;
1451 case ']': $bracket_level--; continue 2;
1452 case '(': $paren_level++; continue 2;
1453 case ')': $paren_level--; continue 2;
1457 if (($current_class_level != -1) || ($current_function_level != -1)) {
1458 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
1459 array('file' => $file));
1469 if (version_compare(zend_version(), '2.0', '<')) {
1470 if (in_array(strtolower($data),
1471 array('public', 'private', 'protected', 'abstract',
1472 'interface', 'implements', 'throw')
1474 $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_PHP5,
1478 if ($look_for == T_CLASS) {
1479 $current_class = $data;
1480 $current_class_level = $brace_level;
1481 $declared_classes[] = $current_class;
1482 } elseif ($look_for == T_INTERFACE) {
1483 $current_interface = $data;
1484 $current_class_level = $brace_level;
1485 $declared_interfaces[] = $current_interface;
1486 } elseif ($look_for == T_IMPLEMENTS) {
1487 $implements[$current_class] = $data;
1488 } elseif ($look_for == T_EXTENDS) {
1489 $extends[$current_class] = $data;
1490 } elseif ($look_for == T_FUNCTION) {
1491 if ($current_class) {
1492 $current_function = "$current_class::$data";
1493 $declared_methods[$current_class][] = $data;
1494 } elseif ($current_interface) {
1495 $current_function = "$current_interface::$data";
1496 $declared_methods[$current_interface][] = $data;
1498 $current_function = $data;
1499 $declared_functions[] = $current_function;
1501 $current_function_level = $brace_level;
1503 } elseif ($look_for == T_NEW) {
1504 $used_classes[$data] = true;
1513 if (preg_match('!^/\*\*\s!', $data)) {
1514 $lastphpdoc = $data;
1515 if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
1516 $nodeps = array_merge($nodeps, $m[1]);
1520 case T_DOUBLE_COLON:
1521 if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
1522 $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
1523 array('file' => $file));
1526 $class = $tokens[$i - 1][1];
1527 if (strtolower($class) != 'parent') {
1528 $used_classes[$class] = true;
1534 "source_file" => $file,
1535 "declared_classes" => $declared_classes,
1536 "declared_interfaces" => $declared_interfaces,
1537 "declared_methods" => $declared_methods,
1538 "declared_functions" => $declared_functions,
1539 "used_classes" => array_diff(array_keys($used_classes), $nodeps),
1540 "inheritance" => $extends,
1541 "implements" => $implements,
1546 * Build a "provides" array from data returned by
1547 * analyzeSourceCode(). The format of the built array is like
1551 * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
1556 * @param array $srcinfo array with information about a source file
1557 * as returned by the analyzeSourceCode() method.
1564 function _buildProvidesArray($srcinfo)
1566 if (!$this->_isValid) {
1569 $file = basename($srcinfo['source_file']);
1570 $pn = $this->getPackage();
1572 foreach ($srcinfo['declared_classes'] as $class) {
1573 $key = "class;$class";
1574 if (isset($this->_packageInfo['provides'][$key])) {
1577 $this->_packageInfo['provides'][$key] =
1578 array('file'=> $file, 'type' => 'class', 'name' => $class);
1579 if (isset($srcinfo['inheritance'][$class])) {
1580 $this->_packageInfo['provides'][$key]['extends'] =
1581 $srcinfo['inheritance'][$class];
1584 foreach ($srcinfo['declared_methods'] as $class => $methods) {
1585 foreach ($methods as $method) {
1586 $function = "$class::$method";
1587 $key = "function;$function";
1588 if ($method{0} == '_' || !strcasecmp($method, $class) ||
1589 isset($this->_packageInfo['provides'][$key])) {
1592 $this->_packageInfo['provides'][$key] =
1593 array('file'=> $file, 'type' => 'function', 'name' => $function);
1597 foreach ($srcinfo['declared_functions'] as $function) {
1598 $key = "function;$function";
1599 if ($function{0} == '_' || isset($this->_packageInfo['provides'][$key])) {
1602 if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
1603 $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
1605 $this->_packageInfo['provides'][$key] =
1606 array('file'=> $file, 'type' => 'function', 'name' => $function);