Completed
Push — master ( 6c8b2f...a31f38 )
by Tobias
16:10
created

src/FileExtractor/TwigFileExtractor.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\FileExtractor;
13
14
use Symfony\Component\Finder\SplFileInfo;
15
use Translation\Extractor\Model\SourceCollection;
16
use Translation\Extractor\Visitor\Visitor;
17
18
/**
19
 * @author Tobias Nyholm <[email protected]>
20
 */
21
final class TwigFileExtractor extends \Twig_Extension implements FileExtractor
22
{
23
    /**
24
     * @var Visitor[]|\Twig_NodeVisitorInterface[]
25
     */
26
    private $visitors = [];
27
28
    /**
29
     * @var \Twig_Environment
30
     */
31
    private $twig;
32
33
    /**
34
     * @param \Twig_Environment $twig
35
     */
36 6
    public function __construct(\Twig_Environment $twig)
37
    {
38 6
        $this->twig = $twig;
39 6
        $twig->addExtension($this);
40 6
    }
41
42 6
    public function getSourceLocations(SplFileInfo $file, SourceCollection $collection)
43
    {
44 6
        foreach ($this->visitors as $v) {
45 6
            $v->init($collection, $file);
0 ignored issues
show
The method init does only exist in Translation\Extractor\Visitor\Visitor, but not in Twig_NodeVisitorInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
46
        }
47
48 6
        $path = $file->getRelativePath();
49
50 6
        if (class_exists('Twig_Source')) {
51
            // Twig 2.0
52 6
            $stream = $this->twig->tokenize(new \Twig_Source($file->getContents(), $file->getRelativePathname(), $path));
53
        } else {
54
            $stream = $this->twig->tokenize($file->getContents(), $path);
0 ignored issues
show
$file->getContents() is of type string, but the function expects a object<Twig_Source>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
The call to Twig_Environment::tokenize() has too many arguments starting with $path.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
55
        }
56 6
        $this->twig->parse($stream);
57 6
    }
58
59 1
    public function getType()
60
    {
61 1
        return 'twig';
62
    }
63
64
    /**
65
     * @param \Twig_NodeVisitorInterface $visitor
66
     */
67 6
    public function addVisitor(\Twig_NodeVisitorInterface $visitor)
68
    {
69 6
        $this->visitors[] = $visitor;
70 6
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75 6
    public function getNodeVisitors()
76
    {
77 6
        return $this->visitors;
0 ignored issues
show
The expression return $this->visitors; seems to be an array, but some of its elements' types (Translation\Extractor\Visitor\Visitor) are incompatible with the return type declared by the interface Twig_ExtensionInterface::getNodeVisitors of type Twig_NodeVisitorInterface[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
78
    }
79
80
    public function getName()
81
    {
82
        return 'php.translation';
83
    }
84
}
85