Spade
Mini Shell
| Directory:~$ /proc/self/root/opt/cloudlinux/alt-php72/root/usr/share/pear/PEAR/PackageFile/v2/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* PEAR_PackageFile_v2, package.xml version 2.0, read/write version
*
* PHP versions 4 and 5
*
* @category pear
* @package PEAR
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD
License
* @link http://pear.php.net/package/PEAR
* @since File available since Release 1.4.0a8
*/
/**
* Private validation class used by PEAR_PackageFile_v2 - do not use
directly, its
* sole purpose is to split up the PEAR/PackageFile/v2.php file to make it
smaller
* @category pear
* @package PEAR
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2009 The Authors
* @license http://opensource.org/licenses/bsd-license.php New BSD
License
* @version Release: 1.10.16
* @link http://pear.php.net/package/PEAR
* @since Class available since Release 1.4.0a8
* @access private
*/
class PEAR_PackageFile_v2_Validator
{
/**
* @var array
*/
var $_packageInfo;
/**
* @var PEAR_PackageFile_v2
*/
var $_pf;
/**
* @var PEAR_ErrorStack
*/
var $_stack;
/**
* @var int
*/
var $_isValid = 0;
/**
* @var int
*/
var $_filesValid = 0;
/**
* @var int
*/
var $_curState = 0;
/**
* @param PEAR_PackageFile_v2
* @param int
*/
function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
{
$this->_pf = &$pf;
$this->_curState = $state;
$this->_packageInfo = $this->_pf->getArray();
$this->_isValid = $this->_pf->_isValid;
$this->_filesValid = $this->_pf->_filesValid;
$this->_stack = &$pf->_stack;
$this->_stack->getErrors(true);
if (($this->_isValid & $state) == $state) {
return true;
}
if (!isset($this->_packageInfo) ||
!is_array($this->_packageInfo)) {
return false;
}
if
(!isset($this->_packageInfo['attribs']['version'])
||
($this->_packageInfo['attribs']['version'] !=
'2.0' &&
$this->_packageInfo['attribs']['version'] !=
'2.1')
) {
$this->_noPackageVersion();
}
$structure =
array(
'name',
'channel|uri',
'*extends', // can't be multiple, but this works
fine
'summary',
'description',
'+lead', // these all need content checks
'*developer',
'*contributor',
'*helper',
'date',
'*time',
'version',
'stability',
'license->?uri->?filesource',
'notes',
'contents', //special validation needed
'*compatible',
'dependencies', //special validation needed
'*usesrole',
'*usestask', // reserve these for 1.4.0a1 to
implement
// this will allow a package.xml to gracefully say
it
// needs a certain package installed in order to
implement a role or task
'*providesextension',
'*srcpackage|*srcuri',
'+phprelease|+extsrcrelease|+extbinrelease|' .
'+zendextsrcrelease|+zendextbinrelease|bundle',
//special validation needed
'*changelog',
);
$test = $this->_packageInfo;
if (isset($test['dependencies']) &&
isset($test['dependencies']['required'])
&&
isset($test['dependencies']['required']['pearinstaller'])
&&
isset($test['dependencies']['required']['pearinstaller']['min'])
&&
'1.10.16' != '@package' .
'_version@' &&
version_compare('1.10.16',
$test['dependencies']['required']['pearinstaller']['min'],
'<')
) {
$this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
return false;
}
// ignore post-installation array fields
if (array_key_exists('filelist', $test)) {
unset($test['filelist']);
}
if (array_key_exists('_lastmodified', $test)) {
unset($test['_lastmodified']);
}
if (array_key_exists('#binarypackage', $test)) {
unset($test['#binarypackage']);
}
if (array_key_exists('old', $test)) {
unset($test['old']);
}
if (array_key_exists('_lastversion', $test)) {
unset($test['_lastversion']);
}
if (!$this->_stupidSchemaValidate($structure, $test,
'<package>')) {
return false;
}
if (empty($this->_packageInfo['name'])) {
$this->_tagCannotBeEmpty('name');
}
$test = isset($this->_packageInfo['uri']) ?
'uri' :'channel';
if (empty($this->_packageInfo[$test])) {
$this->_tagCannotBeEmpty($test);
}
if (is_array($this->_packageInfo['license'])
&&
(!isset($this->_packageInfo['license']['_content'])
||
empty($this->_packageInfo['license']['_content'])))
{
$this->_tagCannotBeEmpty('license');
} elseif (empty($this->_packageInfo['license'])) {
$this->_tagCannotBeEmpty('license');
}
if (empty($this->_packageInfo['summary'])) {
$this->_tagCannotBeEmpty('summary');
}
if (empty($this->_packageInfo['description'])) {
$this->_tagCannotBeEmpty('description');
}
if (empty($this->_packageInfo['date'])) {
$this->_tagCannotBeEmpty('date');
}
if (empty($this->_packageInfo['notes'])) {
$this->_tagCannotBeEmpty('notes');
}
if (isset($this->_packageInfo['time']) &&
empty($this->_packageInfo['time'])) {
$this->_tagCannotBeEmpty('time');
}
if (isset($this->_packageInfo['dependencies'])) {
$this->_validateDependencies();
}
if (isset($this->_packageInfo['compatible'])) {
$this->_validateCompatible();
}
if (!isset($this->_packageInfo['bundle'])) {
if (empty($this->_packageInfo['contents'])) {
$this->_tagCannotBeEmpty('contents');
}
if
(!isset($this->_packageInfo['contents']['dir'])) {
$this->_filelistMustContainDir('contents');
return false;
}
if
(isset($this->_packageInfo['contents']['file'])) {
$this->_filelistCannotContainFile('contents');
return false;
}
}
$this->_validateMaintainers();
$this->_validateStabilityVersion();
$fail = false;
if (array_key_exists('usesrole', $this->_packageInfo))
{
$roles = $this->_packageInfo['usesrole'];
if (!is_array($roles) || !isset($roles[0])) {
$roles = array($roles);
}
foreach ($roles as $role) {
if (!isset($role['role'])) {
$this->_usesroletaskMustHaveRoleTask('usesrole',
'role');
$fail = true;
} else {
if (!isset($role['channel'])) {
if (!isset($role['uri'])) {
$this->_usesroletaskMustHaveChannelOrUri($role['role'],
'usesrole');
$fail = true;
}
} elseif (!isset($role['package'])) {
$this->_usesroletaskMustHavePackage($role['role'],
'usesrole');
$fail = true;
}
}
}
}
if (array_key_exists('usestask', $this->_packageInfo))
{
$roles = $this->_packageInfo['usestask'];
if (!is_array($roles) || !isset($roles[0])) {
$roles = array($roles);
}
foreach ($roles as $role) {
if (!isset($role['task'])) {
$this->_usesroletaskMustHaveRoleTask('usestask',
'task');
$fail = true;
} else {
if (!isset($role['channel'])) {
if (!isset($role['uri'])) {
$this->_usesroletaskMustHaveChannelOrUri($role['task'],
'usestask');
$fail = true;
}
} elseif (!isset($role['package'])) {
$this->_usesroletaskMustHavePackage($role['task'],
'usestask');
$fail = true;
}
}
}
}
if ($fail) {
return false;
}
$list = $this->_packageInfo['contents'];
if (isset($list['dir']) &&
is_array($list['dir']) &&
isset($list['dir'][0])) {
$this->_multipleToplevelDirNotAllowed();
return $this->_isValid = 0;
}
$this->_validateFilelist();
$this->_validateRelease();
if (!$this->_stack->hasErrors()) {
$chan =
$this->_pf->_registry->getChannel($this->_pf->getChannel(),
true);
if (PEAR::isError($chan)) {
$this->_unknownChannel($this->_pf->getChannel());
} else {
$valpack = $chan->getValidationPackage();
// for channel validator packages, always use the default
PEAR validator.
// otherwise, they can't be installed or packaged
$validator =
$chan->getValidationObject($this->_pf->getPackage());
if (!$validator) {
$this->_stack->push(__FUNCTION__,
'error',
array('channel' =>
$chan->getName(),
'package' =>
$this->_pf->getPackage(),
'name' =>
$valpack['_content'],
'version' =>
$valpack['attribs']['version']),
'package "%channel%/%package%"
cannot be properly validated without ' .
'validation package
"%channel%/%name%-%version%"');
return $this->_isValid = 0;
}
$validator->setPackageFile($this->_pf);
$validator->validate($state);
$failures = $validator->getFailures();
foreach ($failures['errors'] as $error) {
$this->_stack->push(__FUNCTION__,
'error', $error,
'Channel validator error: field
"%field%" - %reason%');
}
foreach ($failures['warnings'] as $warning) {
$this->_stack->push(__FUNCTION__,
'warning', $warning,
'Channel validator warning: field
"%field%" - %reason%');
}
}
}
$this->_pf->_isValid = $this->_isValid =
!$this->_stack->hasErrors('error');
if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING
&& !$this->_filesValid) {
if ($this->_pf->getPackageType() == 'bundle') {
if ($this->_analyzeBundledPackages()) {
$this->_filesValid = $this->_pf->_filesValid =
true;
} else {
$this->_pf->_isValid = $this->_isValid = 0;
}
} else {
if (!$this->_analyzePhpFiles()) {
$this->_pf->_isValid = $this->_isValid = 0;
} else {
$this->_filesValid = $this->_pf->_filesValid =
true;
}
}
}
if ($this->_isValid) {
return $this->_pf->_isValid = $this->_isValid =
$state;
}
return $this->_pf->_isValid = $this->_isValid = 0;
}
function _stupidSchemaValidate($structure, $xml, $root)
{
if (!is_array($xml)) {
$xml = array();
}
$keys = array_keys($xml);
reset($keys);
$key = current($keys);
while ($key == 'attribs' || $key ==
'_contents') {
$key = next($keys);
}
$unfoundtags = $optionaltags = array();
$ret = true;
$mismatch = false;
foreach ($structure as $struc) {
if ($key) {
$tag = $xml[$key];
}
$test = $this->_processStructure($struc);
if (isset($test['choices'])) {
$loose = true;
foreach ($test['choices'] as $choice) {
if ($key == $choice['tag']) {
$key = next($keys);
while ($key == 'attribs' || $key ==
'_contents') {
$key = next($keys);
}
$unfoundtags = $optionaltags = array();
$mismatch = false;
if ($key && $key !=
$choice['tag'] && isset($choice['multiple'])) {
$unfoundtags[] = $choice['tag'];
$optionaltags[] = $choice['tag'];
if ($key) {
$mismatch = true;
}
}
$ret &= $this->_processAttribs($choice,
$tag, $root);
continue 2;
} else {
$unfoundtags[] = $choice['tag'];
$mismatch = true;
}
if (!isset($choice['multiple']) ||
$choice['multiple'] != '*') {
$loose = false;
} else {
$optionaltags[] = $choice['tag'];
}
}
if (!$loose) {
$this->_invalidTagOrder($unfoundtags, $key, $root);
return false;
}
} else {
if ($key != $test['tag']) {
if (isset($test['multiple']) &&
$test['multiple'] != '*') {
$unfoundtags[] = $test['tag'];
$this->_invalidTagOrder($unfoundtags, $key,
$root);
return false;
} else {
if ($key) {
$mismatch = true;
}
$unfoundtags[] = $test['tag'];
$optionaltags[] = $test['tag'];
}
if (!isset($test['multiple'])) {
$this->_invalidTagOrder($unfoundtags, $key,
$root);
return false;
}
continue;
} else {
$unfoundtags = $optionaltags = array();
$mismatch = false;
}
$key = next($keys);
while ($key == 'attribs' || $key ==
'_contents') {
$key = next($keys);
}
if ($key && $key != $test['tag']
&& isset($test['multiple'])) {
$unfoundtags[] = $test['tag'];
$optionaltags[] = $test['tag'];
$mismatch = true;
}
$ret &= $this->_processAttribs($test, $tag, $root);
continue;
}
}
if (!$mismatch && count($optionaltags)) {
// don't error out on any optional tags
$unfoundtags = array_diff($unfoundtags, $optionaltags);
}
if (count($unfoundtags)) {
$this->_invalidTagOrder($unfoundtags, $key, $root);
} elseif ($key) {
// unknown tags
$this->_invalidTagOrder('*no tags allowed here*',
$key, $root);
while ($key = next($keys)) {
$this->_invalidTagOrder('*no tags allowed
here*', $key, $root);
}
}
return $ret;
}
function _processAttribs($choice, $tag, $context)
{
if (isset($choice['attribs'])) {
if (!is_array($tag)) {
$tag = array($tag);
}
$tags = $tag;
if (!isset($tags[0])) {
$tags = array($tags);
}
$ret = true;
foreach ($tags as $i => $tag) {
if (!is_array($tag) || !isset($tag['attribs'])) {
foreach ($choice['attribs'] as $attrib) {
if ($attrib[0] != '?') {
$ret &=
$this->_tagHasNoAttribs($choice['tag'],
$context);
continue 2;
}
}
}
foreach ($choice['attribs'] as $attrib) {
if ($attrib[0] != '?') {
if (!isset($tag['attribs'][$attrib])) {
$ret &=
$this->_tagMissingAttribute($choice['tag'],
$attrib, $context);
}
}
}
}
return $ret;
}
return true;
}
function _processStructure($key)
{
$ret = array();
if (count($pieces = explode('|', $key)) > 1) {
$ret['choices'] = array();
foreach ($pieces as $piece) {
$ret['choices'][] =
$this->_processStructure($piece);
}
return $ret;
}
$multi = $key[0];
if ($multi == '+' || $multi == '*') {
$ret['multiple'] = $key[0];
$key = substr($key, 1);
}
if (count($attrs = explode('->', $key)) > 1) {
$ret['tag'] = array_shift($attrs);
$ret['attribs'] = $attrs;
} else {
$ret['tag'] = $key;
}
return $ret;
}
function _validateStabilityVersion()
{
$structure = array('release', 'api');
$a = $this->_stupidSchemaValidate($structure,
$this->_packageInfo['version'], '<version>');
$a &= $this->_stupidSchemaValidate($structure,
$this->_packageInfo['stability'],
'<stability>');
if ($a) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$this->_packageInfo['version']['release'])) {
$this->_invalidVersion('release',
$this->_packageInfo['version']['release']);
}
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$this->_packageInfo['version']['api'])) {
$this->_invalidVersion('api',
$this->_packageInfo['version']['api']);
}
if
(!in_array($this->_packageInfo['stability']['release'],
array('snapshot', 'devel',
'alpha', 'beta', 'stable'))) {
$this->_invalidState('release',
$this->_packageInfo['stability']['release']);
}
if
(!in_array($this->_packageInfo['stability']['api'],
array('devel', 'alpha',
'beta', 'stable'))) {
$this->_invalidState('api',
$this->_packageInfo['stability']['api']);
}
}
}
function _validateMaintainers()
{
$structure =
array(
'name',
'user',
'email',
'active',
);
foreach (array('lead', 'developer',
'contributor', 'helper') as $type) {
if (!isset($this->_packageInfo[$type])) {
continue;
}
if (isset($this->_packageInfo[$type][0])) {
foreach ($this->_packageInfo[$type] as $lead) {
$this->_stupidSchemaValidate($structure, $lead,
'<' . $type . '>');
}
} else {
$this->_stupidSchemaValidate($structure,
$this->_packageInfo[$type],
'<' . $type . '>');
}
}
}
function _validatePhpDep($dep, $installcondition = false)
{
$structure = array(
'min',
'*max',
'*exclude',
);
$type = $installcondition ?
'<installcondition><php>' :
'<dependencies><required><php>';
$this->_stupidSchemaValidate($structure, $dep, $type);
if (isset($dep['min'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
$dep['min'])) {
$this->_invalidVersion($type . '<min>',
$dep['min']);
}
}
if (isset($dep['max'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
$dep['max'])) {
$this->_invalidVersion($type . '<max>',
$dep['max']);
}
}
if (isset($dep['exclude'])) {
if (!is_array($dep['exclude'])) {
$dep['exclude'] =
array($dep['exclude']);
}
foreach ($dep['exclude'] as $exclude) {
if (!preg_match(
'/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
$exclude)) {
$this->_invalidVersion($type .
'<exclude>', $exclude);
}
}
}
}
function _validatePearinstallerDep($dep)
{
$structure = array(
'min',
'*max',
'*recommended',
'*exclude',
);
$this->_stupidSchemaValidate($structure, $dep,
'<dependencies><required><pearinstaller>');
if (isset($dep['min'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['min'])) {
$this->_invalidVersion('<dependencies><required><pearinstaller><min>',
$dep['min']);
}
}
if (isset($dep['max'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['max'])) {
$this->_invalidVersion('<dependencies><required><pearinstaller><max>',
$dep['max']);
}
}
if (isset($dep['recommended'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['recommended'])) {
$this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
$dep['recommended']);
}
}
if (isset($dep['exclude'])) {
if (!is_array($dep['exclude'])) {
$dep['exclude'] =
array($dep['exclude']);
}
foreach ($dep['exclude'] as $exclude) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$exclude)) {
$this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
$exclude);
}
}
}
}
function _validatePackageDep($dep, $group, $type =
'<package>')
{
if (isset($dep['uri'])) {
if (isset($dep['conflicts'])) {
$structure = array(
'name',
'uri',
'conflicts',
'*providesextension',
);
} else {
$structure = array(
'name',
'uri',
'*providesextension',
);
}
} else {
if (isset($dep['conflicts'])) {
$structure = array(
'name',
'channel',
'*min',
'*max',
'*exclude',
'conflicts',
'*providesextension',
);
} else {
$structure = array(
'name',
'channel',
'*min',
'*max',
'*recommended',
'*exclude',
'*nodefault',
'*providesextension',
);
}
}
if (isset($dep['name'])) {
$type .= '<name>' . $dep['name'] .
'</name>';
}
$this->_stupidSchemaValidate($structure, $dep,
'<dependencies>' . $group . $type);
if (isset($dep['uri']) &&
(isset($dep['min']) || isset($dep['max']) ||
isset($dep['recommended']) ||
isset($dep['exclude']))) {
$this->_uriDepsCannotHaveVersioning('<dependencies>' .
$group . $type);
}
if (isset($dep['channel']) &&
strtolower($dep['channel']) == '__uri') {
$this->_DepchannelCannotBeUri('<dependencies>' . $group
. $type);
}
if (isset($dep['min'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['min'])) {
$this->_invalidVersion('<dependencies>'
. $group . $type . '<min>', $dep['min']);
}
}
if (isset($dep['max'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['max'])) {
$this->_invalidVersion('<dependencies>'
. $group . $type . '<max>', $dep['max']);
}
}
if (isset($dep['recommended'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['recommended'])) {
$this->_invalidVersion('<dependencies>'
. $group . $type . '<recommended>',
$dep['recommended']);
}
}
if (isset($dep['exclude'])) {
if (!is_array($dep['exclude'])) {
$dep['exclude'] =
array($dep['exclude']);
}
foreach ($dep['exclude'] as $exclude) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$exclude)) {
$this->_invalidVersion('<dependencies>' . $group . $type
. '<exclude>',
$exclude);
}
}
}
}
function _validateSubpackageDep($dep, $group)
{
$this->_validatePackageDep($dep, $group,
'<subpackage>');
if (isset($dep['providesextension'])) {
$this->_subpackageCannotProvideExtension(isset($dep['name']) ?
$dep['name'] : '');
}
if (isset($dep['conflicts'])) {
$this->_subpackagesCannotConflict(isset($dep['name']) ?
$dep['name'] : '');
}
}
function _validateExtensionDep($dep, $group = false, $installcondition
= false)
{
if (isset($dep['conflicts'])) {
$structure = array(
'name',
'*min',
'*max',
'*exclude',
'conflicts',
);
} else {
$structure = array(
'name',
'*min',
'*max',
'*recommended',
'*exclude',
);
}
if ($installcondition) {
$type = '<installcondition><extension>';
} else {
$type = '<dependencies>' . $group .
'<extension>';
}
if (isset($dep['name'])) {
$type .= '<name>' . $dep['name'] .
'</name>';
}
$this->_stupidSchemaValidate($structure, $dep, $type);
if (isset($dep['min'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['min'])) {
$this->_invalidVersion(substr($type, 1) .
'<min', $dep['min']);
}
}
if (isset($dep['max'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['max'])) {
$this->_invalidVersion(substr($type, 1) .
'<max', $dep['max']);
}
}
if (isset($dep['recommended'])) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$dep['recommended'])) {
$this->_invalidVersion(substr($type, 1) .
'<recommended', $dep['recommended']);
}
}
if (isset($dep['exclude'])) {
if (!is_array($dep['exclude'])) {
$dep['exclude'] =
array($dep['exclude']);
}
foreach ($dep['exclude'] as $exclude) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$exclude)) {
$this->_invalidVersion(substr($type, 1) .
'<exclude', $exclude);
}
}
}
}
function _validateOsDep($dep, $installcondition = false)
{
$structure = array(
'name',
'*conflicts',
);
$type = $installcondition ?
'<installcondition><os>' :
'<dependencies><required><os>';
if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
if ($dep['name'] == '*') {
if (array_key_exists('conflicts', $dep)) {
$this->_cannotConflictWithAllOs($type);
}
}
}
}
function _validateArchDep($dep, $installcondition = false)
{
$structure = array(
'pattern',
'*conflicts',
);
$type = $installcondition ?
'<installcondition><arch>' :
'<dependencies><required><arch>';
$this->_stupidSchemaValidate($structure, $dep, $type);
}
function _validateInstallConditions($cond, $release)
{
$structure = array(
'*php',
'*extension',
'*os',
'*arch',
);
if (!$this->_stupidSchemaValidate($structure,
$cond, $release)) {
return false;
}
foreach (array('php', 'extension',
'os', 'arch') as $type) {
if (isset($cond[$type])) {
$iter = $cond[$type];
if (!is_array($iter) || !isset($iter[0])) {
$iter = array($iter);
}
foreach ($iter as $package) {
if ($type == 'extension') {
$this->{"_validate{$type}Dep"}($package, false, true);
} else {
$this->{"_validate{$type}Dep"}($package, true);
}
}
}
}
}
function _validateDependencies()
{
$structure = array(
'required',
'*optional',
'*group->name->hint'
);
if (!$this->_stupidSchemaValidate($structure,
$this->_packageInfo['dependencies'],
'<dependencies>')) {
return false;
}
foreach (array('required', 'optional') as
$simpledep) {
if
(isset($this->_packageInfo['dependencies'][$simpledep])) {
if ($simpledep == 'optional') {
$structure = array(
'*package',
'*subpackage',
'*extension',
);
} else {
$structure = array(
'php',
'pearinstaller',
'*package',
'*subpackage',
'*extension',
'*os',
'*arch',
);
}
if ($this->_stupidSchemaValidate($structure,
$this->_packageInfo['dependencies'][$simpledep],
"<dependencies><$simpledep>"))
{
foreach (array('package',
'subpackage', 'extension') as $type) {
if
(isset($this->_packageInfo['dependencies'][$simpledep][$type]))
{
$iter =
$this->_packageInfo['dependencies'][$simpledep][$type];
if (!isset($iter[0])) {
$iter = array($iter);
}
foreach ($iter as $package) {
if ($type != 'extension') {
if (isset($package['uri'])) {
if
(isset($package['channel'])) {
$this->_UrlOrChannel($type,
$package['name']);
}
} else {
if
(!isset($package['channel'])) {
$this->_NoChannel($type,
$package['name']);
}
}
}
$this->{"_validate{$type}Dep"}($package,
"<$simpledep>");
}
}
}
if ($simpledep == 'optional') {
continue;
}
foreach (array('php',
'pearinstaller', 'os', 'arch') as $type) {
if
(isset($this->_packageInfo['dependencies'][$simpledep][$type]))
{
$iter =
$this->_packageInfo['dependencies'][$simpledep][$type];
if (!isset($iter[0])) {
$iter = array($iter);
}
foreach ($iter as $package) {
$this->{"_validate{$type}Dep"}($package);
}
}
}
}
}
}
if
(isset($this->_packageInfo['dependencies']['group']))
{
$groups =
$this->_packageInfo['dependencies']['group'];
if (!isset($groups[0])) {
$groups = array($groups);
}
$structure = array(
'*package',
'*subpackage',
'*extension',
);
foreach ($groups as $group) {
if ($this->_stupidSchemaValidate($structure, $group,
'<group>')) {
if
(!PEAR_Validate::validGroupName($group['attribs']['name']))
{
$this->_invalidDepGroupName($group['attribs']['name']);
}
foreach (array('package',
'subpackage', 'extension') as $type) {
if (isset($group[$type])) {
$iter = $group[$type];
if (!isset($iter[0])) {
$iter = array($iter);
}
foreach ($iter as $package) {
if ($type != 'extension') {
if (isset($package['uri'])) {
if
(isset($package['channel'])) {
$this->_UrlOrChannelGroup($type,
$package['name'],
$group['name']);
}
} else {
if
(!isset($package['channel'])) {
$this->_NoChannelGroup($type,
$package['name'],
$group['name']);
}
}
}
$this->{"_validate{$type}Dep"}($package, '<group
name="' .
$group['attribs']['name'] . '">');
}
}
}
}
}
}
}
function _validateCompatible()
{
$compat = $this->_packageInfo['compatible'];
if (!isset($compat[0])) {
$compat = array($compat);
}
$required = array('name', 'channel',
'min', 'max', '*exclude');
foreach ($compat as $package) {
$type = '<compatible>';
if (is_array($package) &&
array_key_exists('name', $package)) {
$type .= '<name>' .
$package['name'] . '</name>';
}
$this->_stupidSchemaValidate($required, $package, $type);
if (is_array($package) &&
array_key_exists('min', $package)) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$package['min'])) {
$this->_invalidVersion(substr($type, 1) .
'<min', $package['min']);
}
}
if (is_array($package) &&
array_key_exists('max', $package)) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$package['max'])) {
$this->_invalidVersion(substr($type, 1) .
'<max', $package['max']);
}
}
if (is_array($package) &&
array_key_exists('exclude', $package)) {
if (!is_array($package['exclude'])) {
$package['exclude'] =
array($package['exclude']);
}
foreach ($package['exclude'] as $exclude) {
if
(!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
$exclude)) {
$this->_invalidVersion(substr($type, 1) .
'<exclude', $exclude);
}
}
}
}
}
function _validateBundle($list)
{
if (!is_array($list) || !isset($list['bundledpackage']))
{
return $this->_NoBundledPackages();
}
if (!is_array($list['bundledpackage']) ||
!isset($list['bundledpackage'][0])) {
return $this->_AtLeast2BundledPackages();
}
foreach ($list['bundledpackage'] as $package) {
if (!is_string($package)) {
$this->_bundledPackagesMustBeFilename();
}
}
}
function _validateFilelist($list = false, $allowignore = false, $dirs =
'')
{
$iscontents = false;
if (!$list) {
$iscontents = true;
$list = $this->_packageInfo['contents'];
if (isset($this->_packageInfo['bundle'])) {
return $this->_validateBundle($list);
}
}
if ($allowignore) {
$struc = array(
'*install->name->as',
'*ignore->name'
);
} else {
$struc = array(
'*dir->name->?baseinstalldir',
'*file->name->role->?baseinstalldir->?md5sum'
);
if (isset($list['dir']) &&
isset($list['file'])) {
// stave off validation errors without requiring a set
order.
$_old = $list;
if (isset($list['attribs'])) {
$list = array('attribs' =>
$_old['attribs']);
}
$list['dir'] = $_old['dir'];
$list['file'] = $_old['file'];
}
}
if (!isset($list['attribs']) ||
!isset($list['attribs']['name'])) {
$unknown = $allowignore ? '<filelist>' :
'<dir name="*unknown*">';
$dirname = $iscontents ? '<contents>' :
$unknown;
} else {
$dirname = '<dir name="' .
$list['attribs']['name'] . '">';
if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
str_replace('\\', '/',
$list['attribs']['name']))) {
// file contains .. parent directory or . cur directory
$this->_invalidDirName($list['attribs']['name']);
}
}
$res = $this->_stupidSchemaValidate($struc, $list, $dirname);
if ($allowignore && $res) {
$ignored_or_installed = array();
$this->_pf->getFilelist();
$fcontents = $this->_pf->getContents();
$filelist = array();
if (!isset($fcontents['dir']['file'][0])) {
$fcontents['dir']['file'] =
array($fcontents['dir']['file']);
}
foreach ($fcontents['dir']['file'] as
$file) {
$filelist[$file['attribs']['name']] =
true;
}
if (isset($list['install'])) {
if (!isset($list['install'][0])) {
$list['install'] =
array($list['install']);
}
foreach ($list['install'] as $file) {
if
(!isset($filelist[$file['attribs']['name']])) {
$this->_notInContents($file['attribs']['name'],
'install');
continue;
}
if
(array_key_exists($file['attribs']['name'],
$ignored_or_installed)) {
$this->_multipleInstallAs($file['attribs']['name']);
}
if
(!isset($ignored_or_installed[$file['attribs']['name']]))
{
$ignored_or_installed[$file['attribs']['name']] =
array();
}
$ignored_or_installed[$file['attribs']['name']][] = 1;
if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
str_replace('\\',
'/', $file['attribs']['as']))) {
// file contains .. parent directory or . cur
directory references
$this->_invalidFileInstallAs($file['attribs']['name'],
$file['attribs']['as']);
}
}
}
if (isset($list['ignore'])) {
if (!isset($list['ignore'][0])) {
$list['ignore'] =
array($list['ignore']);
}
foreach ($list['ignore'] as $file) {
if
(!isset($filelist[$file['attribs']['name']])) {
$this->_notInContents($file['attribs']['name'],
'ignore');
continue;
}
if
(array_key_exists($file['attribs']['name'],
$ignored_or_installed)) {
$this->_ignoreAndInstallAs($file['attribs']['name']);
}
}
}
}
if (!$allowignore && isset($list['file'])) {
if (is_string($list['file'])) {
$this->_oldStyleFileNotAllowed();
return false;
}
if (!isset($list['file'][0])) {
// single file
$list['file'] = array($list['file']);
}
foreach ($list['file'] as $i => $file)
{
if (isset($file['attribs']) &&
isset($file['attribs']['name'])) {
if ($file['attribs']['name'][0] ==
'.' &&
$file['attribs']['name'][1]
== '/') {
// name is something like
"./doc/whatever.txt"
$this->_invalidFileName($file['attribs']['name'],
$dirname);
}
if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
str_replace('\\',
'/', $file['attribs']['name']))) {
// file contains .. parent directory or . cur
directory
$this->_invalidFileName($file['attribs']['name'],
$dirname);
}
}
if (isset($file['attribs']) &&
isset($file['attribs']['role'])) {
if
(!$this->_validateRole($file['attribs']['role'])) {
if
(isset($this->_packageInfo['usesrole'])) {
$roles =
$this->_packageInfo['usesrole'];
if (!isset($roles[0])) {
$roles = array($roles);
}
foreach ($roles as $role) {
if ($role['role'] =
$file['attribs']['role']) {
$msg = 'This package contains role
"%role%" and requires ' .
'package "%package%"
to be used';
if (isset($role['uri'])) {
$params = array('role'
=> $role['role'],
'package' =>
$role['uri']);
} else {
$params = array('role'
=> $role['role'],
'package' =>
$this->_pf->_registry->
parsedPackageNameToString(array('package' =>
$role['package'],
'channel' => $role['channel']),
true));
}
$this->_stack->push('_mustInstallRole', 'error',
$params, $msg);
}
}
}
$this->_invalidFileRole($file['attribs']['name'],
$dirname,
$file['attribs']['role']);
}
}
if (!isset($file['attribs'])) {
continue;
}
$save = $file['attribs'];
if ($dirs) {
$save['name'] = $dirs . '/' .
$save['name'];
}
unset($file['attribs']);
if (count($file) && $this->_curState !=
PEAR_VALIDATE_DOWNLOADING) { // has tasks
foreach ($file as $task => $value) {
if ($tagClass = $this->_pf->getTask($task)) {
if (!is_array($value) || !isset($value[0])) {
$value = array($value);
}
foreach ($value as $v) {
$ret = call_user_func(array($tagClass,
'validateXml'),
$this->_pf, $v,
$this->_pf->_config, $save);
if (is_array($ret)) {
$this->_invalidTask($task, $ret,
isset($save['name']) ?
$save['name'] :
'');
}
}
} else {
if
(isset($this->_packageInfo['usestask'])) {
$roles =
$this->_packageInfo['usestask'];
if (!isset($roles[0])) {
$roles = array($roles);
}
foreach ($roles as $role) {
if ($role['task'] = $task) {
$msg = 'This package contains
task "%task%" and requires ' .
'package
"%package%" to be used';
if (isset($role['uri']))
{
$params =
array('task' => $role['task'],
'package' =>
$role['uri']);
} else {
$params =
array('task' => $role['task'],
'package' =>
$this->_pf->_registry->
parsedPackageNameToString(array('package' =>
$role['package'], 'channel' =>
$role['channel']),
true));
}
$this->_stack->push('_mustInstallTask', 'error',
$params, $msg);
}
}
}
$this->_unknownTask($task,
$save['name']);
}
}
}
}
}
if (isset($list['ignore'])) {
if (!$allowignore) {
$this->_ignoreNotAllowed('ignore');
}
}
if (isset($list['install'])) {
if (!$allowignore) {
$this->_ignoreNotAllowed('install');
}
}
if (isset($list['file'])) {
if ($allowignore) {
$this->_fileNotAllowed('file');
}
}
if (isset($list['dir'])) {
if ($allowignore) {
$this->_fileNotAllowed('dir');
} else {
if (!isset($list['dir'][0])) {
$list['dir'] = array($list['dir']);
}
foreach ($list['dir'] as $dir) {
if (isset($dir['attribs']) &&
isset($dir['attribs']['name'])) {
if ($dir['attribs']['name'] ==
'/' ||
!isset($this->_packageInfo['contents']['dir']['dir']))
{
// always use nothing if the filelist has
already been flattened
$newdirs = '';
} elseif ($dirs == '') {
$newdirs =
$dir['attribs']['name'];
} else {
$newdirs = $dirs . '/' .
$dir['attribs']['name'];
}
} else {
$newdirs = $dirs;
}
$this->_validateFilelist($dir, $allowignore,
$newdirs);
}
}
}
}
function _validateRelease()
{
if (isset($this->_packageInfo['phprelease'])) {
$release = 'phprelease';
if
(isset($this->_packageInfo['providesextension'])) {
$this->_cannotProvideExtension($release);
}
if (isset($this->_packageInfo['srcpackage']) ||
isset($this->_packageInfo['srcuri'])) {
$this->_cannotHaveSrcpackage($release);
}
$releases = $this->_packageInfo['phprelease'];
if (!is_array($releases)) {
return true;
}
if (!isset($releases[0])) {
$releases = array($releases);
}
foreach ($releases as $rel) {
$this->_stupidSchemaValidate(array(
'*installconditions',
'*filelist',
), $rel, '<phprelease>');
}
}
foreach (array('', 'zend') as $prefix) {
$releasetype = $prefix . 'extsrcrelease';
if (isset($this->_packageInfo[$releasetype])) {
$release = $releasetype;
if
(!isset($this->_packageInfo['providesextension'])) {
$this->_mustProvideExtension($release);
}
if (isset($this->_packageInfo['srcpackage'])
|| isset($this->_packageInfo['srcuri'])) {
$this->_cannotHaveSrcpackage($release);
}
$releases = $this->_packageInfo[$releasetype];
if (!is_array($releases)) {
return true;
}
if (!isset($releases[0])) {
$releases = array($releases);
}
foreach ($releases as $rel) {
$this->_stupidSchemaValidate(array(
'*installconditions',
'*configureoption->name->prompt->?default',
'*binarypackage',
'*filelist',
), $rel, '<' . $releasetype .
'>');
if (isset($rel['binarypackage'])) {
if (!is_array($rel['binarypackage']) ||
!isset($rel['binarypackage'][0])) {
$rel['binarypackage'] =
array($rel['binarypackage']);
}
foreach ($rel['binarypackage'] as $bin) {
if (!is_string($bin)) {
$this->_binaryPackageMustBePackagename();
}
}
}
}
}
$releasetype = 'extbinrelease';
if (isset($this->_packageInfo[$releasetype])) {
$release = $releasetype;
if
(!isset($this->_packageInfo['providesextension'])) {
$this->_mustProvideExtension($release);
}
if (isset($this->_packageInfo['channel'])
&&
!isset($this->_packageInfo['srcpackage'])) {
$this->_mustSrcPackage($release);
}
if (isset($this->_packageInfo['uri'])
&& !isset($this->_packageInfo['srcuri'])) {
$this->_mustSrcuri($release);
}
$releases = $this->_packageInfo[$releasetype];
if (!is_array($releases)) {
return true;
}
if (!isset($releases[0])) {
$releases = array($releases);
}
foreach ($releases as $rel) {
$this->_stupidSchemaValidate(array(
'*installconditions',
'*filelist',
), $rel, '<' . $releasetype .
'>');
}
}
}
if (isset($this->_packageInfo['bundle'])) {
$release = 'bundle';
if
(isset($this->_packageInfo['providesextension'])) {
$this->_cannotProvideExtension($release);
}
if (isset($this->_packageInfo['srcpackage']) ||
isset($this->_packageInfo['srcuri'])) {
$this->_cannotHaveSrcpackage($release);
}
$releases = $this->_packageInfo['bundle'];
if (!is_array($releases) || !isset($releases[0])) {
$releases = array($releases);
}
foreach ($releases as $rel) {
$this->_stupidSchemaValidate(array(
'*installconditions',
'*filelist',
), $rel, '<bundle>');
}
}
foreach ($releases as $rel) {
if (is_array($rel) &&
array_key_exists('installconditions', $rel)) {
$this->_validateInstallConditions($rel['installconditions'],
"<$release><installconditions>");
}
if (is_array($rel) &&
array_key_exists('filelist', $rel)) {
if ($rel['filelist']) {
$this->_validateFilelist($rel['filelist'],
true);
}
}
}
}
/**
* This is here to allow role extension through plugins
* @param string
*/
function _validateRole($role)
{
return in_array($role,
PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
}
function _pearVersionTooLow($version)
{
$this->_stack->push(__FUNCTION__, 'error',
array('version' => $version),
'This package.xml requires PEAR version %version% to parse
properly, we are ' .
'version 1.10.16');
}
function _invalidTagOrder($oktags, $actual, $root)
{
$this->_stack->push(__FUNCTION__, 'error',
array('oktags' => $oktags, 'actual'
=> $actual, 'root' => $root),
'Invalid tag order in %root%, found <%actual%>
expected one of "%oktags%"');
}
function _ignoreNotAllowed($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type),
'<%type%> is not allowed inside global
<contents>, only inside ' .
'<phprelease>/<extbinrelease>/<zendextbinrelease>,
use <dir> and <file> only');
}
function _fileNotAllowed($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type),
'<%type%> is not allowed inside release
<filelist>, only inside ' .
'<contents>, use <ignore> and <install>
only');
}
function _oldStyleFileNotAllowed()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'Old-style <file>name</file> is not allowed.
Use' .
'<file name="name"
role="role"/>');
}
function _tagMissingAttribute($tag, $attr, $context)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $tag,
'attribute' => $attr, 'context' =>
$context),
'tag <%tag%> in context "%context%" has no
attribute "%attribute%"');
}
function _tagHasNoAttribs($tag, $context)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $tag,
'context' => $context),
'tag <%tag%> has no attributes in context
"%context%"');
}
function _invalidInternalStructure()
{
$this->_stack->push(__FUNCTION__, 'exception',
array(),
'internal array was not generated by compatible parser, or
extreme parser error, cannot continue');
}
function _invalidFileRole($file, $dir, $role)
{
$this->_stack->push(__FUNCTION__, 'error', array(
'file' => $file, 'dir' => $dir,
'role' => $role,
'roles' =>
PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
'File "%file%" in directory "%dir%"
has invalid role "%role%", should be one of %roles%');
}
function _invalidFileName($file, $dir)
{
$this->_stack->push(__FUNCTION__, 'error', array(
'file' => $file),
'File "%file%" in directory "%dir%"
cannot begin with "./" or contain ".."');
}
function _invalidFileInstallAs($file, $as)
{
$this->_stack->push(__FUNCTION__, 'error', array(
'file' => $file, 'as' => $as),
'File "%file%" <install
as="%as%"/> cannot contain "./" or contain
".."');
}
function _invalidDirName($dir)
{
$this->_stack->push(__FUNCTION__, 'error', array(
'dir' => $file),
'Directory "%dir%" cannot begin with
"./" or contain ".."');
}
function _filelistCannotContainFile($filelist)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $filelist),
'<%tag%> can only contain <dir>, contains
<file>. Use ' .
'<dir name="/"> as the first dir
element');
}
function _filelistMustContainDir($filelist)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $filelist),
'<%tag%> must contain <dir>. Use <dir
name="/"> as the ' .
'first dir element');
}
function _tagCannotBeEmpty($tag)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $tag),
'<%tag%> cannot be empty (<%tag%/>)');
}
function _UrlOrChannel($type, $name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type,
'name' => $name),
'Required dependency <%type%> "%name%" can
have either url OR ' .
'channel attributes, and not both');
}
function _NoChannel($type, $name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type,
'name' => $name),
'Required dependency <%type%> "%name%"
must have either url OR ' .
'channel attributes');
}
function _UrlOrChannelGroup($type, $name, $group)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type,
'name' => $name, 'group' => $group),
'Group "%group%" dependency <%type%>
"%name%" can have either url OR ' .
'channel attributes, and not both');
}
function _NoChannelGroup($type, $name, $group)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type,
'name' => $name, 'group' => $group),
'Group "%group%" dependency <%type%>
"%name%" must have either url OR ' .
'channel attributes');
}
function _unknownChannel($channel)
{
$this->_stack->push(__FUNCTION__, 'error',
array('channel' => $channel),
'Unknown channel "%channel%"');
}
function _noPackageVersion()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'package.xml <package> tag has no version attribute,
or version is not 2.0');
}
function _NoBundledPackages()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'No <bundledpackage> tag was found in
<contents>, required for bundle packages');
}
function _AtLeast2BundledPackages()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'At least 2 packages must be bundled in a bundle
package');
}
function _ChannelOrUri($name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('name' => $name),
'Bundled package "%name%" can have either a uri
or a channel, not both');
}
function _noChildTag($child, $tag)
{
$this->_stack->push(__FUNCTION__, 'error',
array('child' => $child, 'tag' => $tag),
'Tag <%tag%> is missing child tag
<%child%>');
}
function _invalidVersion($type, $value)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type, 'value' => $value),
'Version type <%type%> is not a valid version
(%value%)');
}
function _invalidState($type, $value)
{
$states = array('stable', 'beta',
'alpha', 'devel');
if ($type != 'api') {
$states[] = 'snapshot';
}
if (strtolower($value) == 'rc') {
$this->_stack->push(__FUNCTION__, 'error',
array('version' =>
$this->_packageInfo['version']['release']),
'RC is not a state, it is a version postfix, try
%version%RC1, stability beta');
}
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type, 'value' => $value,
'types' => $states),
'Stability type <%type%> is not a valid stability
(%value%), must be one of ' .
'%types%');
}
function _invalidTask($task, $ret, $file)
{
switch ($ret[0]) {
case PEAR_TASK_ERROR_MISSING_ATTRIB :
$info = array('attrib' => $ret[1],
'task' => $task, 'file' => $file);
$msg = 'task <%task%> is missing attribute
"%attrib%" in file %file%';
break;
case PEAR_TASK_ERROR_NOATTRIBS :
$info = array('task' => $task,
'file' => $file);
$msg = 'task <%task%> has no attributes in file
%file%';
break;
case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
$info = array('attrib' => $ret[1],
'values' => $ret[3],
'was' => $ret[2], 'task' =>
$task, 'file' => $file);
$msg = 'task <%task%> attribute
"%attrib%" has the wrong value "%was%" '.
'in file %file%, expecting one of
"%values%"';
break;
case PEAR_TASK_ERROR_INVALID :
$info = array('reason' => $ret[1],
'task' => $task, 'file' => $file);
$msg = 'task <%task%> in file %file% is invalid
because of "%reason%"';
break;
}
$this->_stack->push(__FUNCTION__, 'error', $info,
$msg);
}
function _unknownTask($task, $file)
{
$this->_stack->push(__FUNCTION__, 'error',
array('task' => $task, 'file' => $file),
'Unknown task "%task%" passed in file <file
name="%file%">');
}
function _subpackageCannotProvideExtension($name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('name' => $name),
'Subpackage dependency "%name%" cannot use
<providesextension>, ' .
'only package dependencies can use this tag');
}
function _subpackagesCannotConflict($name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('name' => $name),
'Subpackage dependency "%name%" cannot use
<conflicts/>, ' .
'only package dependencies can use this tag');
}
function _cannotProvideExtension($release)
{
$this->_stack->push(__FUNCTION__, 'error',
array('release' => $release),
'<%release%> packages cannot use
<providesextension>, only extbinrelease, extsrcrelease,
zendextsrcrelease, and zendextbinrelease can provide a PHP
extension');
}
function _mustProvideExtension($release)
{
$this->_stack->push(__FUNCTION__, 'error',
array('release' => $release),
'<%release%> packages must use
<providesextension> to indicate which PHP extension is
provided');
}
function _cannotHaveSrcpackage($release)
{
$this->_stack->push(__FUNCTION__, 'error',
array('release' => $release),
'<%release%> packages cannot specify a source code
package, only extension binaries may use the <srcpackage> tag');
}
function _mustSrcPackage($release)
{
$this->_stack->push(__FUNCTION__, 'error',
array('release' => $release),
'<extbinrelease>/<zendextbinrelease> packages
must specify a source code package with <srcpackage>');
}
function _mustSrcuri($release)
{
$this->_stack->push(__FUNCTION__, 'error',
array('release' => $release),
'<extbinrelease>/<zendextbinrelease> packages
must specify a source code package with <srcuri>');
}
function _uriDepsCannotHaveVersioning($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type),
'%type%: dependencies with a <uri> tag cannot have
any versioning information');
}
function _conflictingDepsCannotHaveVersioning($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type),
'%type%: conflicting dependencies cannot have versioning
info, use <exclude> to ' .
'exclude specific versions of a dependency');
}
function _DepchannelCannotBeUri($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('type' => $type),
'%type%: channel cannot be __uri, this is a pseudo-channel
reserved for uri ' .
'dependencies only');
}
function _bundledPackagesMustBeFilename()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'<bundledpackage> tags must contain only the
filename of a package release ' .
'in the bundle');
}
function _binaryPackageMustBePackagename()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'<binarypackage> tags must contain the name of a
package that is ' .
'a compiled version of this extsrc/zendextsrc
package');
}
function _fileNotFound($file)
{
$this->_stack->push(__FUNCTION__, 'error',
array('file' => $file),
'File "%file%" in package.xml does not
exist');
}
function _notInContents($file, $tag)
{
$this->_stack->push(__FUNCTION__, 'error',
array('file' => $file, 'tag' => $tag),
'<%tag% name="%file%"> is invalid, file is
not in <contents>');
}
function _cannotValidateNoPathSet()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'Cannot validate files, no path to package file is set
(use setPackageFile())');
}
function _usesroletaskMustHaveChannelOrUri($role, $tag)
{
$this->_stack->push(__FUNCTION__, 'error',
array('role' => $role, 'tag' => $tag),
'<%tag%> for role "%role%" must contain
either <uri>, or <channel> and <package>');
}
function _usesroletaskMustHavePackage($role, $tag)
{
$this->_stack->push(__FUNCTION__, 'error',
array('role' => $role, 'tag' => $tag),
'<%tag%> for role "%role%" must contain
<package>');
}
function _usesroletaskMustHaveRoleTask($tag, $type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $tag, 'type' => $type),
'<%tag%> must contain <%type%> defining the
%type% to be used');
}
function _cannotConflictWithAllOs($type)
{
$this->_stack->push(__FUNCTION__, 'error',
array('tag' => $tag),
'%tag% cannot conflict with all OSes');
}
function _invalidDepGroupName($name)
{
$this->_stack->push(__FUNCTION__, 'error',
array('name' => $name),
'Invalid dependency group name "%name%"');
}
function _multipleToplevelDirNotAllowed()
{
$this->_stack->push(__FUNCTION__, 'error', array(),
'Multiple top-level <dir> tags are not allowed.
Enclose them ' .
'in a <dir name="/">');
}
function _multipleInstallAs($file)
{
$this->_stack->push(__FUNCTION__, 'error',
array('file' => $file),
'Only one <install> tag is allowed for file
"%file%"');
}
function _ignoreAndInstallAs($file)
{
$this->_stack->push(__FUNCTION__, 'error',
array('file' => $file),
'Cannot have both <ignore> and <install> tags
for file "%file%"');
}
function _analyzeBundledPackages()
{
if (!$this->_isValid) {
return false;
}
if (!$this->_pf->getPackageType() == 'bundle') {
return false;
}
if (!isset($this->_pf->_packageFile)) {
return false;
}
$dir_prefix = dirname($this->_pf->_packageFile);
$common = new PEAR_Common;
$log = isset($this->_pf->_logger) ?
array(&$this->_pf->_logger, 'log') :
array($common, 'log');
$info = $this->_pf->getContents();
$info = $info['bundledpackage'];
if (!is_array($info)) {
$info = array($info);
}
$pkg = new PEAR_PackageFile($this->_pf->_config);
foreach ($info as $package) {
if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package))
{
$this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR .
$package);
$this->_isValid = 0;
continue;
}
call_user_func_array($log, array(1, "Analyzing bundled
package $package"));
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR .
$package,
PEAR_VALIDATE_NORMAL);
PEAR::popErrorHandling();
if (PEAR::isError($ret)) {
call_user_func_array($log, array(0, "ERROR: package
$package is not a valid " .
'package'));
$inf = $ret->getUserInfo();
if (is_array($inf)) {
foreach ($inf as $err) {
call_user_func_array($log, array(1,
$err['message']));
}
}
return false;
}
}
return true;
}
function _analyzePhpFiles()
{
if (!$this->_isValid) {
return false;
}
if (!isset($this->_pf->_packageFile)) {
$this->_cannotValidateNoPathSet();
return false;
}
$dir_prefix = dirname($this->_pf->_packageFile);
$common = new PEAR_Common;
$log = isset($this->_pf->_logger) ?
array(&$this->_pf->_logger, 'log') :
array(&$common, 'log');
$info = $this->_pf->getContents();
if (!$info || !isset($info['dir']['file'])) {
$this->_tagCannotBeEmpty('contents><dir');
return false;
}
$info = $info['dir']['file'];
if (isset($info['attribs'])) {
$info = array($info);
}
$provides = array();
foreach ($info as $fa) {
$fa = $fa['attribs'];
$file = $fa['name'];
if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
$this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR .
$file);
$this->_isValid = 0;
continue;
}
if (in_array($fa['role'],
PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
call_user_func_array($log, array(1, "Analyzing
$file"));
$srcinfo = $this->analyzeSourceCode($dir_prefix .
DIRECTORY_SEPARATOR . $file);
if ($srcinfo) {
$provides = array_merge($provides,
$this->_buildProvidesArray($srcinfo));
}
}
}
$this->_packageName = $pn = $this->_pf->getPackage();
$pnl = strlen($pn);
foreach ($provides as $key => $what) {
if (isset($what['explicit']) || !$what) {
// skip conformance checks if the provides entry is
// specified in the package.xml file
continue;
}
extract($what);
if ($type == 'class') {
if (!strncasecmp($name, $pn, $pnl)) {
continue;
}
$this->_stack->push(__FUNCTION__,
'warning',
array('file' => $file, 'type'
=> $type, 'name' => $name, 'package' => $pn),
'in %file%: %type% "%name%" not prefixed
with package name "%package%"');
} elseif ($type == 'function') {
if (strstr($name, '::') || !strncasecmp($name,
$pn, $pnl)) {
continue;
}
$this->_stack->push(__FUNCTION__,
'warning',
array('file' => $file, 'type'
=> $type, 'name' => $name, 'package' => $pn),
'in %file%: %type% "%name%" not prefixed
with package name "%package%"');
}
}
return $this->_isValid;
}
/**
* Analyze the source code of the given PHP file
*
* @param string Filename of the PHP file
* @param boolean whether to analyze $file as the file contents
* @return mixed
*/
function analyzeSourceCode($file, $string = false)
{
if (!function_exists("token_get_all")) {
$this->_stack->push(__FUNCTION__, 'error',
array('file' => $file),
'Parser error: token_get_all() function must exist to
analyze source code, PHP may have been compiled with
--disable-tokenizer');
return false;
}
if (!defined('T_DOC_COMMENT')) {
define('T_DOC_COMMENT', T_COMMENT);
}
if (!defined('T_INTERFACE')) {
define('T_INTERFACE', -1);
}
if (!defined('T_IMPLEMENTS')) {
define('T_IMPLEMENTS', -1);
}
if ($string) {
$contents = $file;
} else {
if (!$fp = @fopen($file, "r")) {
return false;
}
fclose($fp);
$contents = file_get_contents($file);
}
// Silence this function so we can catch PHP Warnings and show our
own custom message
$tokens = @token_get_all($contents);
if (isset($php_errormsg)) {
if (isset($this->_stack)) {
$pn = $this->_pf->getPackage();
$this->_stack->push(__FUNCTION__,
'warning',
array('file' => $file,
'package' => $pn),
'in %file%: Could not process file for unknown
reasons,' .
' possibly a PHP parse error in %file% from
%package%');
}
}
/*
for ($i = 0; $i < sizeof($tokens); $i++) {
@list($token, $data) = $tokens[$i];
if (is_string($token)) {
var_dump($token);
} else {
print token_name($token) . ' ';
var_dump(rtrim($data));
}
}
*/
$look_for = 0;
$paren_level = 0;
$bracket_level = 0;
$brace_level = 0;
$lastphpdoc = '';
$current_class = '';
$current_interface = '';
$current_class_level = -1;
$current_function = '';
$current_function_level = -1;
$declared_classes = array();
$declared_interfaces = array();
$declared_functions = array();
$declared_methods = array();
$used_classes = array();
$used_functions = array();
$extends = array();
$implements = array();
$nodeps = array();
$inquote = false;
$interface = false;
for ($i = 0; $i < sizeof($tokens); $i++) {
if (is_array($tokens[$i])) {
list($token, $data) = $tokens[$i];
} else {
$token = $tokens[$i];
$data = '';
}
if ($inquote) {
if ($token != '"' && $token !=
T_END_HEREDOC) {
continue;
} else {
$inquote = false;
continue;
}
}
switch ($token) {
case T_WHITESPACE :
continue 2;
case ';':
if ($interface) {
$current_function = '';
$current_function_level = -1;
}
break;
case '"':
case T_START_HEREDOC:
$inquote = true;
break;
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case '{': $brace_level++; continue 2;
case '}':
$brace_level--;
if ($current_class_level == $brace_level) {
$current_class = '';
$current_class_level = -1;
}
if ($current_function_level == $brace_level) {
$current_function = '';
$current_function_level = -1;
}
continue 2;
case '[': $bracket_level++; continue 2;
case ']': $bracket_level--; continue 2;
case '(': $paren_level++; continue 2;
case ')': $paren_level--; continue 2;
case T_INTERFACE:
$interface = true;
case T_CLASS:
if (($current_class_level != -1) ||
($current_function_level != -1)) {
if (isset($this->_stack)) {
$this->_stack->push(__FUNCTION__,
'error', array('file' => $file),
'Parser error: invalid PHP found in file
"%file%"');
} else {
PEAR::raiseError("Parser error: invalid
PHP found in file \"$file\"",
PEAR_COMMON_ERROR_INVALIDPHP);
}
return false;
}
case T_FUNCTION:
case T_NEW:
case T_EXTENDS:
case T_IMPLEMENTS:
$look_for = $token;
continue 2;
case T_STRING:
if ($look_for == T_CLASS) {
$current_class = $data;
$current_class_level = $brace_level;
$declared_classes[] = $current_class;
} elseif ($look_for == T_INTERFACE) {
$current_interface = $data;
$current_class_level = $brace_level;
$declared_interfaces[] = $current_interface;
} elseif ($look_for == T_IMPLEMENTS) {
$implements[$current_class] = $data;
} elseif ($look_for == T_EXTENDS) {
$extends[$current_class] = $data;
} elseif ($look_for == T_FUNCTION) {
if ($current_class) {
$current_function =
"$current_class::$data";
$declared_methods[$current_class][] = $data;
} elseif ($current_interface) {
$current_function =
"$current_interface::$data";
$declared_methods[$current_interface][] =
$data;
} else {
$current_function = $data;
$declared_functions[] = $current_function;
}
$current_function_level = $brace_level;
$m = array();
} elseif ($look_for == T_NEW) {
$used_classes[$data] = true;
}
$look_for = 0;
continue 2;
case T_VARIABLE:
$look_for = 0;
continue 2;
case T_DOC_COMMENT:
case T_COMMENT:
if (preg_match('!^/\*\*\s!', $data)) {
$lastphpdoc = $data;
if (preg_match_all('/@nodep\s+(\S+)/',
$lastphpdoc, $m)) {
$nodeps = array_merge($nodeps, $m[1]);
}
}
continue 2;
case T_DOUBLE_COLON:
$token = $tokens[$i - 1][0];
if (!($token == T_WHITESPACE || $token == T_STRING ||
$token == T_STATIC || $token == T_VARIABLE)) {
if (isset($this->_stack)) {
$this->_stack->push(__FUNCTION__,
'warning', array('file' => $file),
'Parser error: invalid PHP found in
file "%file%"');
} else {
PEAR::raiseError("Parser error: invalid
PHP found in file \"$file\"",
PEAR_COMMON_ERROR_INVALIDPHP);
}
return false;
}
$class = $tokens[$i - 1][1];
if (strtolower($class) != 'parent') {
$used_classes[$class] = true;
}
continue 2;
}
}
return array(
"source_file" => $file,
"declared_classes" => $declared_classes,
"declared_interfaces" => $declared_interfaces,
"declared_methods" => $declared_methods,
"declared_functions" => $declared_functions,
"used_classes" =>
array_diff(array_keys($used_classes), $nodeps),
"inheritance" => $extends,
"implements" => $implements,
);
}
/**
* Build a "provides" array from data returned by
* analyzeSourceCode(). The format of the built array is like
* this:
*
* array(
* 'class;MyClass' => 'array('type'
=> 'class', 'name' => 'MyClass'),
* ...
* )
*
*
* @param array $srcinfo array with information about a source file
* as returned by the analyzeSourceCode() method.
*
* @return void
*
* @access private
*
*/
function _buildProvidesArray($srcinfo)
{
if (!$this->_isValid) {
return array();
}
$providesret = array();
$file = basename($srcinfo['source_file']);
$pn = isset($this->_pf) ?
$this->_pf->getPackage() : '';
$pnl = strlen($pn);
foreach ($srcinfo['declared_classes'] as $class) {
$key = "class;$class";
if (isset($providesret[$key])) {
continue;
}
$providesret[$key] =
array('file'=> $file, 'type' =>
'class', 'name' => $class);
if (isset($srcinfo['inheritance'][$class])) {
$providesret[$key]['extends'] =
$srcinfo['inheritance'][$class];
}
}
foreach ($srcinfo['declared_methods'] as $class =>
$methods) {
foreach ($methods as $method) {
$function = "$class::$method";
$key = "function;$function";
if ($method[0] == '_' || !strcasecmp($method,
$class) ||
isset($providesret[$key])) {
continue;
}
$providesret[$key] =
array('file'=> $file, 'type'
=> 'function', 'name' => $function);
}
}
foreach ($srcinfo['declared_functions'] as $function) {
$key = "function;$function";
if ($function[0] == '_' || isset($providesret[$key]))
{
continue;
}
if (!strstr($function, '::') &&
strncasecmp($function, $pn, $pnl)) {
$warnings[] = "in1 " . $file . ": function
\"$function\" not prefixed with package name
\"$pn\"";
}
$providesret[$key] =
array('file'=> $file, 'type' =>
'function', 'name' => $function);
}
return $providesret;
}
}