PageRenderTime 64ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/common/libraries/plugin/pear/PEAR/PackageFile/v2/Validator.php

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