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