PageRenderTime 76ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/script/lib/PEAR/PackageFile/v2/Validator.php

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