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

Source for file Validator.php

Documentation is available at Validator.php

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

Documentation generated on Wed, 06 Jul 2011 23:31:43 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.