Code Coverage for /home/user/workspace/all/Pyrus/src/Pyrus/Validate.php

Coverage: 1%

Aggregate Code Coverage for all tests

       1           : <?php
       2           : /**
       3           :  * PEAR2_Pyrus_Validate
       4           :  *
       5           :  * PHP version 5
       6           :  *
       7           :  * LICENSE: This source file is subject to version 3.0 of the PHP license
       8           :  * that is available through the world-wide-web at the following URI:
       9           :  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
      10           :  * the PHP License and are unable to obtain it through the web, please
      11           :  * send a note to license@php.net so we can mail you a copy immediately.
      12           :  *
      13           :  * @category  PEAR2
      14           :  * @package   PEAR2_Pyrus
      15           :  * @author    Greg Beaver <cellog@php.net>
      16           :  * @copyright 2008 The PEAR Group
      17           :  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
      18           :  * @link      http://svn.pear.php.net/wsvn/PEARSVN/Pyrus/
      19           :  */
      20           : 
      21           : /**
      22           :  * Validation class for package.xml - channel-level advanced validation
      23           :  *
      24           :  * @category  PEAR2
      25           :  * @package   PEAR2_Pyrus
      26           :  * @author    Greg Beaver <cellog@php.net>
      27           :  * @copyright 2008 The PEAR Group
      28           :  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
      29           :  * @link      http://svn.pear.php.net/wsvn/PEARSVN/Pyrus/
      30           :  */
      31           : class PEAR2_Pyrus_Validate
      32         2 : {
      33           : /**#@+
      34           :  * Constants for install stage
      35           :  */
      36           :     const INSTALLING = 1;
      37           :     const UNINSTALLING = 2; // this is not bit-mapped like the others
      38           :     const NORMAL = 3;
      39           :     const DOWNLOADING = 4; // this is not bit-mapped like the others
      40           :     const PACKAGING = 7;
      41           : /**#@-*/
      42           :     var $packageregex = '[A-Za-z][a-zA-Z0-9_]+';
      43           :     /**
      44           :      * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
      45           :      */
      46           :     var $_packagexml;
      47           :     /**
      48           :      * @var int one of the PEAR2_Pyrus_Validate::* constants
      49           :      */
      50           :     var $_state = PEAR2_Pyrus_Validate::NORMAL;
      51           :     /**
      52           :      * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
      53           :      * @var array
      54           :      * @access private
      55           :      */
      56           :     var $_failures = array('error' => array(), 'warning' => array());
      57           : 
      58           :     /**
      59           :      * Override this method to handle validation of normal package names
      60           :      * @param string
      61           :      * @return bool
      62           :      * @access protected
      63           :      */
      64           :     protected function _validPackageName($name)
      65           :     {
      66           :         return (bool) preg_match('/^' . $this->packageregex . '$/', $name);
      67           :     }
      68           : 
      69           :     /**
      70           :      * @param string package name to validate
      71           :      * @param string name of channel-specific validation package
      72           :      * @final
      73           :      */
      74           :     final function validPackageName($name, $validatepackagename = false)
      75           :     {
      76           :         if ($validatepackagename) {
      77           :             if (strtolower($name) == strtolower($validatepackagename)) {
      78           :                 return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*$/', $name);
      79           :             }
      80           :         }
      81           :         return $this->_validPackageName($name);
      82           :     }
      83           : 
      84           :     /**
      85           :      * This validates a bundle name, and bundle names must conform
      86           :      * to the PEAR naming convention, so the method is final and static.
      87           :      * @param string
      88           :      * @final
      89           :      * @static
      90           :      */
      91           :     static final function validGroupName($name)
      92           :     {
      93           :         return (bool) preg_match('/^[A-Za-z][a-zA-Z0-9_]+$/', $name);
      94           :     }
      95           : 
      96           :     /**
      97           :      * Determine whether $state represents a valid stability level
      98           :      * @param string
      99           :      * @return bool
     100           :      * @static
     101           :      * @final
     102           :      */
     103           :     static final function validState($state)
     104           :     {
     105           :         return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
     106           :     }
     107           : 
     108           :     /**
     109           :      * Get a list of valid stability levels
     110           :      * @return array
     111           :      * @static
     112           :      * @final
     113           :      */
     114           :     static final function getValidStates()
     115           :     {
     116           :         return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
     117           :     }
     118           : 
     119           :     /**
     120           :      * Determine whether a version is a properly formatted version number that can be used
     121           :      * by version_compare
     122           :      * @param string
     123           :      * @return bool
     124           :      * @static
     125           :      * @final
     126           :      */
     127           :     static final function validVersion($ver)
     128           :     {
     129           :         return (bool) preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?$/', $ver);
     130           :     }
     131           : 
     132           :     /**
     133           :      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
     134           :      */
     135           :     function setPackageFile($pf)
     136           :     {
     137           :         $this->_packagexml = $pf;
     138           :     }
     139           : 
     140           :     /**
     141           :      * @access private
     142           :      */
     143           :     function _addFailure($field, $reason)
     144           :     {
     145           :         $this->_failures->E_ERROR[] =
     146           :             new PEAR2_Pyrus_Validate_Exception($reason, $field);
     147           :     }
     148           : 
     149           :     /**
     150           :      * @access private
     151           :      */
     152           :     private function _addWarning($field, $reason)
     153           :     {
     154           :         $this->_failures->E_WARNING[] =
     155           :             new PEAR2_Pyrus_Validate_Exception($reason, $field);
     156           :     }
     157           : 
     158           :     function getFailures()
     159           :     {
     160           :         return $this->_failures;
     161           :     }
     162           : 
     163           :     /**
     164           :      * @param int one of the PEAR2_Pyrus_Validate::* constants
     165           :      */
     166           :     function validate($state = null)
     167           :     {
     168           :         if (!isset($this->_packagexml)) {
     169           :             return false;
     170           :         }
     171           :         if ($state !== null) {
     172           :             $this->_state = $state;
     173           :         }
     174           :         $this->_failures = new PEAR2_MultiErrors;
     175           :         $this->validatePackageName();
     176           :         $this->validateVersion();
     177           :         $this->validateMaintainers();
     178           :         $this->validateDate();
     179           :         $this->validateSummary();
     180           :         $this->validateDescription();
     181           :         $this->validateLicense();
     182           :         $this->validateNotes();
     183           :         $this->validateTime();
     184           :         $this->validateStability();
     185           :         $this->validateDependencies();
     186           :         $this->validateMainFilelist();
     187           :         $this->validateReleaseFilelist();
     188           :         //$this->validateGlobalTasks();
     189           :         $this->validateChangelog();
     190           :         return !((bool) count($this->_failures->E_ERROR));
     191           :     }
     192           : 
     193           :     /**
     194           :      * @access protected
     195           :      */
     196           :     function validatePackageName()
     197           :     {
     198           :         if ($this->_state == PEAR2_Pyrus_Validate::PACKAGING ||
     199           :               $this->_state == PEAR2_Pyrus_Validate::NORMAL) {
     200           :             if ($this->_packagexml->extends) {
     201           :                 $version = $this->_packagexml->version['release'] . '';
     202           :                 $name = $this->_packagexml->name;
     203           :                 $test = array_shift($a = explode('.', $version));
     204           :                 if ($test == '0') {
     205           :                     return true;
     206           :                 }
     207           :                 $vlen = strlen($test);
     208           :                 $majver = substr($name, strlen($name) - $vlen);
     209           :                 while ($majver && !is_numeric($majver{0})) {
     210           :                     $majver = substr($majver, 1);
     211           :                 }
     212           :                 if ($majver != $test) {
     213           :                     $this->_addWarning('package', "package $name extends package " .
     214           :                         $this->_packagexml->extends . ' and so the name should ' .
     215           :                         'have a postfix equal to the major version like "' .
     216           :                         $this->_packagexml->extends . $test . '"');
     217           :                     return true;
     218           :                 } elseif (substr($name, 0, strlen($name) - $vlen) !=
     219           :                             $this->_packagexml->extends) {
     220           :                     $this->_addWarning('package', "package $name extends package " .
     221           :                         $this->_packagexml->extends . ' and so the name must ' .
     222           :                         'be an extension like "' . $this->_packagexml->extends .
     223           :                         $test . '"');
     224           :                     return true;
     225           :                 }
     226           :             }
     227           :         }
     228           :         if (!$this->validPackageName($this->_packagexml->name)) {
     229           :             $this->_addFailure('name', 'package name "' .
     230           :                 $this->_packagexml->name . '" is invalid');
     231           :             return false;
     232           :         }
     233           :     }
     234           : 
     235           :     /**
     236           :      * @access protected
     237           :      */
     238           :     function validateVersion()
     239           :     {
     240           :         if ($this->_state != PEAR2_Pyrus_Validate::PACKAGING) {
     241           :             if (!$this->validVersion($this->_packagexml->version['release'])) {
     242           :                 $this->_addFailure('version',
     243           :                     'Invalid version number "' . $this->_packagexml->version['release'] . '"');
     244           :             }
     245           :             return false;
     246           :         }
     247           :         $version = $this->_packagexml->version['release'];
     248           :         $versioncomponents = explode('.', $version);
     249           :         if (count($versioncomponents) != 3) {
     250           :             $this->_addWarning('version',
     251           :                 'A version number should have 3 decimals (x.y.z)');
     252           :             return true;
     253           :         }
     254           :         $name = $this->_packagexml->name;
     255           :         // version must be based upon state
     256           :         switch ($this->_packagexml->stability['release']) {
     257           :             case 'snapshot' :
     258           :                 return true;
     259           :             case 'devel' :
     260           :                 if ($versioncomponents[0] . 'a' == '0a') {
     261           :                     return true;
     262           :                 }
     263           :                 if ($versioncomponents[0] == 0) {
     264           :                     $versioncomponents[0] = '0';
     265           :                     $this->_addWarning('version',
     266           :                         'version "' . $version . '" should be "' .
     267           :                         implode('.' ,$versioncomponents) . '"');
     268           :                 } else {
     269           :                     $this->_addWarning('version',
     270           :                         'packages with devel stability must be < version 1.0.0');
     271           :                 }
     272           :                 return true;
     273           :             break;
     274           :             case 'alpha' :
     275           :             case 'beta' :
     276           :                 // check for a package that extends a package,
     277           :                 // like Foo and Foo2
     278           :                 if ($this->_state == PEAR2_Pyrus_Validate::PACKAGING) {
     279           :                     if (substr($versioncomponents[2], 1, 2) == 'rc') {
     280           :                         $this->_addFailure('version', 'Release Candidate versions ' .
     281           :                             'must have capital RC, not lower-case rc');
     282           :                         return false;
     283           :                     }
     284           :                 }
     285           :                 if (!$this->_packagexml->extends) {
     286           :                     if ($versioncomponents[0] == '1') {
     287           :                         if ($versioncomponents[2]{0} == '0') {
     288           :                             if ($versioncomponents[2] == '0') {
     289           :                                 // version 1.*.0000
     290           :                                 $this->_addWarning('version',
     291           :                                     'version 1.' . $versioncomponents[1] .
     292           :                                         '.0 probably should not be alpha or beta');
     293           :                                 return true;
     294           :                             } elseif (strlen($versioncomponents[2]) > 1) {
     295           :                                 // version 1.*.0RC1 or 1.*.0beta24 etc.
     296           :                                 return true;
     297           :                             } else {
     298           :                                 // version 1.*.0
     299           :                                 $this->_addWarning('version',
     300           :                                     'version 1.' . $versioncomponents[1] .
     301           :                                         '.0 probably should not be alpha or beta');
     302           :                                 return true;
     303           :                             }
     304           :                         } else {
     305           :                             $this->_addWarning('version',
     306           :                                 'bugfix versions (1.3.x where x > 0) probably should ' .
     307           :                                 'not be alpha or beta');
     308           :                             return true;
     309           :                         }
     310           :                     } elseif ($versioncomponents[0] != '0') {
     311           :                         $this->_addWarning('version',
     312           :                             'major versions greater than 1 are not allowed for packages ' .
     313           :                             'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
     314           :                         return true;
     315           :                     }
     316           :                     if ($versioncomponents[0] . 'a' == '0a') {
     317           :                         return true;
     318           :                     }
     319           :                     if ($versioncomponents[0] == 0) {
     320           :                         $versioncomponents[0] = '0';
     321           :                         $this->_addWarning('version',
     322           :                             'version "' . $version . '" should be "' .
     323           :                             implode('.' ,$versioncomponents) . '"');
     324           :                     }
     325           :                 } else {
     326           :                     $vlen = strlen($versioncomponents[0] . '');
     327           :                     $majver = substr($name, strlen($name) - $vlen);
     328           :                     while ($majver && !is_numeric($majver{0})) {
     329           :                         $majver = substr($majver, 1);
     330           :                     }
     331           :                     if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
     332           :                         $this->_addWarning('version', 'first version number "' .
     333           :                             $versioncomponents[0] . '" must match the postfix of ' .
     334           :                             'package name "' . $name . '" (' .
     335           :                             $majver . ')');
     336           :                         return true;
     337           :                     }
     338           :                     if ($versioncomponents[0] == $majver) {
     339           :                         if ($versioncomponents[2]{0} == '0') {
     340           :                             if ($versioncomponents[2] == '0') {
     341           :                                 // version 2.*.0000
     342           :                                 $this->_addWarning('version',
     343           :                                     "version $majver." . $versioncomponents[1] .
     344           :                                         '.0 probably should not be alpha or beta');
     345           :                                 return false;
     346           :                             } elseif (strlen($versioncomponents[2]) > 1) {
     347           :                                 // version 2.*.0RC1 or 2.*.0beta24 etc.
     348           :                                 return true;
     349           :                             } else {
     350           :                                 // version 2.*.0
     351           :                                 $this->_addWarning('version',
     352           :                                     "version $majver." . $versioncomponents[1] .
     353           :                                         '.0 cannot be alpha or beta');
     354           :                                 return true;
     355           :                             }
     356           :                         } else {
     357           :                             $this->_addWarning('version',
     358           :                                 "bugfix versions ($majver.x.y where y > 0) should " .
     359           :                                 'not be alpha or beta');
     360           :                             return true;
     361           :                         }
     362           :                     } elseif ($versioncomponents[0] != '0') {
     363           :                         $this->_addWarning('version',
     364           :                             "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
     365           :                         return true;
     366           :                     }
     367           :                     if ($versioncomponents[0] . 'a' == '0a') {
     368           :                         return true;
     369           :                     }
     370           :                     if ($versioncomponents[0] == 0) {
     371           :                         $versioncomponents[0] = '0';
     372           :                         $this->_addWarning('version',
     373           :                             'version "' . $version . '" should be "' .
     374           :                             implode('.' ,$versioncomponents) . '"');
     375           :                     }
     376           :                 }
     377           :                 return true;
     378           :             break;
     379           :             case 'stable' :
     380           :                 if ($versioncomponents[0] == '0') {
     381           :                     $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
     382           :                     'be stable');
     383           :                     return true;
     384           :                 }
     385           :                 if (!is_numeric($versioncomponents[2])) {
     386           :                     if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
     387           :                           $versioncomponents[2])) {
     388           :                         $this->_addWarning('version', 'version "' . $version . '" or any ' .
     389           :                             'RC/beta/alpha version cannot be stable');
     390           :                         return true;
     391           :                     }
     392           :                 }
     393           :                 // check for a package that extends a package,
     394           :                 // like Foo and Foo2
     395           :                 if ($this->_packagexml->extends) {
     396           :                     $vlen = strlen($versioncomponents[0] . '');
     397           :                     $majver = substr($name, strlen($name) - $vlen);
     398           :                     while ($majver && !is_numeric($majver{0})) {
     399           :                         $majver = substr($majver, 1);
     400           :                     }
     401           :                     if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
     402           :                         $this->_addWarning('version', 'first version number "' .
     403           :                             $versioncomponents[0] . '" must match the postfix of ' .
     404           :                             'package name "' . $name . '" (' .
     405           :                             $majver . ')');
     406           :                         return true;
     407           :                     }
     408           :                 } elseif ($versioncomponents[0] > 1) {
     409           :                     $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
     410           :                         '1 for any package that does not have an <extends> tag');
     411           :                 }
     412           :                 return true;
     413           :             break;
     414           :             default :
     415           :                 return false;
     416           :             break;
     417           :         }
     418           :     }
     419           : 
     420           :     /**
     421           :      * @access protected
     422           :      */
     423           :     function validateMaintainers()
     424           :     {
     425           :         // maintainers can only be truly validated server-side for most channels
     426           :         // but allow this customization for those who wish it
     427           :         return true;
     428           :     }
     429           : 
     430           :     /**
     431           :      * @access protected
     432           :      */
     433           :     function validateDate()
     434           :     {
     435           :         if ($this->_state == PEAR2_Pyrus_Validate::NORMAL ||
     436           :               $this->_state == PEAR2_Pyrus_Validate::PACKAGING) {
     437           : 
     438           :             if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
     439           :                   $this->_packagexml->date, $res) ||
     440           :                   count($res) < 4
     441           :                   || !checkdate($res[2], $res[3], $res[1])
     442           :                 ) {
     443           :                 $this->_addFailure('date', 'invalid release date "' .
     444           :                     $this->_packagexml->date . '"');
     445           :                 return false;
     446           :             }
     447           : 
     448           : 
     449           :             if ($this->_state == PEAR2_Pyrus_Validate::PACKAGING &&
     450           :                   $this->_packagexml->date != date('Y-m-d')) {
     451           :                 $this->_addWarning('date', 'Release Date "' .
     452           :                     $this->_packagexml->date . '" is not today');
     453           :             }
     454           :         }
     455           :         return true;
     456           :     }
     457           : 
     458           :     /**
     459           :      * @access protected
     460           :      */
     461           :     function validateTime()
     462           :     {
     463           :         if (!$this->_packagexml->time) {
     464           :             // default of no time value set
     465           :             return true;
     466           :         }
     467           :         // packager automatically sets time, so only validate if
     468           :         // pear validate is called
     469           :         if ($this->_state = PEAR2_Pyrus_Validate::NORMAL) {
     470           :             if (!preg_match('/\d\d:\d\d:\d\d/',
     471           :                   $this->_packagexml->time)) {
     472           :                 $this->_addFailure('time', 'invalid release time "' .
     473           :                     $this->_packagexml->time . '"');
     474           :                 return false;
     475           :             }
     476           :             if (strtotime($this->_packagexml->time) == -1) {
     477           :                 $this->_addFailure('time', 'invalid release time "' .
     478           :                     $this->_packagexml->time . '"');
     479           :                 return false;
     480           :             }
     481           :         }
     482           :         return true;
     483           :     }
     484           : 
     485           :     /**
     486           :      * @access protected
     487           :      */
     488           :     function validateStability()
     489           :     {
     490           :         $ret = true;
     491           :         $packagestability = $this->_packagexml->stability['release'];
     492           :         $apistability = $this->_packagexml->stability['api'];
     493           :         if (!self::validState($packagestability)) {
     494           :             $this->_addFailure('state', 'invalid release stability "' .
     495           :                 $this->_packagexml->stability['release'] . '", must be one of: ' .
     496           :                 implode(', ', self::getValidStates()));
     497           :             $ret = false;
     498           :         }
     499           :         $apistates = self::getValidStates();
     500           :         array_shift($apistates); // snapshot is not allowed
     501           :         if (!in_array($apistability, $apistates)) {
     502           :             $this->_addFailure('state', 'invalid API stability "' .
     503           :                 $this->_packagexml->stability['api'] . '", must be one of: ' .
     504           :                 implode(', ', $apistates));
     505           :             $ret = false;
     506           :         }
     507           :         return $ret;
     508           :     }
     509           : 
     510           :     /**
     511           :      * @access protected
     512           :      */
     513           :     function validateSummary()
     514           :     {
     515           :         return true;
     516           :     }
     517           : 
     518           :     /**
     519           :      * @access protected
     520           :      */
     521           :     function validateDescription()
     522           :     {
     523           :         return true;
     524           :     }
     525           : 
     526           :     /**
     527           :      * @access protected
     528           :      */
     529           :     function validateLicense()
     530           :     {
     531           :         return true;
     532           :     }
     533           : 
     534           :     /**
     535           :      * @access protected
     536           :      */
     537           :     function validateNotes()
     538           :     {
     539           :         return true;
     540           :     }
     541           : 
     542           :     /**
     543           :      * for package.xml 2.0 only - channels can't use package.xml 1.0
     544           :      * @access protected
     545           :      */
     546           :     function validateDependencies()
     547           :     {
     548           :         return true;
     549           :     }
     550           : 
     551           :     /**
     552           :      * for package.xml 2.0 only
     553           :      * @access protected
     554           :      */
     555           :     function validateMainFilelist()
     556           :     {
     557           :         return true; // placeholder for now
     558           :     }
     559           : 
     560           :     /**
     561           :      * for package.xml 2.0 only
     562           :      * @access protected
     563           :      */
     564           :     function validateReleaseFilelist()
     565           :     {
     566           :         return true; // placeholder for now
     567           :     }
     568           : 
     569           :     /**
     570           :      * @access protected
     571           :      */
     572           :     function validateChangelog()
     573           :     {
     574           :         return true;
     575           :     }
     576           : }
     577           : ?>