home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / tmp / PEAR-1.7.1 / PEAR / PackageFile / v2 / Validator.php < prev   
Encoding:
PHP Script  |  2008-02-15  |  83.0 KB  |  2,109 lines

  1. <?php
  2. /**
  3.  * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Greg Beaver <cellog@php.net>
  16.  * @copyright  1997-2008 The PHP Group
  17.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  18.  * @version    CVS: $Id: Validator.php,v 1.103 2008/01/03 20:26:37 cellog Exp $
  19.  * @link       http://pear.php.net/package/PEAR
  20.  * @since      File available since Release 1.4.0a8
  21.  */
  22. /**
  23.  * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
  24.  * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
  25.  * @category   pear
  26.  * @package    PEAR
  27.  * @author     Greg Beaver <cellog@php.net>
  28.  * @copyright  1997-2008 The PHP Group
  29.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  30.  * @version    Release: 1.7.1
  31.  * @link       http://pear.php.net/package/PEAR
  32.  * @since      Class available since Release 1.4.0a8
  33.  * @access private
  34.  */
  35. class PEAR_PackageFile_v2_Validator
  36. {
  37.     /**
  38.      * @var array
  39.      */
  40.     var $_packageInfo;
  41.     /**
  42.      * @var PEAR_PackageFile_v2
  43.      */
  44.     var $_pf;
  45.     /**
  46.      * @var PEAR_ErrorStack
  47.      */
  48.     var $_stack;
  49.     /**
  50.      * @var int
  51.      */
  52.     var $_isValid = 0;
  53.     /**
  54.      * @var int
  55.      */
  56.     var $_filesValid = 0;
  57.     /**
  58.      * @var int
  59.      */
  60.     var $_curState = 0;
  61.     /**
  62.      * @param PEAR_PackageFile_v2
  63.      * @param int
  64.      */
  65.     function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
  66.     {
  67.         $this->_pf = &$pf;
  68.         $this->_curState = $state;
  69.         $this->_packageInfo = $this->_pf->getArray();
  70.         $this->_isValid = $this->_pf->_isValid;
  71.         $this->_filesValid = $this->_pf->_filesValid;
  72.         $this->_stack = &$pf->_stack;
  73.         $this->_stack->getErrors(true);
  74.         if (($this->_isValid & $state) == $state) {
  75.             return true;
  76.         }
  77.         if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  78.             return false;
  79.         }
  80.         if (!isset($this->_packageInfo['attribs']['version']) ||
  81.               ($this->_packageInfo['attribs']['version'] != '2.0' &&
  82.                $this->_packageInfo['attribs']['version'] != '2.1')) {
  83.             $this->_noPackageVersion();
  84.         }
  85.         $structure =
  86.         array(
  87.             'name',
  88.             'channel|uri',
  89.             '*extends', // can't be multiple, but this works fine
  90.             'summary',
  91.             'description',
  92.             '+lead', // these all need content checks
  93.             '*developer',
  94.             '*contributor',
  95.             '*helper',
  96.             'date',
  97.             '*time',
  98.             'version',
  99.             'stability',
  100.             'license->?uri->?filesource',
  101.             'notes',
  102.             'contents', //special validation needed
  103.             '*compatible',
  104.             'dependencies', //special validation needed
  105.             '*usesrole',
  106.             '*usestask', // reserve these for 1.4.0a1 to implement
  107.                          // this will allow a package.xml to gracefully say it
  108.                          // needs a certain package installed in order to implement a role or task
  109.             '*providesextension',
  110.             '*srcpackage|*srcuri',
  111.             '+phprelease|+extsrcrelease|+extbinrelease|' .
  112.                 '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
  113.             '*changelog',
  114.         );
  115.         $test = $this->_packageInfo;
  116.         if (isset($test['dependencies']) &&
  117.               isset($test['dependencies']['required']) &&
  118.               isset($test['dependencies']['required']['pearinstaller']) &&
  119.               isset($test['dependencies']['required']['pearinstaller']['min']) &&
  120.               version_compare('1.7.1',
  121.                 $test['dependencies']['required']['pearinstaller']['min'], '<')) {
  122.             $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
  123.             return false;
  124.         }
  125.         // ignore post-installation array fields
  126.         if (array_key_exists('filelist', $test)) {
  127.             unset($test['filelist']);
  128.         }
  129.         if (array_key_exists('_lastmodified', $test)) {
  130.             unset($test['_lastmodified']);
  131.         }
  132.         if (array_key_exists('#binarypackage', $test)) {
  133.             unset($test['#binarypackage']);
  134.         }
  135.         if (array_key_exists('old', $test)) {
  136.             unset($test['old']);
  137.         }
  138.         if (array_key_exists('_lastversion', $test)) {
  139.             unset($test['_lastversion']);
  140.         }
  141.         if (!$this->_stupidSchemaValidate($structure,
  142.                                           $test, '<package>')) {
  143.             return false;
  144.         }
  145.         if (empty($this->_packageInfo['name'])) {
  146.             $this->_tagCannotBeEmpty('name');
  147.         }
  148.         if (isset($this->_packageInfo['uri'])) {
  149.             $test = 'uri';
  150.         } else {
  151.             $test = 'channel';
  152.         }
  153.         if (empty($this->_packageInfo[$test])) {
  154.             $this->_tagCannotBeEmpty($test);
  155.         }
  156.         if (is_array($this->_packageInfo['license']) &&
  157.               (!isset($this->_packageInfo['license']['_content']) ||
  158.               empty($this->_packageInfo['license']['_content']))) {
  159.             $this->_tagCannotBeEmpty('license');
  160.         } elseif (empty($this->_packageInfo['license'])) {
  161.             $this->_tagCannotBeEmpty('license');
  162.         }
  163.         if (empty($this->_packageInfo['summary'])) {
  164.             $this->_tagCannotBeEmpty('summary');
  165.         }
  166.         if (empty($this->_packageInfo['description'])) {
  167.             $this->_tagCannotBeEmpty('description');
  168.         }
  169.         if (empty($this->_packageInfo['date'])) {
  170.             $this->_tagCannotBeEmpty('date');
  171.         }
  172.         if (empty($this->_packageInfo['notes'])) {
  173.             $this->_tagCannotBeEmpty('notes');
  174.         }
  175.         if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
  176.             $this->_tagCannotBeEmpty('time');
  177.         }
  178.         if (isset($this->_packageInfo['dependencies'])) {
  179.             $this->_validateDependencies();
  180.         }
  181.         if (isset($this->_packageInfo['compatible'])) {
  182.             $this->_validateCompatible();
  183.         }
  184.         if (!isset($this->_packageInfo['bundle'])) {
  185.             if (empty($this->_packageInfo['contents'])) {
  186.                 $this->_tagCannotBeEmpty('contents');
  187.             }
  188.             if (!isset($this->_packageInfo['contents']['dir'])) {
  189.                 $this->_filelistMustContainDir('contents');
  190.                 return false;
  191.             }
  192.             if (isset($this->_packageInfo['contents']['file'])) {
  193.                 $this->_filelistCannotContainFile('contents');
  194.                 return false;
  195.             }
  196.         }
  197.         $this->_validateMaintainers();
  198.         $this->_validateStabilityVersion();
  199.         $fail = false;
  200.         if (array_key_exists('usesrole', $this->_packageInfo)) {
  201.             $roles = $this->_packageInfo['usesrole'];
  202.             if (!is_array($roles) || !isset($roles[0])) {
  203.                 $roles = array($roles);
  204.             }
  205.             foreach ($roles as $role) {
  206.                 if (!isset($role['role'])) {
  207.                     $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
  208.                     $fail = true;
  209.                 } else {
  210.                     if (!isset($role['channel'])) {
  211.                         if (!isset($role['uri'])) {
  212.                             $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
  213.                             $fail = true;
  214.                         }
  215.                     } elseif (!isset($role['package'])) {
  216.                         $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
  217.                         $fail = true;
  218.                     }
  219.                 }
  220.             }
  221.         }
  222.         if (array_key_exists('usestask', $this->_packageInfo)) {
  223.             $roles = $this->_packageInfo['usestask'];
  224.             if (!is_array($roles) || !isset($roles[0])) {
  225.                 $roles = array($roles);
  226.             }
  227.             foreach ($roles as $role) {
  228.                 if (!isset($role['task'])) {
  229.                     $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
  230.                     $fail = true;
  231.                 } else {
  232.                     if (!isset($role['channel'])) {
  233.                         if (!isset($role['uri'])) {
  234.                             $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
  235.                             $fail = true;
  236.                         }
  237.                     } elseif (!isset($role['package'])) {
  238.                         $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
  239.                         $fail = true;
  240.                     }
  241.                 }
  242.             }
  243.         }
  244.         if ($fail) {
  245.             return false;
  246.         }
  247.         $list = $this->_packageInfo['contents'];
  248.         if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
  249.             $this->_multipleToplevelDirNotAllowed();
  250.             return $this->_isValid = 0;
  251.         }
  252.         $this->_validateFilelist();
  253.         $this->_validateRelease();
  254.         if (!$this->_stack->hasErrors()) {
  255.             $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
  256.             if (PEAR::isError($chan)) {
  257.                 $this->_unknownChannel($this->_pf->getChannel());
  258.             } else {
  259.                 $valpack = $chan->getValidationPackage();
  260.                 // for channel validator packages, always use the default PEAR validator.
  261.                 // otherwise, they can't be installed or packaged
  262.                 $validator = $chan->getValidationObject($this->_pf->getPackage());
  263.                 if (!$validator) {
  264.                     $this->_stack->push(__FUNCTION__, 'error',
  265.                         array_merge(
  266.                             array('channel' => $chan->getName(),
  267.                                   'package' => $this->_pf->getPackage()),
  268.                               $valpack
  269.                             ),
  270.                         'package "%channel%/%package%" cannot be properly validated without ' .
  271.                         'validation package "%channel%/%name%-%version%"');
  272.                     return $this->_isValid = 0;
  273.                 }
  274.                 $validator->setPackageFile($this->_pf);
  275.                 $validator->validate($state);
  276.                 $failures = $validator->getFailures();
  277.                 foreach ($failures['errors'] as $error) {
  278.                     $this->_stack->push(__FUNCTION__, 'error', $error,
  279.                         'Channel validator error: field "%field%" - %reason%');
  280.                 }
  281.                 foreach ($failures['warnings'] as $warning) {
  282.                     $this->_stack->push(__FUNCTION__, 'warning', $warning,
  283.                         'Channel validator warning: field "%field%" - %reason%');
  284.                 }
  285.             }
  286.         }
  287.         $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
  288.         if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
  289.             if ($this->_pf->getPackageType() == 'bundle') {
  290.                 if ($this->_analyzeBundledPackages()) {
  291.                     $this->_filesValid = $this->_pf->_filesValid = true;
  292.                 } else {
  293.                     $this->_pf->_isValid = $this->_isValid = 0;
  294.                 }
  295.             } else {
  296.                 if (!$this->_analyzePhpFiles()) {
  297.                     $this->_pf->_isValid = $this->_isValid = 0;
  298.                 } else {
  299.                     $this->_filesValid = $this->_pf->_filesValid = true;
  300.                 }
  301.             }
  302.         }
  303.         if ($this->_isValid) {
  304.             return $this->_pf->_isValid = $this->_isValid = $state;
  305.         }
  306.         return $this->_pf->_isValid = $this->_isValid = 0;
  307.     }
  308.  
  309.     function _stupidSchemaValidate($structure, $xml, $root)
  310.     {
  311.         if (!is_array($xml)) {
  312.             $xml = array();
  313.         }
  314.         $keys = array_keys($xml);
  315.         reset($keys);
  316.         $key = current($keys);
  317.         while ($key == 'attribs' || $key == '_contents') {
  318.             $key = next($keys);
  319.         }
  320.         $unfoundtags = $optionaltags = array();
  321.         $ret = true;
  322.         $mismatch = false;
  323.         foreach ($structure as $struc) {
  324.             if ($key) {
  325.                 $tag = $xml[$key];
  326.             }
  327.             $test = $this->_processStructure($struc);
  328.             if (isset($test['choices'])) {
  329.                 $loose = true;
  330.                 foreach ($test['choices'] as $choice) {
  331.                     if ($key == $choice['tag']) {
  332.                         $key = next($keys);
  333.                         while ($key == 'attribs' || $key == '_contents') {
  334.                             $key = next($keys);
  335.                         }
  336.                         $unfoundtags = $optionaltags = array();
  337.                         $mismatch = false;
  338.                         if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
  339.                             $unfoundtags[] = $choice['tag'];
  340.                             $optionaltags[] = $choice['tag'];
  341.                             if ($key) {
  342.                                 $mismatch = true;
  343.                             }
  344.                         }
  345.                         $ret &= $this->_processAttribs($choice, $tag, $root);
  346.                         continue 2;
  347.                     } else {
  348.                         $unfoundtags[] = $choice['tag'];
  349.                         $mismatch = true;
  350.                     }
  351.                     if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
  352.                         $loose = false;
  353.                     } else {
  354.                         $optionaltags[] = $choice['tag'];
  355.                     }
  356.                 }
  357.                 if (!$loose) {
  358.                     $this->_invalidTagOrder($unfoundtags, $key, $root);
  359.                     return false;
  360.                 }
  361.             } else {
  362.                 if ($key != $test['tag']) {
  363.                     if (isset($test['multiple']) && $test['multiple'] != '*') {
  364.                         $unfoundtags[] = $test['tag'];
  365.                         $this->_invalidTagOrder($unfoundtags, $key, $root);
  366.                         return false;
  367.                     } else {
  368.                         if ($key) {
  369.                             $mismatch = true;
  370.                         }
  371.                         $unfoundtags[] = $test['tag'];
  372.                         $optionaltags[] = $test['tag'];
  373.                     }
  374.                     if (!isset($test['multiple'])) {
  375.                         $this->_invalidTagOrder($unfoundtags, $key, $root);
  376.                         return false;
  377.                     }
  378.                     continue;
  379.                 } else {
  380.                     $unfoundtags = $optionaltags = array();
  381.                     $mismatch = false;
  382.                 }
  383.                 $key = next($keys);
  384.                 while ($key == 'attribs' || $key == '_contents') {
  385.                     $key = next($keys);
  386.                 }
  387.                 if ($key && $key != $test['tag'] && isset($test['multiple'])) {
  388.                     $unfoundtags[] = $test['tag'];
  389.                     $optionaltags[] = $test['tag'];
  390.                     $mismatch = true;
  391.                 }
  392.                 $ret &= $this->_processAttribs($test, $tag, $root);
  393.                 continue;
  394.             }
  395.         }
  396.         if (!$mismatch && count($optionaltags)) {
  397.             // don't error out on any optional tags
  398.             $unfoundtags = array_diff($unfoundtags, $optionaltags);
  399.         }
  400.         if (count($unfoundtags)) {
  401.             $this->_invalidTagOrder($unfoundtags, $key, $root);
  402.         } elseif ($key) {
  403.             // unknown tags
  404.             $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  405.             while ($key = next($keys)) {
  406.                 $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  407.             }
  408.         }
  409.         return $ret;
  410.     }
  411.  
  412.     function _processAttribs($choice, $tag, $context)
  413.     {
  414.         if (isset($choice['attribs'])) {
  415.             if (!is_array($tag)) {
  416.                 $tag = array($tag);
  417.             }
  418.             $tags = $tag;
  419.             if (!isset($tags[0])) {
  420.                 $tags = array($tags);
  421.             }
  422.             $ret = true;
  423.             foreach ($tags as $i => $tag) {
  424.                 if (!is_array($tag) || !isset($tag['attribs'])) {
  425.                     foreach ($choice['attribs'] as $attrib) {
  426.                         if ($attrib{0} != '?') {
  427.                             $ret &= $this->_tagHasNoAttribs($choice['tag'],
  428.                                 $context);
  429.                             continue 2;
  430.                         }
  431.                     }
  432.                 }
  433.                 foreach ($choice['attribs'] as $attrib) {
  434.                     if ($attrib{0} != '?') {
  435.                         if (!isset($tag['attribs'][$attrib])) {
  436.                             $ret &= $this->_tagMissingAttribute($choice['tag'],
  437.                                 $attrib, $context);
  438.                         }
  439.                     }
  440.                 }
  441.             }
  442.             return $ret;
  443.         }
  444.         return true;
  445.     }
  446.  
  447.     function _processStructure($key)
  448.     {
  449.         $ret = array();
  450.         if (count($pieces = explode('|', $key)) > 1) {
  451.             $ret['choices'] = array();
  452.             foreach ($pieces as $piece) {
  453.                 $ret['choices'][] = $this->_processStructure($piece);
  454.             }
  455.             return $ret;
  456.         }
  457.         $multi = $key{0};
  458.         if ($multi == '+' || $multi == '*') {
  459.             $ret['multiple'] = $key{0};
  460.             $key = substr($key, 1);
  461.         }
  462.         if (count($attrs = explode('->', $key)) > 1) {
  463.             $ret['tag'] = array_shift($attrs);
  464.             $ret['attribs'] = $attrs;
  465.         } else {
  466.             $ret['tag'] = $key;
  467.         }
  468.         return $ret;
  469.     }
  470.  
  471.     function _validateStabilityVersion()
  472.     {
  473.         $structure = array('release', 'api');
  474.         $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
  475.         $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
  476.         if ($a) {
  477.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  478.                   $this->_packageInfo['version']['release'])) {
  479.                 $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
  480.             }
  481.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  482.                   $this->_packageInfo['version']['api'])) {
  483.                 $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
  484.             }
  485.             if (!in_array($this->_packageInfo['stability']['release'],
  486.                   array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
  487.                 $this->_invalidState('release', $this->_packageinfo['stability']['release']);
  488.             }
  489.             if (!in_array($this->_packageInfo['stability']['api'],
  490.                   array('devel', 'alpha', 'beta', 'stable'))) {
  491.                 $this->_invalidState('api', $this->_packageinfo['stability']['api']);
  492.             }
  493.         }
  494.     }
  495.  
  496.     function _validateMaintainers()
  497.     {
  498.         $structure =
  499.             array(
  500.                 'name',
  501.                 'user',
  502.                 'email',
  503.                 'active',
  504.             );
  505.         foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
  506.             if (!isset($this->_packageInfo[$type])) {
  507.                 continue;
  508.             }
  509.             if (isset($this->_packageInfo[$type][0])) {
  510.                 foreach ($this->_packageInfo[$type] as $lead) {
  511.                     $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
  512.                 }
  513.             } else {
  514.                 $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
  515.                     '<' . $type . '>');
  516.             }
  517.         }
  518.     }
  519.  
  520.     function _validatePhpDep($dep, $installcondition = false)
  521.     {
  522.         $structure = array(
  523.             'min',
  524.             '*max',
  525.             '*exclude',
  526.         );
  527.         $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
  528.         $this->_stupidSchemaValidate($structure, $dep, $type);
  529.         if (isset($dep['min'])) {
  530.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  531.                   $dep['min'])) {
  532.                 $this->_invalidVersion($type . '<min>', $dep['min']);
  533.             }
  534.         }
  535.         if (isset($dep['max'])) {
  536.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  537.                   $dep['max'])) {
  538.                 $this->_invalidVersion($type . '<max>', $dep['max']);
  539.             }
  540.         }
  541.         if (isset($dep['exclude'])) {
  542.             if (!is_array($dep['exclude'])) {
  543.                 $dep['exclude'] = array($dep['exclude']);
  544.             }
  545.             foreach ($dep['exclude'] as $exclude) {
  546.                 if (!preg_match(
  547.                      '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  548.                      $exclude)) {
  549.                     $this->_invalidVersion($type . '<exclude>', $exclude);
  550.                 }
  551.             }
  552.         }
  553.     }
  554.  
  555.     function _validatePearinstallerDep($dep)
  556.     {
  557.         $structure = array(
  558.             'min',
  559.             '*max',
  560.             '*recommended',
  561.             '*exclude',
  562.         );
  563.         $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
  564.         if (isset($dep['min'])) {
  565.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  566.                   $dep['min'])) {
  567.                 $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
  568.                     $dep['min']);
  569.             }
  570.         }
  571.         if (isset($dep['max'])) {
  572.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  573.                   $dep['max'])) {
  574.                 $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
  575.                     $dep['max']);
  576.             }
  577.         }
  578.         if (isset($dep['recommended'])) {
  579.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  580.                   $dep['recommended'])) {
  581.                 $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
  582.                     $dep['recommended']);
  583.             }
  584.         }
  585.         if (isset($dep['exclude'])) {
  586.             if (!is_array($dep['exclude'])) {
  587.                 $dep['exclude'] = array($dep['exclude']);
  588.             }
  589.             foreach ($dep['exclude'] as $exclude) {
  590.                 if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  591.                       $exclude)) {
  592.                     $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
  593.                         $exclude);
  594.                 }
  595.             }
  596.         }
  597.     }
  598.  
  599.     function _validatePackageDep($dep, $group, $type = '<package>')
  600.     {
  601.         if (isset($dep['uri'])) {
  602.             if (isset($dep['conflicts'])) {
  603.                 $structure = array(
  604.                     'name',
  605.                     'uri',
  606.                     'conflicts',
  607.                     '*providesextension',
  608.                 );
  609.             } else {
  610.                 $structure = array(
  611.                     'name',
  612.                     'uri',
  613.                     '*providesextension',
  614.                 );
  615.             }
  616.         } else {
  617.             if (isset($dep['conflicts'])) {
  618.                 $structure = array(
  619.                     'name',
  620.                     'channel',
  621.                     '*min',
  622.                     '*max',
  623.                     '*exclude',
  624.                     'conflicts',
  625.                     '*providesextension',
  626.                 );
  627.             } else {
  628.                 $structure = array(
  629.                     'name',
  630.                     'channel',
  631.                     '*min',
  632.                     '*max',
  633.                     '*recommended',
  634.                     '*exclude',
  635.                     '*nodefault',
  636.                     '*providesextension',
  637.                 );
  638.             }
  639.         }
  640.         if (isset($dep['name'])) {
  641.             $type .= '<name>' . $dep['name'] . '</name>';
  642.         }
  643.         $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
  644.         if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
  645.               isset($dep['recommended']) || isset($dep['exclude']))) {
  646.             $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
  647.         }
  648.         if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
  649.             $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
  650.         }
  651.         if (isset($dep['min'])) {
  652.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  653.                   $dep['min'])) {
  654.                 $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
  655.             }
  656.         }
  657.         if (isset($dep['max'])) {
  658.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  659.                   $dep['max'])) {
  660.                 $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
  661.             }
  662.         }
  663.         if (isset($dep['recommended'])) {
  664.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  665.                   $dep['recommended'])) {
  666.                 $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
  667.                     $dep['recommended']);
  668.             }
  669.         }
  670.         if (isset($dep['exclude'])) {
  671.             if (!is_array($dep['exclude'])) {
  672.                 $dep['exclude'] = array($dep['exclude']);
  673.             }
  674.             foreach ($dep['exclude'] as $exclude) {
  675.                 if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  676.                       $exclude)) {
  677.                     $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
  678.                         $exclude);
  679.                 }
  680.             }
  681.         }
  682.     }
  683.  
  684.     function _validateSubpackageDep($dep, $group)
  685.     {
  686.         $this->_validatePackageDep($dep, $group, '<subpackage>');
  687.         if (isset($dep['providesextension'])) {
  688.             $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
  689.         }
  690.         if (isset($dep['conflicts'])) {
  691.             $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
  692.         }
  693.     }
  694.  
  695.     function _validateExtensionDep($dep, $group = false, $installcondition = false)
  696.     {
  697.         if (isset($dep['conflicts'])) {
  698.             $structure = array(
  699.                 'name',
  700.                 '*min',
  701.                 '*max',
  702.                 '*exclude',
  703.                 'conflicts',
  704.             );
  705.         } else {
  706.             $structure = array(
  707.                 'name',
  708.                 '*min',
  709.                 '*max',
  710.                 '*recommended',
  711.                 '*exclude',
  712.             );
  713.         }
  714.         if ($installcondition) {
  715.             $type = '<installcondition><extension>';
  716.         } else {
  717.             $type = '<dependencies>' . $group . '<extension>';
  718.         }
  719.         if (isset($dep['name'])) {
  720.             $type .= '<name>' . $dep['name'] . '</name>';
  721.         }
  722.         $this->_stupidSchemaValidate($structure, $dep, $type);
  723.         if (isset($dep['min'])) {
  724.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  725.                   $dep['min'])) {
  726.                 $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
  727.             }
  728.         }
  729.         if (isset($dep['max'])) {
  730.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  731.                   $dep['max'])) {
  732.                 $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
  733.             }
  734.         }
  735.         if (isset($dep['recommended'])) {
  736.             if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  737.                   $dep['recommended'])) {
  738.                 $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
  739.             }
  740.         }
  741.         if (isset($dep['exclude'])) {
  742.             if (!is_array($dep['exclude'])) {
  743.                 $dep['exclude'] = array($dep['exclude']);
  744.             }
  745.             foreach ($dep['exclude'] as $exclude) {
  746.                 if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  747.                       $exclude)) {
  748.                     $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  749.                 }
  750.             }
  751.         }
  752.     }
  753.  
  754.     function _validateOsDep($dep, $installcondition = false)
  755.     {
  756.         $structure = array(
  757.             'name',
  758.             '*conflicts',
  759.         );
  760.         $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
  761.         if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
  762.             if ($dep['name'] == '*') {
  763.                 if (array_key_exists('conflicts', $dep)) {
  764.                     $this->_cannotConflictWithAllOs($type);
  765.                 }
  766.             }
  767.         }
  768.     }
  769.  
  770.     function _validateArchDep($dep, $installcondition = false)
  771.     {
  772.         $structure = array(
  773.             'pattern',
  774.             '*conflicts',
  775.         );
  776.         $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
  777.         $this->_stupidSchemaValidate($structure, $dep, $type);
  778.     }
  779.  
  780.     function _validateInstallConditions($cond, $release)
  781.     {
  782.         $structure = array(
  783.             '*php',
  784.             '*extension',
  785.             '*os',
  786.             '*arch',
  787.         );
  788.         if (!$this->_stupidSchemaValidate($structure,
  789.               $cond, $release)) {
  790.             return false;
  791.         }
  792.         foreach (array('php', 'extension', 'os', 'arch') as $type) {
  793.             if (isset($cond[$type])) {
  794.                 $iter = $cond[$type];
  795.                 if (!is_array($iter) || !isset($iter[0])) {
  796.                     $iter = array($iter);
  797.                 }
  798.                 foreach ($iter as $package) {
  799.                     if ($type == 'extension') {
  800.                         $this->{"_validate{$type}Dep"}($package, false, true);
  801.                     } else {
  802.                         $this->{"_validate{$type}Dep"}($package, true);
  803.                     }
  804.                 }
  805.             }
  806.         }
  807.     }
  808.  
  809.     function _validateDependencies()
  810.     {
  811.         $structure = array(
  812.             'required',
  813.             '*optional',
  814.             '*group->name->hint'
  815.         );
  816.         if (!$this->_stupidSchemaValidate($structure,
  817.               $this->_packageInfo['dependencies'], '<dependencies>')) {
  818.             return false;
  819.         }
  820.         foreach (array('required', 'optional') as $simpledep) {
  821.             if (isset($this->_packageInfo['dependencies'][$simpledep])) {
  822.                 if ($simpledep == 'optional') {
  823.                     $structure = array(
  824.                         '*package',
  825.                         '*subpackage',
  826.                         '*extension',
  827.                     );
  828.                 } else {
  829.                     $structure = array(
  830.                         'php',
  831.                         'pearinstaller',
  832.                         '*package',
  833.                         '*subpackage',
  834.                         '*extension',
  835.                         '*os',
  836.                         '*arch',
  837.                     );
  838.                 }
  839.                 if ($this->_stupidSchemaValidate($structure,
  840.                       $this->_packageInfo['dependencies'][$simpledep],
  841.                       "<dependencies><$simpledep>")) {
  842.                     foreach (array('package', 'subpackage', 'extension') as $type) {
  843.                         if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  844.                             $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  845.                             if (!isset($iter[0])) {
  846.                                 $iter = array($iter);
  847.                             }
  848.                             foreach ($iter as $package) {
  849.                                 if ($type != 'extension') {
  850.                                     if (isset($package['uri'])) {
  851.                                         if (isset($package['channel'])) {
  852.                                             $this->_UrlOrChannel($type,
  853.                                                 $package['name']);
  854.                                         }
  855.                                     } else {
  856.                                         if (!isset($package['channel'])) {
  857.                                             $this->_NoChannel($type, $package['name']);
  858.                                         }
  859.                                     }
  860.                                 }
  861.                                 $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
  862.                             }
  863.                         }
  864.                     }
  865.                     if ($simpledep == 'optional') {
  866.                         continue;
  867.                     }
  868.                     foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
  869.                         if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  870.                             $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  871.                             if (!isset($iter[0])) {
  872.                                 $iter = array($iter);
  873.                             }
  874.                             foreach ($iter as $package) {
  875.                                 $this->{"_validate{$type}Dep"}($package);
  876.                             }
  877.                         }
  878.                     }
  879.                 }
  880.             }
  881.         }
  882.         if (isset($this->_packageInfo['dependencies']['group'])) {
  883.             $groups = $this->_packageInfo['dependencies']['group'];
  884.             if (!isset($groups[0])) {
  885.                 $groups = array($groups);
  886.             }
  887.             $structure = array(
  888.                 '*package',
  889.                 '*subpackage',
  890.                 '*extension',
  891.             );
  892.             foreach ($groups as $group) {
  893.                 if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
  894.                     if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
  895.                         $this->_invalidDepGroupName($group['attribs']['name']);
  896.                     }
  897.                     foreach (array('package', 'subpackage', 'extension') as $type) {
  898.                         if (isset($group[$type])) {
  899.                             $iter = $group[$type];
  900.                             if (!isset($iter[0])) {
  901.                                 $iter = array($iter);
  902.                             }
  903.                             foreach ($iter as $package) {
  904.                                 if ($type != 'extension') {
  905.                                     if (isset($package['uri'])) {
  906.                                         if (isset($package['channel'])) {
  907.                                             $this->_UrlOrChannelGroup($type,
  908.                                                 $package['name'],
  909.                                                 $group['name']);
  910.                                         }
  911.                                     } else {
  912.                                         if (!isset($package['channel'])) {
  913.                                             $this->_NoChannelGroup($type,
  914.                                                 $package['name'],
  915.                                                 $group['name']);
  916.                                         }
  917.                                     }
  918.                                 }
  919.                                 $this->{"_validate{$type}Dep"}($package, '<group name="' .
  920.                                     $group['attribs']['name'] . '">');
  921.                             }
  922.                         }
  923.                     }
  924.                 }
  925.             }
  926.         }
  927.     }
  928.  
  929.     function _validateCompatible()
  930.     {
  931.         $compat = $this->_packageInfo['compatible'];
  932.         if (!isset($compat[0])) {
  933.             $compat = array($compat);
  934.         }
  935.         $required = array('name', 'channel', 'min', 'max', '*exclude');
  936.         foreach ($compat as $package) {
  937.             $type = '<compatible>';
  938.             if (is_array($package) && array_key_exists('name', $package)) {
  939.                 $type .= '<name>' . $package['name'] . '</name>';
  940.             }
  941.             $this->_stupidSchemaValidate($required, $package, $type);
  942.             if (is_array($package) && array_key_exists('min', $package)) {
  943.                 if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  944.                       $package['min'])) {
  945.                     $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
  946.                 }
  947.             }
  948.             if (is_array($package) && array_key_exists('max', $package)) {
  949.                 if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  950.                       $package['max'])) {
  951.                     $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
  952.                 }
  953.             }
  954.             if (is_array($package) && array_key_exists('exclude', $package)) {
  955.                 if (!is_array($package['exclude'])) {
  956.                     $package['exclude'] = array($package['exclude']);
  957.                 }
  958.                 foreach ($package['exclude'] as $exclude) {
  959.                     if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  960.                           $exclude)) {
  961.                         $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  962.                     }
  963.                 }
  964.             }
  965.         }
  966.     }
  967.  
  968.     function _validateBundle($list)
  969.     {
  970.         if (!is_array($list) || !isset($list['bundledpackage'])) {
  971.             return $this->_NoBundledPackages();
  972.         }
  973.         if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
  974.             return $this->_AtLeast2BundledPackages();
  975.         }
  976.         foreach ($list['bundledpackage'] as $package) {
  977.             if (!is_string($package)) {
  978.                 $this->_bundledPackagesMustBeFilename();
  979.             }
  980.         }
  981.     }
  982.  
  983.     function _validateFilelist($list = false, $allowignore = false, $dirs = '')
  984.     {
  985.         $iscontents = false;
  986.         if (!$list) {
  987.             $iscontents = true;
  988.             $list = $this->_packageInfo['contents'];
  989.             if (isset($this->_packageInfo['bundle'])) {
  990.                 return $this->_validateBundle($list);
  991.             }
  992.         }
  993.         if ($allowignore) {
  994.             $struc = array(
  995.                 '*install->name->as',
  996.                 '*ignore->name'
  997.             );
  998.         } else {
  999.             $struc = array(
  1000.                 '*dir->name->?baseinstalldir',
  1001.                 '*file->name->role->?baseinstalldir->?md5sum'
  1002.             );
  1003.             if (isset($list['dir']) && isset($list['file'])) {
  1004.                 // stave off validation errors without requiring a set order.
  1005.                 $_old = $list;
  1006.                 if (isset($list['attribs'])) {
  1007.                     $list = array('attribs' => $_old['attribs']);
  1008.                 }
  1009.                 $list['dir'] = $_old['dir'];
  1010.                 $list['file'] = $_old['file'];
  1011.             }
  1012.         }
  1013.         if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
  1014.             $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
  1015.             $dirname = $iscontents ? '<contents>' : $unknown;
  1016.         } else {
  1017.             $dirname = '<dir name="' . $list['attribs']['name'] . '">';
  1018.             if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  1019.                           str_replace('\\', '/', $list['attribs']['name']))) {
  1020.                 // file contains .. parent directory or . cur directory
  1021.                 $this->_invalidDirName($list['attribs']['name']);
  1022.             }
  1023.         }
  1024.         $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
  1025.         if ($allowignore && $res) {
  1026.             $ignored_or_installed = array();
  1027.             $this->_pf->getFilelist();
  1028.             $fcontents = $this->_pf->getContents();
  1029.             $filelist = array();
  1030.             if (!isset($fcontents['dir']['file'][0])) {
  1031.                 $fcontents['dir']['file'] = array($fcontents['dir']['file']);
  1032.             }
  1033.             foreach ($fcontents['dir']['file'] as $file) {
  1034.                 $filelist[$file['attribs']['name']] = true;
  1035.             }
  1036.             if (isset($list['install'])) {
  1037.                 if (!isset($list['install'][0])) {
  1038.                     $list['install'] = array($list['install']);
  1039.                 }
  1040.                 foreach ($list['install'] as $file) {
  1041.                     if (!isset($filelist[$file['attribs']['name']])) {
  1042.                         $this->_notInContents($file['attribs']['name'], 'install');
  1043.                         continue;
  1044.                     }
  1045.                     if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  1046.                         $this->_multipleInstallAs($file['attribs']['name']);
  1047.                     }
  1048.                     if (!isset($ignored_or_installed[$file['attribs']['name']])) {
  1049.                         $ignored_or_installed[$file['attribs']['name']] = array();
  1050.                     }
  1051.                     $ignored_or_installed[$file['attribs']['name']][] = 1;
  1052.                     if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  1053.                                   str_replace('\\', '/', $file['attribs']['as']))) {
  1054.                         // file contains .. parent directory or . cur directory references
  1055.                         $this->_invalidFileInstallAs($file['attribs']['name'],
  1056.                             $file['attribs']['as']);
  1057.                     }
  1058.                 }
  1059.             }
  1060.             if (isset($list['ignore'])) {
  1061.                 if (!isset($list['ignore'][0])) {
  1062.                     $list['ignore'] = array($list['ignore']);
  1063.                 }
  1064.                 foreach ($list['ignore'] as $file) {
  1065.                     if (!isset($filelist[$file['attribs']['name']])) {
  1066.                         $this->_notInContents($file['attribs']['name'], 'ignore');
  1067.                         continue;
  1068.                     }
  1069.                     if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  1070.                         $this->_ignoreAndInstallAs($file['attribs']['name']);
  1071.                     }
  1072.                 }
  1073.             }
  1074.         }
  1075.         if (!$allowignore && isset($list['file'])) {
  1076.             if (is_string($list['file'])) {
  1077.                 $this->_oldStyleFileNotAllowed();
  1078.                 return false;
  1079.             }
  1080.             if (!isset($list['file'][0])) {
  1081.                 // single file
  1082.                 $list['file'] = array($list['file']);
  1083.             }
  1084.             foreach ($list['file'] as $i => $file)
  1085.             {
  1086.                 if (isset($file['attribs']) && isset($file['attribs']['name'])) {
  1087.                     if ($file['attribs']['name']{0} == '.' &&
  1088.                           $file['attribs']['name']{1} == '/') {
  1089.                         // name is something like "./doc/whatever.txt"
  1090.                         $this->_invalidFileName($file['attribs']['name'], $dirname);
  1091.                     }
  1092.                     if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  1093.                                   str_replace('\\', '/', $file['attribs']['name']))) {
  1094.                         // file contains .. parent directory or . cur directory
  1095.                         $this->_invalidFileName($file['attribs']['name'], $dirname);
  1096.                     }
  1097.                 }
  1098.                 if (isset($file['attribs']) && isset($file['attribs']['role'])) {
  1099.                     if (!$this->_validateRole($file['attribs']['role'])) {
  1100.                         if (isset($this->_packageInfo['usesrole'])) {
  1101.                             $roles = $this->_packageInfo['usesrole'];
  1102.                             if (!isset($roles[0])) {
  1103.                                 $roles = array($roles);
  1104.                             }
  1105.                             foreach ($roles as $role) {
  1106.                                 if ($role['role'] = $file['attribs']['role']) {
  1107.                                     $msg = 'This package contains role "%role%" and requires ' .
  1108.                                         'package "%package%" to be used';
  1109.                                     if (isset($role['uri'])) {
  1110.                                         $params = array('role' => $role['role'],
  1111.                                             'package' => $role['uri']);
  1112.                                     } else {
  1113.                                         $params = array('role' => $role['role'],
  1114.                                             'package' => $this->_pf->_registry->
  1115.                                             parsedPackageNameToString(array('package' =>
  1116.                                                 $role['package'], 'channel' => $role['channel']),
  1117.                                                 true));
  1118.                                     }
  1119.                                     $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
  1120.                                 }
  1121.                             }
  1122.                         }
  1123.                         $this->_invalidFileRole($file['attribs']['name'],
  1124.                             $dirname, $file['attribs']['role']);
  1125.                     }
  1126.                 }
  1127.                 if (!isset($file['attribs'])) {
  1128.                     continue;
  1129.                 }
  1130.                 $save = $file['attribs'];
  1131.                 if ($dirs) {
  1132.                     $save['name'] = $dirs . '/' . $save['name'];
  1133.                 }
  1134.                 unset($file['attribs']);
  1135.                 if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
  1136.                     foreach ($file as $task => $value) {
  1137.                         if ($tagClass = $this->_pf->getTask($task)) {
  1138.                             if (!is_array($value) || !isset($value[0])) {
  1139.                                 $value = array($value);
  1140.                             }
  1141.                             foreach ($value as $v) {
  1142.                                 $ret = call_user_func(array($tagClass, 'validateXml'),
  1143.                                     $this->_pf, $v, $this->_pf->_config, $save);
  1144.                                 if (is_array($ret)) {
  1145.                                     $this->_invalidTask($task, $ret, isset($save['name']) ?
  1146.                                         $save['name'] : '');
  1147.                                 }
  1148.                             }
  1149.                         } else {
  1150.                             if (isset($this->_packageInfo['usestask'])) {
  1151.                                 $roles = $this->_packageInfo['usestask'];
  1152.                                 if (!isset($roles[0])) {
  1153.                                     $roles = array($roles);
  1154.                                 }
  1155.                                 foreach ($roles as $role) {
  1156.                                     if ($role['task'] = $task) {
  1157.                                         $msg = 'This package contains task "%task%" and requires ' .
  1158.                                             'package "%package%" to be used';
  1159.                                         if (isset($role['uri'])) {
  1160.                                             $params = array('task' => $role['task'],
  1161.                                                 'package' => $role['uri']);
  1162.                                         } else {
  1163.                                             $params = array('task' => $role['task'],
  1164.                                                 'package' => $this->_pf->_registry->
  1165.                                                 parsedPackageNameToString(array('package' =>
  1166.                                                     $role['package'], 'channel' => $role['channel']),
  1167.                                                     true));
  1168.                                         }
  1169.                                         $this->_stack->push('_mustInstallTask', 'error',
  1170.                                             $params, $msg);
  1171.                                     }
  1172.                                 }
  1173.                             }
  1174.                             $this->_unknownTask($task, $save['name']);
  1175.                         }
  1176.                     }
  1177.                 }
  1178.             }
  1179.         }
  1180.         if (isset($list['ignore'])) {
  1181.             if (!$allowignore) {
  1182.                 $this->_ignoreNotAllowed('ignore');
  1183.             }
  1184.         }
  1185.         if (isset($list['install'])) {
  1186.             if (!$allowignore) {
  1187.                 $this->_ignoreNotAllowed('install');
  1188.             }
  1189.         }
  1190.         if (isset($list['file'])) {
  1191.             if ($allowignore) {
  1192.                 $this->_fileNotAllowed('file');
  1193.             }
  1194.         }
  1195.         if (isset($list['dir'])) {
  1196.             if ($allowignore) {
  1197.                 $this->_fileNotAllowed('dir');
  1198.             } else {
  1199.                 if (!isset($list['dir'][0])) {
  1200.                     $list['dir'] = array($list['dir']);
  1201.                 }
  1202.                 foreach ($list['dir'] as $dir) {
  1203.                     if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
  1204.                         if ($dir['attribs']['name'] == '/' ||
  1205.                               !isset($this->_packageInfo['contents']['dir']['dir'])) {
  1206.                             // always use nothing if the filelist has already been flattened
  1207.                             $newdirs = '';
  1208.                         } elseif ($dirs == '') {
  1209.                             $newdirs = $dir['attribs']['name'];
  1210.                         } else {
  1211.                             $newdirs = $dirs . '/' . $dir['attribs']['name'];
  1212.                         }
  1213.                     } else {
  1214.                         $newdirs = $dirs;
  1215.                     }
  1216.                     $this->_validateFilelist($dir, $allowignore, $newdirs);
  1217.                 }
  1218.             }
  1219.         }
  1220.     }
  1221.  
  1222.     function _validateRelease()
  1223.     {
  1224.         if (isset($this->_packageInfo['phprelease'])) {
  1225.             $release = 'phprelease';
  1226.             if (isset($this->_packageInfo['providesextension'])) {
  1227.                 $this->_cannotProvideExtension($release);
  1228.             }
  1229.             if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  1230.                 $this->_cannotHaveSrcpackage($release);
  1231.             }
  1232.             $releases = $this->_packageInfo['phprelease'];
  1233.             if (!is_array($releases)) {
  1234.                 return true;
  1235.             }
  1236.             if (!isset($releases[0])) {
  1237.                 $releases = array($releases);
  1238.             }
  1239.             foreach ($releases as $rel) {
  1240.                 $this->_stupidSchemaValidate(array(
  1241.                     '*installconditions',
  1242.                     '*filelist',
  1243.                 ), $rel, '<phprelease>');
  1244.             }
  1245.         }
  1246.         foreach (array('', 'zend') as $prefix) {
  1247.             $releasetype = $prefix . 'extsrcrelease';
  1248.             if (isset($this->_packageInfo[$releasetype])) {
  1249.                 $release = $releasetype;
  1250.                 if (!isset($this->_packageInfo['providesextension'])) {
  1251.                     $this->_mustProvideExtension($release);
  1252.                 }
  1253.                 if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  1254.                     $this->_cannotHaveSrcpackage($release);
  1255.                 }
  1256.                 $releases = $this->_packageInfo[$releasetype];
  1257.                 if (!is_array($releases)) {
  1258.                     return true;
  1259.                 }
  1260.                 if (!isset($releases[0])) {
  1261.                     $releases = array($releases);
  1262.                 }
  1263.                 foreach ($releases as $rel) {
  1264.                     $this->_stupidSchemaValidate(array(
  1265.                         '*installconditions',
  1266.                         '*configureoption->name->prompt->?default',
  1267.                         '*binarypackage',
  1268.                         '*filelist',
  1269.                     ), $rel, '<' . $releasetype . '>');
  1270.                     if (isset($rel['binarypackage'])) {
  1271.                         if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
  1272.                             $rel['binarypackage'] = array($rel['binarypackage']);
  1273.                         }
  1274.                         foreach ($rel['binarypackage'] as $bin) {
  1275.                             if (!is_string($bin)) {
  1276.                                 $this->_binaryPackageMustBePackagename();
  1277.                             }
  1278.                         }
  1279.                     }
  1280.                 }
  1281.             }
  1282.             $releasetype = 'extbinrelease';
  1283.             if (isset($this->_packageInfo[$releasetype])) {
  1284.                 $release = $releasetype;
  1285.                 if (!isset($this->_packageInfo['providesextension'])) {
  1286.                     $this->_mustProvideExtension($release);
  1287.                 }
  1288.                 if (isset($this->_packageInfo['channel']) &&
  1289.                       !isset($this->_packageInfo['srcpackage'])) {
  1290.                     $this->_mustSrcPackage($release);
  1291.                 }
  1292.                 if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
  1293.                     $this->_mustSrcuri($release);
  1294.                 }
  1295.                 $releases = $this->_packageInfo[$releasetype];
  1296.                 if (!is_array($releases)) {
  1297.                     return true;
  1298.                 }
  1299.                 if (!isset($releases[0])) {
  1300.                     $releases = array($releases);
  1301.                 }
  1302.                 foreach ($releases as $rel) {
  1303.                     $this->_stupidSchemaValidate(array(
  1304.                         '*installconditions',
  1305.                         '*filelist',
  1306.                     ), $rel, '<' . $releasetype . '>');
  1307.                 }
  1308.             }
  1309.         }
  1310.         if (isset($this->_packageInfo['bundle'])) {
  1311.             $release = 'bundle';
  1312.             if (isset($this->_packageInfo['providesextension'])) {
  1313.                 $this->_cannotProvideExtension($release);
  1314.             }
  1315.             if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  1316.                 $this->_cannotHaveSrcpackage($release);
  1317.             }
  1318.             $releases = $this->_packageInfo['bundle'];
  1319.             if (!is_array($releases) || !isset($releases[0])) {
  1320.                 $releases = array($releases);
  1321.             }
  1322.             foreach ($releases as $rel) {
  1323.                 $this->_stupidSchemaValidate(array(
  1324.                     '*installconditions',
  1325.                     '*filelist',
  1326.                 ), $rel, '<bundle>');
  1327.             }
  1328.         }
  1329.         foreach ($releases as $rel) {
  1330.             if (is_array($rel) && array_key_exists('installconditions', $rel)) {
  1331.                 $this->_validateInstallConditions($rel['installconditions'],
  1332.                     "<$release><installconditions>");
  1333.             }
  1334.             if (is_array($rel) && array_key_exists('filelist', $rel)) {
  1335.                 if ($rel['filelist']) {
  1336.                     
  1337.                     $this->_validateFilelist($rel['filelist'], true);
  1338.                 }
  1339.             }
  1340.         }
  1341.     }
  1342.  
  1343.     /**
  1344.      * This is here to allow role extension through plugins
  1345.      * @param string
  1346.      */
  1347.     function _validateRole($role)
  1348.     {
  1349.         return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
  1350.     }
  1351.  
  1352.     function _pearVersionTooLow($version)
  1353.     {
  1354.         $this->_stack->push(__FUNCTION__, 'error',
  1355.             array('version' => $version),
  1356.             'This package.xml requires PEAR version %version% to parse properly, we are ' .
  1357.             'version 1.7.1');
  1358.     }
  1359.  
  1360.     function _invalidTagOrder($oktags, $actual, $root)
  1361.     {
  1362.         $this->_stack->push(__FUNCTION__, 'error',
  1363.             array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
  1364.             'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
  1365.     }
  1366.  
  1367.     function _ignoreNotAllowed($type)
  1368.     {
  1369.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  1370.             '<%type%> is not allowed inside global <contents>, only inside ' .
  1371.             '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
  1372.     }
  1373.  
  1374.     function _fileNotAllowed($type)
  1375.     {
  1376.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  1377.             '<%type%> is not allowed inside release <filelist>, only inside ' .
  1378.             '<contents>, use <ignore> and <install> only');
  1379.     }
  1380.  
  1381.     function _oldStyleFileNotAllowed()
  1382.     {
  1383.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1384.             'Old-style <file>name</file> is not allowed.  Use' .
  1385.             '<file name="name" role="role"/>');
  1386.     }
  1387.  
  1388.     function _tagMissingAttribute($tag, $attr, $context)
  1389.     {
  1390.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  1391.             'attribute' => $attr, 'context' => $context),
  1392.             'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
  1393.     }
  1394.  
  1395.     function _tagHasNoAttribs($tag, $context)
  1396.     {
  1397.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  1398.             'context' => $context),
  1399.             'tag <%tag%> has no attributes in context "%context%"');
  1400.     }
  1401.  
  1402.     function _invalidInternalStructure()
  1403.     {
  1404.         $this->_stack->push(__FUNCTION__, 'exception', array(),
  1405.             'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
  1406.     }
  1407.  
  1408.     function _invalidFileRole($file, $dir, $role)
  1409.     {
  1410.         $this->_stack->push(__FUNCTION__, 'error', array(
  1411.             'file' => $file, 'dir' => $dir, 'role' => $role,
  1412.             'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
  1413.             'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
  1414.     }
  1415.  
  1416.     function _invalidFileName($file, $dir)
  1417.     {
  1418.         $this->_stack->push(__FUNCTION__, 'error', array(
  1419.             'file' => $file),
  1420.             'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
  1421.     }
  1422.  
  1423.     function _invalidFileInstallAs($file, $as)
  1424.     {
  1425.         $this->_stack->push(__FUNCTION__, 'error', array(
  1426.             'file' => $file, 'as' => $as),
  1427.             'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
  1428.     }
  1429.  
  1430.     function _invalidDirName($dir)
  1431.     {
  1432.         $this->_stack->push(__FUNCTION__, 'error', array(
  1433.             'dir' => $file),
  1434.             'Directory "%dir%" cannot begin with "./" or contain ".."');
  1435.     }
  1436.  
  1437.     function _filelistCannotContainFile($filelist)
  1438.     {
  1439.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  1440.             '<%tag%> can only contain <dir>, contains <file>.  Use ' .
  1441.             '<dir name="/"> as the first dir element');
  1442.     }
  1443.  
  1444.     function _filelistMustContainDir($filelist)
  1445.     {
  1446.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  1447.             '<%tag%> must contain <dir>.  Use <dir name="/"> as the ' .
  1448.             'first dir element');
  1449.     }
  1450.  
  1451.     function _tagCannotBeEmpty($tag)
  1452.     {
  1453.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  1454.             '<%tag%> cannot be empty (<%tag%/>)');
  1455.     }
  1456.  
  1457.     function _UrlOrChannel($type, $name)
  1458.     {
  1459.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  1460.             'name' => $name),
  1461.             'Required dependency <%type%> "%name%" can have either url OR ' .
  1462.             'channel attributes, and not both');
  1463.     }
  1464.  
  1465.     function _NoChannel($type, $name)
  1466.     {
  1467.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  1468.             'name' => $name),
  1469.             'Required dependency <%type%> "%name%" must have either url OR ' .
  1470.             'channel attributes');
  1471.     }
  1472.  
  1473.     function _UrlOrChannelGroup($type, $name, $group)
  1474.     {
  1475.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  1476.             'name' => $name, 'group' => $group),
  1477.             'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
  1478.             'channel attributes, and not both');
  1479.     }
  1480.  
  1481.     function _NoChannelGroup($type, $name, $group)
  1482.     {
  1483.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  1484.             'name' => $name, 'group' => $group),
  1485.             'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
  1486.             'channel attributes');
  1487.     }
  1488.  
  1489.     function _unknownChannel($channel)
  1490.     {
  1491.         $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
  1492.             'Unknown channel "%channel%"');
  1493.     }
  1494.  
  1495.     function _noPackageVersion()
  1496.     {
  1497.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1498.             'package.xml <package> tag has no version attribute, or version is not 2.0');
  1499.     }
  1500.  
  1501.     function _NoBundledPackages()
  1502.     {
  1503.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1504.             'No <bundledpackage> tag was found in <contents>, required for bundle packages');
  1505.     }
  1506.  
  1507.     function _AtLeast2BundledPackages()
  1508.     {
  1509.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1510.             'At least 2 packages must be bundled in a bundle package');
  1511.     }
  1512.  
  1513.     function _ChannelOrUri($name)
  1514.     {
  1515.         $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  1516.             'Bundled package "%name%" can have either a uri or a channel, not both');
  1517.     }
  1518.  
  1519.     function _noChildTag($child, $tag)
  1520.     {
  1521.         $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
  1522.             'Tag <%tag%> is missing child tag <%child%>');
  1523.     }
  1524.  
  1525.     function _invalidVersion($type, $value)
  1526.     {
  1527.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
  1528.             'Version type <%type%> is not a valid version (%value%)');
  1529.     }
  1530.  
  1531.     function _invalidState($type, $value)
  1532.     {
  1533.         $states = array('stable', 'beta', 'alpha', 'devel');
  1534.         if ($type != 'api') {
  1535.             $states[] = 'snapshot';
  1536.         }
  1537.         if (strtolower($value) == 'rc') {
  1538.             $this->_stack->push(__FUNCTION__, 'error',
  1539.                 array('version' => $this->_packageInfo['version']['release']),
  1540.                 'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
  1541.         }
  1542.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
  1543.             'types' => $states),
  1544.             'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
  1545.             '%types%');
  1546.     }
  1547.  
  1548.     function _invalidTask($task, $ret, $file)
  1549.     {
  1550.         switch ($ret[0]) {
  1551.             case PEAR_TASK_ERROR_MISSING_ATTRIB :
  1552.                 $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
  1553.                 $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
  1554.             break;
  1555.             case PEAR_TASK_ERROR_NOATTRIBS :
  1556.                 $info = array('task' => $task, 'file' => $file);
  1557.                 $msg = 'task <%task%> has no attributes in file %file%';
  1558.             break;
  1559.             case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
  1560.                 $info = array('attrib' => $ret[1], 'values' => $ret[3],
  1561.                     'was' => $ret[2], 'task' => $task, 'file' => $file);
  1562.                 $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
  1563.                     'in file %file%, expecting one of "%values%"';
  1564.             break;
  1565.             case PEAR_TASK_ERROR_INVALID :
  1566.                 $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
  1567.                 $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
  1568.             break;
  1569.         }
  1570.         $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
  1571.     }
  1572.  
  1573.     function _unknownTask($task, $file)
  1574.     {
  1575.         $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
  1576.             'Unknown task "%task%" passed in file <file name="%file%">');
  1577.     }
  1578.  
  1579.     function _subpackageCannotProvideExtension($name)
  1580.     {
  1581.         $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  1582.             'Subpackage dependency "%name%" cannot use <providesextension>, ' .
  1583.             'only package dependencies can use this tag');
  1584.     }
  1585.  
  1586.     function _subpackagesCannotConflict($name)
  1587.     {
  1588.         $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  1589.             'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
  1590.             'only package dependencies can use this tag');
  1591.     }
  1592.  
  1593.     function _cannotProvideExtension($release)
  1594.     {
  1595.         $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  1596.             '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
  1597.     }
  1598.  
  1599.     function _mustProvideExtension($release)
  1600.     {
  1601.         $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  1602.             '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
  1603.     }
  1604.  
  1605.     function _cannotHaveSrcpackage($release)
  1606.     {
  1607.         $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  1608.             '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
  1609.     }
  1610.  
  1611.     function _mustSrcPackage($release)
  1612.     {
  1613.         $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  1614.             '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
  1615.     }
  1616.  
  1617.     function _mustSrcuri($release)
  1618.     {
  1619.         $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  1620.             '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
  1621.     }
  1622.  
  1623.     function _uriDepsCannotHaveVersioning($type)
  1624.     {
  1625.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  1626.             '%type%: dependencies with a <uri> tag cannot have any versioning information');
  1627.     }
  1628.  
  1629.     function _conflictingDepsCannotHaveVersioning($type)
  1630.     {
  1631.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  1632.             '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
  1633.             'exclude specific versions of a dependency');
  1634.     }
  1635.  
  1636.     function _DepchannelCannotBeUri($type)
  1637.     {
  1638.         $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  1639.             '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
  1640.             'dependencies only');
  1641.     }
  1642.  
  1643.     function _bundledPackagesMustBeFilename()
  1644.     {
  1645.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1646.             '<bundledpackage> tags must contain only the filename of a package release ' .
  1647.             'in the bundle');
  1648.     }
  1649.  
  1650.     function _binaryPackageMustBePackagename()
  1651.     {
  1652.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1653.             '<binarypackage> tags must contain the name of a package that is ' .
  1654.             'a compiled version of this extsrc/zendextsrc package');
  1655.     }
  1656.  
  1657.     function _fileNotFound($file)
  1658.     {
  1659.         $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  1660.             'File "%file%" in package.xml does not exist');
  1661.     }
  1662.  
  1663.     function _notInContents($file, $tag)
  1664.     {
  1665.         $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
  1666.             '<%tag% name="%file%"> is invalid, file is not in <contents>');
  1667.     }
  1668.  
  1669.     function _cannotValidateNoPathSet()
  1670.     {
  1671.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1672.             'Cannot validate files, no path to package file is set (use setPackageFile())');
  1673.     }
  1674.  
  1675.     function _usesroletaskMustHaveChannelOrUri($role, $tag)
  1676.     {
  1677.         $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  1678.             '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
  1679.     }
  1680.  
  1681.     function _usesroletaskMustHavePackage($role, $tag)
  1682.     {
  1683.         $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  1684.             '<%tag%> for role "%role%" must contain <package>');
  1685.     }
  1686.  
  1687.     function _usesroletaskMustHaveRoleTask($tag, $type)
  1688.     {
  1689.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
  1690.             '<%tag%> must contain <%type%> defining the %type% to be used');
  1691.     }
  1692.  
  1693.     function _cannotConflictWithAllOs($type)
  1694.     {
  1695.         $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  1696.             '%tag% cannot conflict with all OSes');
  1697.     }
  1698.  
  1699.     function _invalidDepGroupName($name)
  1700.     {
  1701.         $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  1702.             'Invalid dependency group name "%name%"');
  1703.     }
  1704.  
  1705.     function _multipleToplevelDirNotAllowed()
  1706.     {
  1707.         $this->_stack->push(__FUNCTION__, 'error', array(),
  1708.             'Multiple top-level <dir> tags are not allowed.  Enclose them ' .
  1709.                 'in a <dir name="/">');
  1710.     }
  1711.  
  1712.     function _multipleInstallAs($file)
  1713.     {
  1714.         $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  1715.             'Only one <install> tag is allowed for file "%file%"');
  1716.     }
  1717.  
  1718.     function _ignoreAndInstallAs($file)
  1719.     {
  1720.         $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  1721.             'Cannot have both <ignore> and <install> tags for file "%file%"');
  1722.     }
  1723.  
  1724.     function _analyzeBundledPackages()
  1725.     {
  1726.         if (!$this->_isValid) {
  1727.             return false;
  1728.         }
  1729.         if (!$this->_pf->getPackageType() == 'bundle') {
  1730.             return false;
  1731.         }
  1732.         if (!isset($this->_pf->_packageFile)) {
  1733.             return false;
  1734.         }
  1735.         $dir_prefix = dirname($this->_pf->_packageFile);
  1736.         $common = new PEAR_Common;
  1737.         $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  1738.             array($common, 'log');
  1739.         $info = $this->_pf->getContents();
  1740.         $info = $info['bundledpackage'];
  1741.         if (!is_array($info)) {
  1742.             $info = array($info);
  1743.         }
  1744.         $pkg = &new PEAR_PackageFile($this->_pf->_config);
  1745.         foreach ($info as $package) {
  1746.             if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
  1747.                 $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
  1748.                 $this->_isValid = 0;
  1749.                 continue;
  1750.             }
  1751.             call_user_func_array($log, array(1, "Analyzing bundled package $package"));
  1752.             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  1753.             $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
  1754.                 PEAR_VALIDATE_NORMAL);
  1755.             PEAR::popErrorHandling();
  1756.             if (PEAR::isError($ret)) {
  1757.                 call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
  1758.                     'package'));
  1759.                 $inf = $ret->getUserInfo();
  1760.                 if (is_array($inf)) {
  1761.                     foreach ($inf as $err) {
  1762.                         call_user_func_array($log, array(1, $err['message']));
  1763.                     }
  1764.                 }
  1765.                 return false;
  1766.             }
  1767.         }
  1768.         return true;
  1769.     }
  1770.  
  1771.     function _analyzePhpFiles()
  1772.     {
  1773.         if (!$this->_isValid) {
  1774.             return false;
  1775.         }
  1776.         if (!isset($this->_pf->_packageFile)) {
  1777.             $this->_cannotValidateNoPathSet();
  1778.             return false;
  1779.         }
  1780.         $dir_prefix = dirname($this->_pf->_packageFile);
  1781.         $common = new PEAR_Common;
  1782.         $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  1783.             array(&$common, 'log');
  1784.         $info = $this->_pf->getContents();
  1785.         if (!$info || !isset($info['dir']['file'])) {
  1786.             $this->_tagCannotBeEmpty('contents><dir');
  1787.             return false;
  1788.         }
  1789.         $info = $info['dir']['file'];
  1790.         if (isset($info['attribs'])) {
  1791.             $info = array($info);
  1792.         }
  1793.         $provides = array();
  1794.         foreach ($info as $fa) {
  1795.             $fa = $fa['attribs'];
  1796.             $file = $fa['name'];
  1797.             if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
  1798.                 $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
  1799.                 $this->_isValid = 0;
  1800.                 continue;
  1801.             }
  1802.             if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
  1803.                 call_user_func_array($log, array(1, "Analyzing $file"));
  1804.                 $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
  1805.                 if ($srcinfo) {
  1806.                     $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
  1807.                 }
  1808.             }
  1809.         }
  1810.         $this->_packageName = $pn = $this->_pf->getPackage();
  1811.         $pnl = strlen($pn);
  1812.         foreach ($provides as $key => $what) {
  1813.             if (isset($what['explicit']) || !$what) {
  1814.                 // skip conformance checks if the provides entry is
  1815.                 // specified in the package.xml file
  1816.                 continue;
  1817.             }
  1818.             extract($what);
  1819.             if ($type == 'class') {
  1820.                 if (!strncasecmp($name, $pn, $pnl)) {
  1821.                     continue;
  1822.                 }
  1823.                 $this->_stack->push(__FUNCTION__, 'warning',
  1824.                     array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  1825.                     'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  1826.             } elseif ($type == 'function') {
  1827.                 if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
  1828.                     continue;
  1829.                 }
  1830.                 $this->_stack->push(__FUNCTION__, 'warning',
  1831.                     array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  1832.                     'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  1833.             }
  1834.         }
  1835.         return $this->_isValid;
  1836.     }
  1837.  
  1838.     /**
  1839.      * Analyze the source code of the given PHP file
  1840.      *
  1841.      * @param  string Filename of the PHP file
  1842.      * @param  boolean whether to analyze $file as the file contents
  1843.      * @return mixed
  1844.      */
  1845.     function analyzeSourceCode($file, $string = false)
  1846.     {
  1847.         if (!function_exists("token_get_all")) {
  1848.             $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  1849.                 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
  1850.             return false;
  1851.         }
  1852.         if (!defined('T_DOC_COMMENT')) {
  1853.             define('T_DOC_COMMENT', T_COMMENT);
  1854.         }
  1855.         if (!defined('T_INTERFACE')) {
  1856.             define('T_INTERFACE', -1);
  1857.         }
  1858.         if (!defined('T_IMPLEMENTS')) {
  1859.             define('T_IMPLEMENTS', -1);
  1860.         }
  1861.         if ($string) {
  1862.             $contents = $file;
  1863.         } else {
  1864.             if (!$fp = @fopen($file, "r")) {
  1865.                 return false;
  1866.             }
  1867.             fclose($fp);
  1868.             $contents = file_get_contents($file);
  1869.         }
  1870.         $tokens = token_get_all($contents);
  1871. /*
  1872.         for ($i = 0; $i < sizeof($tokens); $i++) {
  1873.             @list($token, $data) = $tokens[$i];
  1874.             if (is_string($token)) {
  1875.                 var_dump($token);
  1876.             } else {
  1877.                 print token_name($token) . ' ';
  1878.                 var_dump(rtrim($data));
  1879.             }
  1880.         }
  1881. */
  1882.         $look_for = 0;
  1883.         $paren_level = 0;
  1884.         $bracket_level = 0;
  1885.         $brace_level = 0;
  1886.         $lastphpdoc = '';
  1887.         $current_class = '';
  1888.         $current_interface = '';
  1889.         $current_class_level = -1;
  1890.         $current_function = '';
  1891.         $current_function_level = -1;
  1892.         $declared_classes = array();
  1893.         $declared_interfaces = array();
  1894.         $declared_functions = array();
  1895.         $declared_methods = array();
  1896.         $used_classes = array();
  1897.         $used_functions = array();
  1898.         $extends = array();
  1899.         $implements = array();
  1900.         $nodeps = array();
  1901.         $inquote = false;
  1902.         $interface = false;
  1903.         for ($i = 0; $i < sizeof($tokens); $i++) {
  1904.             if (is_array($tokens[$i])) {
  1905.                 list($token, $data) = $tokens[$i];
  1906.             } else {
  1907.                 $token = $tokens[$i];
  1908.                 $data = '';
  1909.             }
  1910.             if ($inquote) {
  1911.                 if ($token != '"' && $token != T_END_HEREDOC) {
  1912.                     continue;
  1913.                 } else {
  1914.                     $inquote = false;
  1915.                     continue;
  1916.                 }
  1917.             }
  1918.             switch ($token) {
  1919.                 case T_WHITESPACE :
  1920.                     continue;
  1921.                 case ';':
  1922.                     if ($interface) {
  1923.                         $current_function = '';
  1924.                         $current_function_level = -1;
  1925.                     }
  1926.                     break;
  1927.                 case '"':
  1928.                 case T_START_HEREDOC:
  1929.                     $inquote = true;
  1930.                     break;
  1931.                 case T_CURLY_OPEN:
  1932.                 case T_DOLLAR_OPEN_CURLY_BRACES:
  1933.                 case '{': $brace_level++; continue 2;
  1934.                 case '}':
  1935.                     $brace_level--;
  1936.                     if ($current_class_level == $brace_level) {
  1937.                         $current_class = '';
  1938.                         $current_class_level = -1;
  1939.                     }
  1940.                     if ($current_function_level == $brace_level) {
  1941.                         $current_function = '';
  1942.                         $current_function_level = -1;
  1943.                     }
  1944.                     continue 2;
  1945.                 case '[': $bracket_level++; continue 2;
  1946.                 case ']': $bracket_level--; continue 2;
  1947.                 case '(': $paren_level++;   continue 2;
  1948.                 case ')': $paren_level--;   continue 2;
  1949.                 case T_INTERFACE:
  1950.                     $interface = true;
  1951.                 case T_CLASS:
  1952.                     if (($current_class_level != -1) || ($current_function_level != -1)) {
  1953.                         $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  1954.                         'Parser error: invalid PHP found in file "%file%"');
  1955.                         return false;
  1956.                     }
  1957.                 case T_FUNCTION:
  1958.                 case T_NEW:
  1959.                 case T_EXTENDS:
  1960.                 case T_IMPLEMENTS:
  1961.                     $look_for = $token;
  1962.                     continue 2;
  1963.                 case T_STRING:
  1964.                     if (version_compare(zend_version(), '2.0', '<')) {
  1965.                         if (in_array(strtolower($data),
  1966.                             array('public', 'private', 'protected', 'abstract',
  1967.                                   'interface', 'implements', 'throw') 
  1968.                                  )) {
  1969.                             $this->_stack->push(__FUNCTION__, 'warning', array(
  1970.                                 'file' => $file),
  1971.                                 'Error, PHP5 token encountered in %file%,' .
  1972.                                 ' analysis should be in PHP5');
  1973.                         }
  1974.                     }
  1975.                     if ($look_for == T_CLASS) {
  1976.                         $current_class = $data;
  1977.                         $current_class_level = $brace_level;
  1978.                         $declared_classes[] = $current_class;
  1979.                     } elseif ($look_for == T_INTERFACE) {
  1980.                         $current_interface = $data;
  1981.                         $current_class_level = $brace_level;
  1982.                         $declared_interfaces[] = $current_interface;
  1983.                     } elseif ($look_for == T_IMPLEMENTS) {
  1984.                         $implements[$current_class] = $data;
  1985.                     } elseif ($look_for == T_EXTENDS) {
  1986.                         $extends[$current_class] = $data;
  1987.                     } elseif ($look_for == T_FUNCTION) {
  1988.                         if ($current_class) {
  1989.                             $current_function = "$current_class::$data";
  1990.                             $declared_methods[$current_class][] = $data;
  1991.                         } elseif ($current_interface) {
  1992.                             $current_function = "$current_interface::$data";
  1993.                             $declared_methods[$current_interface][] = $data;
  1994.                         } else {
  1995.                             $current_function = $data;
  1996.                             $declared_functions[] = $current_function;
  1997.                         }
  1998.                         $current_function_level = $brace_level;
  1999.                         $m = array();
  2000.                     } elseif ($look_for == T_NEW) {
  2001.                         $used_classes[$data] = true;
  2002.                     }
  2003.                     $look_for = 0;
  2004.                     continue 2;
  2005.                 case T_VARIABLE:
  2006.                     $look_for = 0;
  2007.                     continue 2;
  2008.                 case T_DOC_COMMENT:
  2009.                 case T_COMMENT:
  2010.                     if (preg_match('!^/\*\*\s!', $data)) {
  2011.                         $lastphpdoc = $data;
  2012.                         if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  2013.                             $nodeps = array_merge($nodeps, $m[1]);
  2014.                         }
  2015.                     }
  2016.                     continue 2;
  2017.                 case T_DOUBLE_COLON:
  2018.                     if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
  2019.                         $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
  2020.                             'Parser error: invalid PHP found in file "%file%"');
  2021.                         return false;
  2022.                     }
  2023.                     $class = $tokens[$i - 1][1];
  2024.                     if (strtolower($class) != 'parent') {
  2025.                         $used_classes[$class] = true;
  2026.                     }
  2027.                     continue 2;
  2028.             }
  2029.         }
  2030.         return array(
  2031.             "source_file" => $file,
  2032.             "declared_classes" => $declared_classes,
  2033.             "declared_interfaces" => $declared_interfaces,
  2034.             "declared_methods" => $declared_methods,
  2035.             "declared_functions" => $declared_functions,
  2036.             "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  2037.             "inheritance" => $extends,
  2038.             "implements" => $implements,
  2039.             );
  2040.     }
  2041.  
  2042.     /**
  2043.      * Build a "provides" array from data returned by
  2044.      * analyzeSourceCode().  The format of the built array is like
  2045.      * this:
  2046.      *
  2047.      *  array(
  2048.      *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  2049.      *    ...
  2050.      *  )
  2051.      *
  2052.      *
  2053.      * @param array $srcinfo array with information about a source file
  2054.      * as returned by the analyzeSourceCode() method.
  2055.      *
  2056.      * @return void
  2057.      *
  2058.      * @access private
  2059.      *
  2060.      */
  2061.     function _buildProvidesArray($srcinfo)
  2062.     {
  2063.         if (!$this->_isValid) {
  2064.             return array();
  2065.         }
  2066.         $providesret = array();
  2067.         $file = basename($srcinfo['source_file']);
  2068.         $pn = $this->_pf->getPackage();
  2069.         $pnl = strlen($pn);
  2070.         foreach ($srcinfo['declared_classes'] as $class) {
  2071.             $key = "class;$class";
  2072.             if (isset($providesret[$key])) {
  2073.                 continue;
  2074.             }
  2075.             $providesret[$key] =
  2076.                 array('file'=> $file, 'type' => 'class', 'name' => $class);
  2077.             if (isset($srcinfo['inheritance'][$class])) {
  2078.                 $providesret[$key]['extends'] =
  2079.                     $srcinfo['inheritance'][$class];
  2080.             }
  2081.         }
  2082.         foreach ($srcinfo['declared_methods'] as $class => $methods) {
  2083.             foreach ($methods as $method) {
  2084.                 $function = "$class::$method";
  2085.                 $key = "function;$function";
  2086.                 if ($method{0} == '_' || !strcasecmp($method, $class) ||
  2087.                     isset($providesret[$key])) {
  2088.                     continue;
  2089.                 }
  2090.                 $providesret[$key] =
  2091.                     array('file'=> $file, 'type' => 'function', 'name' => $function);
  2092.             }
  2093.         }
  2094.  
  2095.         foreach ($srcinfo['declared_functions'] as $function) {
  2096.             $key = "function;$function";
  2097.             if ($function{0} == '_' || isset($providesret[$key])) {
  2098.                 continue;
  2099.             }
  2100.             if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  2101.                 $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  2102.             }
  2103.             $providesret[$key] =
  2104.                 array('file'=> $file, 'type' => 'function', 'name' => $function);
  2105.         }
  2106.         return $providesret;
  2107.     }
  2108. }
  2109. ?>