annotate vendor/symfony/translation/Extractor/PhpExtractor.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@14 1 <?php
Chris@14 2
Chris@14 3 /*
Chris@14 4 * This file is part of the Symfony package.
Chris@14 5 *
Chris@14 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@14 7 *
Chris@14 8 * For the full copyright and license information, please view the LICENSE
Chris@14 9 * file that was distributed with this source code.
Chris@14 10 */
Chris@14 11
Chris@14 12 namespace Symfony\Component\Translation\Extractor;
Chris@14 13
Chris@14 14 use Symfony\Component\Finder\Finder;
Chris@14 15 use Symfony\Component\Translation\MessageCatalogue;
Chris@14 16
Chris@14 17 /**
Chris@14 18 * PhpExtractor extracts translation messages from a PHP template.
Chris@14 19 *
Chris@14 20 * @author Michel Salib <michelsalib@hotmail.com>
Chris@14 21 */
Chris@14 22 class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
Chris@14 23 {
Chris@14 24 const MESSAGE_TOKEN = 300;
Chris@14 25 const METHOD_ARGUMENTS_TOKEN = 1000;
Chris@14 26 const DOMAIN_TOKEN = 1001;
Chris@14 27
Chris@14 28 /**
Chris@14 29 * Prefix for new found message.
Chris@14 30 *
Chris@14 31 * @var string
Chris@14 32 */
Chris@14 33 private $prefix = '';
Chris@14 34
Chris@14 35 /**
Chris@14 36 * The sequence that captures translation messages.
Chris@14 37 *
Chris@14 38 * @var array
Chris@14 39 */
Chris@17 40 protected $sequences = [
Chris@17 41 [
Chris@14 42 '->',
Chris@14 43 'trans',
Chris@14 44 '(',
Chris@14 45 self::MESSAGE_TOKEN,
Chris@14 46 ',',
Chris@14 47 self::METHOD_ARGUMENTS_TOKEN,
Chris@14 48 ',',
Chris@14 49 self::DOMAIN_TOKEN,
Chris@17 50 ],
Chris@17 51 [
Chris@14 52 '->',
Chris@14 53 'transChoice',
Chris@14 54 '(',
Chris@14 55 self::MESSAGE_TOKEN,
Chris@14 56 ',',
Chris@14 57 self::METHOD_ARGUMENTS_TOKEN,
Chris@14 58 ',',
Chris@14 59 self::METHOD_ARGUMENTS_TOKEN,
Chris@14 60 ',',
Chris@14 61 self::DOMAIN_TOKEN,
Chris@17 62 ],
Chris@17 63 [
Chris@14 64 '->',
Chris@14 65 'trans',
Chris@14 66 '(',
Chris@14 67 self::MESSAGE_TOKEN,
Chris@17 68 ],
Chris@17 69 [
Chris@14 70 '->',
Chris@14 71 'transChoice',
Chris@14 72 '(',
Chris@14 73 self::MESSAGE_TOKEN,
Chris@17 74 ],
Chris@17 75 ];
Chris@14 76
Chris@14 77 /**
Chris@14 78 * {@inheritdoc}
Chris@14 79 */
Chris@14 80 public function extract($resource, MessageCatalogue $catalog)
Chris@14 81 {
Chris@14 82 $files = $this->extractFiles($resource);
Chris@14 83 foreach ($files as $file) {
Chris@14 84 $this->parseTokens(token_get_all(file_get_contents($file)), $catalog);
Chris@14 85
Chris@14 86 if (\PHP_VERSION_ID >= 70000) {
Chris@14 87 // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
Chris@14 88 gc_mem_caches();
Chris@14 89 }
Chris@14 90 }
Chris@14 91 }
Chris@14 92
Chris@14 93 /**
Chris@14 94 * {@inheritdoc}
Chris@14 95 */
Chris@14 96 public function setPrefix($prefix)
Chris@14 97 {
Chris@14 98 $this->prefix = $prefix;
Chris@14 99 }
Chris@14 100
Chris@14 101 /**
Chris@14 102 * Normalizes a token.
Chris@14 103 *
Chris@14 104 * @param mixed $token
Chris@14 105 *
Chris@14 106 * @return string
Chris@14 107 */
Chris@14 108 protected function normalizeToken($token)
Chris@14 109 {
Chris@14 110 if (isset($token[1]) && 'b"' !== $token) {
Chris@14 111 return $token[1];
Chris@14 112 }
Chris@14 113
Chris@14 114 return $token;
Chris@14 115 }
Chris@14 116
Chris@14 117 /**
Chris@14 118 * Seeks to a non-whitespace token.
Chris@14 119 */
Chris@14 120 private function seekToNextRelevantToken(\Iterator $tokenIterator)
Chris@14 121 {
Chris@14 122 for (; $tokenIterator->valid(); $tokenIterator->next()) {
Chris@14 123 $t = $tokenIterator->current();
Chris@14 124 if (T_WHITESPACE !== $t[0]) {
Chris@14 125 break;
Chris@14 126 }
Chris@14 127 }
Chris@14 128 }
Chris@14 129
Chris@14 130 private function skipMethodArgument(\Iterator $tokenIterator)
Chris@14 131 {
Chris@14 132 $openBraces = 0;
Chris@14 133
Chris@14 134 for (; $tokenIterator->valid(); $tokenIterator->next()) {
Chris@14 135 $t = $tokenIterator->current();
Chris@14 136
Chris@14 137 if ('[' === $t[0] || '(' === $t[0]) {
Chris@14 138 ++$openBraces;
Chris@14 139 }
Chris@14 140
Chris@14 141 if (']' === $t[0] || ')' === $t[0]) {
Chris@14 142 --$openBraces;
Chris@14 143 }
Chris@14 144
Chris@14 145 if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) {
Chris@14 146 break;
Chris@14 147 }
Chris@14 148 }
Chris@14 149 }
Chris@14 150
Chris@14 151 /**
Chris@14 152 * Extracts the message from the iterator while the tokens
Chris@14 153 * match allowed message tokens.
Chris@14 154 */
Chris@14 155 private function getValue(\Iterator $tokenIterator)
Chris@14 156 {
Chris@14 157 $message = '';
Chris@14 158 $docToken = '';
Chris@17 159 $docPart = '';
Chris@14 160
Chris@14 161 for (; $tokenIterator->valid(); $tokenIterator->next()) {
Chris@14 162 $t = $tokenIterator->current();
Chris@17 163 if ('.' === $t) {
Chris@17 164 // Concatenate with next token
Chris@17 165 continue;
Chris@17 166 }
Chris@14 167 if (!isset($t[1])) {
Chris@14 168 break;
Chris@14 169 }
Chris@14 170
Chris@14 171 switch ($t[0]) {
Chris@14 172 case T_START_HEREDOC:
Chris@14 173 $docToken = $t[1];
Chris@14 174 break;
Chris@14 175 case T_ENCAPSED_AND_WHITESPACE:
Chris@14 176 case T_CONSTANT_ENCAPSED_STRING:
Chris@17 177 if ('' === $docToken) {
Chris@17 178 $message .= PhpStringTokenParser::parse($t[1]);
Chris@17 179 } else {
Chris@17 180 $docPart = $t[1];
Chris@17 181 }
Chris@14 182 break;
Chris@14 183 case T_END_HEREDOC:
Chris@17 184 $message .= PhpStringTokenParser::parseDocString($docToken, $docPart);
Chris@17 185 $docToken = '';
Chris@17 186 $docPart = '';
Chris@17 187 break;
Chris@17 188 case T_WHITESPACE:
Chris@17 189 break;
Chris@14 190 default:
Chris@14 191 break 2;
Chris@14 192 }
Chris@14 193 }
Chris@14 194
Chris@14 195 return $message;
Chris@14 196 }
Chris@14 197
Chris@14 198 /**
Chris@14 199 * Extracts trans message from PHP tokens.
Chris@14 200 *
Chris@14 201 * @param array $tokens
Chris@14 202 * @param MessageCatalogue $catalog
Chris@14 203 */
Chris@14 204 protected function parseTokens($tokens, MessageCatalogue $catalog)
Chris@14 205 {
Chris@14 206 $tokenIterator = new \ArrayIterator($tokens);
Chris@14 207
Chris@14 208 for ($key = 0; $key < $tokenIterator->count(); ++$key) {
Chris@14 209 foreach ($this->sequences as $sequence) {
Chris@14 210 $message = '';
Chris@14 211 $domain = 'messages';
Chris@14 212 $tokenIterator->seek($key);
Chris@14 213
Chris@14 214 foreach ($sequence as $sequenceKey => $item) {
Chris@14 215 $this->seekToNextRelevantToken($tokenIterator);
Chris@14 216
Chris@14 217 if ($this->normalizeToken($tokenIterator->current()) === $item) {
Chris@14 218 $tokenIterator->next();
Chris@14 219 continue;
Chris@14 220 } elseif (self::MESSAGE_TOKEN === $item) {
Chris@14 221 $message = $this->getValue($tokenIterator);
Chris@14 222
Chris@17 223 if (\count($sequence) === ($sequenceKey + 1)) {
Chris@14 224 break;
Chris@14 225 }
Chris@14 226 } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
Chris@14 227 $this->skipMethodArgument($tokenIterator);
Chris@14 228 } elseif (self::DOMAIN_TOKEN === $item) {
Chris@18 229 $domainToken = $this->getValue($tokenIterator);
Chris@18 230 if ('' !== $domainToken) {
Chris@18 231 $domain = $domainToken;
Chris@18 232 }
Chris@14 233
Chris@14 234 break;
Chris@14 235 } else {
Chris@14 236 break;
Chris@14 237 }
Chris@14 238 }
Chris@14 239
Chris@14 240 if ($message) {
Chris@14 241 $catalog->set($message, $this->prefix.$message, $domain);
Chris@14 242 break;
Chris@14 243 }
Chris@14 244 }
Chris@14 245 }
Chris@14 246 }
Chris@14 247
Chris@14 248 /**
Chris@14 249 * @param string $file
Chris@14 250 *
Chris@14 251 * @return bool
Chris@14 252 *
Chris@14 253 * @throws \InvalidArgumentException
Chris@14 254 */
Chris@14 255 protected function canBeExtracted($file)
Chris@14 256 {
Chris@14 257 return $this->isFile($file) && 'php' === pathinfo($file, PATHINFO_EXTENSION);
Chris@14 258 }
Chris@14 259
Chris@14 260 /**
Chris@14 261 * @param string|array $directory
Chris@14 262 *
Chris@14 263 * @return array
Chris@14 264 */
Chris@14 265 protected function extractFromDirectory($directory)
Chris@14 266 {
Chris@14 267 $finder = new Finder();
Chris@14 268
Chris@14 269 return $finder->files()->name('*.php')->in($directory);
Chris@14 270 }
Chris@14 271 }