PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/pear/php/PHP/CompatInfo/Parser.php

https://github.com/wrobel/horde-glue
PHP | 1926 lines | 1625 code | 46 blank | 255 comment | 68 complexity | eaecdf9cd36876b82308ebfa9159b79e MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Copyright (c) 2008-2009, Davey Shafik <davey@php.net>
  4. * Laurent Laville <pear@laurent-laville.org>
  5. *
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * * Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * * Neither the name of the authors nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  25. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. * POSSIBILITY OF SUCH DAMAGE.
  32. *
  33. * PHP versions 4 and 5
  34. *
  35. * @category PHP
  36. * @package PHP_CompatInfo
  37. * @author Davey Shafik <davey@php.net>
  38. * @author Laurent Laville <pear@laurent-laville.org>
  39. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  40. * @version CVS: $Id: Parser.php,v 1.21 2009/01/02 10:18:47 farell Exp $
  41. * @link http://pear.php.net/package/PHP_CompatInfo
  42. * @since File available since Release 1.8.0b2
  43. */
  44. require_once 'Event/Dispatcher.php';
  45. require_once 'File/Find.php';
  46. /**
  47. * An array of class init versions and extension
  48. */
  49. require_once 'PHP/CompatInfo/class_array.php';
  50. /**
  51. * An array of function init versions and extension
  52. */
  53. require_once 'PHP/CompatInfo/func_array.php';
  54. /**
  55. * An array of constants and their init versions
  56. */
  57. require_once 'PHP/CompatInfo/const_array.php';
  58. /**
  59. * An abstract base class for CompatInfo renderers
  60. */
  61. require_once 'PHP/CompatInfo/Renderer.php';
  62. /**
  63. * Event name of parsing data source start process
  64. */
  65. define('PHP_COMPATINFO_EVENT_AUDITSTARTED', 'auditStarted');
  66. /**
  67. * Event name of parsing data source end process
  68. */
  69. define('PHP_COMPATINFO_EVENT_AUDITFINISHED', 'auditFinished');
  70. /**
  71. * Event name of parsing a file start process
  72. */
  73. define('PHP_COMPATINFO_EVENT_FILESTARTED', 'fileStarted');
  74. /**
  75. * Event name of parsing a file end process
  76. */
  77. define('PHP_COMPATINFO_EVENT_FILEFINISHED', 'fileFinished');
  78. /**
  79. * Event name of parsing a file start process
  80. */
  81. define('PHP_COMPATINFO_EVENT_CODESTARTED', 'codeStarted');
  82. /**
  83. * Event name of parsing a file end process
  84. */
  85. define('PHP_COMPATINFO_EVENT_CODEFINISHED', 'codeFinished');
  86. /**
  87. * Parser logic
  88. *
  89. * This class is the model in the MVC design pattern of API 1.8.0 (since beta 2)
  90. *
  91. * @category PHP
  92. * @package PHP_CompatInfo
  93. * @author Laurent Laville <pear@laurent-laville.org>
  94. * @license http://www.opensource.org/licenses/bsd-license.php BSD
  95. * @version Release: 1.9.0
  96. * @link http://pear.php.net/package/PHP_CompatInfo
  97. * @since Class available since Release 1.8.0b2
  98. */
  99. class PHP_CompatInfo_Parser
  100. {
  101. /**
  102. * Instance of concrete renderer used to show parse results
  103. *
  104. * @var object
  105. * @since 1.8.0b2
  106. * @access protected
  107. */
  108. var $renderer;
  109. /**
  110. * Stores the event dispatcher which handles notifications
  111. *
  112. * @var object
  113. * @since 1.8.0b2
  114. * @access protected
  115. */
  116. var $dispatcher;
  117. /**
  118. * Count the number of observer registered.
  119. * The Event_Dispatcher will be add on first observer registration, and
  120. * will be removed with the last observer.
  121. *
  122. * @var integer
  123. * @since 1.8.0b2
  124. * @access private
  125. */
  126. var $_observerCount;
  127. /**
  128. * @var string Earliest version of PHP to use
  129. * @since 0.7.0
  130. */
  131. var $latest_version = '4.0.0';
  132. /**
  133. * @var string Last version of PHP to use
  134. */
  135. var $earliest_version = '';
  136. /**
  137. * @var array Parsing options
  138. */
  139. var $options;
  140. /**
  141. * @var array Data Source
  142. * @since 1.8.0b2
  143. */
  144. var $dataSource;
  145. /**
  146. * @var array Directory list found when parsing data source
  147. * @since 1.8.0b2
  148. * @see getDirlist()
  149. */
  150. var $directories;
  151. /**
  152. * @var array List of files ignored when parsing data source
  153. * @since 1.8.0b2
  154. * @see getIgnoredFiles()
  155. */
  156. var $ignored_files = array();
  157. /**
  158. * @var array Result of the latest data source parsing
  159. * @since 1.9.0b1
  160. * @see parseData()
  161. */
  162. var $latest_parse = null;
  163. /**
  164. * Class constructor (ZE1) for PHP4
  165. *
  166. * @access public
  167. * @since version 1.8.0b2 (2008-06-03)
  168. */
  169. function PHP_CompatInfo_Parser()
  170. {
  171. $this->__construct();
  172. }
  173. /**
  174. * Class constructor (ZE2) for PHP5+
  175. *
  176. * @access public
  177. * @since version 1.8.0b2 (2008-06-03)
  178. */
  179. function __construct()
  180. {
  181. $this->options = array(
  182. 'file_ext' => array('php', 'php4', 'inc', 'phtml'),
  183. 'recurse_dir' => true,
  184. 'debug' => false,
  185. 'is_string' => false,
  186. 'ignore_files' => array(),
  187. 'ignore_dirs' => array()
  188. );
  189. }
  190. /**
  191. * Set up driver to be used
  192. *
  193. * Set up driver to be used, dependant on specified type.
  194. *
  195. * @param string $type Name the type of driver (html, text...)
  196. * @param array $conf A hash containing any additional configuration
  197. *
  198. * @access public
  199. * @return void
  200. * @since version 1.8.0b2 (2008-06-03)
  201. */
  202. function setOutputDriver($type, $conf = array())
  203. {
  204. $this->renderer =& PHP_CompatInfo_Renderer::factory($this, $type, $conf);
  205. }
  206. /**
  207. * Registers a new listener
  208. *
  209. * Registers a new listener with the given criteria.
  210. *
  211. * @param mixed $callback A PHP callback
  212. * @param string $nName (optional) Expected notification name
  213. *
  214. * @access public
  215. * @return void
  216. * @since version 1.8.0b2 (2008-06-03)
  217. */
  218. function addListener($callback, $nName = EVENT_DISPATCHER_GLOBAL)
  219. {
  220. $this->dispatcher =& Event_Dispatcher::getInstance();
  221. // $this->dispatcher->setNotificationClass('PHP_CompatInfo_Audit');
  222. $this->dispatcher->addObserver($callback, $nName);
  223. $this->_observerCount++;
  224. }
  225. /**
  226. * Removes a registered listener
  227. *
  228. * Removes a registered listener that correspond to the given criteria.
  229. *
  230. * @param mixed $callback A PHP callback
  231. * @param string $nName (optional) Expected notification name
  232. *
  233. * @access public
  234. * @return bool True if listener was removed, false otherwise.
  235. * @since version 1.8.0b2 (2008-06-03)
  236. */
  237. function removeListener($callback, $nName = EVENT_DISPATCHER_GLOBAL)
  238. {
  239. $result = $this->dispatcher->removeObserver($callback, $nName);
  240. if ($result) {
  241. $this->_observerCount--;
  242. if ($this->_observerCount == 0) {
  243. unset($this->dispatcher);
  244. }
  245. }
  246. return $result;
  247. }
  248. /**
  249. * Post a new notification to all listeners registered.
  250. *
  251. * This notification occured only if a dispatcher exists. That means if
  252. * at least one listener was registered.
  253. *
  254. * @param string $event Name of the notification handler
  255. * @param array $info (optional) Additional information about the notification
  256. *
  257. * @access public
  258. * @return void
  259. * @since version 1.8.0b2 (2008-06-03)
  260. */
  261. function notifyListeners($event, $info = array())
  262. {
  263. if (isset($this->dispatcher)) {
  264. $this->dispatcher->post($this, $event, $info);
  265. }
  266. }
  267. /**
  268. * Load components list
  269. *
  270. * Load components list for a PHP version or subset
  271. *
  272. * @param string $min PHP minimal version
  273. * @param string|boolean $max (optional) PHP maximal version
  274. * @param boolean $include_const (optional) include constants list
  275. * in final result
  276. * @param boolean $groupby_vers (optional) give initial php version
  277. * of function or constant
  278. *
  279. * @return array An array of php function/constant names history
  280. * @access public
  281. * @static
  282. * @since version 1.2.0 (2006-08-23)
  283. */
  284. function loadVersion($min, $max = false,
  285. $include_const = false, $groupby_vers = false)
  286. {
  287. $keys = array();
  288. foreach ($GLOBALS['_PHP_COMPATINFO_FUNCS'] as $func => $arr) {
  289. if (isset($arr['pecl']) && $arr['pecl'] === true) {
  290. continue;
  291. }
  292. $vmin = $arr['init'];
  293. if (version_compare($vmin, $min) < 0) {
  294. continue;
  295. }
  296. if ($max) {
  297. $end = (isset($arr['end'])) ? $arr['end'] : $vmin;
  298. if (version_compare($end, $max) < 1) {
  299. if ($groupby_vers === true) {
  300. $keys[$vmin][] = $func;
  301. } else {
  302. $keys[] = $func;
  303. }
  304. }
  305. } else {
  306. if ($groupby_vers === true) {
  307. $keys[$vmin][] = $func;
  308. } else {
  309. $keys[] = $func;
  310. }
  311. }
  312. }
  313. if ($groupby_vers === true) {
  314. foreach ($keys as $vmin => $func) {
  315. sort($keys[$vmin]);
  316. }
  317. ksort($keys);
  318. } else {
  319. sort($keys);
  320. }
  321. if ($include_const === true) {
  322. $keys = array('functions' => $keys, 'constants' => array());
  323. foreach ($GLOBALS['_PHP_COMPATINFO_CONST'] as $const => $arr) {
  324. $vmin = $arr['init'];
  325. if (version_compare($vmin, $min) < 0) {
  326. continue;
  327. }
  328. if ($max) {
  329. $end = (isset($arr['end'])) ? $arr['end'] : $vmin;
  330. if (version_compare($end, $max) < 1) {
  331. if ($groupby_vers === true) {
  332. $keys['constants'][$vmin][] = $arr['name'];
  333. } else {
  334. $keys['constants'][] = $arr['name'];
  335. }
  336. }
  337. } else {
  338. if ($groupby_vers === true) {
  339. $keys['constants'][$vmin][] = $arr['name'];
  340. } else {
  341. $keys['constants'][] = $arr['name'];
  342. }
  343. }
  344. }
  345. ksort($keys['constants']);
  346. }
  347. return $keys;
  348. }
  349. /**
  350. * Returns list of directory parsed
  351. *
  352. * Returns list of directory parsed, depending of restrictive parser options.
  353. *
  354. * @param mixed $dir The directory name
  355. * @param array $options An array of parser options. See parseData() method.
  356. *
  357. * @access public
  358. * @return array list of directories that should be parsed
  359. * @since version 1.8.0b2 (2008-06-03)
  360. */
  361. function getDirlist($dir, $options)
  362. {
  363. if (!isset($this->directories)) {
  364. $this->getFilelist($dir, $options);
  365. }
  366. return $this->directories;
  367. }
  368. /**
  369. * Returns list of files parsed
  370. *
  371. * Returns list of files parsed, depending of restrictive parser options.
  372. *
  373. * @param mixed $dir The directory name where to look files
  374. * @param array $options An array of parser options. See parseData() method.
  375. *
  376. * @access public
  377. * @return array list of files that should be parsed
  378. * @since version 1.8.0b2 (2008-06-03)
  379. */
  380. function getFilelist($dir, $options)
  381. {
  382. $skipped = array();
  383. $ignored = array();
  384. $options = array_merge($this->options, $options);
  385. $options['file_ext'] = array_map('strtolower', $options['file_ext']);
  386. if ($dir{strlen($dir)-1} == '/' || $dir{strlen($dir)-1} == '\\') {
  387. $dir = substr($dir, 0, -1);
  388. }
  389. // use system directory separator rather than forward slash by default
  390. $ff = new File_Find();
  391. $ff->dirsep = DIRECTORY_SEPARATOR;
  392. // get directory list that should be ignored from scope
  393. $ignore_dirs = array();
  394. if (count($options['ignore_dirs']) > 0) {
  395. foreach ($options['ignore_dirs'] as $cond) {
  396. $cond = str_replace('\\', "\\\\", $cond);
  397. $dirs = $ff->search('`'.$cond.'`', $dir, 'perl',
  398. true, 'directories');
  399. $ignore_dirs = array_merge($ignore_dirs, $dirs);
  400. }
  401. }
  402. // get file list that should be ignored from scope
  403. $ignore_files = array();
  404. if (count($options['ignore_files']) > 0) {
  405. foreach ($options['ignore_files'] as $cond) {
  406. $cond = str_replace('\\', "\\\\", $cond);
  407. $files = $ff->search('`'.$cond.'`', $dir, 'perl',
  408. true, 'files');
  409. $ignore_files = array_merge($ignore_files, $files);
  410. }
  411. }
  412. list($directories, $files) = $ff->maptree($dir);
  413. foreach ($files as $file) {
  414. $file_info = pathinfo($file);
  415. if ($options['recurse_dir'] == false
  416. && $file_info['dirname'] != $dir) {
  417. $skipped[] = $file;
  418. continue;
  419. }
  420. if (in_array($file_info['dirname'], $ignore_dirs)) {
  421. $ignored[] = $file;
  422. } elseif (in_array($file, $ignore_files)) {
  423. $ignored[] = $file;
  424. } else {
  425. if (isset($file_info['extension'])
  426. && in_array(strtolower($file_info['extension']),
  427. $options['file_ext'])) {
  428. continue;
  429. }
  430. $ignored[] = $file;
  431. }
  432. }
  433. $files = PHP_CompatInfo_Parser::_arrayDiff($files,
  434. array_merge($ignored, $skipped));
  435. $this->directories
  436. = PHP_CompatInfo_Parser::_arrayDiff($directories, $ignore_dirs);
  437. $this->ignored_files
  438. = $ignored;
  439. return $files;
  440. }
  441. /**
  442. * Returns list of files ignored
  443. *
  444. * Returns list of files ignored while parsing directories
  445. *
  446. * @access public
  447. * @return array or false on error
  448. * @since version 1.8.0b2 (2008-06-03)
  449. */
  450. function getIgnoredFiles()
  451. {
  452. return $this->ignored_files;
  453. }
  454. /**
  455. * Returns the latest parse data source ignored functions
  456. *
  457. * Returns the latest parse data source ignored functions list
  458. *
  459. * @param mixed $file (optional) A specific filename or not (false)
  460. *
  461. * @access public
  462. * @return mixed Null on error or if there were no previous data parsing
  463. * @since version 1.9.0b2 (2008-12-19)
  464. */
  465. function getIgnoredFunctions($file = false)
  466. {
  467. if (!is_array($this->latest_parse)) {
  468. // no code analysis found
  469. $functions = null;
  470. } elseif ($file === false) {
  471. $functions = $this->latest_parse['ignored_functions'];
  472. } elseif (isset($this->latest_parse[$file])) {
  473. $functions = $this->latest_parse[$file]['ignored_functions'];
  474. } else {
  475. $functions = null;
  476. }
  477. return $functions;
  478. }
  479. /**
  480. * Returns the latest parse data source ignored extensions
  481. *
  482. * Returns the latest parse data source ignored extensions list
  483. *
  484. * @param mixed $file (optional) A specific filename or not (false)
  485. *
  486. * @access public
  487. * @return mixed Null on error or if there were no previous data parsing
  488. * @since version 1.9.0b2 (2008-12-19)
  489. */
  490. function getIgnoredExtensions($file = false)
  491. {
  492. if (!is_array($this->latest_parse)) {
  493. // no code analysis found
  494. $extensions = null;
  495. } elseif ($file === false) {
  496. $extensions = $this->latest_parse['ignored_extensions'];
  497. } elseif (isset($this->latest_parse[$file])) {
  498. $extensions = $this->latest_parse[$file]['ignored_extensions'];
  499. } else {
  500. $extensions = null;
  501. }
  502. return $extensions;
  503. }
  504. /**
  505. * Returns the latest parse data source ignored constants
  506. *
  507. * Returns the latest parse data source ignored constants list
  508. *
  509. * @param mixed $file (optional) A specific filename or not (false)
  510. *
  511. * @access public
  512. * @return mixed Null on error or if there were no previous data parsing
  513. * @since version 1.9.0b2 (2008-12-19)
  514. */
  515. function getIgnoredConstants($file = false)
  516. {
  517. if (!is_array($this->latest_parse)) {
  518. // no code analysis found
  519. $constants = null;
  520. } elseif ($file === false) {
  521. $constants = $this->latest_parse['ignored_constants'];
  522. } elseif (isset($this->latest_parse[$file])) {
  523. $constants = $this->latest_parse[$file]['ignored_constants'];
  524. } else {
  525. $constants = null;
  526. }
  527. return $constants;
  528. }
  529. /**
  530. * Returns the latest parse data source version
  531. *
  532. * Returns the latest parse data source version, minimum and/or maximum
  533. *
  534. * @param mixed $file (optional) A specific filename or not (false)
  535. * @param bool $max (optional) Level with or without contextual data
  536. *
  537. * @access public
  538. * @return mixed Null on error or if there were no previous data parsing
  539. * @since version 1.9.0b1 (2008-11-30)
  540. */
  541. function getVersion($file = false, $max = false)
  542. {
  543. $key = ($max === true) ? 'max_version' : 'version';
  544. if (!is_array($this->latest_parse)) {
  545. // no code analysis found
  546. $version = null;
  547. } elseif ($file === false) {
  548. $version = $this->latest_parse[$key];
  549. } elseif (isset($this->latest_parse[$file])) {
  550. $version = $this->latest_parse[$file][$key];
  551. } else {
  552. $version = null;
  553. }
  554. return $version;
  555. }
  556. /**
  557. * Returns the latest parse data source classes declared
  558. *
  559. * Returns the latest parse data source classes declared (internal or
  560. * end-user defined)
  561. *
  562. * @param mixed $file (optional) A specific filename or not (false)
  563. *
  564. * @access public
  565. * @return mixed Null on error or if there were no previous data parsing
  566. * @since version 1.9.0b1 (2008-11-30)
  567. */
  568. function getClasses($file = false)
  569. {
  570. if (!is_array($this->latest_parse)) {
  571. // no code analysis found
  572. $classes = null;
  573. } elseif ($file === false) {
  574. $classes = $this->latest_parse['classes'];
  575. } elseif (isset($this->latest_parse[$file])) {
  576. $classes = $this->latest_parse[$file]['classes'];
  577. } else {
  578. $classes = null;
  579. }
  580. return $classes;
  581. }
  582. /**
  583. * Returns the latest parse data source functions declared
  584. *
  585. * Returns the latest parse data source functions declared (internal or
  586. * end-user defined)
  587. *
  588. * @param mixed $file (optional) A specific filename or not (false)
  589. *
  590. * @access public
  591. * @return mixed Null on error or if there were no previous data parsing
  592. * @since version 1.9.0b1 (2008-11-30)
  593. */
  594. function getFunctions($file = false)
  595. {
  596. if (!is_array($this->latest_parse)) {
  597. // no code analysis found
  598. $functions = null;
  599. } elseif ($file === false) {
  600. $functions = $this->latest_parse['functions'];
  601. } elseif (isset($this->latest_parse[$file])) {
  602. $functions = $this->latest_parse[$file]['functions'];
  603. } else {
  604. $functions = null;
  605. }
  606. return $functions;
  607. }
  608. /**
  609. * Returns the latest parse data source extensions used
  610. *
  611. * Returns the latest parse data source extensions used
  612. *
  613. * @param mixed $file (optional) A specific filename or not (false)
  614. *
  615. * @access public
  616. * @return mixed Null on error or if there were no previous data parsing
  617. * @since version 1.9.0b1 (2008-11-30)
  618. */
  619. function getExtensions($file = false)
  620. {
  621. if (!is_array($this->latest_parse)) {
  622. // no code analysis found
  623. $extensions = null;
  624. } elseif ($file === false) {
  625. $extensions = $this->latest_parse['extensions'];
  626. } elseif (isset($this->latest_parse[$file])) {
  627. $extensions = $this->latest_parse[$file]['extensions'];
  628. } else {
  629. $extensions = null;
  630. }
  631. return $extensions;
  632. }
  633. /**
  634. * Returns the latest parse data source constants declared
  635. *
  636. * Returns the latest parse data source constants declared (internal or
  637. * end-user defined)
  638. *
  639. * @param mixed $file (optional) A specific filename or not (false)
  640. *
  641. * @access public
  642. * @return mixed Null on error or if there were no previous data parsing
  643. * @since version 1.9.0b1 (2008-11-30)
  644. */
  645. function getConstants($file = false)
  646. {
  647. if (!is_array($this->latest_parse)) {
  648. // no code analysis found
  649. $constants = null;
  650. } elseif ($file === false) {
  651. $constants = $this->latest_parse['constants'];
  652. } elseif (isset($this->latest_parse[$file])) {
  653. $constants = $this->latest_parse[$file]['constants'];
  654. } else {
  655. $constants = null;
  656. }
  657. return $constants;
  658. }
  659. /**
  660. * Returns the latest parse data source tokens declared
  661. *
  662. * Returns the latest parse data source PHP5+ tokens declared
  663. *
  664. * @param mixed $file (optional) A specific filename or not (false)
  665. *
  666. * @access public
  667. * @return mixed Null on error or if there were no previous data parsing
  668. * @since version 1.9.0b1 (2008-11-30)
  669. */
  670. function getTokens($file = false)
  671. {
  672. if (!is_array($this->latest_parse)) {
  673. // no code analysis found
  674. } elseif ($file === false) {
  675. $tokens = $this->latest_parse['tokens'];
  676. } elseif (isset($this->latest_parse[$file])) {
  677. $tokens = $this->latest_parse[$file]['tokens'];
  678. } else {
  679. $tokens = null;
  680. }
  681. return $tokens;
  682. }
  683. /**
  684. * Returns the latest parse data source conditions
  685. *
  686. * Returns the latest parse data source conditions, with or without
  687. * contextual data
  688. *
  689. * @param mixed $file (optional) A specific filename or not (false)
  690. * @param bool $levelOnly (optional) Level with or without contextual data
  691. *
  692. * @access public
  693. * @return mixed Null on error or if there were no previous data parsing
  694. * @since version 1.9.0b1 (2008-11-30)
  695. */
  696. function getConditions($file = false, $levelOnly = false)
  697. {
  698. if (!is_array($this->latest_parse)) {
  699. // no code analysis found
  700. $conditions = null;
  701. } elseif ($file === false) {
  702. $conditions = $this->latest_parse['cond_code'];
  703. } elseif (isset($this->latest_parse[$file])) {
  704. $conditions = $this->latest_parse[$file]['cond_code'];
  705. } else {
  706. $conditions = null;
  707. }
  708. if (is_array($conditions) && $levelOnly === true) {
  709. $conditions = $conditions[0];
  710. }
  711. return $conditions;
  712. }
  713. /**
  714. * Parse a data source
  715. *
  716. * Parse a data source with auto detect ability. This data source, may be
  717. * one of these follows: a directory, a file, a string (chunk of code),
  718. * an array of multiple origin.
  719. *
  720. * Each of five parsing functions support common and specifics options.
  721. *
  722. * * Common options :
  723. * - 'debug' Contains a boolean to control whether
  724. * extra ouput is shown.
  725. * - 'ignore_functions' Contains an array of functions to ignore
  726. * when calculating the version needed.
  727. * - 'ignore_constants' Contains an array of constants to ignore
  728. * when calculating the version needed.
  729. * - 'ignore_extensions' Contains an array of php extensions to ignore
  730. * when calculating the version needed.
  731. * - 'ignore_versions' Contains an array of php versions to ignore
  732. * when calculating the version needed.
  733. * - 'ignore_functions_match' Contains an array of function patterns to ignore
  734. * when calculating the version needed.
  735. * - 'ignore_extensions_match' Contains an array of extension patterns to ignore
  736. * when calculating the version needed.
  737. * - 'ignore_constants_match' Contains an array of constant patterns to ignore
  738. * when calculating the version needed.
  739. *
  740. * * parseArray, parseDir|parseFolder, specific options :
  741. * - 'file_ext' Contains an array of file extensions to parse
  742. * for PHP code. Default: php, php4, inc, phtml
  743. * - 'ignore_files' Contains an array of files to ignore.
  744. * File names are case insensitive.
  745. *
  746. * * parseArray specific options :
  747. * - 'is_string' Contains a boolean which says if the array values
  748. * are strings or file names.
  749. *
  750. * * parseDir|parseFolder specific options :
  751. * - 'recurse_dir' Boolean on whether to recursively find files
  752. * - 'ignore_dirs' Contains an array of directories to ignore.
  753. * Directory names are case insensitive.
  754. *
  755. * @param mixed $dataSource The data source (may be file, dir, string, or array)
  756. * @param array $options An array of options. See above.
  757. *
  758. * @access public
  759. * @return array or false on error
  760. * @since version 1.8.0b2 (2008-06-03)
  761. */
  762. function parseData($dataSource, $options = array())
  763. {
  764. $this->options = array_merge($this->options, $options);
  765. $dataType = gettype($dataSource);
  766. $dataCount = 0;
  767. // - when array source with mixed content incompatible
  768. // - if all directories are not readable
  769. // - if data source invalid type: other than file, directory, string
  770. if ($dataType == 'string' || $dataType == 'array') {
  771. if (is_array($dataSource)) {
  772. //$dataType = 'array';
  773. } elseif (is_dir($dataSource)) {
  774. $dataType = 'directory';
  775. $dataSource = array($dataSource);
  776. } elseif (is_file($dataSource)) {
  777. $dataType = 'file';
  778. $dataSource = array($dataSource);
  779. } elseif (substr($dataSource, 0, 5) == '<?php') {
  780. //$dataType = 'string';
  781. $this->options = array_merge($this->options,
  782. array('is_string' => true));
  783. $dataSource = array($dataSource);
  784. } else {
  785. //$dataType = 'string';
  786. // directory or file are misspelled
  787. }
  788. if (is_array($dataSource)) {
  789. $dataSource = $this->_validateDataSource($dataSource,
  790. $this->options);
  791. $dataCount = count($dataSource);
  792. }
  793. }
  794. $this->dataSource = array('dataSource' => $dataSource,
  795. 'dataType' => $dataType,
  796. 'dataCount' => $dataCount);
  797. $eventInfo = array_merge($this->dataSource,
  798. array('parseOptions' => $this->options));
  799. // notify all observers that parsing data source begin
  800. $this->notifyListeners(PHP_COMPATINFO_EVENT_AUDITSTARTED, $eventInfo);
  801. if ($dataCount == 0) {
  802. $parseData = false;
  803. } else {
  804. switch ($dataType) {
  805. case 'array' :
  806. $parseData = $this->_parseArray($dataSource, $this->options);
  807. break;
  808. case 'string' :
  809. $parseData = $this->_parseString($dataSource, $this->options);
  810. break;
  811. case 'file' :
  812. $parseData = $this->_parseFile($dataSource, $this->options);
  813. break;
  814. case 'directory' :
  815. $parseData = $this->_parseDir($dataSource, $this->options);
  816. break;
  817. }
  818. }
  819. // notify all observers that parsing data source is over
  820. $this->notifyListeners(PHP_COMPATINFO_EVENT_AUDITFINISHED, $parseData);
  821. $this->latest_parse = $parseData;
  822. return $parseData;
  823. }
  824. /**
  825. * Validate content of data source
  826. *
  827. * Validate content of data source list, before parsing each source
  828. *
  829. * @param mixed $dataSource The data source (may be file, dir, or string)
  830. * @param array $options Parser options (see parseData() method for details)
  831. *
  832. * @access private
  833. * @return array empty array on error
  834. * @since version 1.8.0b3 (2008-06-07)
  835. */
  836. function _validateDataSource($dataSource, $options = array())
  837. {
  838. /**
  839. * Array by default expect to contains list of files and/or directories.
  840. * If you want a list of chunk of code (strings), 'is_string' option
  841. * must be set to true.
  842. */
  843. $list = array();
  844. foreach ($dataSource as $source) {
  845. if ($options['is_string'] === true) {
  846. if (is_string($source)) {
  847. $list[] = $source;
  848. } else {
  849. /**
  850. * One of items is not a string (chunk of code). All
  851. * data sources parsing are stopped and considered as invalid.
  852. */
  853. $list = array();
  854. break;
  855. }
  856. } else {
  857. if (is_dir($source) && is_readable($source)) {
  858. $files = $this->getFilelist($source, $options);
  859. $list = array_merge($list, $files);
  860. } elseif (is_file($source)) {
  861. $list[] = $source;
  862. } else {
  863. /**
  864. * One of items is not a valid file or directory. All
  865. * data sources parsing are stopped and considered as invalid.
  866. */
  867. $list = array();
  868. break;
  869. }
  870. }
  871. }
  872. return $list;
  873. }
  874. /**
  875. * Parse an Array of Files
  876. *
  877. * You can parse an array of Files or Strings, to parse
  878. * strings, $options['is_string'] must be set to true
  879. *
  880. * @param array $dataSource Array of file &| directory names or code strings
  881. * @param array $options Parser options (see parseData() method for details)
  882. *
  883. * @access private
  884. * @return array or false on error
  885. * @since version 0.7.0 (2004-03-09)
  886. * @see parseData()
  887. */
  888. function _parseArray($dataSource, $options = array())
  889. {
  890. // Each data source have been checked before (see _validateDataSource() )
  891. if (is_file($dataSource[0])) {
  892. $parseData = $this->_parseDir($dataSource, $options);
  893. } else {
  894. $parseData = $this->_parseString($dataSource, $options);
  895. }
  896. return $parseData;
  897. }
  898. /**
  899. * Parse a string
  900. *
  901. * Parse a string for its compatibility info.
  902. *
  903. * @param array $strings PHP Code to parse
  904. * @param array $options Parser options (see parseData() method for details)
  905. *
  906. * @access private
  907. * @return array or false on error
  908. * @since version 0.7.0 (2004-03-09)
  909. * @see parseData()
  910. */
  911. function _parseString($strings, $options = array())
  912. {
  913. $results = $this->_parseElements($strings, $options);
  914. return $results;
  915. }
  916. /**
  917. * Parse a single file
  918. *
  919. * Parse a single file for its compatibility info.
  920. *
  921. * @param string $file File to parse
  922. * @param array $options Parser options (see parseData() method for details)
  923. *
  924. * @access private
  925. * @return array or false on error
  926. * @since version 0.7.0 (2004-03-09)
  927. * @see parseData()
  928. */
  929. function _parseFile($file, $options = array())
  930. {
  931. $results = $this->_parseElements($file, $options);
  932. return $results;
  933. }
  934. /**
  935. * Parse a directory
  936. *
  937. * Parse a directory recursively for its compatibility info
  938. *
  939. * @param array $files Files list of folder to parse
  940. * @param array $options Parser options (see parseData() method for details)
  941. *
  942. * @access private
  943. * @return array or false on error
  944. * @since version 0.8.0 (2004-04-22)
  945. * @see parseData()
  946. */
  947. function _parseDir($files, $options = array())
  948. {
  949. $results = $this->_parseElements($files, $options);
  950. return $results;
  951. }
  952. /**
  953. * Parse a list of elements
  954. *
  955. * Parse a list of directory|file elements, or chunk of code (strings)
  956. *
  957. * @param array $elements Array of file &| directory names or code strings
  958. * @param array $options Parser options (see parseData() method for details)
  959. *
  960. * @access private
  961. * @return array
  962. * @since version 1.8.0b3 (2008-06-07)
  963. * @see _parseString(), _parseDir()
  964. */
  965. function _parseElements($elements, $options = array())
  966. {
  967. $files_parsed = array();
  968. $latest_version = $this->latest_version;
  969. $earliest_version = $this->earliest_version;
  970. $all_functions = array();
  971. $classes = array();
  972. $functions = array();
  973. $extensions = array();
  974. $constants = array();
  975. $tokens = array();
  976. $ignored_functions = array();
  977. $ignored_extensions = array();
  978. $ignored_constants = array();
  979. $function_exists = array();
  980. $extension_loaded = array();
  981. $defined = array();
  982. $cond_code = 0;
  983. foreach ($elements as $p => $element) {
  984. $index = $p + 1;
  985. if (is_file($element)) {
  986. if (in_array($element, $options['ignore_files'])) {
  987. $this->ignored_files[] = $element;
  988. continue;
  989. }
  990. $eventInfo
  991. = array('filename' => $element, 'fileindex' => $index);
  992. $this->notifyListeners(PHP_COMPATINFO_EVENT_FILESTARTED, $eventInfo);
  993. $tokens_list = $this->_tokenize($element);
  994. $kfile = $element;
  995. $files_parsed[$kfile] = $this->_parseTokens($tokens_list, $options);
  996. $this->notifyListeners(PHP_COMPATINFO_EVENT_FILEFINISHED);
  997. } else {
  998. $eventInfo
  999. = array('stringdata' => $element, 'stringindex' => $index);
  1000. $this->notifyListeners(PHP_COMPATINFO_EVENT_CODESTARTED, $eventInfo);
  1001. $tokens_list = $this->_tokenize($element, true);
  1002. $kfile = 'string_' . $index;
  1003. $files_parsed[$kfile] = $this->_parseTokens($tokens_list, $options);
  1004. $this->notifyListeners(PHP_COMPATINFO_EVENT_CODEFINISHED);
  1005. }
  1006. }
  1007. foreach ($files_parsed as $fn => $file) {
  1008. $cmp = version_compare($latest_version, $file['version']);
  1009. if ($cmp === -1) {
  1010. $latest_version = $file['version'];
  1011. }
  1012. if ($file['max_version'] != '') {
  1013. $cmp = version_compare($earliest_version, $file['max_version']);
  1014. if ($earliest_version == '' || $cmp === 1) {
  1015. $earliest_version = $file['max_version'];
  1016. }
  1017. }
  1018. foreach ($file['classes'] as $class) {
  1019. if (!in_array($class, $classes)) {
  1020. $classes[] = $class;
  1021. }
  1022. }
  1023. foreach ($file['functions'] as $func) {
  1024. if (!in_array($func, $functions)) {
  1025. $functions[] = $func;
  1026. }
  1027. }
  1028. foreach ($file['extensions'] as $ext) {
  1029. if (!in_array($ext, $extensions)) {
  1030. $extensions[] = $ext;
  1031. }
  1032. }
  1033. foreach ($file['constants'] as $const) {
  1034. if (!in_array($const, $constants)) {
  1035. $constants[] = $const;
  1036. }
  1037. }
  1038. foreach ($file['tokens'] as $token) {
  1039. if (!in_array($token, $tokens)) {
  1040. $tokens[] = $token;
  1041. }
  1042. }
  1043. foreach ($file['ignored_functions'] as $if) {
  1044. if (!in_array($if, $ignored_functions)) {
  1045. $ignored_functions[] = $if;
  1046. }
  1047. }
  1048. foreach ($file['ignored_extensions'] as $ie) {
  1049. if (!in_array($ie, $ignored_extensions)) {
  1050. $ignored_extensions[] = $ie;
  1051. }
  1052. }
  1053. foreach ($file['ignored_constants'] as $ic) {
  1054. if (!in_array($ic, $ignored_constants)) {
  1055. $ignored_constants[] = $ic;
  1056. }
  1057. }
  1058. foreach ($file['cond_code'][1][0] as $ccf) {
  1059. if (!in_array($ccf, $function_exists)) {
  1060. $function_exists[] = $ccf;
  1061. }
  1062. }
  1063. foreach ($file['cond_code'][1][1] as $cce) {
  1064. if (!in_array($cce, $extension_loaded)) {
  1065. $extension_loaded[] = $cce;
  1066. }
  1067. }
  1068. foreach ($file['cond_code'][1][2] as $ccc) {
  1069. if (!in_array($ccc, $defined)) {
  1070. $defined[] = $ccc;
  1071. }
  1072. }
  1073. if ($options['debug'] === false) {
  1074. unset($files_parsed[$fn]['cond_code'][1]);
  1075. } else {
  1076. unset($file['ignored_functions']);
  1077. unset($file['ignored_extensions']);
  1078. unset($file['ignored_constants']);
  1079. unset($file['max_version']);
  1080. unset($file['version']);
  1081. unset($file['classes']);
  1082. unset($file['functions']);
  1083. unset($file['extensions']);
  1084. unset($file['constants']);
  1085. unset($file['tokens']);
  1086. unset($file['cond_code']);
  1087. foreach ($file as $version => $file_functions) {
  1088. // extra information available only when debug mode is on
  1089. if (isset($all_functions[$version])) {
  1090. foreach ($file_functions as $func) {
  1091. $k = array_search($func, $all_functions[$version]);
  1092. if ($k === false) {
  1093. $all_functions[$version][] = $func;
  1094. }
  1095. }
  1096. } else {
  1097. $all_functions[$version] = $file_functions;
  1098. }
  1099. }
  1100. }
  1101. }
  1102. if (count($files_parsed) == 0) {
  1103. return false;
  1104. }
  1105. if (count($function_exists) > 0) {
  1106. $cond_code += 1;
  1107. }
  1108. if (count($extension_loaded) > 0) {
  1109. $cond_code += 2;
  1110. }
  1111. if (count($defined) > 0) {
  1112. $cond_code += 4;
  1113. }
  1114. if ($options['debug'] === false) {
  1115. $cond_code = array($cond_code);
  1116. } else {
  1117. sort($function_exists);
  1118. sort($extension_loaded);
  1119. sort($defined);
  1120. $cond_code = array($cond_code, array($function_exists,
  1121. $extension_loaded,
  1122. $defined));
  1123. }
  1124. sort($ignored_functions);
  1125. sort($ignored_extensions);
  1126. sort($ignored_constants);
  1127. sort($classes);
  1128. sort($functions);
  1129. natcasesort($extensions);
  1130. sort($constants);
  1131. sort($tokens);
  1132. $main_info = array('ignored_files' => $this->getIgnoredFiles(),
  1133. 'ignored_functions' => $ignored_functions,
  1134. 'ignored_extensions' => $ignored_extensions,
  1135. 'ignored_constants' => $ignored_constants,
  1136. 'max_version' => $earliest_version,
  1137. 'version' => $latest_version,
  1138. 'classes' => $classes,
  1139. 'functions' => $functions,
  1140. 'extensions' => array_values($extensions),
  1141. 'constants' => $constants,
  1142. 'tokens' => $tokens,
  1143. 'cond_code' => $cond_code);
  1144. if (count($files_parsed) == 1) {
  1145. if ($options['debug'] === false) {
  1146. $parseData = $main_info;
  1147. } else {
  1148. $main_info = array('ignored_files' => $this->getIgnoredFiles());
  1149. $parseData = array_merge($main_info,
  1150. $files_parsed[$kfile], $all_functions);
  1151. }
  1152. } else {
  1153. if ($options['debug'] === false) {
  1154. $parseData = array_merge($main_info, $files_parsed);
  1155. } else {
  1156. $parseData = array_merge($main_info, $all_functions, $files_parsed);
  1157. }
  1158. }
  1159. $this->notifyListeners(PHP_COMPATINFO_EVENT_FILEFINISHED, $parseData);
  1160. return $parseData;
  1161. }
  1162. /**
  1163. * Token a file or string
  1164. *
  1165. * @param string $input Filename or PHP code
  1166. * @param boolean $is_string Whether or note the input is a string
  1167. * @param boolean $debug add token names for human read
  1168. *
  1169. * @access private
  1170. * @return array
  1171. * @since version 0.7.0 (2004-03-09)
  1172. */
  1173. function _tokenize($input, $is_string = false, $debug = false)
  1174. {
  1175. if ($is_string === false) {
  1176. $input = file_get_contents($input, true);
  1177. }
  1178. $tokens = token_get_all($input);
  1179. if ($debug === true) {
  1180. $r = array();
  1181. foreach ($tokens as $token) {
  1182. if (is_array($token)) {
  1183. $token[] = token_name($token[0]);
  1184. } else {
  1185. $token = $token[0];
  1186. }
  1187. $r[] = $token;
  1188. }
  1189. } else {
  1190. $r = $tokens;
  1191. }
  1192. return $r;
  1193. }
  1194. /**
  1195. * Parse the given Tokens
  1196. *
  1197. * The tokens are those returned by token_get_all() which is nicely
  1198. * wrapped in PHP_CompatInfo::_tokenize
  1199. *
  1200. * @param array $tokens Array of PHP Tokens
  1201. * @param boolean $options Show Extra Output
  1202. *
  1203. * @access private
  1204. * @return array
  1205. * @since version 0.7.0 (2004-03-09)
  1206. */
  1207. function _parseTokens($tokens, $options)
  1208. {
  1209. static $akeys;
  1210. $classes = array();
  1211. $functions = array();
  1212. $functions_version = array();
  1213. $latest_version = $this->latest_version;
  1214. $earliest_version = $this->earliest_version;
  1215. $extensions = array();
  1216. $constants = array();
  1217. $constant_names = array();
  1218. $token_names = array();
  1219. $udf = array();
  1220. $ignore_functions = array();
  1221. $ignored_functions = array();
  1222. $ignore_extensions = array();
  1223. $ignored_extensions = array();
  1224. $ignore_constants = array();
  1225. $ignored_constants = array();
  1226. $function_exists = array();
  1227. $extension_loaded = array();
  1228. $defined = array();
  1229. $cond_code = 0;
  1230. if (isset($options['ignore_constants'])) {
  1231. $options['ignore_constants']
  1232. = array_map('strtoupper', $options['ignore_constants']);
  1233. } else {
  1234. $options['ignore_constants'] = array();
  1235. }
  1236. if (isset($options['ignore_extensions'])) {
  1237. $options['ignore_extensions']
  1238. = array_map('strtolower', $options['ignore_extensions']);
  1239. } else {
  1240. $options['ignore_extensions'] = array();
  1241. }
  1242. if (isset($options['ignore_versions'][0])) {
  1243. $min_ver = $options['ignore_versions'][0];
  1244. } else {
  1245. $min_ver = false;
  1246. }
  1247. if (isset($options['ignore_versions'][1])) {
  1248. $max_ver = $options['ignore_versions'][1];
  1249. } else {
  1250. $max_ver = false;
  1251. }
  1252. if (isset($options['ignore_functions_match'])) {
  1253. list($ifm_compare, $ifm_patterns) = $options['ignore_functions_match'];
  1254. } else {
  1255. $ifm_compare = false;
  1256. }
  1257. if (isset($options['ignore_extensions_match'])) {
  1258. list($iem_compare, $iem_patterns) = $options['ignore_extensions_match'];
  1259. } else {
  1260. $iem_compare = false;
  1261. }
  1262. if (isset($options['ignore_constants_match'])) {
  1263. list($icm_compare, $icm_patterns) = $options['ignore_constants_match'];
  1264. } else {
  1265. $icm_compare = false;
  1266. }
  1267. $token_count = sizeof($tokens);
  1268. $i = 0;
  1269. $found_class = false;
  1270. while ($i < $token_count) {
  1271. if ($this->_isToken($tokens[$i], 'T_FUNCTION')) {
  1272. $found_func = false;
  1273. } else {
  1274. $found_func = true;
  1275. }
  1276. while ($found_func == false) {
  1277. $i += 1;
  1278. if ($this->_isToken($tokens[$i], 'T_STRING')) {
  1279. $found_func = true;
  1280. $func = $tokens[$i][1];
  1281. if ($found_class === false
  1282. || in_array($func, $function_exists)) {
  1283. $udf[] = $func;
  1284. }
  1285. }
  1286. }
  1287. // Try to detect PHP method chaining implementation
  1288. if ($this->_isToken($tokens[$i], 'T_VARIABLE')
  1289. && $this->_isToken($tokens[$i+1], 'T_OBJECT_OPERATOR')
  1290. && $this->_isToken($tokens[$i+2], 'T_STRING')
  1291. && $this->_isToken($tokens[$i+3], '(')) {
  1292. $i += 3;
  1293. $php5_method_chaining = false;
  1294. while (((!is_array($tokens[$i]) && $tokens[$i] == ';') === false)
  1295. && (!$this->_isToken($tokens[$i], 'T_CLOSE_TAG'))
  1296. ) {
  1297. $i += 1;
  1298. if ((($this->_isToken($tokens[$i], ')'))
  1299. || ($this->_isToken($tokens[$i], 'T_WHITESPACE')))
  1300. && $this->_isToken($tokens[$i+1], 'T_OBJECT_OPERATOR')) {
  1301. $php5_method_chaining = true;
  1302. }
  1303. }
  1304. }
  1305. // Compare "ignore_functions_match" pre-condition
  1306. if (is_string($ifm_compare)) {

Large files files are truncated, but you can click here to view the full file