DefinitionConfiguration::defineRelationships()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 21
cts 21
cp 1
rs 9.488
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Component\JsonApi\Mapper\Definition;
5
6
use Symfony\Component\Config\Definition\ConfigurationInterface;
7
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
8
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
9
10
/**
11
 * Configuration (schema) of mapping definition
12
 *
13
 * @package Mikemirten\Component\JsonApi\Mapper\Definition
14
 */
15
class DefinitionConfiguration implements ConfigurationInterface
16
{
17
    /**
18
     * {@inheritdoc}
19
     */
20 2
    public function getConfigTreeBuilder(): TreeBuilder
21
    {
22 2
        $builder    = new TreeBuilder();
23 2
        $definition = $builder->root('definition')->children();
24
25 2
        $definition->scalarNode('type')
26 2
            ->cannotBeEmpty();
27
28 2
        $this->defineLinks($definition);
29 2
        $this->defineAttributes($definition);
30 2
        $this->defineRelationships($definition);
31
32 2
        return $builder;
33
    }
34
35
    /**
36
     * Define "attributes" section of configuration
37
     *
38
     * @param NodeBuilder $builder
39
     */
40 2
    protected function defineAttributes(NodeBuilder $builder)
41
    {
42 2
        $builder->arrayNode('attributes')
43 2
            ->useAttributeAsKey('')
44 2
            ->prototype('array')
45 2
            ->children()
46
47 2
            ->scalarNode('type')
48 2
                ->cannotBeEmpty()
49 2
            ->end()
50
51 2
            ->scalarNode('getter')
52 2
                ->cannotBeEmpty()
53 2
            ->end()
54
55 2
            ->scalarNode('setter')
56 2
                ->cannotBeEmpty()
57 2
            ->end()
58
59 2
            ->booleanNode('processNull')
60 2
                ->defaultFalse()
61 2
            ->end();
62 2
    }
63
64
    /**
65
     * Define "relationships" section of configuration
66
     *
67
     * @param NodeBuilder $builder
68
     */
69 2
    protected function defineRelationships(NodeBuilder $builder)
70
    {
71 2
        $relationships = $builder->arrayNode('relationships')
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method children() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
72 2
            ->useAttributeAsKey('')
73 2
            ->prototype('array')
74 2
            ->children()
75
76 2
            ->enumNode('type')
77 2
                ->values(['one', 'many'])
78 2
                ->cannotBeEmpty()
79 2
                ->defaultValue('one')
80 2
            ->end()
81
82 2
            ->scalarNode('getter')
83 2
                ->cannotBeEmpty()
84 2
            ->end()
85
86 2
            ->booleanNode('dataAllowed')
87 2
                ->defaultFalse()
88 2
            ->end()
89
90 2
            ->integerNode('dataLimit')
91 2
                ->defaultValue(0)
92 2
            ->end();
93
94 2
        $this->defineLinks($relationships);
95 2
    }
96
97
    /**
98
     * Define "links" section of configuration
99
     *
100
     * @param NodeBuilder $builder
101
     */
102 2
    protected function defineLinks(NodeBuilder $builder)
103
    {
104 2
        $builder->arrayNode('links')
105 2
            ->useAttributeAsKey('')
106 2
            ->prototype('array')
107 2
            ->children()
108
109 2
            ->scalarNode('resource')
110 2
                ->isRequired()
111 2
                ->cannotBeEmpty()
112 2
            ->end()
113
114 2
            ->arrayNode('parameters')
115 2
                ->prototype('scalar')->end()
116 2
            ->end()
117
118 2
            ->arrayNode('metadata')
119 2
                ->prototype('scalar')->end()
120 2
            ->end();
121
    }
122
}