PEAR
[ class tree: PEAR ] [ index: PEAR ] [ all elements ]

Source for file Validator.php

Documentation is available at Validator.php

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

Documentation generated on Mon, 11 Mar 2019 14:46:10 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.