/vendor/jms/translation-bundle/JMS/TranslationBundle/Translation/Extractor/File/FormExtractor.php

https://gitlab.com/matijabelec/bigpandadev · PHP · 264 lines · 197 code · 46 blank · 21 comment · 46 complexity · 8364c4b479ea0449b01eb894c9c6a2f7 MD5 · raw file

  1. <?php
  2. /*
  3. * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\TranslationBundle\Translation\Extractor\File;
  18. use JMS\TranslationBundle\Exception\RuntimeException;
  19. use JMS\TranslationBundle\Model\FileSource;
  20. use JMS\TranslationBundle\Model\Message;
  21. use JMS\TranslationBundle\Annotation\Meaning;
  22. use JMS\TranslationBundle\Annotation\Desc;
  23. use JMS\TranslationBundle\Annotation\Ignore;
  24. use Doctrine\Common\Annotations\DocParser;
  25. use JMS\TranslationBundle\Model\MessageCatalogue;
  26. use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface;
  27. use JMS\TranslationBundle\Logger\LoggerAwareInterface;
  28. use Symfony\Component\HttpKernel\Log\LoggerInterface;
  29. class FormExtractor implements FileVisitorInterface, \PHPParser_NodeVisitor
  30. {
  31. private $docParser;
  32. private $traverser;
  33. private $file;
  34. private $catalogue;
  35. private $logger;
  36. private $defaultDomain;
  37. private $defaultDomainMessages;
  38. public function __construct(DocParser $docParser)
  39. {
  40. $this->docParser = $docParser;
  41. $this->traverser = new \PHPParser_NodeTraverser();
  42. $this->traverser->addVisitor($this);
  43. }
  44. public function enterNode(\PHPParser_Node $node)
  45. {
  46. if ($node instanceof \PHPParser_Node_Stmt_Class) {
  47. $this->defaultDomain = null;
  48. $this->defaultDomainMessages = array();
  49. }
  50. if ($node instanceof \PHPParser_Node_Expr_MethodCall) {
  51. if (!is_string($node->name)) {
  52. return;
  53. }
  54. $name = strtolower($node->name);
  55. if ('setdefaults' === $name || 'replacedefaults' === $name) {
  56. $this->parseDefaultsCall($name, $node);
  57. return;
  58. }
  59. }
  60. if ($node instanceof \PHPParser_Node_Expr_Array) {
  61. // first check if a translation_domain is set for this field
  62. $domain = null;
  63. foreach ($node->items as $item) {
  64. if (!$item->key instanceof \PHPParser_Node_Scalar_String) {
  65. continue;
  66. }
  67. if ('translation_domain' === $item->key->value) {
  68. if (!$item->value instanceof \PHPParser_Node_Scalar_String) {
  69. continue;
  70. }
  71. $domain = $item->value->value;
  72. }
  73. }
  74. // look for options containing a message
  75. foreach ($node->items as $item) {
  76. if (!$item->key instanceof \PHPParser_Node_Scalar_String) {
  77. continue;
  78. }
  79. if ('empty_value' === $item->key->value && $item->value instanceof \PHPParser_Node_Expr_ConstFetch
  80. && $item->value->name instanceof \PHPParser_Node_Name && 'false' === $item->value->name->parts[0]) {
  81. continue;
  82. }
  83. if ('choices' === $item->key->value && !$item->value instanceof \PHPParser_Node_Expr_Array) {
  84. continue;
  85. }
  86. if ('label' !== $item->key->value && 'empty_value' !== $item->key->value && 'choices' !== $item->key->value && 'invalid_message' !== $item->key->value) {
  87. continue;
  88. }
  89. if ('choices' === $item->key->value) {
  90. foreach ($item->value->items as $sitem) {
  91. $this->parseItem($sitem, $domain);
  92. }
  93. } elseif ('invalid_message' === $item->key->value) {
  94. $this->parseItem($item, 'validators');
  95. } else {
  96. $this->parseItem($item, $domain);
  97. }
  98. }
  99. }
  100. }
  101. private function parseDefaultsCall($name, \PHPParser_Node $node)
  102. {
  103. static $returningMethods = array(
  104. 'setdefaults' => true, 'replacedefaults' => true, 'setoptional' => true, 'setrequired' => true,
  105. 'setallowedvalues' => true, 'addallowedvalues' => true, 'setallowedtypes' => true,
  106. 'addallowedtypes' => true, 'setfilters' => true
  107. );
  108. $var = $node->var;
  109. while ($var instanceof \PHPParser_Node_Expr_MethodCall) {
  110. if (!isset($returningMethods[strtolower($var->name)])) {
  111. return;
  112. }
  113. $var = $var->var;
  114. }
  115. if (!$var instanceof \PHPParser_Node_Expr_Variable) {
  116. return;
  117. }
  118. // check if options were passed
  119. if (!isset($node->args[0])) {
  120. return;
  121. }
  122. // ignore everything except an array
  123. if (!$node->args[0]->value instanceof \PHPParser_Node_Expr_Array) {
  124. return;
  125. }
  126. // check if a translation_domain is set as a default option
  127. $domain = null;
  128. foreach ($node->args[0]->value->items as $item) {
  129. if (!$item->key instanceof \PHPParser_Node_Scalar_String) {
  130. continue;
  131. }
  132. if ('translation_domain' === $item->key->value) {
  133. if (!$item->value instanceof \PHPParser_Node_Scalar_String) {
  134. continue;
  135. }
  136. $this->defaultDomain = $item->value->value;
  137. }
  138. }
  139. }
  140. private function parseItem($item, $domain = null)
  141. {
  142. // get doc comment
  143. $ignore = false;
  144. $desc = $meaning = null;
  145. $docComment = $item->key->getDocComment();
  146. $docComment = $docComment ? $docComment : $item->value->getDocComment();
  147. if ($docComment) {
  148. foreach ($this->docParser->parse($docComment, 'file '.$this->file.' near line '.$item->value->getLine()) as $annot) {
  149. if ($annot instanceof Ignore) {
  150. $ignore = true;
  151. } else if ($annot instanceof Desc) {
  152. $desc = $annot->text;
  153. } else if ($annot instanceof Meaning) {
  154. $meaning = $annot->text;
  155. }
  156. }
  157. }
  158. if (!$item->value instanceof \PHPParser_Node_Scalar_String) {
  159. if ($ignore) {
  160. return;
  161. }
  162. $message = sprintf('Unable to extract translation id for form label from non-string values, but got "%s" in %s on line %d. Please refactor your code to pass a string, or add "/** @Ignore */".', get_class($item->value), $this->file, $item->value->getLine());
  163. if ($this->logger) {
  164. $this->logger->err($message);
  165. return;
  166. }
  167. throw new RuntimeException($message);
  168. }
  169. $source = new FileSource((string) $this->file, $item->value->getLine());
  170. $id = $item->value->value;
  171. if (null === $domain) {
  172. $this->defaultDomainMessages[] = array(
  173. 'id' => $id,
  174. 'source' => $source,
  175. 'desc' => $desc,
  176. 'meaning' => $meaning
  177. );
  178. } else {
  179. $this->addToCatalogue($id, $source, $domain, $desc, $meaning);
  180. }
  181. }
  182. private function addToCatalogue($id, $source, $domain = null, $desc = null, $meaning = null)
  183. {
  184. if (null === $domain) {
  185. $message = new Message($id);
  186. } else {
  187. $message = new Message($id, $domain);
  188. }
  189. $message->addSource($source);
  190. if ($desc) {
  191. $message->setDesc($desc);
  192. }
  193. if ($meaning) {
  194. $message->setMeaning($meaning);
  195. }
  196. $this->catalogue->add($message);
  197. }
  198. public function visitPhpFile(\SplFileInfo $file, MessageCatalogue $catalogue, array $ast)
  199. {
  200. $this->file = $file;
  201. $this->catalogue = $catalogue;
  202. $this->traverser->traverse($ast);
  203. if ($this->defaultDomainMessages) {
  204. foreach ($this->defaultDomainMessages as $message) {
  205. $this->addToCatalogue($message['id'], $message['source'], $this->defaultDomain, $message['desc'], $message['meaning']);
  206. }
  207. }
  208. }
  209. public function leaveNode(\PHPParser_Node $node) { }
  210. public function beforeTraverse(array $nodes) { }
  211. public function afterTraverse(array $nodes) { }
  212. public function visitFile(\SplFileInfo $file, MessageCatalogue $catalogue) { }
  213. public function visitTwigFile(\SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast) { }
  214. public function setLogger(LoggerInterface $logger)
  215. {
  216. $this->logger = $logger;
  217. }
  218. }