Configuration::getChannelsNode()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Yokai\MessengerBundle\DependencyInjection;
4
5
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
6
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
7
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
8
use Symfony\Component\Config\Definition\ConfigurationInterface;
9
10
/**
11
 * @author Yann Eugoné <[email protected]>
12
 */
13
class Configuration implements ConfigurationInterface
14
{
15
    /**
16
     * @inheritdoc
17
     */
18 14
    public function getConfigTreeBuilder()
19
    {
20 14
        $builder = $this->getBuilder();
21 14
        $root = $builder->root('yokai_messenger');
22
23
        $root
24 14
            ->addDefaultsIfNotSet()
25 14
            ->fixXmlConfig('message')
26 14
            ->children()
27 14
                ->scalarNode('logging_channel')->defaultValue('app')->end()
28 14
                ->append($this->getContentBuilderNode())
29 14
                ->append($this->getChannelsNode())
30 14
                ->append($this->getMessagesNode())
31 14
            ->end()
32
        ;
33
34 14
        return $builder;
35
    }
36
37
    /**
38
     * @return TreeBuilder
39
     */
40 14
    private function getBuilder()
41
    {
42 14
        return new TreeBuilder();
43
    }
44
45
    /**
46
     * @param string $name
47
     *
48
     * @return ArrayNodeDefinition
49
     */
50 14
    private function root($name)
51
    {
52 14
        return $this->getBuilder()->root($name);
53
    }
54
55
    /**
56
     * @return NodeDefinition
57
     */
58 14
    private function getChannelsNode()
59
    {
60 14
        $node = $this->root('channels');
61
62
        $node
63 14
            ->addDefaultsIfNotSet()
64 14
            ->children()
65 14
                ->append($this->getSwiftmailerChannelNode())
66 14
                ->append($this->getTwilioChannelNode())
67 14
                ->append($this->getDoctrineChannelNode())
68 14
                ->append($this->getMobileChannelNode())
69 14
            ->end()
70
        ;
71
72 14
        return $node;
73
    }
74
75
    /**
76
     * @return NodeDefinition
77
     */
78 14
    private function getMessagesNode()
79
    {
80 14
        $node = $this->root('messages');
81
82
        $node
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 fixXmlConfig() 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...
83 14
            ->defaultValue([])
84 14
            ->prototype('array')
85 14
                ->fixXmlConfig('channel')
86 14
                ->children()
87 14
                    ->scalarNode('id')->isRequired()->end()
88 14
                    ->arrayNode('channels')
89 14
                        ->beforeNormalization()
90 14
                            ->ifString()->then($this->stringToArray())
91 14
                        ->end()
92 14
                        ->requiresAtLeastOneElement()
93 14
                        ->prototype('scalar')->end()
94 14
                    ->end()
95 14
                    ->arrayNode('defaults')
96 14
                        ->defaultValue([])
97 14
                        ->prototype('variable')->end()
98 14
                    ->end()
99 14
                    ->arrayNode('options')
100 14
                        ->defaultValue([])
101 14
                        ->useAttributeAsKey('channel')
102 14
                        ->prototype('variable')
103 14
                            ->validate()
104 14
                                ->ifTrue($this->isNotHash())
105 14
                                    ->thenInvalid('Expected a hash for channel options, got %s.')
106 14
                            ->end()
107 14
                        ->end()
108 14
                    ->end()
109 14
                ->end()
110 14
            ->end()
111
        ;
112
113 14
        return $node;
114
    }
115
116
    /**
117
     * @return NodeDefinition
118
     */
119 14
    private function getSwiftmailerChannelNode()
120
    {
121 14
        $node = $this->root('swiftmailer');
122
123
        $node
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...
124 14
            ->canBeEnabled()
125 14
            ->validate()
126 14
                ->ifTrue($this->nodeRequiredIfEnabled('from'))
127 14
                    ->thenInvalid($this->nodeMustBeConfigured('from', 'channels.swiftmailer'))
128 14
            ->end()
129 14
            ->children()
130 14
                ->arrayNode('from')
131 14
                    ->normalizeKeys(false)
132 14
                    ->beforeNormalization()
133 14
                        ->ifString()->then($this->stringToArray())
134 14
                    ->end()
135 14
                    ->requiresAtLeastOneElement()
136 14
                    ->prototype('scalar')->end()
137 14
                ->end()
138 14
                ->scalarNode('translator_catalog')->defaultValue('notifications')->end()
139 14
            ->end()
140
        ;
141
142 14
        return $node;
143
    }
144
145
    /**
146
     * @return NodeDefinition
147
     */
148 14
    private function getTwilioChannelNode()
149
    {
150 14
        $node = $this->root('twilio');
151
152
        $node
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...
153 14
            ->canBeEnabled()
154 14
            ->validate()
155 14
                ->ifTrue($this->nodeRequiredIfEnabled('from'))
156 14
                    ->thenInvalid($this->nodeMustBeConfigured('from', 'channels.twilio'))
157 14
                ->ifTrue($this->nodeRequiredIfEnabled('api_id'))
158 14
                    ->thenInvalid($this->nodeMustBeConfigured('api_id', 'channels.twilio'))
159 14
                ->ifTrue($this->nodeRequiredIfEnabled('api_token'))
160 14
                    ->thenInvalid($this->nodeMustBeConfigured('api_token', 'channels.twilio'))
161 14
            ->end()
162 14
            ->children()
163 14
                ->scalarNode('from')->end()
164 14
                ->scalarNode('api_id')->end()
165 14
                ->scalarNode('api_token')->end()
166 14
            ->end()
167
        ;
168
169 14
        return $node;
170
    }
171
172
    /**
173
     * @return NodeDefinition
174
     */
175 14
    private function getDoctrineChannelNode()
176
    {
177 14
        $node = $this->root('doctrine');
178
179
        $node
180 14
            ->canBeEnabled()
181 14
            ->children()
182 14
                ->scalarNode('manager')->defaultValue('default')->end()
183 14
            ->end()
184
        ;
185
186 14
        return $node;
187
    }
188
189
    /**
190
     * @return NodeDefinition
191
     */
192 14
    private function getMobileChannelNode()
193
    {
194 14
        $node = $this->root('mobile');
195
196
        $node
197 14
            ->canBeEnabled()
198 14
            ->children()
199 14
                ->scalarNode('environment')->defaultValue('dev')->end()
200 14
                ->arrayNode('apns')
201 14
                    ->canBeDisabled()
202 14
                    ->validate()
203 14
                        ->ifTrue($this->nodeRequiredIfEnabled('certificate'))
204 14
                            ->thenInvalid($this->nodeMustBeConfigured('certificate', 'channels.mobile.apns'))
205 14
                        ->ifTrue($this->nodeRequiredIfEnabled('certificate'))
206 14
                            ->thenInvalid($this->nodeMustBeConfigured('pass_phrase', 'channels.mobile.apns'))
207 14
                    ->end()
208 14
                    ->children()
209 14
                        ->scalarNode('certificate')->defaultNull()->end()
210 14
                        ->scalarNode('pass_phrase')->defaultNull()->end()
211 14
                    ->end()
212 14
                ->end()
213 14
                ->arrayNode('gcm')
214 14
                    ->canBeDisabled()
215 14
                    ->validate()
216 14
                        ->ifTrue($this->nodeRequiredIfEnabled('api_key'))
217 14
                            ->thenInvalid($this->nodeMustBeConfigured('api_key', 'channels.mobile.gcm'))
218 14
                    ->end()
219 14
                    ->children()
220 14
                        ->scalarNode('api_key')->defaultNull()->end()
221 14
                    ->end()
222 14
                ->end()
223 14
            ->end()
224
        ;
225
226 14
        return $node;
227
    }
228
229
    /**
230
     * @return NodeDefinition
231
     */
232 14
    private function getContentBuilderNode()
233
    {
234 14
        $node = $this->root('content_builder');
235
236
        $node
0 ignored issues
show
Unused Code introduced by
The call to the method Symfony\Component\Config...r\NodeDefinition::end() 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...
237 14
            ->defaultValue([])
238 14
            ->useAttributeAsKey('name')
239 14
            ->prototype('variable')
240 14
            ->end()
241
        ;
242
243 14
        return $node;
244
    }
245
246
    /**
247
     * @return callable
248
     */
249
    private function stringToArray()
250
    {
251 14
        return function ($value) {
252 2
            return [$value];
253 14
        };
254
    }
255
256
    /**
257
     * @return callable
258
     */
259
    private function isNotHash()
260
    {
261 14
        return function ($value) {
262
            if (!is_array($value)) {
263
                return true;
264
            }
265
266
            if (array_values($value) === $value) {
267
                return true;
268
            }
269
270
            return false;
271 14
        };
272
    }
273
274
    /**
275
     * @param string $node
276
     *
277
     * @return callable
278
     */
279
    private function nodeRequiredIfEnabled($node)
280
    {
281 14
        return function ($value) use ($node) {
282 7
            if (!$value['enabled']) {
283 1
                return false;
284
            }
285
286 6
            if (isset($value[$node]) && !empty($value[$node])) {
287 6
                return false;
288
            }
289
290
            return true;
291 14
        };
292
    }
293
294
    /**
295
     * @param string $node
296
     * @param string $path
297
     *
298
     * @return string
299
     */
300 14
    private function nodeMustBeConfigured($node, $path)
301
    {
302 14
        return sprintf(
303 14
            'The child node "%s" at path "yokai_messenger.%s" must be configured.',
304 14
            $node,
305 14
            $path
306
        );
307
    }
308
}
309