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

Source for file Validator.php

Documentation is available at Validator.php

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

Documentation generated on Mon, 11 Mar 2019 15:09:40 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.