PageRenderTime 56ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/userguide/classes/kohana/kodoc.php

https://bitbucket.org/herson091/kohana1
PHP | 357 lines | 215 code | 58 blank | 84 comment | 22 complexity | d98e47744a28361b2e8f63532f2aa81a MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Documentation generator.
  4. *
  5. * @package Kohana/Userguide
  6. * @category Base
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2012 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. class Kohana_Kodoc {
  12. /**
  13. * @var string PCRE fragment for matching 'Class', 'Class::method', 'Class::method()' or 'Class::$property'
  14. */
  15. public static $regex_class_member = '((\w++)(?:::(\$?\w++))?(?:\(\))?)';
  16. /**
  17. * Make a class#member API link using an array of matches from [Kodoc::$regex_class_member]
  18. *
  19. * @param array $matches array( 1 => link text, 2 => class name, [3 => member name] )
  20. * @return string
  21. */
  22. public static function link_class_member($matches)
  23. {
  24. $link = $matches[1];
  25. $class = $matches[2];
  26. $member = NULL;
  27. if (isset($matches[3]))
  28. {
  29. // If the first char is a $ it is a property, e.g. Kohana::$base_url
  30. if ($matches[3][0] === '$')
  31. {
  32. $member = '#property:'.substr($matches[3], 1);
  33. }
  34. else
  35. {
  36. $member = '#'.$matches[3];
  37. }
  38. }
  39. return HTML::anchor(Route::get('docs/api')->uri(array('class' => $class)).$member, $link, NULL, NULL, TRUE);
  40. }
  41. public static function factory($class)
  42. {
  43. return new Kodoc_Class($class);
  44. }
  45. /**
  46. * Creates an html list of all classes sorted by category (or package if no category)
  47. *
  48. * @return string the html for the menu
  49. */
  50. public static function menu()
  51. {
  52. $classes = Kodoc::classes();
  53. foreach ($classes as $class)
  54. {
  55. if (isset($classes['kohana_'.$class]))
  56. {
  57. // Remove extended classes
  58. unset($classes['kohana_'.$class]);
  59. }
  60. }
  61. ksort($classes);
  62. $menu = array();
  63. $route = Route::get('docs/api');
  64. foreach ($classes as $class)
  65. {
  66. $class = Kodoc_Class::factory($class);
  67. // Test if we should show this class
  68. if ( ! Kodoc::show_class($class))
  69. continue;
  70. $link = HTML::anchor($route->uri(array('class' => $class->class->name)), $class->class->name);
  71. if (isset($class->tags['package']))
  72. {
  73. foreach ($class->tags['package'] as $package)
  74. {
  75. if (isset($class->tags['category']))
  76. {
  77. foreach ($class->tags['category'] as $category)
  78. {
  79. $menu[$package][$category][] = $link;
  80. }
  81. }
  82. else
  83. {
  84. $menu[$package]['Base'][] = $link;
  85. }
  86. }
  87. }
  88. else
  89. {
  90. $menu['[Unknown]']['Base'][] = $link;
  91. }
  92. }
  93. // Sort the packages
  94. ksort($menu);
  95. return View::factory('userguide/api/menu')
  96. ->bind('menu', $menu);
  97. }
  98. /**
  99. * Returns an array of all the classes available, built by listing all files in the classes folder and then trying to create that class.
  100. *
  101. * This means any empty class files (as in complety empty) will cause an exception
  102. *
  103. * @param array array of files, obtained using Kohana::list_files
  104. * @return array an array of all the class names
  105. */
  106. public static function classes(array $list = NULL)
  107. {
  108. if ($list === NULL)
  109. {
  110. $list = Kohana::list_files('classes');
  111. }
  112. $classes = array();
  113. // This will be used a lot!
  114. $ext_length = strlen(EXT);
  115. foreach ($list as $name => $path)
  116. {
  117. if (is_array($path))
  118. {
  119. $classes += Kodoc::classes($path);
  120. }
  121. elseif (substr($name, -$ext_length) === EXT)
  122. {
  123. // Remove "classes/" and the extension
  124. $class = substr($name, 8, -$ext_length);
  125. // Convert slashes to underscores
  126. $class = str_replace(DIRECTORY_SEPARATOR, '_', strtolower($class));
  127. $classes[$class] = $class;
  128. }
  129. }
  130. return $classes;
  131. }
  132. /**
  133. * Get all classes and methods of files in a list.
  134. *
  135. * > I personally don't like this as it was used on the index page. Way too much stuff on one page. It has potential for a package index page though.
  136. * > For example: class_methods( Kohana::list_files('classes/sprig') ) could make a nice index page for the sprig package in the api browser
  137. * > ~bluehawk
  138. *
  139. */
  140. public static function class_methods(array $list = NULL)
  141. {
  142. $list = Kodoc::classes($list);
  143. $classes = array();
  144. foreach ($list as $class)
  145. {
  146. $_class = new ReflectionClass($class);
  147. if (stripos($_class->name, 'Kohana_') === 0)
  148. {
  149. // Skip transparent extension classes
  150. continue;
  151. }
  152. $methods = array();
  153. foreach ($_class->getMethods() as $_method)
  154. {
  155. $declares = $_method->getDeclaringClass()->name;
  156. if (stripos($declares, 'Kohana_') === 0)
  157. {
  158. // Remove "Kohana_"
  159. $declares = substr($declares, 7);
  160. }
  161. if ($declares === $_class->name OR $declares === "Core")
  162. {
  163. $methods[] = $_method->name;
  164. }
  165. }
  166. sort($methods);
  167. $classes[$_class->name] = $methods;
  168. }
  169. return $classes;
  170. }
  171. /**
  172. * Parse a comment to extract the description and the tags
  173. *
  174. * @param string the comment retreived using ReflectionClass->getDocComment()
  175. * @return array array(string $description, array $tags)
  176. */
  177. public static function parse($comment)
  178. {
  179. // Normalize all new lines to \n
  180. $comment = str_replace(array("\r\n", "\n"), "\n", $comment);
  181. // Remove the phpdoc open/close tags and split
  182. $comment = array_slice(explode("\n", $comment), 1, -1);
  183. // Tag content
  184. $tags = array();
  185. foreach ($comment as $i => $line)
  186. {
  187. // Remove all leading whitespace
  188. $line = preg_replace('/^\s*\* ?/m', '', $line);
  189. // Search this line for a tag
  190. if (preg_match('/^@(\S+)(?:\s*(.+))?$/', $line, $matches))
  191. {
  192. // This is a tag line
  193. unset($comment[$i]);
  194. $name = $matches[1];
  195. $text = isset($matches[2]) ? $matches[2] : '';
  196. switch ($name)
  197. {
  198. case 'license':
  199. if (strpos($text, '://') !== FALSE)
  200. {
  201. // Convert the lincense into a link
  202. $text = HTML::anchor($text);
  203. }
  204. break;
  205. case 'link':
  206. $text = preg_split('/\s+/', $text, 2);
  207. $text = HTML::anchor($text[0], isset($text[1]) ? $text[1] : $text[0]);
  208. break;
  209. case 'copyright':
  210. if (strpos($text, '(c)') !== FALSE)
  211. {
  212. // Convert the copyright sign
  213. $text = str_replace('(c)', '&copy;', $text);
  214. }
  215. break;
  216. case 'throws':
  217. if (preg_match('/^(\w+)\W(.*)$/', $text, $matches))
  218. {
  219. $text = HTML::anchor(Route::get('docs/api')->uri(array('class' => $matches[1])), $matches[1]).' '.$matches[2];
  220. }
  221. else
  222. {
  223. $text = HTML::anchor(Route::get('docs/api')->uri(array('class' => $text)), $text);
  224. }
  225. break;
  226. case 'uses':
  227. if (preg_match('/^'.Kodoc::$regex_class_member.'$/i', $text, $matches))
  228. {
  229. $text = Kodoc::link_class_member($matches);
  230. }
  231. break;
  232. // Don't show @access lines, they are shown elsewhere
  233. case 'access':
  234. continue 2;
  235. }
  236. // Add the tag
  237. $tags[$name][] = $text;
  238. }
  239. else
  240. {
  241. // Overwrite the comment line
  242. $comment[$i] = (string) $line;
  243. }
  244. }
  245. // Concat the comment lines back to a block of text
  246. if ($comment = trim(implode("\n", $comment)))
  247. {
  248. // Parse the comment with Markdown
  249. $comment = Kodoc_Markdown::markdown($comment);
  250. }
  251. return array($comment, $tags);
  252. }
  253. /**
  254. * Get the source of a function
  255. *
  256. * @param string the filename
  257. * @param int start line?
  258. * @param int end line?
  259. */
  260. public static function source($file, $start, $end)
  261. {
  262. if ( ! $file) return FALSE;
  263. $file = file($file, FILE_IGNORE_NEW_LINES);
  264. $file = array_slice($file, $start - 1, $end - $start + 1);
  265. if (preg_match('/^(\s+)/', $file[0], $matches))
  266. {
  267. $padding = strlen($matches[1]);
  268. foreach ($file as & $line)
  269. {
  270. $line = substr($line, $padding);
  271. }
  272. }
  273. return implode("\n", $file);
  274. }
  275. /**
  276. * Test whether a class should be shown, based on the api_packages config option
  277. *
  278. * @param Kodoc_Class the class to test
  279. * @return bool whether this class should be shown
  280. */
  281. public static function show_class(Kodoc_Class $class)
  282. {
  283. $api_packages = Kohana::$config->load('userguide.api_packages');
  284. // If api_packages is true, all packages should be shown
  285. if ($api_packages === TRUE)
  286. return TRUE;
  287. // Get the package tags for this class (as an array)
  288. $packages = Arr::get($class->tags, 'package', array('None'));
  289. $show_this = FALSE;
  290. // Loop through each package tag
  291. foreach ($packages as $package)
  292. {
  293. // If this package is in the allowed packages, set show this to true
  294. if (in_array($package, explode(',', $api_packages)))
  295. $show_this = TRUE;
  296. }
  297. return $show_this;
  298. }
  299. } // End Kodoc