LimeLexerTransformAnnotations.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. * Transforms annotated code in a file into functions.
  13. *
  14. * The created function names are returned by the function parse(), indexed
  15. * by annotation name.
  16. *
  17. * <code>
  18. * $lexer = new LimeLexerTransformAnnotations('path/to/transformed/file.php', array('First', 'Second'));
  19. * $functions = $lexer->parse('/path/to/original/file.php');
  20. *
  21. * // => array('First' => array(...), 'Second' => array(...))
  22. * </code>
  23. *
  24. * The annotated source file for the above code could look like this:
  25. *
  26. * <code>
  27. * $test = 'nothing';
  28. *
  29. * // @First
  30. * $test = 'First';
  31. *
  32. * // @Second
  33. * $test = 'Second';
  34. *
  35. * // @First
  36. * echo $test;
  37. * </code>
  38. *
  39. * You can include the transformed file and execute a certain subset of
  40. * annotations:
  41. *
  42. * <code>
  43. * include 'path/to/transformed/file.php';
  44. *
  45. * foreach ($functions['First'] as $function)
  46. * {
  47. * $function();
  48. * }
  49. *
  50. * // => First
  51. * </code>
  52. *
  53. * @package Lime
  54. * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
  55. * @version SVN: $Id: LimeLexerTransformAnnotations.php 25934 2009-12-27 20:44:07Z bschussek $
  56. * @see LimeLexerAnnotationAware
  57. */
  58. class LimeLexerTransformAnnotations extends LimeLexerAnnotationAware
  59. {
  60. protected static
  61. $annotations = array('Test', 'Before', 'After', 'BeforeAll', 'AfterAll');
  62. protected
  63. $fileName,
  64. $file,
  65. $variables,
  66. $functions,
  67. $functionCount,
  68. $initialized,
  69. $testVariable,
  70. $classBuffer,
  71. $classNotLoaded,
  72. $firstAnnotation;
  73. /**
  74. * Constructor.
  75. *
  76. * @param string $targetFile The file where the transformed code
  77. * will be written.
  78. * @param array $allowedAnnotations The allowed annotations.
  79. */
  80. public function __construct($targetFile)
  81. {
  82. parent::__construct(self::$annotations);
  83. $this->fileName = $targetFile;
  84. }
  85. /**
  86. * Transforms the annoated code in the given file and writes it to the
  87. * target file.
  88. *
  89. * @see LimeLexer#parse($content)
  90. */
  91. public function parse($content)
  92. {
  93. if (is_readable($content))
  94. {
  95. $content = file_get_contents($content);
  96. }
  97. $lexer = new LimeLexerVariables($this->getAllowedAnnotations(), array('Before'));
  98. $this->variables = $lexer->parse($content);
  99. $lexer = new LimeLexerTestVariable();
  100. $this->testVariable = $lexer->parse($content);
  101. $this->initialized = false;
  102. $this->functionCount = 0;
  103. $this->functions = array();
  104. $this->classBuffer = '';
  105. $this->classNotLoaded = false;
  106. $this->firstAnnotation = true;
  107. foreach ($this->getAllowedAnnotations() as $annotation)
  108. {
  109. $this->functions[$annotation] = array();
  110. }
  111. // backup the contents for the case that the path == filename
  112. $this->file = fopen($this->fileName, 'w');
  113. $result = parent::parse($content);
  114. if ($this->inAnnotation())
  115. {
  116. fwrite($this->file, "\n}");
  117. }
  118. fclose($this->file);
  119. return $result;
  120. }
  121. /**
  122. * Returns the name of the first global variable that contains an instance
  123. * of LimeTest or any subclass.
  124. *
  125. * If no such variable could be detected, NULL is returned.
  126. *
  127. * @return string
  128. */
  129. public function getTestVariable()
  130. {
  131. return $this->testVariable;
  132. }
  133. /**
  134. * (non-PHPdoc)
  135. * @see LimeLexer#process($text, $id)
  136. */
  137. protected function process($text, $id)
  138. {
  139. if (!$this->inClassDeclaration())
  140. {
  141. $this->classBuffer = '';
  142. }
  143. if (!$this->inClass())
  144. {
  145. $this->classNotLoaded = false;
  146. }
  147. // Some classes are automatically loaded when the script is opened, others
  148. // are not. These other classes need to be left in the source code,
  149. // otherwise they cannot be instantiated later.
  150. // This functionality is covered in LimeAnnotationSupportTest 11+12
  151. if ($this->inClassDeclaration())
  152. {
  153. if ($this->getCurrentClass() && !class_exists($this->getCurrentClass()) && !interface_exists($this->getCurrentClass()))
  154. {
  155. $this->classNotLoaded = true;
  156. $text = $this->classBuffer.$text;
  157. $this->classBuffer = '';
  158. }
  159. else
  160. {
  161. $this->classBuffer .= $text;
  162. }
  163. }
  164. // Closures and anonymous functions should not be stripped from the output
  165. if ($this->inFunction())
  166. {
  167. if ($this->inFunctionDeclaration())
  168. {
  169. $this->functionBuffer .= $text;
  170. $text = '';
  171. }
  172. // if the name of the function is NULL, it is a closure/anonymous function
  173. else if (!$this->getCurrentFunction() || $this->inClass())
  174. {
  175. $text = $this->functionBuffer.$text;
  176. $this->functionBuffer = '';
  177. }
  178. else
  179. {
  180. $text = str_repeat("\n", count(explode("\n", $this->functionBuffer.$text)) - 1);
  181. $this->functionBuffer = '';
  182. }
  183. }
  184. if ($id == T_OPEN_TAG && !$this->initialized)
  185. {
  186. if (count($this->variables))
  187. {
  188. $text .= 'global '.implode(', ', $this->variables).';';
  189. }
  190. $this->initialized = true;
  191. }
  192. else if ($this->inClass() && !$this->classNotLoaded)
  193. {
  194. $text = str_repeat("\n", count(explode("\n", $text)) - 1);
  195. }
  196. else if ($this->inAnnotationDeclaration())
  197. {
  198. $functionName = '__lime_annotation_'.($this->functionCount++);
  199. $this->functions[$this->getCurrentAnnotation()][] = array($functionName, $this->getCurrentAnnotationComment());
  200. $text = $this->firstAnnotation ? '' : '} ';
  201. $this->firstAnnotation = false;
  202. $variables = count($this->variables) ? sprintf('global %s;', implode(', ', $this->variables)) : '';
  203. $text .= sprintf("function %s() { %s\n", $functionName, $variables);
  204. }
  205. fwrite($this->file, $text);
  206. }
  207. /**
  208. * (non-PHPdoc)
  209. * @see LimeLexer#getResult()
  210. */
  211. protected function getResult()
  212. {
  213. return $this->functions;
  214. }
  215. }