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

TypeHintSystem::verify()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 10.9144

Importance

Changes 0
Metric Value
cc 8
nc 8
nop 2
dl 0
loc 22
ccs 9
cts 14
cp 0.6429
crap 10.9144
rs 8.4444
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\ProvidesTypeIndication;
15
use Railt\Reflection\Contracts\Definition\Dependent\ArgumentDefinition;
16
use Railt\Reflection\Contracts\Definition\Dependent\FieldDefinition;
17
use Railt\Reflection\Contracts\Definition\Dependent\InputFieldDefinition;
18
use Railt\Reflection\Contracts\Definition\TypeDefinition;
19
use Railt\Reflection\Definition\Behaviour\HasTypeIndication;
20
use Railt\SDL\Ast\ProvidesTypeHint;
21
use Railt\SDL\Exception\TypeConflictException;
22
23
/**
24
 * Class TypeHintSystem
25
 */
26
class TypeHintSystem extends System
27
{
28
    /**
29
     * @var int
30
     */
31
    private const IS_RENDERABLE = 0x01;
32
33
    /**
34
     * @var int
35
     */
36
    private const IS_INPUTABLE = 0x02;
37
38
    /**
39
     * @var int[]
40
     */
41
    private const TYPE_BEHAVIOURS = [
42
        FieldDefinition::class      => self::IS_RENDERABLE,
43
        ArgumentDefinition::class   => self::IS_INPUTABLE,
44
        InputFieldDefinition::class => self::IS_INPUTABLE,
45
    ];
46
47
    /**
48
     * @param Definition|HasTypeIndication|TypeDefinition $definition
49
     * @param RuleInterface $ast
50
     */
51 119
    public function resolve(Definition $definition, RuleInterface $ast): void
52
    {
53 119
        if ($definition instanceof ProvidesTypeIndication && $ast instanceof ProvidesTypeHint) {
54 6
            $hint = $ast->getTypeHintNode();
55
56 6
            $definition->withModifiers($hint->getModifiers());
57
58 6
            $this->linker(function () use ($hint, $definition) {
59
                /** @var TypeDefinition $resolved */
60 6
                $resolved = $this->get($hint->getFullName(), $definition);
61
62 6
                $this->verify($definition, $resolved);
63 6
            });
64
        }
65 119
    }
66
67
    /**
68
     * @param TypeDefinition $context
69
     * @param TypeDefinition $hint
70
     * @throws \Railt\SDL\Exception\CompilerException
71
     */
72 6
    private function verify(TypeDefinition $context, TypeDefinition $hint): void
73
    {
74 6
        foreach (self::TYPE_BEHAVIOURS as $type => $behaviour) {
75 6
            if ($context instanceof $type) {
76 6
                $error = null;
77
78
                switch ($behaviour) {
79 6
                    case self::IS_RENDERABLE && ! $hint->isRenderable():
80
                        $error = '%s "%s" can contain only renderable type (unions, objects, etc), but %s given';
81
                        break;
82 6
                    case self::IS_INPUTABLE && ! $hint->isInputable():
83
                        $error = '%s "%s" can contain only inputable type (inputs, scalars, etc), but %s given';
84
                        break;
85
                }
86
87 6
                if ($error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
88
                    $error = \sprintf($error, $context::getType(), $context->getName(), $hint);
89 6
                    throw (new TypeConflictException($error))->in($context);
90
                }
91
            }
92
        }
93 6
    }
94
}
95