Skip to content

Commit

Permalink
ValidationContext extractor
Browse files Browse the repository at this point in the history
  • Loading branch information
artursvonda committed Apr 11, 2021
1 parent e609c5b commit d8f545c
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 0 deletions.
26 changes: 26 additions & 0 deletions Tests/Translation/Extractor/File/Fixture/MyEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace JMS\TranslationBundle\Tests\Translation\Extractor\File\Fixture;

use Symfony\Component\Validator\Context\ExecutionContext;

/**
* Class MyEntity
*/
class MyEntity
{
public function validateConstraintWithDefaultDomain(ExecutionContext $context)
{
$context
->buildViolation('entity.default')
->addViolation();
}

public function validateConstraintWithCustomDomain(ExecutionContext $context)
{
$context
->buildViolation('entity.custom-domain')
->setTranslationDomain('custom-domain')
->addViolation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace JMS\TranslationBundle\Tests\Translation\Extractor\File;

use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Model\MessageCatalogue;
use JMS\TranslationBundle\Translation\Extractor\File\ValidationContextExtractor;

class ValidationContextExtractorTest extends BasePhpFileExtractorTest
{
public function testExtractValidationMessages()
{
$fileSourceFactory = $this->getFileSourceFactory();
$fixtureSplInfo = new \SplFileInfo(__DIR__.'/Fixture/MyEntity.php');


$expected = new MessageCatalogue();

$message = new Message('entity.default');
$message->addSource($fileSourceFactory->create($fixtureSplInfo, 15));
$expected->add($message);

$message = new Message('entity.custom-domain', 'custom-domain');
$message->addSource($fileSourceFactory->create($fixtureSplInfo, 22));
$expected->add($message);

$this->assertEquals($expected, $this->extract('MyEntity.php'));
}

protected function getDefaultExtractor()
{
return new ValidationContextExtractor($this->getFileSourceFactory());
}
}
256 changes: 256 additions & 0 deletions Translation/Extractor/File/ValidationContextExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
<?php

/*
* Copyright 2016 Arturs Vonda <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace JMS\TranslationBundle\Translation\Extractor\File;

use JMS\TranslationBundle\Model\FileSource;
use JMS\TranslationBundle\Model\Message;
use JMS\TranslationBundle\Model\MessageCatalogue;
use JMS\TranslationBundle\Model\SourceInterface;
use JMS\TranslationBundle\Translation\Extractor\FileVisitorInterface;
use JMS\TranslationBundle\Translation\FileSourceFactory;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor;
use SplFileInfo;

/**
* Class ValidationContextExtractor
*
* Extracts
*/
class ValidationContextExtractor implements FileVisitorInterface, NodeVisitor
{
/**
* @var NodeTraverser
*/
private $traverser;
/**
* @var array
*/
private $messages = [];
/**
* @var MessageCatalogue
*/
private $catalogue;
/**
* @var SplFileInfo
*/
private $file;
/**
* @var array
*/
private $aliases = [];
/**
* @var string
*/
private $contextVariable;
/**
* @var string|null
*/
private $domain;
/**
* @var string|null
*/
private $id;
/**
* @var FileSource|null
*/
private $source;
private $fileSourceFactory;

/**
* ValidationContextExtractor constructor.
*
* @param FileSourceFactory $fileSourceFactory
*/
public function __construct(FileSourceFactory $fileSourceFactory)
{
$this->fileSourceFactory = $fileSourceFactory;
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor($this);
}

/**
* {@inheritdoc}
*/
public function visitFile(SplFileInfo $file, MessageCatalogue $catalogue)
{
}

/**
* {@inheritdoc}
*/
public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, array $ast)
{
$this->file = $file;
$this->catalogue = $catalogue;
$this->messages = [];
$this->traverser->traverse($ast);

foreach ($this->messages as $message) {
$this->addToCatalogue($message['id'], $message['source'], $message['domain']);
}
}

/**
* {@inheritdoc}
*/
public function visitTwigFile(SplFileInfo $file, MessageCatalogue $catalogue, \Twig_Node $ast)
{
}

/**
* {@inheritdoc}
*/
public function beforeTraverse(array $nodes)
{
}

/**
* {@inheritdoc}
*/
public function enterNode(Node $node)
{
if ($node instanceof Node\Stmt\Namespace_) {
$this->aliases = [];

return;
}

if ($node instanceof Node\Stmt\Use_) {
foreach ($node->uses as $use) {
$this->aliases[$use->alias] = (string) $use->name;
}

return;
}

if ($node instanceof Node\Stmt\ClassMethod) {
$params = $node->getParams();
if (!count($params)) {
return;
}
$param1 = $params[0];
$paramClass = $this->resolveAlias((string) $param1->type);
if (is_subclass_of($paramClass, '\Symfony\Component\Validator\Context\ExecutionContextInterface')) {
$this->contextVariable = $param1->name;
}

return;
}

if ($node instanceof Node\Expr\MethodCall) {
$this->parseMethodCall($node);
}
}

/**
* @param Node\Expr\MethodCall $node
*/
private function parseMethodCall(Node\Expr\MethodCall $node)
{
if (!$this->contextVariable) {
return;
}

if ($node->var instanceof Node\Expr\MethodCall) {
$this->parseMethodCall($node->var);
}

if ($node->name === 'buildViolation') {
$this->id = null;
$this->domain = null;

if ($node->args) {
$arg1 = $node->args[0];
if ($arg1->value instanceof Node\Scalar\String_) {
$this->id = $arg1->value->value;
$this->source = $this->fileSourceFactory->create($this->file, $arg1->value->getLine());
}
}
} elseif ($node->name === 'setTranslationDomain') {
if ($node->args) {
$arg1 = $node->args[0];
if ($arg1->value instanceof Node\Scalar\String_) {
$this->domain = $arg1->value->value;
}
}
} elseif ($node->name === 'addViolation') {
if ($this->id and $this->source) {
$this->messages[] = [
'id' => $this->id,
'source' => $this->source,
'domain' => $this->domain,
];
}

$this->id = null;
$this->domain = null;
$this->source = null;
}
}

/**
* {@inheritdoc}
*/
public function leaveNode(Node $node)
{
if ($node instanceof Node\Stmt\ClassMethod) {
$this->contextVariable = null;
$this->domain = null;
$this->id = null;
$this->source = null;
}
}

/**
* {@inheritdoc}
*/
public function afterTraverse(array $nodes)
{
}

/**
* @param string $id
* @param SourceInterface $source
* @param string|null $domain
*/
private function addToCatalogue($id, SourceInterface $source, $domain = null)
{
if (null === $domain) {
$message = new Message($id);
} else {
$message = new Message($id, $domain);
}

$message->addSource($source);

$this->catalogue->add($message);
}

/**
* @param $class
*
* @return string
*/
private function resolveAlias($class)
{
return isset($this->aliases[$class]) ? $this->aliases[$class] : $class;
}
}

0 comments on commit d8f545c

Please sign in to comment.