LimeLexerAnnotationAware.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /*
  3. * This file is part of the Lime test framework.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  6. * (c) Bernhard Schussek <bernhard.schussek@symfony-project.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. /**
  12. * Analyzes PHP scripts taking annotations into account.
  13. *
  14. * Like LimeLexer, this class analyzes PHP scripts syntactically but is aware
  15. * of annotations. Annotations are expected to be expressed using single
  16. * line comments. Optionally, you can add a comment to the annotation, which
  17. * needs to be separated from the annotation by any number of colons or spaces.
  18. *
  19. * <code>
  20. * // @Annotation: Optional comment
  21. * </code>
  22. *
  23. * You can extend this class if you want to write your own lexer that takes
  24. * existing annotations into account. You have to pass a number of
  25. * expected annotations to the constructor. Any other exception that is not
  26. * passed in this array will result in an exception during parsing.
  27. *
  28. * <code>
  29. * $lexer = new CustomLexerAnnotationAware(array('Annotation1', 'Annotation2'));
  30. * </code>
  31. *
  32. * The following script will lead to an error when parsed:
  33. *
  34. * <code>
  35. * $i = 1;
  36. * // @Annotation3
  37. * $i++;
  38. * </code>
  39. *
  40. * @package Lime
  41. * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
  42. * @version SVN: $Id: LimeLexerAnnotationAware.php 23701 2009-11-08 21:23:40Z bschussek $
  43. * @see LimeLexer
  44. */
  45. abstract class LimeLexerAnnotationAware extends LimeLexer
  46. {
  47. private
  48. $allowedAnnotations,
  49. $currentAnnotation,
  50. $currentAnnotationComment,
  51. $inAnnotation,
  52. $inAnnotationDeclaration;
  53. /**
  54. * Constructor.
  55. *
  56. * Accepts an array of expected annotation names as argument. Any annotation
  57. * that is not listed in this array will cause an exception during parsing.
  58. *
  59. * @param array $allowedAnnotations The list of allowed annotations.
  60. */
  61. public function __construct(array $allowedAnnotations = array())
  62. {
  63. $this->allowedAnnotations = $allowedAnnotations;
  64. }
  65. /**
  66. * (non-PHPdoc)
  67. * @see lexer/LimeLexer#parse($content)
  68. */
  69. public function parse($content)
  70. {
  71. $this->currentAnnotation = null;
  72. $this->currentAnnotationComment = null;
  73. $this->inAnnotation = false;
  74. $this->inAnnotationDeclaration = false;
  75. return parent::parse($content);
  76. }
  77. /**
  78. * (non-PHPdoc)
  79. * @see lexer/LimeLexer#beforeProcess($text, $id)
  80. */
  81. protected function beforeProcess($text, $id)
  82. {
  83. if (!$this->inClass() && !$this->inFunction() && $id = T_COMMENT && strpos($text, '//') === 0)
  84. {
  85. list($annotation, $comment) = $this->extractAnnotation($text);
  86. if (!is_null($annotation))
  87. {
  88. $this->currentAnnotation = $annotation;
  89. $this->currentAnnotationComment = $comment;
  90. $this->inAnnotation = true;
  91. $this->inAnnotationDeclaration = true;
  92. }
  93. }
  94. else
  95. {
  96. $this->inAnnotationDeclaration = false;
  97. }
  98. }
  99. /**
  100. * Returns whether the parser currently is within any annotation.
  101. *
  102. * All the code following an annotation declaration is considered to be
  103. * inside this annotation's block. In annotated script, this method will thus
  104. * only return false before the first annotation declaration.
  105. *
  106. * @return boolean TRUE if any annotation declaration preceded the current
  107. * position of the lexer
  108. */
  109. protected function inAnnotation()
  110. {
  111. return $this->inAnnotation;
  112. }
  113. /**
  114. * Returns whether the parser is currently inside an annotation declaration.
  115. *
  116. * An annotation declaration is any single line comment with a word that
  117. * starts with "@" and any optional following comments. Annotations and
  118. * comments have to be separated by one or more spaces or colons.
  119. *
  120. * <code>
  121. * // @Annotation: Optional comment
  122. * </code>
  123. *
  124. * @return boolean
  125. */
  126. protected function inAnnotationDeclaration()
  127. {
  128. return $this->inAnnotationDeclaration;
  129. }
  130. /**
  131. * Returns the name of the currently active annotation.
  132. *
  133. * @return boolean
  134. * @see inAnnotation()
  135. */
  136. protected function getCurrentAnnotation()
  137. {
  138. return $this->currentAnnotation;
  139. }
  140. /**
  141. * Returns the comment of the currently active annotation.
  142. *
  143. * @return boolean
  144. * @see inAnnotation()
  145. */
  146. protected function getCurrentAnnotationComment()
  147. {
  148. return $this->currentAnnotationComment;
  149. }
  150. /**
  151. * Returns the array of allowed annotation names.
  152. *
  153. * This array can be set in the constructor.
  154. *
  155. * @return array
  156. */
  157. protected function getAllowedAnnotations()
  158. {
  159. return $this->allowedAnnotations;
  160. }
  161. /**
  162. * Extracts an annotation from a single-line comment and validates it.
  163. *
  164. * Possible valid annotations are:
  165. * <code>
  166. * // @Annotation
  167. * // @Annotation: Some comment here
  168. * </code>
  169. *
  170. * The results for those annotations are:
  171. * <code>
  172. * array('Annotation', null);
  173. * array('Annotation', 'Some comment here');
  174. * </code>
  175. *
  176. * @param string $text Some code
  177. *
  178. * @return array An array with the annotation name and the annotation
  179. * comment. If either of both cannot be read, it is NULL.
  180. */
  181. protected function extractAnnotation($text)
  182. {
  183. if (preg_match('/^\/\/\s*@(\w+)([:\s]+(.*))?\s*$/', $text, $matches))
  184. {
  185. $annotation = $matches[1];
  186. $data = count($matches) > 3 ? trim($matches[3]) : null;
  187. if (!in_array($annotation, $this->allowedAnnotations))
  188. {
  189. throw new LogicException(sprintf('The annotation "%s" is not valid', $annotation));
  190. }
  191. return array($annotation, $data);
  192. }
  193. else
  194. {
  195. return array(null, null);
  196. }
  197. }
  198. }