Completed
Push — master ( c1a156...8cadde )
by Kirill
08:14
created

ImplementationSystem::resolve()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 0
Metric Value
cc 6
nc 3
nop 2
dl 0
loc 18
ccs 10
cts 12
cp 0.8333
crap 6.1666
rs 9.0444
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Compiler\System;
11
12
use Railt\Parser\Ast\RuleInterface;
13
use Railt\Reflection\Contracts\Definition;
14
use Railt\Reflection\Contracts\Definition\Behaviour\ProvidesInterfaces;
15
use Railt\Reflection\Contracts\Definition\InterfaceDefinition;
16
use Railt\Reflection\Contracts\Definition\ObjectDefinition;
17
use Railt\Reflection\Contracts\Definition\TypeDefinition;
18
use Railt\Reflection\Definition\Behaviour\HasInterfaces;
19
use Railt\SDL\Ast\Common\TypeNameNode;
20
use Railt\SDL\Ast\ProvidesInterfaceNodes;
21
use Railt\SDL\Exception\CompilerException;
22
use Railt\SDL\Exception\TypeConflictException;
23
24
/**
25
 * Class ImplementationSystem
26
 */
27
class ImplementationSystem extends System
28
{
29
    /**
30
     * @param Definition|ProvidesInterfaces|HasInterfaces $parent
31
     * @param RuleInterface|ProvidesInterfaceNodes $ast
32
     */
33 119
    public function resolve(Definition $parent, RuleInterface $ast): void
34
    {
35 119
        if ($parent instanceof ProvidesInterfaces && $ast instanceof ProvidesInterfaceNodes) {
36 30
            $this->inference(function() use ($ast, $parent) {
37 30
                foreach ($ast->getInterfaceNodes() as $node) {
38
                    $this->implement($parent, $node);
39
                }
40 30
            });
41
42 30
            if ($parent instanceof ObjectDefinition) {
43 15
                $this->inference(function () use ($parent) {
44 15
                    foreach ($parent->getInterfaces() as $interface) {
45
                        $this->verifyFieldsInheritance($parent, $interface);
0 ignored issues
show
Unused Code introduced by
The call to the method Railt\SDL\Compiler\Syste...rifyFieldsInheritance() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
46
                    }
47 15
                });
48
            }
49
        }
50 119
    }
51
52
    /**
53
     * @param ProvidesInterfaces|HasInterfaces|TypeDefinition $parent
54
     * @param TypeNameNode $ast
55
     * @throws \Railt\Io\Exception\ExternalFileException
56
     * @throws \Railt\Reflection\Exception\TypeNotFoundException
57
     */
58
    private function implement(ProvidesInterfaces $parent, TypeNameNode $ast): void
59
    {
60
        /** @var InterfaceDefinition $interface */
61
        $interface = $this->get($ast->getFullName(), $parent);
0 ignored issues
show
Documentation introduced by
$parent is of type object<Railt\Reflection\...our\ProvidesInterfaces>, but the function expects a object<Railt\Reflection\Contracts\Definition>.

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...
62
63
        try {
64
            $this->verifyIsInterface($parent, $interface);
0 ignored issues
show
Documentation introduced by
$parent is of type object<Railt\Reflection\...our\ProvidesInterfaces>, but the function expects a object<Railt\Reflection\Contracts\Definition>.

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
            $this->verifyInterfaceDuplication($parent, $interface);
66
67
            $parent->withInterface($interface);
68
69
            $this->verifySelfReferenceImplementation($parent, $interface);
70
        } catch (CompilerException $e) {
71
            throw $e->throwsIn($parent->getFile(), $ast->getOffset());
72
        }
73
    }
74
75
    /**
76
     * @param ObjectDefinition $object
77
     * @param InterfaceDefinition $interface
78
     */
79
    private function verifyFieldsInheritance(ObjectDefinition $object, InterfaceDefinition $interface): void
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $interface is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
80
    {
81
        // TODO
82
    }
83
84
    /**
85
     * @param Definition $parent
86
     * @param TypeDefinition $definition
87
     * @throws TypeConflictException
88
     */
89
    private function verifyIsInterface(Definition $parent, TypeDefinition $definition): void
90
    {
91
        if (! $definition instanceof InterfaceDefinition) {
92
            $error = '%s can implement only interfaces, but %s given';
93
            throw new TypeConflictException(\sprintf($error, $parent, $definition));
94
        }
95
    }
96
97
    /**
98
     * @param ProvidesInterfaces $parent
99
     * @param TypeDefinition $interface
100
     * @throws TypeConflictException
101
     */
102
    private function verifyInterfaceDuplication(ProvidesInterfaces $parent, TypeDefinition $interface): void
103
    {
104
        if ($parent->isImplements($interface)) {
105
            $error = \sprintf('Can not implement the same interface %s twice', $interface);
106
107
            throw new TypeConflictException($error);
108
        }
109
    }
110
111
    /**
112
     * @param ProvidesInterfaces $parent
113
     * @param TypeDefinition $interface
114
     * @throws TypeConflictException
115
     */
116
    private function verifySelfReferenceImplementation(ProvidesInterfaces $parent, TypeDefinition $interface): void
117
    {
118
        if ($parent->isImplements($parent)) {
0 ignored issues
show
Documentation introduced by
$parent is of type object<Railt\Reflection\...our\ProvidesInterfaces>, but the function expects a string|object<Railt\Refl...inition\TypeDefinition>.

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...
119
            $error = 'Can not implement the interface %s by %s, ' .
120
                'because it contains a reference to the already implemented type %s';
121
            $error = \sprintf($error, $interface, $parent, $parent);
122
123
            throw new TypeConflictException($error);
124
        }
125
    }
126
}
127