Completed
Push — master ( cb6bda...ce700d )
by Olivier
07:22
created

Configuration::appendNameNormalization()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 8.6737
cc 5
eloc 12
nc 1
nop 1
crap 5
1
<?php
2
3
namespace Ola\RabbitMqAdminToolkitBundle\DependencyInjection;
4
5
use Ola\RabbitMqAdminToolkitBundle\Manager\QueueManager;
6
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
7
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
8
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
9
use Symfony\Component\Config\Definition\ConfigurationInterface;
10
11
class Configuration implements ConfigurationInterface
12
{
13
    /**
14
     * {@inheritdoc}
15
     */
16 4
    public function getConfigTreeBuilder()
17
    {
18 4
        $treeBuilder = new TreeBuilder();
19 4
        $rootNode = $treeBuilder->root('ola_rabbit_mq_admin_toolkit');
20
21
        $rootNode
22 4
            ->addDefaultsIfNotSet()
23 4
            ->children()
24 4
                ->scalarNode('default_vhost')->defaultValue('default')->end()
25 4
                ->scalarNode('delete_allowed')->defaultFalse()->end()
26 4
                ->scalarNode('silent_failure')->defaultFalse()->end()
27 4
                ->arrayNode('connections')
28 4
                    ->prototype('scalar')
29 4
                    ->end()
30 4
                ->end()
31 4
                ->arrayNode('vhosts')
32 4
                    ->prototype('array')
33 4
                        ->children()
34 4
                            ->scalarNode('name')->end()
35 4
                            ->scalarNode('connection')->defaultValue('default')->end()
36 4
                            ->scalarNode('delete_allowed')->end()
37 4
                            ->append($this->getPermissionsConfiguration())
38 4
                            ->append($this->getExchangesConfiguration())
39 4
                            ->append($this->getQueuesConfiguration())
40 4
                        ->end()
41 4
                    ->end()
42 4
                ->end()
43 4
            ->end()
44
        ;
45 4
        return $treeBuilder;
46
    }
47
48
    /**
49
     * @return NodeDefinition
50
     */
51 4
    private function getPermissionsConfiguration()
52
    {
53 4
        $node = new ArrayNodeDefinition('permissions');
54
55
        return $node
1 ignored issue
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...
56 4
            ->prototype('array')
57 4
                ->children()
58 4
                    ->scalarNode('configure')->defaultValue('.*')->end()
59 4
                    ->scalarNode('read')->defaultValue('.*')->end()
60 4
                    ->scalarNode('write')->defaultValue('.*')->end()
61 4
                ->end()
62 4
            ->end();
63
    }
64
65
    /**
66
     * @return NodeDefinition
67
     */
68 4
    private function getExchangesConfiguration()
69
    {
70 4
        $node = new ArrayNodeDefinition('exchanges');
71
72 4
        $this->appendNameNormalization($node);
73
74
        return $node
1 ignored issue
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...
75 4
            ->prototype('array')
76 4
                ->children()
77 4
                    ->scalarNode('name')->end()
78 4
                    ->scalarNode('type')->defaultValue('topic')->end()
79 4
                    ->scalarNode('durable')->defaultTrue()->end()
80 4
                ->end()
81 4
            ->end();
82
    }
83
84
    /**
85
     * @return NodeDefinition
86
     */
87 4
    private function getQueuesConfiguration()
88
    {
89
        $modulusValidation = function($queues) {
90
91
            $hasModulus = function($string) {
92 4
                return strpos($string, QueueManager::MODULUS_PLACEHOLDER) !== false;
93 4
            };
94
95 4
            foreach ($queues as $name => $queue) {
96 4
                if (isset($queue['modulus']) && is_int($queue['modulus'])) {
97
98 4
                    if (!$hasModulus($queue['name'])) {
99 1
                        return true;
100
                    }
101
102 3
                    $bindingsHaveModulus = false;
103 3
                    foreach ($queue['bindings'] as $binding) {
104 3
                        if ($hasModulus($binding['routing_key'])) {
105 3
                            $bindingsHaveModulus = true;
106
                        }
107
                    }
108
109 3
                    if (!$bindingsHaveModulus) {
110 3
                        return true;
111
                    }
112
                }
113
            }
114 4
        };
115
116 4
        $node = new ArrayNodeDefinition('queues');
117
118 4
        $this->appendNameNormalization($node);
119
120
        return $node
121 4
            ->validate()
122 4
                ->ifTrue($modulusValidation)
123 4
                ->thenInvalid('If queue.modulus is defined queue.name & at least one associated routing_key should contain a {modulus} part')
124 4
            ->end()
125 4
            ->prototype('array')
126 4
                ->children()
127 4
                    ->scalarNode('name')->end()
128 4
                    ->scalarNode('durable')->defaultTrue()->end()
129 4
                    ->scalarNode('modulus')->defaultNull()->end()
130 4
                    ->arrayNode('bindings')
131 4
                        ->prototype('array')
132 4
                            ->children()
133 4
                                ->scalarNode('exchange')->end()
134 4
                                ->scalarNode('routing_key')->end()
135 4
                            ->end()
136 4
                        ->end()
137 4
                    ->end()
138 4
                ->end()
139 4
            ->end();
140
    }
141
142
    /**
143
     * @param NodeDefinition $node
144
     *
145
     * @return NodeDefinition
146
     */
147 4
    private function appendNameNormalization(NodeDefinition $node)
148
    {
149 4
        return $node->beforeNormalization()
150
            ->ifTrue(function($array) {
151 4
                foreach ($array as $item) {
152 4
                    if (!isset($item['name'])) {
153 4
                        return true;
154
                    }
155
                }
156 4
            })
157 4
            ->then(function($array) {
158 4
                foreach ($array as $name => $item) {
159 4
                    if (!isset($item['name'])) {
160 4
                        $array[$name]['name'] = $name;
161
                    }
162
                }
163
164 4
                return $array;
165 4
            })
166 4
            ->end();
167
168
    }
169
}
170