/scripts/gen-api.php

https://github.com/phalcon/docs · PHP · 611 lines · 484 code · 80 blank · 47 comment · 121 complexity · a82a0b7ec065513079a5d042fef4c0a3 MD5 · raw file

  1. <?php
  2. /**
  3. * This scripts generates the restructuredText for the class API.
  4. *
  5. * Change the CPHALCON_DIR constant to point to the dev/ directory in the Phalcon source code
  6. *
  7. * php scripts/gen-api.php
  8. */
  9. if (!extension_loaded('phalcon')) {
  10. throw new Exception("Phalcon extension is required");
  11. }
  12. define('CPHALCON_DIR', '/Users/gutierrezandresfelipe/cphalcon/ext/');
  13. if (!file_exists(CPHALCON_DIR)) {
  14. throw new Exception("CPHALCON directory does not exist");
  15. }
  16. $languages = array('en', 'es', 'ja', 'pl', 'fr', 'ru');
  17. /**
  18. * Class ApiGenerator
  19. */
  20. class ApiGenerator
  21. {
  22. protected $docs = array();
  23. protected $classDocs = array();
  24. /**
  25. * @param $directory
  26. */
  27. public function __construct($directory)
  28. {
  29. $this->scanSources($directory);
  30. }
  31. /**
  32. * @param $directory
  33. */
  34. protected function scanSources($directory)
  35. {
  36. $recursiveDirectoryIterator = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS);
  37. /** @var $iterator RecursiveDirectoryIterator[] */
  38. $iterator = new RecursiveIteratorIterator($recursiveDirectoryIterator);
  39. foreach ($iterator as $item) {
  40. if ($item->getExtension() == 'c') {
  41. if (strpos($item->getPathname(), 'kernel') === false) {
  42. $this->parseDocs($item->getPathname());
  43. }
  44. }
  45. }
  46. }
  47. /**
  48. * Parse docs from file
  49. *
  50. * @param $file
  51. */
  52. protected function parseDocs($file)
  53. {
  54. $firstDoc = true;
  55. $openComment = false;
  56. $nextLineMethod = false;
  57. $comment = '';
  58. foreach (file($file) as $line) {
  59. if (trim($line) == '/**') {
  60. $openComment = true;
  61. $comment .= $line;
  62. }
  63. if ($openComment === true) {
  64. $comment .= $line;
  65. } else {
  66. if ($nextLineMethod === true) {
  67. if (preg_match('/^PHP_METHOD\(([a-zA-Z0-9\_]+), (.*)\)/', $line, $matches)) {
  68. $this->docs[$matches[1]][$matches[2]] = $comment;
  69. $className = $matches[1];
  70. } else {
  71. if (preg_match('/^PHALCON_DOC_METHOD\(([a-zA-Z0-9\_]+), (.*)\)/', $line, $matches)) {
  72. $this->docs[$matches[1]][$matches[2]] = $comment;
  73. $className = $matches[1];
  74. } else {
  75. if ($firstDoc === true) {
  76. $classDoc = $comment;
  77. $firstDoc = false;
  78. $comment = '';
  79. }
  80. }
  81. }
  82. $nextLineMethod = false;
  83. } else {
  84. $comment = '';
  85. }
  86. }
  87. if ($openComment === true) {
  88. if (trim($line) == '*/') {
  89. $comment .= $line;
  90. $openComment = false;
  91. $nextLineMethod = true;
  92. }
  93. }
  94. if (preg_match('/^PHALCON_INIT_CLASS\(([a-zA-Z0-9\_]+)\)/', $line, $matches)) {
  95. $className = $matches[1];
  96. }
  97. }
  98. if (isset($classDoc)) {
  99. if (!isset($className)) {
  100. $fileName = str_replace(CPHALCON_DIR, '', $file);
  101. $fileName = str_replace('.c', '', $fileName);
  102. $parts = array();
  103. foreach (explode(DIRECTORY_SEPARATOR, $fileName) as $part) {
  104. $parts[] = ucfirst($part);
  105. }
  106. $className = 'Phalcon\\' . join('\\', $parts);
  107. } else {
  108. $className = str_replace('_', '\\', $className);
  109. }
  110. //echo $className, PHP_EOL;
  111. if (!isset($this->classDocs[$className])) {
  112. if (class_exists($className) or interface_exists($className)) {
  113. $this->classDocs[$className] = $classDoc;
  114. }
  115. }
  116. }
  117. }
  118. /**
  119. * @return array
  120. */
  121. public function getDocs()
  122. {
  123. return $this->docs;
  124. }
  125. /**
  126. * @return array
  127. */
  128. public function getClassDocs()
  129. {
  130. return $this->classDocs;
  131. }
  132. /**
  133. * @param $phpdoc
  134. * @param $className
  135. * @param null $realClassName
  136. *
  137. * @return array
  138. */
  139. public function getPhpDoc($phpdoc, $className, $realClassName = null)
  140. {
  141. $ret = array();
  142. $lines = array();
  143. $description = '';
  144. $phpdoc = trim($phpdoc);
  145. $phpdoc = str_replace("\r", "", $phpdoc);
  146. foreach (explode("\n", $phpdoc) as $line) {
  147. $line = preg_replace('#^/\*\*#', '', $line);
  148. $line = str_replace('*/', '', $line);
  149. $line = preg_replace('#^[ \t]+\*#', '', $line);
  150. $line = str_replace('*\/', '*/', $line);
  151. $tline = trim($line);
  152. if ($className != $tline) {
  153. $lines[] = $line;
  154. }
  155. }
  156. $rc = str_replace("\\\\", "\\", $realClassName);
  157. $numberBlock = -1;
  158. $insideCode = false;
  159. $codeBlocks = array();
  160. foreach ($lines as $line) {
  161. if (strpos($line, '<code') !== false) {
  162. $numberBlock++;
  163. $insideCode = true;
  164. }
  165. if (strpos($line, '</code') !== false) {
  166. $insideCode = false;
  167. }
  168. if ($insideCode == false) {
  169. $line = str_replace('</code>', '', $line);
  170. if (trim($line) != $rc) {
  171. if (preg_match('/@([a-z0-9]+)/', $line, $matches)) {
  172. $content = trim(str_replace($matches[0], '', $line));
  173. if ($matches[1] == 'param') {
  174. $parts = preg_split('/[ \t]+/', $content);
  175. if (count($parts) == 2) {
  176. $ret['parameters'][$parts[1]] = trim($parts[0]);
  177. } else {
  178. //throw new Exception("Failed proccessing parameters in ".$className.'::'.$methodName);
  179. }
  180. } else {
  181. $ret[$matches[1]] = $content;
  182. }
  183. } else {
  184. $description .= ltrim($line) . "\n";
  185. }
  186. }
  187. } else {
  188. if (!isset($codeBlocks[$numberBlock])) {
  189. $line = str_replace('<code>', '', $line);
  190. $codeBlocks[$numberBlock] = $line . "\n";
  191. $description .= '%%' . $numberBlock . '%%';
  192. } else {
  193. $codeBlocks[$numberBlock] .= $line . "\n";
  194. }
  195. }
  196. }
  197. foreach ($codeBlocks as $n => $cc) {
  198. $c = '';
  199. $firstLine = true;
  200. $p = explode("\n", $cc);
  201. foreach ($p as $pp) {
  202. if ($firstLine) {
  203. if (substr(ltrim($pp), 0, 1) != '[') {
  204. if (!preg_match('#^<?php#', ltrim($pp))) {
  205. if (count($p) == 1) {
  206. $c .= ' <?php ';
  207. } else {
  208. $c .= ' <?php' . PHP_EOL . PHP_EOL;
  209. }
  210. }
  211. }
  212. $firstLine = false;
  213. }
  214. $pp = preg_replace('#^\t#', '', $pp);
  215. if (count($p) != 1) {
  216. $c .= ' ' . $pp . PHP_EOL;
  217. } else {
  218. $c .= $pp . PHP_EOL;
  219. }
  220. }
  221. $c .= PHP_EOL;
  222. $codeBlocks[$n] = rtrim($c);
  223. }
  224. $description = str_replace('<p>', '', $description);
  225. $description = str_replace('</p>', PHP_EOL . PHP_EOL, $description);
  226. $c = $description;
  227. $c = str_replace("\\", "\\\\", $c);
  228. $c = trim(str_replace("\t", "", $c));
  229. $c = trim(str_replace("\n", " ", $c));
  230. foreach ($codeBlocks as $n => $cc) {
  231. if (preg_match('#\[[a-z]+\]#', $cc)) {
  232. $type = 'ini';
  233. } else {
  234. $type = 'php';
  235. }
  236. $c = str_replace(
  237. '%%' . $n . '%%',
  238. PHP_EOL . PHP_EOL . '.. code-block:: ' . $type . PHP_EOL . PHP_EOL . $cc . PHP_EOL . PHP_EOL,
  239. $c
  240. );
  241. }
  242. $final = '';
  243. $blankLine = false;
  244. foreach (explode("\n", $c) as $line) {
  245. if (trim($line) == '') {
  246. if ($blankLine == false) {
  247. $final .= $line . "\n";
  248. $blankLine = true;
  249. }
  250. } else {
  251. $final .= $line . "\n";
  252. $blankLine = false;
  253. }
  254. }
  255. $ret['description'] = $final;
  256. return $ret;
  257. }
  258. }
  259. $index
  260. = 'API Indice
  261. ----------
  262. .. toctree::
  263. :maxdepth: 1' . PHP_EOL . PHP_EOL;
  264. $api = new ApiGenerator(CPHALCON_DIR);
  265. $classDocs = $api->getClassDocs();
  266. $docs = $api->getDocs();
  267. $classes = array();
  268. foreach (get_declared_classes() as $className) {
  269. if (!preg_match('#^Phalcon#', $className)) {
  270. continue;
  271. }
  272. $classes[] = $className;
  273. }
  274. foreach (get_declared_interfaces() as $className) {
  275. if (!preg_match('#^Phalcon#', $className)) {
  276. continue;
  277. }
  278. $classes[] = $className;
  279. }
  280. //Exception class docs
  281. $docs['Exception'] = array(
  282. '__construct' => '/**
  283. * Exception constructor
  284. *
  285. * @param string $message
  286. * @param int $code
  287. * @param Exception $previous
  288. */',
  289. 'getMessage' => '/**
  290. * Gets the Exception message
  291. *
  292. * @return string
  293. */',
  294. 'getCode' => '/**
  295. * Gets the Exception code
  296. *
  297. * @return int
  298. */',
  299. 'getLine' => '/**
  300. * Gets the line in which the exception occurred
  301. *
  302. * @return int
  303. */',
  304. 'getFile' => '/**
  305. * Gets the file in which the exception occurred
  306. *
  307. * @return string
  308. */',
  309. 'getTrace' => '/**
  310. * Gets the stack trace
  311. *
  312. * @return array
  313. */',
  314. 'getTraceAsString' => '/**
  315. * Gets the stack trace as a string
  316. *
  317. * @return Exception
  318. */',
  319. '__clone' => '/**
  320. * Clone the exception
  321. *
  322. * @return Exception
  323. */',
  324. 'getPrevious' => '/**
  325. * Returns previous Exception
  326. *
  327. * @return Exception
  328. */',
  329. '__toString' => '/**
  330. * String representation of the exception
  331. *
  332. * @return string
  333. */',
  334. );
  335. sort($classes);
  336. $indexClasses = array();
  337. $indexInterfaces = array();
  338. foreach ($classes as $className) {
  339. $realClassName = $className;
  340. $simpleClassName = str_replace("\\", "_", $className);
  341. $reflector = new ReflectionClass($className);
  342. $documentationData = array();
  343. $typeClass = 'public';
  344. if ($reflector->isAbstract() == true) {
  345. $typeClass = 'abstract';
  346. }
  347. if ($reflector->isFinal() == true) {
  348. $typeClass = 'final';
  349. }
  350. if ($reflector->isInterface() == true) {
  351. $typeClass = '';
  352. }
  353. $documentationData = array(
  354. 'type' => $typeClass,
  355. 'description' => $realClassName,
  356. 'extends' => $reflector->getParentClass(),
  357. 'implements' => $reflector->getInterfaceNames(),
  358. 'constants' => $reflector->getConstants(),
  359. 'methods' => $reflector->getMethods()
  360. );
  361. if ($reflector->isInterface() == true) {
  362. $indexInterfaces[] = ' ' . $simpleClassName . PHP_EOL;
  363. } else {
  364. $indexClasses[] = ' ' . $simpleClassName . PHP_EOL;
  365. }
  366. $nsClassName = str_replace("\\", "\\\\", $className);
  367. if ($reflector->isInterface() == true) {
  368. $code = 'Interface **' . $nsClassName . '**' . PHP_EOL;
  369. $code .= str_repeat("=", strlen($code) - 1) . PHP_EOL . PHP_EOL;
  370. } else {
  371. $classPrefix = 'Class';
  372. if (strtolower($typeClass) != 'public') {
  373. $classPrefix = ucfirst(strtolower($typeClass)) . ' class';
  374. }
  375. $code = $classPrefix . ' **' . $nsClassName . '**' . PHP_EOL;
  376. $code .= str_repeat("=", strlen($code) - 1) . PHP_EOL . PHP_EOL;
  377. }
  378. if ($documentationData['extends']) {
  379. $extendsName = $documentationData['extends']->name;
  380. if (strpos($extendsName, 'Phalcon') !== false) {
  381. if (class_exists($extendsName)) {
  382. $extendsClass = $extendsName;
  383. $extendsPath = str_replace("\\", "_", $extendsName);
  384. $extendsName = str_replace("\\", "\\\\", $extendsName);
  385. $reflector = new ReflectionClass($extendsClass);
  386. $prefix = 'class';
  387. if ($reflector->isAbstract() == true) {
  388. $prefix = 'abstract class';
  389. }
  390. $code
  391. .= '*extends* ' . $prefix . ' :doc:`' . $extendsName . ' <' . $extendsPath . '>`' . PHP_EOL
  392. . PHP_EOL;
  393. } else {
  394. $code .= '*extends* ' . $extendsName . PHP_EOL . PHP_EOL;
  395. }
  396. } else {
  397. $code .= '*extends* ' . $extendsName . PHP_EOL . PHP_EOL;
  398. }
  399. }
  400. //Generate the interfaces part
  401. if (count($documentationData['implements'])) {
  402. $implements = array();
  403. foreach ($documentationData['implements'] as $interfaceName) {
  404. if (strpos($interfaceName, 'Phalcon') !== false) {
  405. if (interface_exists($interfaceName)) {
  406. $interfacePath = str_replace("\\", "_", $interfaceName);
  407. $interfaceName = str_replace("\\", "\\\\", $interfaceName);
  408. $implements[] = ':doc:`' . $interfaceName . ' <' . $interfacePath . '>`';
  409. } else {
  410. $implements[] = str_replace("\\", "\\\\", $interfaceName);
  411. }
  412. } else {
  413. $implements[] = $interfaceName;
  414. }
  415. }
  416. $code .= '*implements* ' . join(', ', $implements) . PHP_EOL . PHP_EOL;
  417. }
  418. if (isset($classDocs[$realClassName])) {
  419. $ret = $api->getPhpDoc($classDocs[$realClassName], $className, $realClassName);
  420. $code .= $ret['description'] . PHP_EOL . PHP_EOL;
  421. }
  422. if (count($documentationData['constants'])) {
  423. $code .= 'Constants' . PHP_EOL;
  424. $code .= '---------' . PHP_EOL . PHP_EOL;
  425. foreach ($documentationData['constants'] as $name => $constant) {
  426. $code .= '*' . gettype($constant) . '* **' . $name . '**' . PHP_EOL . PHP_EOL;
  427. }
  428. }
  429. if (count($documentationData['methods'])) {
  430. $code .= 'Methods' . PHP_EOL;
  431. $code .= '-------' . PHP_EOL . PHP_EOL;
  432. foreach ($documentationData['methods'] as $method) {
  433. /** @var $method ReflectionMethod */
  434. $docClassName = str_replace("\\", "_", $method->getDeclaringClass()->name);
  435. if (isset($docs[$docClassName])) {
  436. $docMethods = $docs[$docClassName];
  437. } else {
  438. $docMethods = array();
  439. }
  440. if (isset($docMethods[$method->name])) {
  441. $ret = $api->getPhpDoc($docMethods[$method->name], $className);
  442. } else {
  443. $ret = array();
  444. }
  445. $code .= implode(' ', Reflection::getModifierNames($method->getModifiers())) . ' ';
  446. if (isset($ret['return'])) {
  447. if (preg_match('/^(Phalcon[a-zA-Z0-9\\\\]+)/', $ret['return'], $matches)) {
  448. if (class_exists($matches[0]) || interface_exists($matches[0])) {
  449. $extendsPath = str_replace("\\", "_", $matches[1]);
  450. $extendsName = str_replace("\\", "\\\\", $matches[1]);
  451. $code .= str_replace(
  452. $matches[1],
  453. ':doc:`' . $extendsName . ' <' . $extendsPath . '>` ',
  454. $ret['return']
  455. );
  456. } else {
  457. $extendsName = str_replace("\\", "\\\\", $ret['return']);
  458. $code .= '*' . $extendsName . '* ';
  459. }
  460. } else {
  461. $code .= '*' . $ret['return'] . '* ';
  462. }
  463. }
  464. $code .= ' **' . $method->name . '** (';
  465. $cp = array();
  466. foreach ($method->getParameters() as $parameter) {
  467. $name = '$' . $parameter->name;
  468. if (isset($ret['parameters'][$name])) {
  469. if (strpos($ret['parameters'][$name], 'Phalcon') !== false) {
  470. if (class_exists($ret['parameters'][$name]) || interface_exists($ret['parameters'][$name])) {
  471. $parameterPath = str_replace("\\", "_", $ret['parameters'][$name]);
  472. $parameterName = str_replace("\\", "\\\\", $ret['parameters'][$name]);
  473. if (!$parameter->isOptional()) {
  474. $cp[] = ':doc:`' . $parameterName . ' <' . $parameterPath . '>` ' . $name;
  475. } else {
  476. $cp[] = '[:doc:`' . $parameterName . ' <' . $parameterPath . '>` ' . $name . ']';
  477. }
  478. } else {
  479. $parameterName = str_replace("\\", "\\\\", $ret['parameters'][$name]);
  480. if (!$parameter->isOptional()) {
  481. $cp[] = '*' . $parameterName . '* ' . $name;
  482. } else {
  483. $cp[] = '[*' . $parameterName . '* ' . $name . ']';
  484. }
  485. }
  486. } else {
  487. if (!$parameter->isOptional()) {
  488. $cp[] = '*' . $ret['parameters'][$name] . '* ' . $name;
  489. } else {
  490. $cp[] = '[*' . $ret['parameters'][$name] . '* ' . $name . ']';
  491. }
  492. }
  493. } else {
  494. /**
  495. * if ($className != 'Phalcon\Kernel') {
  496. * if ($simpleClassName == $docClassName) {
  497. * throw new Exception("unknown parameter $className::".$method->name."::".$parameter->name, 1);
  498. * }
  499. * }
  500. */
  501. if (!$parameter->isOptional()) {
  502. $cp[] = '*unknown* ' . $name;
  503. } else {
  504. $cp[] = '[*unknown* ' . $name . ']';
  505. }
  506. }
  507. }
  508. $code .= join(', ', $cp) . ')';
  509. if ($simpleClassName != $docClassName) {
  510. $code .= ' inherited from ' . str_replace("\\", "\\\\", $method->getDeclaringClass()->name);
  511. }
  512. $code .= PHP_EOL . PHP_EOL;
  513. if (isset($ret['description'])) {
  514. foreach (explode("\n", $ret['description']) as $dline) {
  515. $code .= "" . $dline . "\n";
  516. }
  517. } else {
  518. $code .= "...\n";
  519. }
  520. $code .= PHP_EOL . PHP_EOL;
  521. }
  522. }
  523. foreach ($languages as $lang) {
  524. @mkdir($lang . '/api/');
  525. file_put_contents($lang . '/api/' . $simpleClassName . '.rst', $code);
  526. }
  527. }
  528. foreach ($languages as $lang) {
  529. file_put_contents($lang . '/api/index.rst', $index . join('', $indexClasses) . join('', $indexInterfaces));
  530. }