Completed
Pull Request — master (#126)
by Phil
19:36 queued 04:37
created

Definition::resolve()   D

Complexity

Conditions 10
Paths 19

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 17
nc 19
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace League\Container\Definition;
4
5
use League\Container\Argument\{
6
    ArgumentResolverInterface, ArgumentResolverTrait, ClassNameInterface, RawArgumentInterface
7
};
8
use League\Container\ContainerAwareTrait;
9
use ReflectionClass;
10
11
class Definition implements ArgumentResolverInterface, DefinitionInterface
12
{
13
    use ArgumentResolverTrait;
14
    use ContainerAwareTrait;
15
16
    /**
17
     * @var string
18
     */
19
    protected $alias;
20
21
    /**
22
     * @var mixed
23
     */
24
    protected $concrete;
25
26
    /**
27
     * @var boolean
28
     */
29
    protected $shared = false;
30
31
    /**
32
     * @var array
33
     */
34
    protected $arguments = [];
35
36
    /**
37
     * @var array
38
     */
39
    protected $methods = [];
40
41
    /**
42
     * @var mixed
43
     */
44
    protected $resolved;
45
46
    /**
47
     * Constructor.
48
     *
49
     * @param string $id
50
     * @param mixed  $concrete
51
     */
52
    public function __construct(string $id, $concrete)
53
    {
54
        $this->alias    = $id;
55
        $this->concrete = $concrete;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function setAlias(string $id): DefinitionInterface
62
    {
63
        $this->alias = $id;
64
65
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...tionInterface::setAlias of type self.

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...
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function getAlias(): string
72
    {
73
        return $this->alias;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function setShared(bool $shared): DefinitionInterface
80
    {
81
        $this->shared = $shared;
82
83
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...ionInterface::setShared of type self.

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...
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function isShared(): bool
90
    {
91
        return $this->shared;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function addArgument($arg): DefinitionInterface
98
    {
99
        $this->arguments[] = $arg;
100
101
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...nInterface::addArgument of type self.

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...
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function addArguments(array $args): DefinitionInterface
108
    {
109
        foreach ($args as $arg) {
110
            $this->addArgument($arg);
111
        }
112
113
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...Interface::addArguments of type self.

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...
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function addMethodCall(string $method, array $args = []): DefinitionInterface
120
    {
121
        $this->methods[] = [
122
            'method'    => $method,
123
            'arguments' => $args
124
        ];
125
126
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...nterface::addMethodCall of type self.

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...
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function addMethodCalls(array $methods = []): DefinitionInterface
133
    {
134
        foreach ($methods as $method => $args) {
135
            $this->addMethodCall($method, $args);
136
        }
137
138
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (League\Container\Definition\Definition) is incompatible with the return type declared by the interface League\Container\Definit...terface::addMethodCalls of type self.

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...
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function resolve(array $args = [], bool $new = false)
145
    {
146
        $concrete = $this->concrete;
147
148
        if ($this->isShared() && ! is_null($this->resolved) && $new === false) {
149
            return $this->resolved;
150
        }
151
152
        if (is_callable($concrete)) {
153
            $concrete = $this->resolveCallable($concrete, $args);
154
        }
155
156
        if ($concrete instanceof RawArgumentInterface) {
157
            $this->resolved = $concrete->getValue();
158
159
            return $concrete->getValue();
160
        }
161
162
        if ($concrete instanceof ClassNameInterface) {
163
            $concrete = $concrete->getValue();
164
        }
165
166
        if (is_string($concrete) && class_exists($concrete)) {
167
            $concrete = $this->resolveClass($concrete, $args);
168
        }
169
170
        if (is_object($concrete)) {
171
            $concrete = $this->invokeMethods($concrete);
172
        }
173
174
        $this->resolved = $concrete;
175
176
        return $concrete;
177
    }
178
179
    /**
180
     * Resolve a callable.
181
     *
182
     * @param callable $concrete
183
     * @param array    $args
184
     *
185
     * @return mixed
186
     */
187
    protected function resolveCallable(callable $concrete, array $args = [])
188
    {
189
        $args     = (empty($args)) ? $this->arguments : $args;
190
        $resolved = $this->resolveArguments($args);
191
192
        return call_user_func_array($concrete, $resolved);
193
    }
194
195
    /**
196
     * Resolve a class.
197
     *
198
     * @param string $concrete
199
     * @param array  $args
200
     *
201
     * @return object
202
     */
203
    protected function resolveClass(string $concrete, array $args = [])
204
    {
205
        $args       = (empty($args)) ? $this->arguments : $args;
206
        $resolved   = $this->resolveArguments($args);
207
        $reflection = new ReflectionClass($concrete);
208
209
        return $reflection->newInstanceArgs($resolved);
210
    }
211
212
    /**
213
     * Invoke methods on resolved instance.
214
     *
215
     * @param object $instance
216
     *
217
     * @return object
218
     */
219
    protected function invokeMethods($instance)
220
    {
221
        foreach ($this->methods as $method) {
222
            $args = $this->resolveArguments($method['arguments']);
223
            call_user_func_array([$instance, $method['method']], $args);
224
        }
225
226
        return $instance;
227
    }
228
}
229