Completed
Push — master ( 12eab9...33e72a )
by Kirill
36:26
created

ScalarValidator   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 90
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 2
dl 0
loc 90
ccs 29
cts 29
cp 1
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A match() 0 5 2
A validate() 0 8 2
A validateScalarCompatibility() 0 7 2
A validateDirectScalarCompatibility() 0 8 2
A throwScalarIncompatibilityException() 0 11 1
A validateInverseScalarCompatibility() 0 8 2
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\Reflection\Validation\Inheritance;
11
12
use Railt\SDL\Contracts\Behavior\AllowsTypeIndication;
13
use Railt\SDL\Contracts\Definitions\ScalarDefinition;
14
use Railt\SDL\Contracts\Definitions\TypeDefinition;
15
use Railt\SDL\Exceptions\TypeConflictException;
16
17
/**
18
 * Class ScalarValidator
19
 */
20
class ScalarValidator extends BaseInheritanceValidator
21
{
22
    /**
23
     * @param AllowsTypeIndication|TypeDefinition $child
24
     * @param AllowsTypeIndication|TypeDefinition $parent
25
     * @return bool
26
     */
27 813
    public function match(TypeDefinition $child, TypeDefinition $parent): bool
28
    {
29 813
        return $parent instanceof AllowsTypeIndication &&
30 813
            $parent->getTypeDefinition() instanceof ScalarDefinition;
31
    }
32
33
    /**
34
     * @param AllowsTypeIndication|TypeDefinition $child
35
     * @param AllowsTypeIndication|TypeDefinition $parent
36
     * @return void
37
     * @throws TypeConflictException
38
     */
39 795
    public function validate(TypeDefinition $child, TypeDefinition $parent): void
40
    {
41 795
        if ($child->getTypeDefinition() instanceof ScalarDefinition) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
42 793
            $this->validateScalarCompatibility($child, $parent);
43
        } else {
44 2
            $this->throwIncompatibleTypes($child, $parent);
45
        }
46 523
    }
47
48
    /**
49
     * @param AllowsTypeIndication|TypeDefinition $child
50
     * @param AllowsTypeIndication|TypeDefinition $parent
51
     * @return void
52
     * @throws TypeConflictException
53
     */
54 793
    private function validateScalarCompatibility(TypeDefinition $child, TypeDefinition $parent): void
55
    {
56 793
        $this->isPostCondition($parent)
0 ignored issues
show
Documentation introduced by
$parent is of type object<Railt\SDL\Contrac...nitions\TypeDefinition>, but the function expects a object<Railt\SDL\Contrac...r\AllowsTypeIndication>.

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...
57 793
            ? $this->validateDirectScalarCompatibility($child, $parent)
58 350
            : $this->validateInverseScalarCompatibility($child, $parent)
59
        ;
60 523
    }
61
62
    /**
63
     * @param AllowsTypeIndication|TypeDefinition $child
64
     * @param AllowsTypeIndication|TypeDefinition $parent
65
     * @return void
66
     * @throws TypeConflictException
67
     */
68 793
    private function validateDirectScalarCompatibility(TypeDefinition $child, TypeDefinition $parent): void
69
    {
70 793
        $parentType = $parent->getTypeDefinition();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
71
72 793
        if (! ($child->getTypeDefinition() instanceof $parentType)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
73 270
            $this->throwScalarIncompatibilityException($child, $parent);
74
        }
75 523
    }
76
77
    /**
78
     * @param AllowsTypeIndication|TypeDefinition $child
79
     * @param AllowsTypeIndication|TypeDefinition $parent
80
     * @return void
81
     * @throws TypeConflictException
82
     */
83 350
    private function validateInverseScalarCompatibility(TypeDefinition $child, TypeDefinition $parent): void
84
    {
85 350
        $childType = $child->getTypeDefinition();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
86
87 350
        if (! ($parent->getTypeDefinition() instanceof $childType)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
88 180
            $this->throwScalarIncompatibilityException($child, $parent);
89
        }
90 170
    }
91
92
    /**
93
     * @param AllowsTypeIndication|TypeDefinition $child
94
     * @param AllowsTypeIndication|TypeDefinition $parent
95
     * @return void
96
     * @throws TypeConflictException
97
     */
98 450
    private function throwScalarIncompatibilityException(TypeDefinition $child, TypeDefinition $parent): void
99
    {
100 450
        [$childType, $parentType] = [$child->getTypeDefinition(), $parent->getTypeDefinition()];
0 ignored issues
show
Bug introduced by
The variable $childType does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $parentType does not exist. Did you mean $parent?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\TypeDefinition as the method getTypeDefinition() does only exist in the following implementations of said interface: Railt\SDL\Base\BaseDocument, Railt\SDL\Base\Dependent\BaseArgument, Railt\SDL\Base\Dependent\BaseField, Railt\SDL\Base\Invocations\BaseDirectiveInvocation, Railt\SDL\Base\Invocations\BaseInputInvocation, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Standard\Directives\Deprecation\Reason, Railt\SDL\Standard\GraphQLDocument.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
101
102 450
        $error = \vsprintf('Scalar %s of %s does not compatible with %s of %s', [
103 450
            $childType, $child,
104 450
            $parentType, $parent,
0 ignored issues
show
Bug introduced by
The variable $parentType does not exist. Did you mean $parent?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
105
        ]);
106
107 450
        throw new TypeConflictException($error, $this->getCallStack());
108
    }
109
}
110