AnnotationTrait::getStartLine()
last analyzed

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 1
nc 1
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 22.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/**
4
 * Codeburner Framework.
5
 *
6
 * @author Alex Rohleder <[email protected]>
7
 * @copyright 2016 Alex Rohleder
8
 * @license http://opensource.org/licenses/MIT
9
 */
10
11
namespace Codeburner\Annotator\Reflection;
12
13
use Codeburner\Annotator\Annotation;
14
use Codeburner\Annotator\Exceptions\WildcardNotAllowedException;
15
16
/**
17
 * Avoid the autoload by manually including the required files.
18
 * This bust significantly the performance.
19
 */
20
21
if (!class_exists('Codeburner\Annotator\Annotation', false)) {
22
	include __DIR__ . '/../Annotation.php';
23
}
24
25
/**
26
 * Implements the annotation methods into a reflection.
27
 *
28
 * @author Alex Rohleder <[email protected]>
29
 */
30
31
trait AnnotationTrait
32
{
33
34
	abstract public function getDocComment();
35
    abstract public function getNamespaceName();
36
    abstract public function getStartLine();
37
38
    /**
39
     * @var string
40
     */
41
42
    private $useStatementsCache;
43
44
    /**
45
     * Get an annotation by it literal name.
46
     * 
47
     * @param  Annotation|null
48
     * @throws MultipleAnnotationException
49
     * @return string
50
     */
51
52
    public function getAnnotation($name)
53
    {
54
        $annotations = $this->getMatchedAnnotations($name);
55
56
        if (empty($annotations)) {
57
            return null;
58
        }
59
60
        if (count($annotations) > 1) {
61
            throw new WildcardNotAllowedException("getAnnotation");
62
        }
63
64
        return $this->getAnnotationObject($annotations[0]);
0 ignored issues
show
Documentation introduced by
$annotations[0] is of type string, but the function expects a array<integer,string>.

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...
65
    }
66
67
    /**
68
     * Get all annotations, or several of then. Note that here annotations can
69
     * have regex on they name.
70
     *
71
     * @param string[] $names if empty will return all annotations. Can have regex.
72
     * @return array ["AnnotationName" => Annotation]
73
     */
74
75
    public function getAnnotations(array $names = array())
76
    {
77
        if (empty($names)) {
78
               $annotations = $this->getMatchedAnnotations("[\w]+");
79
        } else $annotations = $this->getMatchedAnnotations(implode("|", $names));
80
81
        $bucket = [];
82
83
        foreach ($annotations as $annotation) {
84
            $bucket[$annotation[1]] = $this->getAnnotationObject($annotation);
0 ignored issues
show
Documentation introduced by
$annotation is of type string, but the function expects a array<integer,string>.

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...
85
        }
86
87
        return $bucket;
88
    }
89
    
90
    /**
91
     * Check if an annotation exists
92
     *
93
     * @param string $name
94
     * @return bool
95
     */
96
97
    public function hasAnnotation($name)
98
    {
99
        return (bool) preg_match("~@" . $this->getAnnotationName($name) . "~", $this->getDocComment());
100
    }
101
102
    /**
103
     * Find all annotations that the name match the $pattern
104
     *
105
     * @param string $pattern
106
     * @return string[]
107
     */
108
109
    protected function getMatchedAnnotations($pattern)
110
    {
111
        preg_match_all("~@(" . $this->getAnnotationName($pattern) . ")(\s(-f\s?)?(.*))?~i", $this->getDocComment(), $annotations, PREG_SET_ORDER);
112
        return (array) $annotations;
113
    }
114
115
    /**
116
     * Instantiate a new Annotation object, if the annotation has a specific object
117
     * then resolve the name and create it.
118
     *
119
     * @param string[] $annotation The getMatchedAnnotation annotation return.
120
     * @return Annotation
121
     */
122
123
    protected function getAnnotationObject($annotation)
124
    {
125
        $name = $annotation[1];
126
        $value = $annotation[4];
127
        $flag = $annotation[3];
128
129
        if (trim($flag) === "-f") {
130
            $uses = $this->getUseStatements();
131
132
            if (!isset($uses[$name])) {
133
                   $class = $this->getNamespaceName() . "\\$name";
134
                   return new $class($name, $value);
135
            } else return new $uses[$name]($name, $value);
136
        }
137
138
        return new Annotation($name, $value);
139
    }
140
141
    /**
142
     * Get all use statements from the annotated reflection file.
143
     *
144
     * @return string[] [ALIAS_NAME => FULL_CLASS_NAME];
145
     */
146
147
    protected function getUseStatements()
148
    {
149
        if ($this->useStatementsCache) {
150
            return $this->useStatementsCache;
151
        }
152
153
        preg_match_all("~use\s([\w\\\\]+)(\sas\s(\w+))?;~i", $this->getClassFileHeader(), $matches, PREG_SET_ORDER);
154
155
        foreach ((array) $matches as $match) {
156
            // if an alias exists.
157
            if (isset($match[3])) {
158
                $this->useStatementsCache[$match[3]] = $match[1];
159
            }
160
161
            $this->useStatementsCache[$match[1]] = $match[1];
162
        }
163
164
        return $this->useStatementsCache;
165
    }
166
167
    /**
168
     * Get a fraction of the annotated reflection file.
169
     *
170
     * @return string
171
     */
172
173
    private function getClassFileHeader()
174
    {
175
        $file   = fopen($this->getFileName(), "r");
0 ignored issues
show
Bug introduced by
It seems like getFileName() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
176
        $until  = $this->getStartLine();
177
        $line   = 0;
178
        $source = "";
179
180
        while ($line < $until) {
181
            $source .= fgets($file);
182
            ++$line;
183
        }
184
185
        fclose($file);
186
        return $source;
187
    }
188
189
    /**
190
     * Resolve annotation name.
191
     *
192
     * @param string $name
193
     * @return string
194
     */
195
196
    private function getAnnotationName($name)
197
    {
198
        // if it is a pattern like [\w]+
199
        if ($name[0] === "[") {
200
            return $name;
201
        }
202
203
        return str_replace("\\", "\\\\", $name);
204
    }
205
206
}
207