Reader::hasDefinition()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * This program is free software. It comes without any warranty, to
4
 * the extent permitted by applicable law. You can redistribute it
5
 * and/or modify it under the terms of the Do What The Fuck You Want
6
 * To Public License, Version 2, as published by Sam Hocevar. See
7
 * http://www.wtfpl.net/ for more details.
8
 */
9
10
declare(strict_types = 1);
11
12
namespace hanneskod\classtools\Transformer;
13
14
use hanneskod\classtools\Exception\RuntimeException;
15
use hanneskod\classtools\Exception\ReaderException;
16
use hanneskod\classtools\Name;
17
use PhpParser\Lexer\Emulative;
18
use PhpParser\Node\Stmt\Class_;
19
use PhpParser\Node\Stmt\Interface_;
20
use PhpParser\Node\Stmt\Namespace_;
21
use PhpParser\Node\Stmt\Trait_;
22
use PhpParser\Node\Stmt\Use_;
23
use PhpParser\Parser;
24
use PhpParser\ParserFactory;
25
26
/**
27
 * Read classes, interfaces and traits from php snippets
28
 *
29
 * @author Hannes Forsgård <[email protected]>
30
 */
31
class Reader
32
{
33
    /**
34
     * @var Namespace_[] Collection of definitions in snippet
35
     */
36
    private $defs = [];
37
38
    /**
39
     * @var string[] Case sensitive definition names
40
     */
41
    private $names = [];
42
43
    /**
44
     * @var \PhpParser\Node[] The global statement object
45
     */
46
    private $global;
47
48
    /**
49
     * Optionally inject parser
50
     *
51
     * @throws ReaderException If snippet contains a syntax error
52
     */
53
    public function __construct(string $snippet, Parser $parser = null)
54
    {
55
        if (is_null($parser)) {
56
            $parserFactory = new ParserFactory();
57
            $parser = $parserFactory->create(ParserFactory::PREFER_PHP5);
58
        }
59
60
        try {
61
            $this->global = $parser->parse($snippet);
0 ignored issues
show
Documentation Bug introduced by
It seems like $parser->parse($snippet) can be null. However, the property $global is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
62
        } catch (\PhpParser\Error $exception) {
63
            throw new ReaderException($exception->getRawMessage() . ' on line ' . $exception->getStartLine());
64
        }
65
66
        $this->findDefinitions($this->global, new Name(''));
0 ignored issues
show
Bug introduced by
It seems like $this->global can also be of type null; however, hanneskod\classtools\Tra...ader::findDefinitions() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
67
    }
68
69
    /**
70
     * Find class, interface and trait definitions in statemnts
71
     */
72
    private function findDefinitions(array $stmts, Name $namespace): void
73
    {
74
        $useStmts = [];
75
76
        foreach ($stmts as $stmt) {
77
            // Restart if namespace statement is found
78
            if ($stmt instanceof Namespace_) {
79
                $this->findDefinitions($stmt->stmts, new Name((string)$stmt->name));
80
81
                // Save use statement
82
            } elseif ($stmt instanceof Use_) {
83
                $useStmts[] = $stmt;
84
85
                // Save classes, interfaces and traits
86
            } elseif ($stmt instanceof Class_ or $stmt instanceof Interface_ or $stmt instanceof Trait_) {
87
                $defName = new Name("{$namespace}\\{$stmt->name}");
88
                $this->names[$defName->keyize()] = $defName->normalize();
89
                $this->defs[$defName->keyize()] = new Namespace_(
90
                    $namespace->normalize() ? $namespace->createNode() : null,
91
                    $useStmts
92
                );
93
                $this->defs[$defName->keyize()]->stmts[] = $stmt;
94
            }
95
        }
96
    }
97
98
    /**
99
     * Get names of definitions in snippet
100
     *
101
     * @return string[]
102
     */
103
    public function getDefinitionNames(): array
104
    {
105
        return array_values($this->names);
106
    }
107
108
    /**
109
     * Check if snippet contains definition
110
     */
111
    public function hasDefinition(string $name): bool
112
    {
113
        return isset($this->defs[(new Name($name))->keyize()]);
114
    }
115
116
    /**
117
     * Get pars tree for class/interface/trait
118
     *
119
     * @return Namespace_[]
120
     * @throws RuntimeException If $name does not exist
121
     */
122
    public function read(string $name): array
123
    {
124
        if (!$this->hasDefinition($name)) {
125
            throw new RuntimeException("Unable to read <$name>, not found.");
126
        }
127
128
        return [$this->defs[(new Name($name))->keyize()]];
129
    }
130
131
    /**
132
     * Get parse tree for the complete snippet
133
     *
134
     * @return \PhpParser\Node[]
135
     */
136
    public function readAll(): array
137
    {
138
        return $this->global;
139
    }
140
}
141