PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/fuel/modules/fuel/libraries/Inspection.php

https://github.com/jamiegrand/FUEL-CMS
PHP | 1444 lines | 817 code | 166 blank | 461 comment | 120 complexity | 5fb6449e7cb0dd21f9bb2f77efb97106 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * FUEL CMS
  4. * http://www.getfuelcms.com
  5. *
  6. * An open source Content Management System based on the
  7. * Codeigniter framework (http://codeigniter.com)
  8. *
  9. * @package FUEL CMS
  10. * @author David McReynolds @ Daylight Studio
  11. * @copyright Copyright (c) 2014, Run for Daylight LLC.
  12. * @license http://docs.getfuelcms.com/general/license
  13. * @link http://www.getfuelcms.com
  14. * @filesource
  15. */
  16. // ------------------------------------------------------------------------
  17. /**
  18. * Inspection class
  19. *
  20. * This class can be used to inspect other files and will return objects that can be
  21. * further used to generate for example, this page and many of the other pages found in this User Guide.
  22. * It is essentially a wrapper around many of the convenient objects and methods of the <a href="http://php.net/manual/en/class.reflectionclass.php" target="_blank">PHP Refelction class</a>
  23. * but provides extra functionality for parsing out comment information. Below is a list of related classes used by the Inspection class:
  24. <ul>
  25. <li><a href="#inspection_class">Inspection_class</a></li>
  26. <li><a href="#inspection_function">Inspection_function</a></li>
  27. <li><a href="#inspection_method">Inspection_method</a></li>
  28. <li><a href="#inspection_param">Inspection_param</a></li>
  29. <li><a href="#inspection_param">Inspection_property</a></li>
  30. <li><a href="#inspection_comment">Inspection_comment</a></li>
  31. <li><a href="#inspection_base">Inspection_base</a></li>
  32. </ul>
  33. *
  34. * @package FUEL CMS
  35. * @subpackage Libraries
  36. * @category Libraries
  37. * @author David McReynolds @ Daylight Studio
  38. * @link http://docs.getfuelcms.com/libraries/inspection
  39. */
  40. // --------------------------------------------------------------------
  41. class Inspection {
  42. public $file = ''; // path to the file to inspect
  43. protected $_classes = array(); // cache of classes found in the file
  44. protected $_functions = array(); // cache of functions found in the file
  45. protected $_comments = array(); // cache of page comments
  46. // --------------------------------------------------------------------
  47. /**
  48. * Constructor
  49. *
  50. * Accepts an associative array as input, containing preferences (optional)
  51. *
  52. * @access public
  53. * @param array config preferences
  54. * @return void
  55. */
  56. public function __construct($file = NULL)
  57. {
  58. $CI =& get_instance();
  59. $CI->load->helper('inflector');
  60. $CI->load->helper('markdown');
  61. $CI->load->helper('security');
  62. if (!empty($file))
  63. {
  64. $this->initialize($file);
  65. }
  66. }
  67. // --------------------------------------------------------------------
  68. /**
  69. * Initializes the object by grabbing the contents of a file for inspection
  70. *
  71. * Accepts an associative array as input, containing preferences (optional)
  72. *
  73. * @access public
  74. * @param array config preferences
  75. * @return void
  76. */
  77. public function initialize($params = NULL)
  78. {
  79. if (!empty($params))
  80. {
  81. if (is_array($params) AND isset($params['file']))
  82. {
  83. foreach ($params as $key => $val)
  84. {
  85. if (isset($this->$key) AND substr($key, 0, 1) != '_')
  86. {
  87. $this->$key = $val;
  88. }
  89. }
  90. }
  91. else
  92. {
  93. $this->file = $params;
  94. }
  95. }
  96. // no file... no go
  97. if (!file_exists($this->file))
  98. {
  99. return FALSE;
  100. }
  101. // get the contents of the file
  102. $source = file_get_contents($this->file);
  103. // get the classes of the file
  104. $classes = $this->parse_classes($source);
  105. $loaded = FALSE;
  106. if (!empty($classes))
  107. {
  108. foreach($classes as $class)
  109. {
  110. // load the file if the class does not exist
  111. if (!class_exists($class) AND !$loaded)
  112. {
  113. require_once($this->file);
  114. $loaded = TRUE;
  115. }
  116. $this->_classes[$class] = new Inspection_class($class, $source);
  117. }
  118. }
  119. // get the functions of the file
  120. $functions = $this->parse_functions($source);
  121. if (!empty($functions))
  122. {
  123. foreach($functions as $func)
  124. {
  125. // load the helper if the function does not exist
  126. if (!function_exists($func) AND !$loaded)
  127. {
  128. require_once($this->file);
  129. $loaded = TRUE;
  130. }
  131. if (function_exists($func))
  132. {
  133. $this->_functions[$func] = new Inspection_function($func);
  134. }
  135. }
  136. }
  137. // get all comments in the file
  138. $comments = $this->parse_comments($source);
  139. foreach($comments as $comment)
  140. {
  141. $this->_comments[] = new Inspection_comment($comment);
  142. }
  143. }
  144. // --------------------------------------------------------------------
  145. /**
  146. * Returns the classes found in a file as an Inspection_class object
  147. *
  148. * @access public
  149. * @param string The name of a class. If not provided, then an array of all the classes will be returned (optional)
  150. * @return void
  151. */
  152. public function classes($class = NULL)
  153. {
  154. if (!empty($class))
  155. {
  156. if (isset($this->_classes[$class]))
  157. {
  158. return $this->_classes[$class];
  159. }
  160. return FALSE;
  161. }
  162. return $this->_classes;
  163. }
  164. // --------------------------------------------------------------------
  165. /**
  166. * Returns the functions found in a file as an Inspection_function object
  167. *
  168. * @access public
  169. * @param array The name of a function. If not provided, then an array of all the functions will be returned (optional)
  170. * @return void
  171. */
  172. public function functions($function = NULL)
  173. {
  174. if (!empty($function))
  175. {
  176. if (isset($this->_functions[$function]))
  177. {
  178. return $this->_functions[$function];
  179. }
  180. return FALSE;
  181. }
  182. return $this->_functions;
  183. }
  184. // --------------------------------------------------------------------
  185. /**
  186. * Returns the main comment blocks found in a file as an <a href="#inspection_comment">Inspection_comment</a> object
  187. *
  188. * @access public
  189. * @param array The name of a function. If not provided, then an array of all the functions will be returned (optional)
  190. * @return void
  191. */
  192. public function comments($comment = NULL)
  193. {
  194. if (isset($comment))
  195. {
  196. if (isset($this->_comments[$comment]))
  197. {
  198. return $this->_comments[$comment];
  199. }
  200. return FALSE;
  201. }
  202. return $this->_comments;
  203. }
  204. // --------------------------------------------------------------------
  205. /**
  206. * Parses and returns the name of the classes found in the string. Originally found here: http://stackoverflow.com/questions/928928/determining-what-classes-are-defined-in-a-php-class-file
  207. *
  208. * @access public
  209. * @param string The code to parse
  210. * @return void
  211. */
  212. public function parse_classes($code)
  213. {
  214. $classes = array();
  215. $tokens = token_get_all($code);
  216. $count = count($tokens);
  217. for ($i = 2; $i < $count; $i++)
  218. {
  219. if ($tokens[$i - 2][0] == T_CLASS
  220. AND $tokens[$i - 1][0] == T_WHITESPACE
  221. AND $tokens[$i][0] == T_STRING)
  222. {
  223. $class_name = $tokens[$i][1];
  224. $classes[] = $class_name;
  225. }
  226. }
  227. return $classes;
  228. }
  229. // --------------------------------------------------------------------
  230. /**
  231. * Parses and returns the name of the function found in a string. Originally found here: http://stackoverflow.com/questions/2666554/how-to-get-list-of-declared-functions-with-their-data-from-php-file
  232. *
  233. * @access public
  234. * @param string The code to parse
  235. * @param boolean Determines whether to include functions that begin with an underscore
  236. * @return void
  237. */
  238. public function parse_functions($code, $include_underscore_funcs = FALSE)
  239. {
  240. $functions = array();
  241. $tokens = token_get_all($code);
  242. $count = count($tokens);
  243. $next_string_is_func = false;
  244. $in_class = false;
  245. $braces_count = 0;
  246. foreach($tokens as $token)
  247. {
  248. switch($token[0])
  249. {
  250. case T_CLASS:
  251. $in_class = TRUE;
  252. break;
  253. case T_FUNCTION:
  254. if (! $in_class)
  255. {
  256. $next_string_is_func = TRUE;
  257. }
  258. break;
  259. case T_STRING:
  260. if ($next_string_is_func)
  261. {
  262. $next_string_is_func = FALSE;
  263. if (!$include_underscore_funcs AND substr($token[1], 0, 1) == '_')
  264. {
  265. continue;
  266. }
  267. $functions[] = $token[1];
  268. }
  269. break;
  270. // Anonymous functions
  271. case '(':
  272. case ';':
  273. $next_string_is_func = false;
  274. break;
  275. // Exclude Classes
  276. case '{':
  277. if ($in_class)
  278. {
  279. $braces_count++;
  280. }
  281. break;
  282. case '}':
  283. if($in_class)
  284. {
  285. $braces_count--;
  286. if ($braces_count === 0)
  287. {
  288. $in_class = FALSE;
  289. }
  290. }
  291. break;
  292. }
  293. }
  294. return $functions;
  295. }
  296. // --------------------------------------------------------------------
  297. /**
  298. * Parses and returns an array of comments found in a string
  299. *
  300. * @access public
  301. * @param string The code to parse
  302. * @return array
  303. */
  304. public function parse_comments($code)
  305. {
  306. $comments = array();
  307. $tokens = token_get_all($code);
  308. foreach($tokens as $token)
  309. {
  310. switch($token[0])
  311. {
  312. case T_DOC_COMMENT:
  313. $comments[] = $token[1];
  314. break;
  315. }
  316. }
  317. return $comments;
  318. }
  319. }
  320. /**
  321. * Inspection class ... class. A wrapper around the <a href="http://php.net/manual/en/class.reflectionclass.php" target="_blank">PHP ReflectionClass</a>
  322. *
  323. * @package FUEL CMS
  324. * @subpackage Libraries
  325. * @category Libraries
  326. * @author David McReynolds @ Daylight Studio
  327. * @autodoc TRUE
  328. */
  329. class Inspection_class extends Inspection_base {
  330. protected $_methods = NULL; // the method objects of the class
  331. protected $_file = NULL; // for further processing for properties
  332. // --------------------------------------------------------------------
  333. /**
  334. * Constructor
  335. *
  336. * @access public
  337. * @param string the name of the class
  338. * @param string the name of the file the class belongs to (optional)
  339. * @return void
  340. */
  341. public function __construct($class = NULL, $file = '')
  342. {
  343. parent::__construct('ReflectionClass', $class);
  344. $this->_file = $file;
  345. }
  346. // --------------------------------------------------------------------
  347. /**
  348. * Properties of the class
  349. *
  350. * @access public
  351. * @param array The type of properties to include. Options are 'public', 'protected', and 'private. Default will only show public'(optional)
  352. * @param boolean Determines whether to include any parent properties. Default is FALSE (optional)
  353. * @return array An array of Inspection_property objects
  354. */
  355. public function properties($types = array(), $include_parent = FALSE)
  356. {
  357. $ref_props = $this->reflection->getProperties();
  358. foreach($ref_props as $p)
  359. {
  360. $prop_obj = new Inspection_property($this->name, $p->name);
  361. $this->_props[$p->name] = $prop_obj;
  362. }
  363. // get properties area to parse out for comments later on
  364. preg_match('#[c|C]lass\s+'.$this->name.'\s+.*\{.+function\s+\w+\s*\(\w*\)#Us', $this->_file, $props_match);
  365. if (isset($props_match[0]))
  366. {
  367. $props_block = $props_match[0];
  368. unset($props_match);
  369. }
  370. else
  371. {
  372. $props_block = '';
  373. }
  374. if (empty($types))
  375. {
  376. $types = array('public');
  377. }
  378. if (is_string($types))
  379. {
  380. // if all is set, then we set $types to NULL so that it won't do any additional filtering'
  381. if (strtolower($types) == 'all')
  382. {
  383. $types = NULL;
  384. }
  385. else
  386. {
  387. $types = (array) $types;
  388. }
  389. }
  390. $props = array();
  391. foreach($this->_props as $name => $p)
  392. {
  393. if (!$include_parent AND ($p->getDeclaringClass()->name != $this->name))
  394. {
  395. continue;
  396. }
  397. if (!empty($types))
  398. {
  399. foreach($types as $type)
  400. {
  401. $type_method = 'is'.ucfirst($type);
  402. $matches = array();
  403. if (method_exists($p->reflection, $type_method) AND $p->reflection->$type_method())
  404. {
  405. // look for comments on the right of the property
  406. preg_match('#\s*'.$type.'\s+((static|const)*\s+)*(\$'.$name.')\s+.*; *//\s*(.+)#', $props_block, $matches);
  407. if (isset($matches[4]) AND !$p->comment->text())
  408. {
  409. $p->comment->set_text($matches[4]);
  410. }
  411. else
  412. {
  413. // if not found on the right, then we look to the direct top
  414. preg_match('#$\s+//\s*(.+)\s*\n\s+'.$type.'\s+(\$'.$name.')\s+.*#Um', $props_block, $matches);
  415. if (isset($matches[1]) AND !$p->comment->text())
  416. {
  417. $p->comment->set_text($matches[1]);
  418. }
  419. }
  420. $props[$name] = $p;
  421. }
  422. }
  423. }
  424. else
  425. {
  426. $props[$key] = $p;
  427. }
  428. }
  429. return $props;
  430. }
  431. // --------------------------------------------------------------------
  432. /**
  433. * The name of the parent class if it exists
  434. *
  435. * @access public
  436. * @return string
  437. */
  438. public function parent()
  439. {
  440. $parent = $this->reflection->getParentClass();
  441. if ($parent)
  442. {
  443. return $parent->name;
  444. }
  445. }
  446. // --------------------------------------------------------------------
  447. /**
  448. * Returns an <a href="#inspection_method">Inspection_method</a>
  449. *
  450. * @access public
  451. * @param string The name of the method to return
  452. * @return object
  453. */
  454. public function method($method)
  455. {
  456. $methods = $this->methods();
  457. if (isset($methods[$method]))
  458. {
  459. return $methods[$method];
  460. }
  461. return FALSE;
  462. }
  463. // --------------------------------------------------------------------
  464. /**
  465. * Returns an array of all the class methods. The array contains <a href="#inspection_method">Inspection_method</a> objects
  466. *
  467. * @access public
  468. * @param array The type of properties to include. Options are 'public', 'protected', and 'private. Default will only show public'(optional)
  469. * @param boolean Determines whether to include any parent properties. Default is FALSE (optional)
  470. * @param boolean Determines whether to include the contstructor method. Default is FALSE (optional)
  471. * @return array
  472. */
  473. public function methods($types = array(), $include_parent = FALSE, $include_constructor = FALSE)
  474. {
  475. if (!isset($this->_methods))
  476. {
  477. $ref_methods = $this->reflection->getMethods();
  478. foreach($ref_methods as $m)
  479. {
  480. $m_obj = new Inspection_method($m->name, $this->name);
  481. $this->_methods[$m->name] = $m_obj;
  482. }
  483. }
  484. if (empty($types))
  485. {
  486. $types = array('public');
  487. }
  488. if (is_string($types))
  489. {
  490. // if all is set, then we set $types to NULL so that it won't do any additional filtering'
  491. if (strtolower($types) == 'all')
  492. {
  493. $types = NULL;
  494. }
  495. else
  496. {
  497. $types = (array) $types;
  498. }
  499. }
  500. // static, public, protected, private, abstract, final
  501. $methods = array();
  502. foreach($this->_methods as $name => $m)
  503. {
  504. // filter out contstructors
  505. if (!$include_constructor AND ($name == '__construct' OR $name == $this->name))
  506. {
  507. continue;
  508. }
  509. // filter out parent methods
  510. if (!$include_parent AND ($m->getDeclaringClass()->name != $this->name))
  511. {
  512. continue;
  513. }
  514. if (!empty($types))
  515. {
  516. foreach($types as $type)
  517. {
  518. $type_method = 'is'.ucfirst($type);
  519. if (method_exists($m->reflection, $type_method) AND $m->reflection->$type_method())
  520. {
  521. $methods[$name] = $m;
  522. }
  523. }
  524. }
  525. else
  526. {
  527. $methods[$key] = $m;
  528. }
  529. }
  530. return $methods;
  531. }
  532. }
  533. /**
  534. * Inspection function class. A wrapper around the <a href="http://www.php.net/manual/en/class.reflectionfunction.php" target="_blank">PHP ReflectionFunction</a>
  535. *
  536. * @package FUEL CMS
  537. * @subpackage Libraries
  538. * @category Libraries
  539. * @author David McReynolds @ Daylight Studio
  540. * @autodoc TRUE
  541. */
  542. class Inspection_function extends Inspection_base {
  543. // --------------------------------------------------------------------
  544. /**
  545. * Constructor
  546. *
  547. * @access public
  548. * @param string the name of the function
  549. * @return void
  550. */
  551. public function __construct($function = NULL)
  552. {
  553. parent::__construct('ReflectionFunction', $function);
  554. }
  555. }
  556. /**
  557. * Inspection method class. A wrapper around the <a href="http://www.php.net/manual/en/class.reflectionmethod.php" target="_blank">PHP ReflectionMethod</a>
  558. *
  559. * @package FUEL CMS
  560. * @subpackage Libraries
  561. * @category Libraries
  562. * @author David McReynolds @ Daylight Studio
  563. * @autodoc TRUE
  564. */
  565. class Inspection_method extends Inspection_base {
  566. protected $_params = NULL; // the parameters objects of the method
  567. // --------------------------------------------------------------------
  568. /**
  569. * Constructor
  570. *
  571. * @access public
  572. * @param string the name of the method
  573. * @param string the corresponding object of the method
  574. * @return void
  575. */
  576. public function __construct($method = NULL, $obj = NULL)
  577. {
  578. parent::__construct('ReflectionMethod', $method, $obj);
  579. }
  580. }
  581. /**
  582. * Inspection class object. A wrapper around the <a href="http://www.php.net/manual/en/class.reflectionproperty.php" target="_blank">PHP ReflectionProperty</a>
  583. *
  584. * @package FUEL CMS
  585. * @subpackage Libraries
  586. * @category Libraries
  587. * @author David McReynolds @ Daylight Studio
  588. * @autodoc TRUE
  589. */
  590. class Inspection_property extends Inspection_base {
  591. public $class = NULL; // the name of the class the property belongs to
  592. public $value = NULL; // the value of the class property
  593. // --------------------------------------------------------------------
  594. /**
  595. * Constructor
  596. *
  597. * @access public
  598. * @param string the name of the property
  599. * @param string the class name the property belongs to (optional)
  600. * @return void
  601. */
  602. public function __construct($property = NULL, $class = NULL)
  603. {
  604. parent::__construct('ReflectionProperty', $class, $property);
  605. if ($this->is_public())
  606. {
  607. $class = $this->reflection->class;
  608. $this->value = $this->reflection->getValue(new $class());
  609. }
  610. }
  611. }
  612. /**
  613. * Inspection param class. A wrapper around the <a href="http://www.php.net/manual/en/class.reflectionparameter.php" target="_blank">PHP ReflectionParameter</a>
  614. *
  615. * @package FUEL CMS
  616. * @subpackage Libraries
  617. * @category Libraries
  618. * @author David McReynolds @ Daylight Studio
  619. * @autodoc TRUE
  620. */
  621. class Inspection_param extends Inspection_base {
  622. // --------------------------------------------------------------------
  623. /**
  624. * Constructor
  625. *
  626. * @access public
  627. * @param string the name of the method
  628. * @param string the class name the property belongs to (optional)
  629. * @return void
  630. */
  631. public function __construct($method = NULL, $obj = NULL)
  632. {
  633. if (isset($obj))
  634. {
  635. parent::__construct('ReflectionParameter', $method, $obj);
  636. }
  637. else
  638. {
  639. parent::__construct('ReflectionParameter', $method);
  640. }
  641. }
  642. // --------------------------------------------------------------------
  643. /**
  644. * Returns the default value of a parameter
  645. *
  646. * @access public
  647. * @param boolean Determines whether you want the default value as a string or as an object
  648. * @return array
  649. */
  650. public function default_value($to_string = FALSE)
  651. {
  652. if ($this->is_default_value_available())
  653. {
  654. $val = $this->reflection->getDefaultValue();
  655. if ($to_string)
  656. {
  657. if (is_null($val))
  658. {
  659. return 'NULL';
  660. }
  661. else if (is_bool($val))
  662. {
  663. return ($val === TRUE) ? 'TRUE' : 'FALSE';
  664. }
  665. else if (is_string($val))
  666. {
  667. return '\''.$val.'\'';
  668. }
  669. else
  670. {
  671. // remove extra spaces and lowercase
  672. return preg_replace('#\s*#', '', strtolower(print_r($val, TRUE)));
  673. }
  674. }
  675. return $this->reflection->getDefaultValue();
  676. }
  677. return FALSE;
  678. }
  679. // --------------------------------------------------------------------
  680. /**
  681. * Returns whether the function parameter is an array or not
  682. *
  683. * @access public
  684. * @return boolean
  685. */
  686. public function is_default_array()
  687. {
  688. if ($this->is_array())
  689. {
  690. return TRUE;
  691. }
  692. else if ($this->is_default_value_available())
  693. {
  694. return is_array($this->default_value());
  695. }
  696. else if ($this->function->comment->param($this->position(), 'type') == 'array')
  697. {
  698. return TRUE;
  699. }
  700. return FALSE;
  701. }
  702. }
  703. /**
  704. * Inspection comment class. Convenient for dissecting a comment block further
  705. *
  706. * @package FUEL CMS
  707. * @subpackage Libraries
  708. * @category Libraries
  709. * @author David McReynolds @ Daylight Studio
  710. * @autodoc TRUE
  711. */
  712. class Inspection_comment {
  713. protected $_text = ''; // the comment raw text
  714. protected $_description = NULL; // the description part of the comment
  715. protected $_example = NULL; // examples found in the comment
  716. protected $_tags = NULL; // tags found in the comment
  717. protected $_filters = array(); // pre processing functions
  718. // --------------------------------------------------------------------
  719. /**
  720. * Constructor
  721. *
  722. * @access public
  723. * @param string the comment raw text
  724. * @return void
  725. */
  726. public function __construct($comment)
  727. {
  728. $this->initialize($comment);
  729. }
  730. // --------------------------------------------------------------------
  731. /**
  732. * Initializes the comment
  733. *
  734. * @access public
  735. * @param string
  736. * @return void
  737. */
  738. public function initialize($comment)
  739. {
  740. $this->_text = (string)$comment;
  741. $tags = array(
  742. "access" => '',
  743. "author" => '',
  744. "copyright" => '',
  745. "deprecated"=> '',
  746. "example" => '',
  747. "ignore" => '',
  748. "internal" => '',
  749. "link" => '',
  750. "param" => '',
  751. "return" => '',
  752. "see" => '',
  753. "since" => '',
  754. "tutorial" => '',
  755. "version" => '',
  756. "prefix" => '', // specific to FUEL documentation
  757. "autodoc" => '', // specific to FUEL documentation
  758. );
  759. $this->tags = $tags;
  760. preg_match_all('#^\s+\*\s+@(\w+)\s+(.+)\n#m', $this->_text, $matches);
  761. if (!empty($matches[2]))
  762. {
  763. $params = array();
  764. foreach($matches[1] as $key => $tag)
  765. {
  766. $value = $matches[2][$key];
  767. if ($tag == 'param')
  768. {
  769. $params[] = trim(str_replace("\t", ' ', $value));
  770. }
  771. else if (empty($this->_tags[$tag]))
  772. {
  773. $this->_tags[$tag] = trim(str_replace("\t", ' ', $value));
  774. }
  775. }
  776. $this->_tags['param'] = $params;
  777. }
  778. }
  779. // --------------------------------------------------------------------
  780. /**
  781. * Returns a comment "tag" (e.g. param, return, access... etc). If no value is passed, an array of tags will be returned
  782. *
  783. * @access public
  784. * @param string The name of the tag (optional)
  785. * @return mixed
  786. */
  787. public function tags($type = 'param')
  788. {
  789. if (!empty($type))
  790. {
  791. if (isset($this->_tags[$type]))
  792. {
  793. return $this->_tags[$type];
  794. }
  795. return FALSE;
  796. }
  797. return $this->_tags;
  798. }
  799. // --------------------------------------------------------------------
  800. /**
  801. * Returns a parameter tag
  802. *
  803. * @access public
  804. * @param int The index (order) of the parameter to retrieve
  805. * @param string The part of the parameter to retrieve. Options are 'type' and 'comment'
  806. * @return boolean
  807. */
  808. public function param($index, $type = NULL)
  809. {
  810. if (!isset($this->_tags['param']))
  811. {
  812. return FALSE;
  813. }
  814. $params = $this->_tags['param'];
  815. if (!is_int($index) OR !isset($params[$index])) return FALSE;
  816. $value = $params[$index];
  817. if (isset($type))
  818. {
  819. $split = preg_split('#\s#', $value);
  820. if ($type == 'type')
  821. {
  822. $value = $split[0];
  823. }
  824. else if ($type == 'comment' AND isset($split[1]))
  825. {
  826. array_shift($split);
  827. $value = implode(' ', $split);
  828. }
  829. else
  830. {
  831. return FALSE;
  832. }
  833. }
  834. return $value;
  835. }
  836. // --------------------------------------------------------------------
  837. /**
  838. * Returns a parameter tag
  839. *
  840. * @access public
  841. * @param int The index (order) of the parameter to retrieve
  842. * @param string The part of the parameter to retrieve. Options are 'type' and 'comment'
  843. * @return boolean
  844. */
  845. public function return_value($type = NULL)
  846. {
  847. if (!isset($this->_tags['return']))
  848. {
  849. return FALSE;
  850. }
  851. $value = $this->_tags['return'];
  852. if (isset($type))
  853. {
  854. $split = preg_split('#\s#', $value);
  855. if ($type == 'type')
  856. {
  857. $value = $split[0];
  858. }
  859. else if ($type == 'comment' AND isset($split[1]))
  860. {
  861. array_shift($split);
  862. $value = implode(' ', $split);
  863. }
  864. else
  865. {
  866. return FALSE;
  867. }
  868. }
  869. return $value;
  870. }
  871. // --------------------------------------------------------------------
  872. /**
  873. * Returns the comment description. You can pass it an array of formatting parameters which include:
  874. <ul>
  875. <li><strong>markdown:</strong> applies the <a href="[user_guide_url]helpers/markdown_helper">markdown</a> function </li>
  876. <li><strong>short:</strong> filters just the first paragraphs of the description if multiple paragraphs </li>
  877. <li><strong>long:</strong> returns the entire description</li>
  878. <li><strong>one_line:</strong> filters the description to appear on one line by removing returns</li>
  879. <li><strong>entities:</strong> converts html entities</li>
  880. <li><strong>eval:</strong> evaluates php code</li>
  881. <li><strong>periods:</strong> adds periods at the end of lines that don't have them</li>
  882. <li><strong>ucfirst:</strong> uppercases the first word</li>
  883. </ul>
  884. *
  885. * @access public
  886. * @param int The index (order) of the parameter to retrieve
  887. * @param string The part of the parameter to retrieve. Options are 'type' and 'comment'
  888. * @return boolean
  889. */
  890. public function description($format = FALSE)
  891. {
  892. if (!isset($this->_description))
  893. {
  894. preg_match('#/\*\*\s*(.+ )(@|\*\/)#Ums', $this->_text, $matches);
  895. if (isset($matches[1]))
  896. {
  897. $this->_description = $matches[1];
  898. // removing preceding * and tabs
  899. $this->_description = preg_replace('#\* *#m', "", $matches[1]);
  900. $this->_description = preg_replace("#^ +#m", "", $this->_description);
  901. // remove code examples since they are handled by the example method
  902. $this->_description = preg_replace('#<code>.+</code>#ms', '', $this->_description);
  903. $this->_description = trim($this->_description);
  904. }
  905. else
  906. {
  907. $this->_description = $this->_text;
  908. }
  909. }
  910. $desc = $this->_description;
  911. $desc = $this->filter($desc);
  912. // apply different formats
  913. if ($format)
  914. {
  915. if (is_string($format))
  916. {
  917. $format = (array) $format;
  918. }
  919. foreach($format as $f)
  920. {
  921. switch(strtolower($f))
  922. {
  923. case 'markdown':
  924. // must escape underscores to prevent <em> tags
  925. $desc = str_replace('_', '\_', $desc);
  926. $desc = markdown($desc);
  927. // the we replace back any that didn't get processed'(e.g. ones inside links)
  928. $desc = str_replace('\_', '_', $desc);
  929. break;
  930. case 'short':
  931. $desc_lines = explode(PHP_EOL, $desc);
  932. $first_line = TRUE;
  933. foreach($desc_lines as $d)
  934. {
  935. if (!empty($d))
  936. {
  937. if ($first_line)
  938. {
  939. $first_line = FALSE;
  940. $desc = $d;
  941. break;
  942. }
  943. }
  944. }
  945. break;
  946. case 'long':
  947. $desc_lines = explode(PHP_EOL, $desc);
  948. $new_desc = '';
  949. $first_line = TRUE;
  950. foreach($desc_lines as $d)
  951. {
  952. if (!empty($d))
  953. {
  954. if ($first_line)
  955. {
  956. $first_line = FALSE;
  957. continue;
  958. }
  959. else
  960. {
  961. $new_desc .= $d.' ';
  962. }
  963. }
  964. else if (!$first_line)
  965. {
  966. $new_desc .= "\n\n";
  967. }
  968. }
  969. $desc = $new_desc;
  970. break;
  971. case 'one_line':
  972. $desc = str_replace(PHP_EOL, ' ', $desc);
  973. break;
  974. case 'entities':
  975. $desc = htmlentities($desc);
  976. break;
  977. case 'eval':
  978. $desc = eval_string($desc);
  979. break;
  980. case 'periods':
  981. $desc_lines = explode(PHP_EOL, $desc);
  982. $lines = '';
  983. $past_first = FALSE;
  984. foreach($desc_lines as $d)
  985. {
  986. $d = trim($d);
  987. if (!empty($d))
  988. {
  989. if (!$past_first)
  990. {
  991. $d = preg_replace('#(.+[^\.|>]\s*)$#', '$1. ', $d);
  992. }
  993. $lines .= $d.' ';
  994. $past_first = TRUE;
  995. }
  996. else if ($past_first)
  997. {
  998. $lines .= "\n\n";
  999. }
  1000. }
  1001. $lines = preg_replace('#(.+[^\.|>]\s*)$#', '$1. ', trim($lines));
  1002. $desc = $lines;
  1003. case 'ucfirst':
  1004. $desc = ucfirst($desc);
  1005. break;
  1006. }
  1007. }
  1008. }
  1009. // auto link
  1010. $desc = auto_link($desc);
  1011. // trim white space
  1012. $desc = trim($desc);
  1013. // clean
  1014. $desc = xss_clean($desc);
  1015. $desc = strip_image_tags($desc);
  1016. return $desc;
  1017. }
  1018. // --------------------------------------------------------------------
  1019. /**
  1020. * Returns a code example. Must be wrapped in a "code" HTML tag
  1021. *
  1022. * @access public
  1023. * @param string The opening tag to wrap the example in
  1024. * @param string The closing tag to wrap the example in
  1025. * @return string
  1026. */
  1027. public function example($opening = '', $closing = '')
  1028. {
  1029. if (!isset($this->_example))
  1030. {
  1031. preg_match('#/\*\*\s*.+<code>(.+)</code>#Ums', $this->_text, $matches);
  1032. if (isset($matches[1]))
  1033. {
  1034. $this->_example = preg_replace('#\*\s+#ms', '', $matches[1]);
  1035. }
  1036. }
  1037. $this->_example = htmlentities($this->_example, ENT_NOQUOTES, 'UTF-8', FALSE);
  1038. $example = $opening.$this->_example.$closing;
  1039. return $example;
  1040. }
  1041. // --------------------------------------------------------------------
  1042. /**
  1043. * Returns the raw text of the description
  1044. *
  1045. * @access public
  1046. * @return string
  1047. */
  1048. public function text()
  1049. {
  1050. return $this->_text;
  1051. }
  1052. // --------------------------------------------------------------------
  1053. /**
  1054. * Sets the comment text
  1055. *
  1056. * @access public
  1057. * @param string The comment text
  1058. * @return boolean
  1059. */
  1060. public function set_text($text)
  1061. {
  1062. $this->_text = $text;
  1063. }
  1064. // --------------------------------------------------------------------
  1065. /**
  1066. * Adds a filter to be used for processing comments (e.g. the [ user_guide_url ] is rendered using a filter)
  1067. *
  1068. * @access public
  1069. * @param string The name of the function or a lamda
  1070. * @param string A key value to assign to the filter which can be used in removing filters later
  1071. * @return boolean
  1072. */
  1073. public function add_filter($func, $key = NULL)
  1074. {
  1075. if (empty($key))
  1076. {
  1077. $this->_filters[] = $func;
  1078. }
  1079. else
  1080. {
  1081. $this->_filters[$key] = $func;
  1082. }
  1083. }
  1084. // --------------------------------------------------------------------
  1085. /**
  1086. * Removes a filter from the comment
  1087. *
  1088. * @access public
  1089. * @param string The key value of the filter you want to remove
  1090. * @return boolean
  1091. */
  1092. public function remove_filter($key)
  1093. {
  1094. unset($this->_filters[$key]);
  1095. }
  1096. // --------------------------------------------------------------------
  1097. /**
  1098. * Executes the filter on a source (e.g. the comment)
  1099. *
  1100. * @access public
  1101. * @param string The comment text
  1102. * @return boolean
  1103. */
  1104. protected function filter($source)
  1105. {
  1106. foreach($this->_filters as $func)
  1107. {
  1108. $source = call_user_func($func, $source);
  1109. }
  1110. return $source;
  1111. }
  1112. }
  1113. /**
  1114. * Inspection base class object... should be abstract object but causes issue if trying to generate documentation
  1115. *
  1116. * @package FUEL CMS
  1117. * @subpackage Libraries
  1118. * @category Libraries
  1119. * @author David McReynolds @ Daylight Studio
  1120. * @autodoc TRUE
  1121. */
  1122. class Inspection_base {
  1123. public $reflection = NULL; // Reflection object
  1124. public $comment = NULL; // Inspection_comment
  1125. public $name = NULL; // basic name value
  1126. public $obj = NULL; // the object (if any)
  1127. public $params = NULL; // the parameters of the object (if any)
  1128. // --------------------------------------------------------------------
  1129. /**
  1130. * Constructor
  1131. *
  1132. * @access public
  1133. * @param string the name of the Reflection class to use
  1134. * @param string the method name on the object
  1135. * @param string the class name of the object
  1136. * @return void
  1137. */
  1138. public function __construct($ref_class = NULL, $method = NULL, $obj = NULL)
  1139. {
  1140. if (!empty($ref_class) AND !empty($method))
  1141. {
  1142. $this->initialize($ref_class, $method, $obj);
  1143. }
  1144. }
  1145. // --------------------------------------------------------------------
  1146. /**
  1147. * Executes the filter on a source (e.g. the comment)
  1148. *
  1149. * @access public
  1150. * @param string The comment text
  1151. * @return void
  1152. */
  1153. public function initialize($ref_class, $method, $obj = NULL)
  1154. {
  1155. if (is_object($method) AND strncasecmp(get_class($method), 'Reflection', 10) === 0)
  1156. {
  1157. $this->reflection = $method;
  1158. }
  1159. else if (isset($obj))
  1160. {
  1161. $this->reflection = new $ref_class($obj, $method);
  1162. $this->obj = $obj;
  1163. }
  1164. else
  1165. {
  1166. $this->reflection = new $ref_class($method);
  1167. }
  1168. $this->name = $this->reflection->getName();
  1169. if (method_exists($this->reflection, 'getDocComment'))
  1170. {
  1171. $comment = $this->reflection->getDocComment();
  1172. $this->comment = new Inspection_comment($comment);
  1173. }
  1174. // used for functions and method objects
  1175. if (method_exists($this->reflection, 'getParameters'))
  1176. {
  1177. $params = $this->reflection->getParameters();
  1178. foreach($params as $param)
  1179. {
  1180. $p = new Inspection_param($param);
  1181. $p->function = &$this;
  1182. $this->params[] = $p;
  1183. }
  1184. }
  1185. }
  1186. // --------------------------------------------------------------------
  1187. /**
  1188. * The friendly name of the object
  1189. *
  1190. * @access public
  1191. * @return string
  1192. */
  1193. public function friendly_name()
  1194. {
  1195. return humanize($this->name);
  1196. }
  1197. // --------------------------------------------------------------------
  1198. /**
  1199. * The comment object
  1200. *
  1201. * @access public
  1202. * @return void
  1203. */
  1204. public function comment()
  1205. {
  1206. return $this->comment;
  1207. }
  1208. // --------------------------------------------------------------------
  1209. /**
  1210. * Returns a method or function Inspection_parameter object
  1211. *
  1212. * @access public
  1213. * @param int The index number of the parameter you want to retrieve
  1214. * @return object
  1215. */
  1216. public function param($index)
  1217. {
  1218. if (isset($this->params[$index]))
  1219. {
  1220. $index = (int) $index;
  1221. return $this->params[$index];
  1222. }
  1223. else
  1224. {
  1225. return FALSE;
  1226. }
  1227. }
  1228. // --------------------------------------------------------------------
  1229. /**
  1230. * Returns an array of method or function Inspection_parameters
  1231. *
  1232. * @access public
  1233. * @return boolean
  1234. */
  1235. public function params()
  1236. {
  1237. return $this->params;
  1238. }
  1239. // --------------------------------------------------------------------
  1240. /**
  1241. * Magic method that will automatically look on the <a href="http://php.net/manual/en/class.reflectionclass.php" target="_blank">Reflection class object</a>
  1242. * for a camelized version of the method name
  1243. *
  1244. * @access public
  1245. * @param string The method name
  1246. * @param array An array of arguments to pass to the Reflection class method being called
  1247. * @return boolean
  1248. */
  1249. public function __call($name, $args)
  1250. {
  1251. if (!preg_match('#^get|is|set#', $name))
  1252. {
  1253. $name = 'get_'.$name;
  1254. }
  1255. if (strpos($name, '_') !== FALSE)
  1256. {
  1257. $name = camelize($name);
  1258. }
  1259. if (method_exists($this->reflection, $name))
  1260. {
  1261. if (!empty($args))
  1262. {
  1263. return $this->reflection->$name($args);
  1264. }
  1265. else
  1266. {
  1267. return $this->reflection->$name();
  1268. }
  1269. }
  1270. else
  1271. {
  1272. //throw new Exception(lang('error_method_does_not_exist', $name));
  1273. return FALSE;
  1274. }
  1275. }
  1276. }
  1277. /* End of file Inspection.php */
  1278. /* Location: ./modules/fuel/libraries/Inspection.php */