Skip to content

Commit c953860

Browse files
authored
Support for errors (#31)
* Add support for errors and support for ignore annotation * cs * Removed PHP7 Code * Removed PHP7 Code
1 parent b529c88 commit c953860

16 files changed

+300
-23
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"php": "^5.5 || ^7.0",
1313
"nikic/php-parser": "^3.0",
1414
"symfony/finder": "^2.7 || ^3.0",
15-
"twig/twig": "^1.27 || ^2.0"
15+
"twig/twig": "^1.27 || ^2.0",
16+
"doctrine/annotations": "^1.2"
1617
},
1718
"require-dev": {
1819
"phpunit/phpunit": "^4.5 || ^5.4",

phpunit.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
stopOnFailure="false"
1111
syntaxCheck="false"
1212
codecoverage="true"
13-
bootstrap="./vendor/autoload.php"
13+
bootstrap="./tests/bootstrap.php"
1414
>
1515

1616
<formatter type="clover" usefile="false"/>

src/Annotation/Ignore.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PHP Translation package.
5+
*
6+
* (c) PHP Translation team <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Translation\Extractor\Annotation;
13+
14+
/**
15+
* @Annotation
16+
*/
17+
final class Ignore
18+
{
19+
}

src/Model/Error.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the PHP Translation package.
5+
*
6+
* (c) PHP Translation team <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Translation\Extractor\Model;
13+
14+
/**
15+
* An error with the source code that occured when extracting.
16+
*
17+
* @author Tobias Nyholm <[email protected]>
18+
*/
19+
final class Error
20+
{
21+
/**
22+
* @var string
23+
*/
24+
private $message;
25+
26+
/**
27+
* @var string
28+
*/
29+
private $path;
30+
31+
/**
32+
* @var int
33+
*/
34+
private $line;
35+
36+
/**
37+
* @param string $message
38+
* @param string $path
39+
* @param int $line
40+
*/
41+
public function __construct($message, $path, $line)
42+
{
43+
$this->message = $message;
44+
$this->path = (string) $path;
45+
$this->line = $line;
46+
}
47+
48+
/**
49+
* @return string
50+
*/
51+
public function getMessage()
52+
{
53+
return $this->message;
54+
}
55+
56+
/**
57+
* @return string
58+
*/
59+
public function getPath()
60+
{
61+
return $this->path;
62+
}
63+
64+
/**
65+
* @return int
66+
*/
67+
public function getLine()
68+
{
69+
return $this->line;
70+
}
71+
}

src/Model/SourceCollection.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ final class SourceCollection implements \Countable, \IteratorAggregate
2121
*/
2222
private $sourceLocations = [];
2323

24+
/**
25+
* @var Error[]
26+
*/
27+
private $errors = [];
28+
2429
public function getIterator()
2530
{
2631
return new \ArrayIterator($this->sourceLocations);
@@ -39,6 +44,14 @@ public function addLocation(SourceLocation $location)
3944
$this->sourceLocations[] = $location;
4045
}
4146

47+
/**
48+
* @param Error $error
49+
*/
50+
public function addError(Error $error)
51+
{
52+
$this->errors[] = $error;
53+
}
54+
4255
/**
4356
* @return SourceLocation|null
4457
*/
@@ -64,4 +77,9 @@ public function get($key)
6477

6578
return $this->sourceLocations[$key];
6679
}
80+
81+
public function getErrors()
82+
{
83+
return $this->errors;
84+
}
6785
}

src/Visitor/BaseVisitor.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111

1212
namespace Translation\Extractor\Visitor;
1313

14+
use Doctrine\Common\Annotations\DocParser;
15+
use PhpParser\Node;
1416
use Symfony\Component\Finder\SplFileInfo;
17+
use Translation\Extractor\Annotation\Ignore;
18+
use Translation\Extractor\Model\Error;
1519
use Translation\Extractor\Model\SourceCollection;
1620

1721
/**
@@ -31,6 +35,11 @@ abstract class BaseVisitor implements Visitor
3135
*/
3236
protected $file;
3337

38+
/**
39+
* @var DocParser
40+
*/
41+
private $docParser;
42+
3443
public function init(SourceCollection $collection, SplFileInfo $file)
3544
{
3645
$this->collection = $collection;
@@ -41,4 +50,55 @@ protected function getAbsoluteFilePath()
4150
{
4251
return $this->file->getRealPath();
4352
}
53+
54+
/**
55+
* @param Node $node
56+
* @param string $errorMessage
57+
*/
58+
protected function addError(Node $node, $errorMessage)
59+
{
60+
$docComment = $node->getDocComment();
61+
$file = $this->getAbsoluteFilePath();
62+
63+
if (property_exists($node, 'value')) {
64+
$line = $node->value->getAttribute('startLine');
65+
} else {
66+
$line = $node->getAttribute('startLine');
67+
}
68+
if (null !== $docComment) {
69+
$context = 'file '.$file.' near line '.$line;
70+
foreach ($this->getDocParser()->parse($docComment->getText(), $context) as $annotation) {
71+
if ($annotation instanceof Ignore) {
72+
return;
73+
}
74+
}
75+
}
76+
77+
$this->collection->addError(new Error($errorMessage, $file, $line));
78+
}
79+
80+
/**
81+
* @return DocParser
82+
*/
83+
private function getDocParser()
84+
{
85+
if (null === $this->docParser) {
86+
$this->docParser = new DocParser();
87+
88+
$this->docParser->setImports([
89+
'ignore' => Ignore::class,
90+
]);
91+
$this->docParser->setIgnoreNotImportedAnnotations(true);
92+
}
93+
94+
return $this->docParser;
95+
}
96+
97+
/**
98+
* @param DocParser $docParser
99+
*/
100+
public function setDocParser(DocParser $docParser)
101+
{
102+
$this->docParser = $docParser;
103+
}
44104
}

src/Visitor/Php/Symfony/FormTypeChoices.php

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,30 +70,32 @@ public function enterNode(Node $node)
7070
continue;
7171
}
7272

73-
if (!$item->value instanceof Node\Expr\Array_) {
73+
$choicesNodes[] = $item->value;
74+
}
75+
76+
if (count($choicesNodes) === 0) {
77+
return;
78+
}
79+
80+
// probably will be only 1, but who knows
81+
foreach ($choicesNodes as $choices) {
82+
// TODO: do something with grouped (multi-dimensional) arrays here
83+
if (!$choices instanceof Node\Expr\Array_) {
84+
$this->addError($choices, 'Form choice is not an array');
85+
7486
continue;
7587
}
7688

77-
$choicesNodes[] = $item->value;
78-
}
89+
foreach ($choices->items as $citem) {
90+
$labelNode = $useKey ? $citem->key : $citem->value;
91+
if (!$labelNode instanceof Node\Scalar\String_) {
92+
$this->addError($citem, 'Choice label is not a scalar string');
7993

80-
if (count($choicesNodes) > 0) {
81-
// probably will be only 1, but who knows
82-
foreach ($choicesNodes as $choices) {
83-
// TODO: do something with grouped (multi-dimensional) arrays here
84-
if (!$choices instanceof Node\Expr\Array_) {
8594
continue;
8695
}
8796

88-
foreach ($choices->items as $citem) {
89-
$labelNode = $useKey ? $citem->key : $citem->value;
90-
if (!$labelNode instanceof Node\Scalar\String_) {
91-
continue;
92-
}
93-
94-
$sl = new SourceLocation($labelNode->value, $this->getAbsoluteFilePath(), $choices->getAttribute('startLine'));
95-
$this->collection->addLocation($sl);
96-
}
97+
$sl = new SourceLocation($labelNode->value, $this->getAbsoluteFilePath(), $choices->getAttribute('startLine'));
98+
$this->collection->addLocation($sl);
9799
}
98100
}
99101
}

src/Visitor/Php/Symfony/FormTypeLabelExplicit.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public function enterNode(Node $node)
5151
}
5252

5353
if (!$item->value instanceof Node\Scalar\String_) {
54+
$this->addError($item, 'Form label is not a scalar string');
55+
5456
continue;
5557
}
5658

src/Visitor/Php/Symfony/FormTypePlaceholder.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,14 @@ public function enterNode(Node $node)
4141
continue;
4242
}
4343

44-
if ($item->key->value === 'placeholder' && $item->value instanceof Node\Scalar\String_) {
45-
$this->collection->addLocation(new SourceLocation($item->value->value, $this->getAbsoluteFilePath(), $item->value->getAttribute('startLine')));
44+
if ($item->key->value === 'placeholder') {
45+
if ($item->value instanceof Node\Scalar\String_) {
46+
$path = $this->getAbsoluteFilePath();
47+
$line = $item->value->getAttribute('startLine');
48+
$this->collection->addLocation(new SourceLocation($item->value->value, $path, $line));
49+
} else {
50+
$this->addError($item, 'Form placeholder is not a scalar string');
51+
}
4652
}
4753
}
4854
}

tests/Functional/Visitor/Php/Symfony/FormTypeChoicesTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,12 @@ public function testChainedChoice()
5353
$this->assertEquals('label1', $collection->get(0)->getMessage());
5454
$this->assertEquals('label2', $collection->get(1)->getMessage());
5555
}
56+
57+
public function testExtractError()
58+
{
59+
$collection = $this->getSourceLocations(new FormTypeChoices(), Resources\Php\Symfony\SimpleChoiceSymfony3xErrorType::class);
60+
61+
$errors = $collection->getErrors();
62+
$this->assertCount(1, $errors);
63+
}
5664
}

0 commit comments

Comments
 (0)