Import latest app updates from streamline

An updated set of source files and initialization was provided by streamline to
address issues observed during initial testing. These files have been updated in
order to generate a new set of app images.
This commit is contained in:
2024-04-11 11:16:51 -07:00
parent cb4c6b4c1a
commit 4a89356390
10435 changed files with 107737 additions and 1220538 deletions
@@ -1,67 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\Exception\InvalidArgumentException;
/**
* Base class used by classes that extract translation messages from files.
*
* @author Marcos D. Sánchez <marcosdsanchez@gmail.com>
*/
abstract class AbstractFileExtractor
{
protected function extractFiles(string|iterable $resource): iterable
{
if (is_iterable($resource)) {
$files = [];
foreach ($resource as $file) {
if ($this->canBeExtracted($file)) {
$files[] = $this->toSplFileInfo($file);
}
}
} elseif (is_file($resource)) {
$files = $this->canBeExtracted($resource) ? [$this->toSplFileInfo($resource)] : [];
} else {
$files = $this->extractFromDirectory($resource);
}
return $files;
}
private function toSplFileInfo(string $file): \SplFileInfo
{
return new \SplFileInfo($file);
}
/**
* @throws InvalidArgumentException
*/
protected function isFile(string $file): bool
{
if (!is_file($file)) {
throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file));
}
return true;
}
/**
* @return bool
*/
abstract protected function canBeExtracted(string $file);
/**
* @return iterable
*/
abstract protected function extractFromDirectory(string|array $resource);
}
@@ -1,59 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\MessageCatalogue;
/**
* ChainExtractor extracts translation messages from template files.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
class ChainExtractor implements ExtractorInterface
{
/**
* The extractors.
*
* @var ExtractorInterface[]
*/
private array $extractors = [];
/**
* Adds a loader to the translation extractor.
*
* @return void
*/
public function addExtractor(string $format, ExtractorInterface $extractor)
{
$this->extractors[$format] = $extractor;
}
/**
* @return void
*/
public function setPrefix(string $prefix)
{
foreach ($this->extractors as $extractor) {
$extractor->setPrefix($prefix);
}
}
/**
* @return void
*/
public function extract(string|iterable $directory, MessageCatalogue $catalogue)
{
foreach ($this->extractors as $extractor) {
$extractor->extract($directory, $catalogue);
}
}
}
@@ -1,39 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
use Symfony\Component\Translation\MessageCatalogue;
/**
* Extracts translation messages from a directory or files to the catalogue.
* New found messages are injected to the catalogue using the prefix.
*
* @author Michel Salib <michelsalib@hotmail.com>
*/
interface ExtractorInterface
{
/**
* Extracts translation messages from files, a file or a directory to the catalogue.
*
* @param string|iterable<string> $resource Files, a file or a directory
*
* @return void
*/
public function extract(string|iterable $resource, MessageCatalogue $catalogue);
/**
* Sets the prefix that should be used for new found messages.
*
* @return void
*/
public function setPrefix(string $prefix);
}
@@ -1,85 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor;
use Symfony\Component\Translation\MessageCatalogue;
/**
* PhpAstExtractor extracts translation messages from a PHP AST.
*
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
final class PhpAstExtractor extends AbstractFileExtractor implements ExtractorInterface
{
private Parser $parser;
public function __construct(
/**
* @param iterable<AbstractVisitor&NodeVisitor> $visitors
*/
private readonly iterable $visitors,
private string $prefix = '',
) {
if (!class_exists(ParserFactory::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the "nikic/php-parser" package is not installed. Try running "composer require nikic/php-parser".', static::class));
}
$this->parser = (new ParserFactory())->createForHostVersion();
}
public function extract(iterable|string $resource, MessageCatalogue $catalogue): void
{
foreach ($this->extractFiles($resource) as $file) {
$traverser = new NodeTraverser();
// This is needed to resolve namespaces in class methods/constants.
$nameResolver = new NodeVisitor\NameResolver();
$traverser->addVisitor($nameResolver);
/** @var AbstractVisitor&NodeVisitor $visitor */
foreach ($this->visitors as $visitor) {
$visitor->initialize($catalogue, $file, $this->prefix);
$traverser->addVisitor($visitor);
}
$nodes = $this->parser->parse(file_get_contents($file));
$traverser->traverse($nodes);
}
}
public function setPrefix(string $prefix): void
{
$this->prefix = $prefix;
}
protected function canBeExtracted(string $file): bool
{
return 'php' === pathinfo($file, \PATHINFO_EXTENSION)
&& $this->isFile($file)
&& preg_match('/\bt\(|->trans\(|TranslatableMessage|Symfony\\\\Component\\\\Validator\\\\Constraints/i', file_get_contents($file));
}
protected function extractFromDirectory(array|string $resource): iterable|Finder
{
if (!class_exists(Finder::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
}
return (new Finder())->files()->name('*.php')->in($resource);
}
}
@@ -1,333 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated, use "%s" instead.', PhpExtractor::class, PhpAstExtractor::class);
use Symfony\Component\Finder\Finder;
use Symfony\Component\Translation\MessageCatalogue;
/**
* PhpExtractor extracts translation messages from a PHP template.
*
* @author Michel Salib <michelsalib@hotmail.com>
*
* @deprecated since Symfony 6.2, use the PhpAstExtractor instead
*/
class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface
{
public const MESSAGE_TOKEN = 300;
public const METHOD_ARGUMENTS_TOKEN = 1000;
public const DOMAIN_TOKEN = 1001;
/**
* Prefix for new found message.
*/
private string $prefix = '';
/**
* The sequence that captures translation messages.
*/
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',
'\Symfony\Component\Translation\TranslatableMessage',
'(',
self::MESSAGE_TOKEN,
',',
self::METHOD_ARGUMENTS_TOKEN,
',',
self::DOMAIN_TOKEN,
],
[
'new',
'\\',
'Symfony',
'\\',
'Component',
'\\',
'Translation',
'\\',
'TranslatableMessage',
'(',
self::MESSAGE_TOKEN,
],
[
'new',
'\Symfony\Component\Translation\TranslatableMessage',
'(',
self::MESSAGE_TOKEN,
],
[
't',
'(',
self::MESSAGE_TOKEN,
',',
self::METHOD_ARGUMENTS_TOKEN,
',',
self::DOMAIN_TOKEN,
],
[
't',
'(',
self::MESSAGE_TOKEN,
],
];
/**
* @return void
*/
public function extract(string|iterable $resource, MessageCatalogue $catalog)
{
$files = $this->extractFiles($resource);
foreach ($files as $file) {
$this->parseTokens(token_get_all(file_get_contents($file)), $catalog, $file);
gc_mem_caches();
}
}
/**
* @return void
*/
public function setPrefix(string $prefix)
{
$this->prefix = $prefix;
}
/**
* Normalizes a token.
*/
protected function normalizeToken(mixed $token): ?string
{
if (isset($token[1]) && 'b"' !== $token) {
return $token[1];
}
return $token;
}
/**
* Seeks to a non-whitespace token.
*/
private function seekToNextRelevantToken(\Iterator $tokenIterator): void
{
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if (\T_WHITESPACE !== $t[0]) {
break;
}
}
}
private function skipMethodArgument(\Iterator $tokenIterator): void
{
$openBraces = 0;
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if ('[' === $t[0] || '(' === $t[0]) {
++$openBraces;
}
if (']' === $t[0] || ')' === $t[0]) {
--$openBraces;
}
if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) {
break;
}
}
}
/**
* Extracts the message from the iterator while the tokens
* match allowed message tokens.
*/
private function getValue(\Iterator $tokenIterator): string
{
$message = '';
$docToken = '';
$docPart = '';
for (; $tokenIterator->valid(); $tokenIterator->next()) {
$t = $tokenIterator->current();
if ('.' === $t) {
// Concatenate with next token
continue;
}
if (!isset($t[1])) {
break;
}
switch ($t[0]) {
case \T_START_HEREDOC:
$docToken = $t[1];
break;
case \T_ENCAPSED_AND_WHITESPACE:
case \T_CONSTANT_ENCAPSED_STRING:
if ('' === $docToken) {
$message .= PhpStringTokenParser::parse($t[1]);
} else {
$docPart = $t[1];
}
break;
case \T_END_HEREDOC:
if ($indentation = strspn($t[1], ' ')) {
$docPartWithLineBreaks = $docPart;
$docPart = '';
foreach (preg_split('~(\r\n|\n|\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) {
if (\in_array($str, ["\r\n", "\n", "\r"], true)) {
$docPart .= $str;
} else {
$docPart .= substr($str, $indentation);
}
}
}
$message .= PhpStringTokenParser::parseDocString($docToken, $docPart);
$docToken = '';
$docPart = '';
break;
case \T_WHITESPACE:
break;
default:
break 2;
}
}
return $message;
}
/**
* Extracts trans message from PHP tokens.
*
* @return void
*/
protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename)
{
$tokenIterator = new \ArrayIterator($tokens);
for ($key = 0; $key < $tokenIterator->count(); ++$key) {
foreach ($this->sequences as $sequence) {
$message = '';
$domain = 'messages';
$tokenIterator->seek($key);
foreach ($sequence as $sequenceKey => $item) {
$this->seekToNextRelevantToken($tokenIterator);
if ($this->normalizeToken($tokenIterator->current()) === $item) {
$tokenIterator->next();
continue;
} elseif (self::MESSAGE_TOKEN === $item) {
$message = $this->getValue($tokenIterator);
if (\count($sequence) === ($sequenceKey + 1)) {
break;
}
} elseif (self::METHOD_ARGUMENTS_TOKEN === $item) {
$this->skipMethodArgument($tokenIterator);
} elseif (self::DOMAIN_TOKEN === $item) {
$domainToken = $this->getValue($tokenIterator);
if ('' !== $domainToken) {
$domain = $domainToken;
}
break;
} else {
break;
}
}
if ($message) {
$catalog->set($message, $this->prefix.$message, $domain);
$metadata = $catalog->getMetadata($message, $domain) ?? [];
$normalizedFilename = preg_replace('{[\\\\/]+}', '/', $filename);
$metadata['sources'][] = $normalizedFilename.':'.$tokens[$key][2];
$catalog->setMetadata($message, $metadata, $domain);
break;
}
}
}
}
/**
* @throws \InvalidArgumentException
*/
protected function canBeExtracted(string $file): bool
{
return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION);
}
protected function extractFromDirectory(string|array $directory): iterable
{
if (!class_exists(Finder::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class));
}
$finder = new Finder();
return $finder->files()->name('*.php')->in($directory);
}
}
@@ -1,141 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor;
trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated.', PhpStringTokenParser::class);
/*
* The following is derived from code at http://github.com/nikic/PHP-Parser
*
* Copyright (c) 2011 by Nikita Popov
*
* Some rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * The names of the contributors may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @deprecated since Symfony 6.2
*/
class PhpStringTokenParser
{
protected static $replacements = [
'\\' => '\\',
'$' => '$',
'n' => "\n",
'r' => "\r",
't' => "\t",
'f' => "\f",
'v' => "\v",
'e' => "\x1B",
];
/**
* Parses a string token.
*
* @param string $str String token content
*/
public static function parse(string $str): string
{
$bLength = 0;
if ('b' === $str[0]) {
$bLength = 1;
}
if ('\'' === $str[$bLength]) {
return str_replace(
['\\\\', '\\\''],
['\\', '\''],
substr($str, $bLength + 1, -1)
);
} else {
return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"');
}
}
/**
* Parses escape sequences in strings (all string types apart from single quoted).
*
* @param string $str String without quotes
* @param string|null $quote Quote type
*/
public static function parseEscapeSequences(string $str, ?string $quote = null): string
{
if (null !== $quote) {
$str = str_replace('\\'.$quote, $quote, $str);
}
return preg_replace_callback(
'~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~',
[__CLASS__, 'parseCallback'],
$str
);
}
private static function parseCallback(array $matches): string
{
$str = $matches[1];
if (isset(self::$replacements[$str])) {
return self::$replacements[$str];
} elseif ('x' === $str[0] || 'X' === $str[0]) {
return \chr(hexdec($str));
} else {
return \chr(octdec($str));
}
}
/**
* Parses a constant doc string.
*
* @param string $startToken Doc string start token content (<<<SMTHG)
* @param string $str String token content
*/
public static function parseDocString(string $startToken, string $str): string
{
// strip last newline (thanks tokenizer for sticking it into the string!)
$str = preg_replace('~(\r\n|\n|\r)$~', '', $str);
// nowdoc string
if (str_contains($startToken, '\'')) {
return $str;
}
return self::parseEscapeSequences($str, null);
}
}
@@ -1,135 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor\Visitor;
use PhpParser\Node;
use Symfony\Component\Translation\MessageCatalogue;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
abstract class AbstractVisitor
{
private MessageCatalogue $catalogue;
private \SplFileInfo $file;
private string $messagePrefix;
public function initialize(MessageCatalogue $catalogue, \SplFileInfo $file, string $messagePrefix): void
{
$this->catalogue = $catalogue;
$this->file = $file;
$this->messagePrefix = $messagePrefix;
}
protected function addMessageToCatalogue(string $message, ?string $domain, int $line): void
{
$domain ??= 'messages';
$this->catalogue->set($message, $this->messagePrefix.$message, $domain);
$metadata = $this->catalogue->getMetadata($message, $domain) ?? [];
$normalizedFilename = preg_replace('{[\\\\/]+}', '/', $this->file);
$metadata['sources'][] = $normalizedFilename.':'.$line;
$this->catalogue->setMetadata($message, $metadata, $domain);
}
protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node, int|string $index, bool $indexIsRegex = false): array
{
if (\is_string($index)) {
return $this->getStringNamedArguments($node, $index, $indexIsRegex);
}
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
if (!($arg = $args[$index] ?? null) instanceof Node\Arg) {
return [];
}
return (array) $this->getStringValue($arg->value);
}
protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): bool
{
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
foreach ($args as $arg) {
if ($arg instanceof Node\Arg && null !== $arg->name) {
return true;
}
}
return false;
}
protected function nodeFirstNamedArgumentIndex(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): int
{
$args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
foreach ($args as $i => $arg) {
if ($arg instanceof Node\Arg && null !== $arg->name) {
return $i;
}
}
return \PHP_INT_MAX;
}
private function getStringNamedArguments(Node\Expr\CallLike|Node\Attribute $node, ?string $argumentName = null, bool $isArgumentNamePattern = false): array
{
$args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args;
$argumentValues = [];
foreach ($args as $arg) {
if (!$isArgumentNamePattern && $arg->name?->toString() === $argumentName) {
$argumentValues[] = $this->getStringValue($arg->value);
} elseif ($isArgumentNamePattern && preg_match($argumentName, $arg->name?->toString() ?? '') > 0) {
$argumentValues[] = $this->getStringValue($arg->value);
}
}
return array_filter($argumentValues);
}
private function getStringValue(Node $node): ?string
{
if ($node instanceof Node\Scalar\String_) {
return $node->value;
}
if ($node instanceof Node\Expr\BinaryOp\Concat) {
if (null === $left = $this->getStringValue($node->left)) {
return null;
}
if (null === $right = $this->getStringValue($node->right)) {
return null;
}
return $left.$right;
}
if ($node instanceof Node\Expr\Assign && $node->expr instanceof Node\Scalar\String_) {
return $node->expr->value;
}
if ($node instanceof Node\Expr\ClassConstFetch) {
try {
$reflection = new \ReflectionClass($node->class->toString());
$constant = $reflection->getReflectionConstant($node->name->toString());
if (false !== $constant && \is_string($constant->getValue())) {
return $constant->getValue();
}
} catch (\ReflectionException) {
}
}
return null;
}
}
@@ -1,112 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor\Visitor;
use PhpParser\Node;
use PhpParser\NodeVisitor;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*
* Code mostly comes from https://github.com/php-translation/extractor/blob/master/src/Visitor/Php/Symfony/Constraint.php
*/
final class ConstraintVisitor extends AbstractVisitor implements NodeVisitor
{
public function __construct(
private readonly array $constraintClassNames = []
) {
}
public function beforeTraverse(array $nodes): ?Node
{
return null;
}
public function enterNode(Node $node): ?Node
{
return null;
}
public function leaveNode(Node $node): ?Node
{
if (!$node instanceof Node\Expr\New_ && !$node instanceof Node\Attribute) {
return null;
}
$className = $node instanceof Node\Attribute ? $node->name : $node->class;
if (!$className instanceof Node\Name) {
return null;
}
$parts = $className->getParts();
$isConstraintClass = false;
foreach ($parts as $part) {
if (\in_array($part, $this->constraintClassNames, true)) {
$isConstraintClass = true;
break;
}
}
if (!$isConstraintClass) {
return null;
}
$arg = $node->args[0] ?? null;
if (!$arg instanceof Node\Arg) {
return null;
}
if ($this->hasNodeNamedArguments($node)) {
$messages = $this->getStringArguments($node, '/message/i', true);
} else {
if (!$arg->value instanceof Node\Expr\Array_) {
// There is no way to guess which argument is a message to be translated.
return null;
}
$messages = [];
$options = $arg->value;
/** @var Node\Expr\ArrayItem $item */
foreach ($options->items as $item) {
if (!$item->key instanceof Node\Scalar\String_) {
continue;
}
if (false === stripos($item->key->value ?? '', 'message')) {
continue;
}
if (!$item->value instanceof Node\Scalar\String_) {
continue;
}
$messages[] = $item->value->value;
break;
}
}
foreach ($messages as $message) {
$this->addMessageToCatalogue($message, 'validators', $node->getStartLine());
}
return null;
}
public function afterTraverse(array $nodes): ?Node
{
return null;
}
}
@@ -1,65 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor\Visitor;
use PhpParser\Node;
use PhpParser\NodeVisitor;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
final class TransMethodVisitor extends AbstractVisitor implements NodeVisitor
{
public function beforeTraverse(array $nodes): ?Node
{
return null;
}
public function enterNode(Node $node): ?Node
{
return null;
}
public function leaveNode(Node $node): ?Node
{
if (!$node instanceof Node\Expr\MethodCall && !$node instanceof Node\Expr\FuncCall) {
return null;
}
if (!\is_string($node->name) && !$node->name instanceof Node\Identifier && !$node->name instanceof Node\Name) {
return null;
}
$name = $node->name instanceof Node\Name ? $node->name->getLast() : (string) $node->name;
if ('trans' === $name || 't' === $name) {
$firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node);
if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) {
return null;
}
$domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null;
foreach ($messages as $message) {
$this->addMessageToCatalogue($message, $domain, $node->getStartLine());
}
}
return null;
}
public function afterTraverse(array $nodes): ?Node
{
return null;
}
}
@@ -1,65 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Translation\Extractor\Visitor;
use PhpParser\Node;
use PhpParser\NodeVisitor;
/**
* @author Mathieu Santostefano <msantostefano@protonmail.com>
*/
final class TranslatableMessageVisitor extends AbstractVisitor implements NodeVisitor
{
public function beforeTraverse(array $nodes): ?Node
{
return null;
}
public function enterNode(Node $node): ?Node
{
return null;
}
public function leaveNode(Node $node): ?Node
{
if (!$node instanceof Node\Expr\New_) {
return null;
}
if (!($className = $node->class) instanceof Node\Name) {
return null;
}
if (!\in_array('TranslatableMessage', $className->getParts(), true)) {
return null;
}
$firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node);
if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) {
return null;
}
$domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null;
foreach ($messages as $message) {
$this->addMessageToCatalogue($message, $domain, $node->getStartLine());
}
return null;
}
public function afterTraverse(array $nodes): ?Node
{
return null;
}
}