PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.5
Independent Analytics – WordPress Analytics Plugin v2.15.5
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / symfony / translation / Extractor / PhpExtractor.php
independent-analytics / vendor / symfony / translation / Extractor Last commit date
AbstractFileExtractor.php 1 month ago ChainExtractor.php 1 month ago ExtractorInterface.php 1 month ago PhpExtractor.php 1 month ago PhpStringTokenParser.php 1 month ago
PhpExtractor.php
207 lines
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11 namespace IAWPSCOPED\Symfony\Component\Translation\Extractor;
12
13 use IAWPSCOPED\Symfony\Component\Finder\Finder;
14 use IAWPSCOPED\Symfony\Component\Translation\MessageCatalogue;
15 /**
16 * PhpExtractor extracts translation messages from a PHP template.
17 *
18 * @author Michel Salib <michelsalib@hotmail.com>
19 * @internal
20 */
21 class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
22 {
23 public const MESSAGE_TOKEN = 300;
24 public const METHOD_ARGUMENTS_TOKEN = 1000;
25 public const DOMAIN_TOKEN = 1001;
26 /**
27 * Prefix for new found message.
28 */
29 private string $prefix = '';
30 /**
31 * The sequence that captures translation messages.
32 */
33 protected $sequences = [['->', 'trans', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['->', 'trans', '(', self::MESSAGE_TOKEN], ['new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN], ['new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', 'IAWPSCOPED\\Symfony\\Component\\Translation\\TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN], ['new', 'IAWPSCOPED\\Symfony\\Component\\Translation\\TranslatableMessage', '(', self::MESSAGE_TOKEN], ['t', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN], ['t', '(', self::MESSAGE_TOKEN]];
34 /**
35 * {@inheritdoc}
36 */
37 public function extract(string|iterable $resource, MessageCatalogue $catalog)
38 {
39 $files = $this->extractFiles($resource);
40 foreach ($files as $file) {
41 $this->parseTokens(\token_get_all(\file_get_contents($file)), $catalog, $file);
42 \gc_mem_caches();
43 }
44 }
45 /**
46 * {@inheritdoc}
47 */
48 public function setPrefix(string $prefix)
49 {
50 $this->prefix = $prefix;
51 }
52 /**
53 * Normalizes a token.
54 */
55 protected function normalizeToken(mixed $token) : ?string
56 {
57 if (isset($token[1]) && 'b"' !== $token) {
58 return $token[1];
59 }
60 return $token;
61 }
62 /**
63 * Seeks to a non-whitespace token.
64 */
65 private function seekToNextRelevantToken(\Iterator $tokenIterator)
66 {
67 for (; $tokenIterator->valid(); $tokenIterator->next()) {
68 $t = $tokenIterator->current();
69 if (\T_WHITESPACE !== $t[0]) {
70 break;
71 }
72 }
73 }
74 private function skipMethodArgument(\Iterator $tokenIterator)
75 {
76 $openBraces = 0;
77 for (; $tokenIterator->valid(); $tokenIterator->next()) {
78 $t = $tokenIterator->current();
79 if ('[' === $t[0] || '(' === $t[0]) {
80 ++$openBraces;
81 }
82 if (']' === $t[0] || ')' === $t[0]) {
83 --$openBraces;
84 }
85 if (0 === $openBraces && ',' === $t[0] || -1 === $openBraces && ')' === $t[0]) {
86 break;
87 }
88 }
89 }
90 /**
91 * Extracts the message from the iterator while the tokens
92 * match allowed message tokens.
93 */
94 private function getValue(\Iterator $tokenIterator)
95 {
96 $message = '';
97 $docToken = '';
98 $docPart = '';
99 for (; $tokenIterator->valid(); $tokenIterator->next()) {
100 $t = $tokenIterator->current();
101 if ('.' === $t) {
102 // Concatenate with next token
103 continue;
104 }
105 if (!isset($t[1])) {
106 break;
107 }
108 switch ($t[0]) {
109 case \T_START_HEREDOC:
110 $docToken = $t[1];
111 break;
112 case \T_ENCAPSED_AND_WHITESPACE:
113 case \T_CONSTANT_ENCAPSED_STRING:
114 if ('' === $docToken) {
115 $message .= PhpStringTokenParser::parse($t[1]);
116 } else {
117 $docPart = $t[1];
118 }
119 break;
120 case \T_END_HEREDOC:
121 if ($indentation = \strspn($t[1], ' ')) {
122 $docPartWithLineBreaks = $docPart;
123 $docPart = '';
124 foreach (\preg_split('~(\\r\\n|\\n|\\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) {
125 if (\in_array($str, ["\r\n", "\n", "\r"], \true)) {
126 $docPart .= $str;
127 } else {
128 $docPart .= \substr($str, $indentation);
129 }
130 }
131 }
132 $message .= PhpStringTokenParser::parseDocString($docToken, $docPart);
133 $docToken = '';
134 $docPart = '';
135 break;
136 case \T_WHITESPACE:
137 break;
138 default:
139 break 2;
140 }
141 }
142 return $message;
143 }
144 /**
145 * Extracts trans message from PHP tokens.
146 */
147 protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename)
148 {
149 $tokenIterator = new \ArrayIterator($tokens);
150 for ($key = 0; $key < $tokenIterator->count(); ++$key) {
151 foreach ($this->sequences as $sequence) {
152 $message = '';
153 $domain = 'messages';
154 $tokenIterator->seek($key);
155 foreach ($sequence as $sequenceKey => $item) {
156 $this->seekToNextRelevantToken($tokenIterator);
157 if ($this->normalizeToken($tokenIterator->current()) === $item) {
158 $tokenIterator->next();
159 continue;
160 } elseif (self::MESSAGE_TOKEN === $item) {
161 $message = $this->getValue($tokenIterator);
162 if (\count($sequence) === $sequenceKey + 1) {
163 break;
164 }
165 } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
166 $this->skipMethodArgument($tokenIterator);
167 } elseif (self::DOMAIN_TOKEN === $item) {
168 $domainToken = $this->getValue($tokenIterator);
169 if ('' !== $domainToken) {
170 $domain = $domainToken;
171 }
172 break;
173 } else {
174 break;
175 }
176 }
177 if ($message) {
178 $catalog->set($message, $this->prefix . $message, $domain);
179 $metadata = $catalog->getMetadata($message, $domain) ?? [];
180 $normalizedFilename = \preg_replace('{[\\\\/]+}', '/', $filename);
181 $metadata['sources'][] = $normalizedFilename . ':' . $tokens[$key][2];
182 $catalog->setMetadata($message, $metadata, $domain);
183 break;
184 }
185 }
186 }
187 }
188 /**
189 * @throws \InvalidArgumentException
190 */
191 protected function canBeExtracted(string $file) : bool
192 {
193 return $this->isFile($file) && 'php' === \pathinfo($file, \PATHINFO_EXTENSION);
194 }
195 /**
196 * {@inheritdoc}
197 */
198 protected function extractFromDirectory(string|array $directory) : iterable
199 {
200 if (!\class_exists(Finder::class)) {
201 throw new \LogicException(\sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
202 }
203 $finder = new Finder();
204 return $finder->files()->name('*.php')->in($directory);
205 }
206 }
207