Completed
Push — master ( 31b6cd...280baa )
by Дмитрий
07:47
created

ClassDefinition::compile()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 6
Bugs 1 Features 0
Metric Value
c 6
b 1
f 0
dl 0
loc 40
ccs 0
cts 31
cp 0
rs 6.7273
cc 7
eloc 24
nc 10
nop 1
crap 56
1
<?php
2
/**
3
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace PHPSA\Definition;
7
8
use PHPSA\CompiledExpression;
9
use PHPSA\Context;
10
use PhpParser\Node;
11
use PHPSA\Variable;
12
13
/**
14
 * Class ClassDefinition
15
 * @package PHPSA\Definition
16
 */
17
class ClassDefinition extends ParentDefinition
18
{
19
    /**
20
     * @var int
21
     */
22
    protected $type;
23
24
    /**
25
     * Class methods
26
     *
27
     * @var ClassMethod[]
28
     */
29
    protected $methods = array();
30
31
    /**
32
     * Class properties
33
     *
34
     * @var Node\Stmt\Property[]
35
     */
36
    protected $properties = array();
37
38
    /**
39
     * Class constants
40
     *
41
     * @var Node\Stmt\Const_[]
42
     */
43
    protected $constants = array();
44
45
    /**
46
     * @todo Use Finder
47
     *
48
     * @var string
49
     */
50
    protected $filepath;
51
52
    /**
53
     * @var string|null
54
     */
55
    protected $extendsClass;
56
57
    /**
58
     * @var ClassDefinition|null
59
     */
60
    protected $extendsClassDefinition;
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $extendsClassDefinition exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
61
62
    /**
63
     * @var array
64
     */
65
    protected $interfaces = array();
66
67
    /**
68
     * @param string $name
69
     * @param integer $type
70
     */
71 370
    public function __construct($name, $type)
72
    {
73 370
        $this->name = $name;
74 370
        $this->type = $type;
75 370
    }
76
77
    /**
78
     * @param ClassMethod $methodDefintion
79
     */
80 1
    public function addMethod(ClassMethod $methodDefintion)
81
    {
82 1
        $this->methods[$methodDefintion->getName()] = $methodDefintion;
83 1
    }
84
85
    /**
86
     * @param Node\Stmt\Property $property
87
     */
88 1
    public function addProperty(Node\Stmt\Property $property)
89
    {
90 1
        $this->properties[$property->props[0]->name] = $property;
91 1
    }
92
93
    /**
94
     * @param Node\Stmt\ClassConst $const
95
     */
96
    public function addConst(Node\Stmt\ClassConst $const)
97
    {
98
        $this->constants[$const->consts[0]->name] = $const;
99
    }
100
101
    /**
102
     * @param Context $context
103
     * @return $this
104
     */
105
    public function compile(Context $context)
106
    {
107
        if ($this->compiled) {
108
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type documented by PHPSA\Definition\ClassDefinition::compile of type PHPSA\Definition\ClassDefinition.

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 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('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...
109
        }
110
111
        $this->compiled = true;
112
        $context->setFilepath($this->filepath);
113
        $context->setScope($this);
114
115
        foreach ($this->methods as $method) {
116
            $context->clearSymbols();
117
118
            if (!$method->isStatic()) {
119
                $thisPtr = new Variable('this', $this, CompiledExpression::OBJECT);
120
                $thisPtr->incGets();
121
                $context->addVariable($thisPtr);
122
            }
123
124
            $method->compile($context);
125
126
            $symbols = $context->getSymbols();
127
            if (count($symbols) > 0) {
128
                foreach ($symbols as $name => $variable) {
129
                    if ($variable->isUnused()) {
130
                        $context->warning(
131
                            'unused-' . $variable->getSymbolType(),
132
                            sprintf(
133
                                'Unused ' . $variable->getSymbolType() . ' $%s in method %s()',
134
                                $variable->getName(),
135
                                $method->getName()
136
                            )
137
                        );
138
                    }
139
                }
140
            }
141
        }
142
143
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (PHPSA\Definition\ClassDefinition) is incompatible with the return type declared by the abstract method PHPSA\Definition\AbstractDefinition::compile of type boolean.

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 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('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...
144
    }
145
146
    /**
147
     * @param string $name
148
     * @param boolean|false $inherit
149
     * @return bool
150
     */
151 1
    public function hasMethod($name, $inherit = false)
152
    {
153 1
        if (isset($this->methods[$name])) {
154 1
            return true;
155
        }
156
157 1
        if ($inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->hasMethod($name, true)) {
158
            $method = $this->extendsClassDefinition->getMethod($name);
159
            return $method->isPublic() || $method->isProtected();
160
        }
161
162 1
        return false;
163
    }
164
165
    /**
166
     * @param $name
167
     * @return bool
168
     */
169
    public function hasConst($name)
170
    {
171
        return isset($this->constants[$name]);
172
    }
173
174
    /**
175
     * @param $name
176
     * @param boolean|false $inherit
177
     * @return ClassMethod
178
     */
179 1
    public function getMethod($name, $inherit = false)
180
    {
181 1
        if (isset($this->methods[$name])) {
182 1
            return $this->methods[$name];
183
        }
184
185
        return $inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->getMethod($name, true);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $inherit && $this...getMethod($name, true); (boolean) is incompatible with the return type documented by PHPSA\Definition\ClassDefinition::getMethod of type PHPSA\Definition\ClassMethod.

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 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('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...
186
    }
187
188
    /**
189
     * @param $name
190
     * @param bool $inherit
191
     * @return bool
192
     */
193 1
    public function hasProperty($name, $inherit = false)
194
    {
195 1
        if (isset($this->properties[$name])) {
196 1
            return isset($this->properties[$name]);
197
        }
198
199 1
        return $inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->hasProperty($name, true);
200
    }
201
202
    /**
203
     * @param $name
204
     * @param bool $inherit
205
     * @return Node\Stmt\Property
206
     */
207
    public function getProperty($name, $inherit = false)
208
    {
209
        assert($this->hasProperty($name, $inherit));
210
211
        if (isset($this->properties[$name])) {
212
            return $this->properties[$name];
213
        }
214
215
        return $inherit && $this->extendsClassDefinition && $this->extendsClassDefinition->getProperty($name, true);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $inherit && $this...tProperty($name, true); (boolean) is incompatible with the return type documented by PHPSA\Definition\ClassDefinition::getProperty of type PhpParser\Node\Stmt\Property.

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 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('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...
216
    }
217
218
    /**
219
     * @return string
220
     */
221
    public function getFilepath()
222
    {
223
        return $this->filepath;
224
    }
225
226
    /**
227
     * @param string $filepath
228
     */
229
    public function setFilepath($filepath)
230
    {
231
        $this->filepath = $filepath;
232
    }
233
234
    /**
235
     * @return bool
236
     */
237
    public function isAbstract()
238
    {
239
        return (bool) ($this->type & Node\Stmt\Class_::MODIFIER_ABSTRACT);
240
    }
241
242
    /**
243
     * @param null|string $extendsClass
244
     */
245
    public function setExtendsClass($extendsClass)
246
    {
247
        $this->extendsClass = $extendsClass;
248
    }
249
250
    /**
251
     * @return null|ClassDefinition
252
     */
253
    public function getExtendsClassDefinition()
254
    {
255
        return $this->extendsClassDefinition;
256
    }
257
258
    /**
259
     * @param ClassDefinition $extendsClassDefinition
260
     */
261
    public function setExtendsClassDefinition(ClassDefinition $extendsClassDefinition)
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $extendsClassDefinition exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
262
    {
263
        $this->extendsClassDefinition = $extendsClassDefinition;
264
    }
265
266
    /**
267
     * @param array $interface
268
     */
269
    public function addInterface($interface)
270
    {
271
        $this->interfaces[] = $interface;
272
    }
273
274
    /**
275
     * @return null|string
276
     */
277
    public function getExtendsClass()
278
    {
279
        return $this->extendsClass;
280
    }
281
}
282