Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#40)
by Jérémiah
05:07
created

TypeWithOutputFieldsDefinition   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 210
Duplicated Lines 10.48 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 86.4%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 29
c 1
b 1
f 0
lcom 1
cbo 8
dl 22
loc 210
ccs 108
cts 125
cp 0.864
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A addArgsBuilderClass() 0 6 1
A addFieldBuilderClass() 0 6 1
B checkBuilderClass() 0 27 4
A getArgsBuilder() 11 11 3
A getFieldBuilder() 11 11 3
D outputFieldsSelection() 0 112 17

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of the OverblogGraphQLBundle package.
5
 *
6
 * (c) Overblog <http://github.com/overblog/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Overblog\GraphQLBundle\Config;
13
14
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
15
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
19
abstract class TypeWithOutputFieldsDefinition extends TypeDefinition
20
{
21
    /**
22
     * @var MappingInterface[]
23
     */
24
    private static $argsBuilderClassMap = [
25
        'ForwardConnectionArgs' => 'Overblog\GraphQLBundle\Relay\Connection\ForwardConnectionArgsDefinition',
26
        'BackwardConnectionArgs' => 'Overblog\GraphQLBundle\Relay\Connection\BackwardConnectionArgsDefinition',
27
        'ConnectionArgs' => 'Overblog\GraphQLBundle\Relay\Connection\ConnectionArgsDefinition',
28
    ];
29
30
    /**
31
     * @var MappingInterface[]
32
     */
33
    private static $fieldBuilderClassMap = [
34
        'Mutation' => 'Overblog\GraphQLBundle\Relay\Mutation\MutationFieldDefinition',
35
        'GlobalId' => 'Overblog\GraphQLBundle\Relay\Node\GlobalIdFieldDefinition',
36
        'Node' => 'Overblog\GraphQLBundle\Relay\Node\NodeFieldDefinition',
37
        'PluralIdentifyingRoot' => 'Overblog\GraphQLBundle\Relay\Node\PluralIdentifyingRootFieldDefinition',
38
    ];
39
40 1
    public static function addArgsBuilderClass($name, $argBuilderClass)
41
    {
42 1
        self::checkBuilderClass($argBuilderClass, 'args');
43
44 1
        self::$argsBuilderClassMap[$name] = $argBuilderClass;
45 1
    }
46
47 1
    public static function addFieldBuilderClass($name, $fieldBuilderClass)
48
    {
49 1
        self::checkBuilderClass($fieldBuilderClass, 'field');
50
51 1
        self::$fieldBuilderClassMap[$name] = $fieldBuilderClass;
52 1
    }
53
54 1
    protected static function checkBuilderClass($builderClass, $type)
55
    {
56 1
        $interface = 'Overblog\\GraphQLBundle\\Definition\\Builder\\MappingInterface';
57
58 1
        if (!is_string($builderClass)) {
59
            throw new \InvalidArgumentException(
60
                sprintf('%s builder class should be string, but "%s" given.', ucfirst($type), gettype($builderClass))
61
            );
62
        }
63
64 1
        if (!class_exists($builderClass)) {
65
            throw new \InvalidArgumentException(
66
                sprintf('%s builder class "%s" not found.', ucfirst($type), $builderClass)
67
            );
68
        }
69
70 1
        if (!is_subclass_of($builderClass, $interface)) {
71
            throw new \InvalidArgumentException(
72
                sprintf(
73
                    '%s builder class should be instance of "%s", but "%s" given.',
74
                    ucfirst($type),
75
                    $interface,
76
                    $builderClass
77
                )
78
            );
79
        }
80 1
    }
81
82
    /**
83
     * @param $name
84
     *
85
     * @return MappingInterface|null
86
     */
87 4 View Code Duplication
    protected function getArgsBuilder($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88
    {
89 4
        static $builders = [];
90 4
        if (isset($builders[$name])) {
91 3
            return $builders[$name];
92
        }
93
94 2
        if (isset(self::$argsBuilderClassMap[$name])) {
95 2
            return $builders[$name] = new self::$argsBuilderClassMap[$name]();
96
        }
97
    }
98
99
    /**
100
     * @param $name
101
     *
102
     * @return MappingInterface|null
103
     */
104 6 View Code Duplication
    protected function getFieldBuilder($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
105
    {
106 6
        static $builders = [];
107 6
        if (isset($builders[$name])) {
108 5
            return $builders[$name];
109
        }
110
111 4
        if (isset(self::$fieldBuilderClassMap[$name])) {
112 4
            return $builders[$name] = new self::$fieldBuilderClassMap[$name]();
113
        }
114
    }
115
116 15
    protected function outputFieldsSelection($name, $withAccess = false)
117
    {
118 15
        $builder = new TreeBuilder();
119 15
        $node = $builder->root($name);
120
        $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 requiresAtLeastOneElement() 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...
121 15
            ->isRequired()
122 15
            ->requiresAtLeastOneElement();
123
124
        /* @var ArrayNodeDefinition $prototype */
125 15
        $prototype = $node->useAttributeAsKey('name', false)->prototype('array');
126
127
        $prototype
128
            // build args if argsBuilder exists
129 15
            ->beforeNormalization()
130
                ->ifTrue(function ($field) {
131 9
                    return isset($field['argsBuilder']);
132 15
                })
133
                ->then(function ($field) {
134 4
                    $argsBuilderName = null;
135
136 4
                    if (is_string($field['argsBuilder'])) {
137 4
                        $argsBuilderName = $field['argsBuilder'];
138 4
                    } elseif (isset($field['argsBuilder']['builder']) && is_string($field['argsBuilder']['builder'])) {
139 1
                        $argsBuilderName = $field['argsBuilder']['builder'];
140 1
                    }
141
142 4
                    if ($argsBuilderName) {
143 4
                        if (!($argsBuilder = $this->getArgsBuilder($argsBuilderName))) {
144
                            throw new InvalidConfigurationException(sprintf('Args builder "%s" not found.', $argsBuilder));
145
                        }
146
147 4
                        $args = $argsBuilder->toMappingDefinition([]);
148
149 4
                        $field['args'] = isset($field['args']) && is_array($field['args']) ? array_merge($args, $field['args']) : $args;
150 4
                    }
151
152 4
                    unset($field['argsBuilder']);
153
154 4
                    return $field;
155 15
                })
156 15
            ->end()
157
            // build field if builder exists
158 15
            ->beforeNormalization()
159 15
                ->always(function ($field) {
160 9
                    $fieldBuilderName = null;
161
162 9
                    if (isset($field['builder']) && is_string($field['builder'])) {
163 6
                        $fieldBuilderName = $field['builder'];
164 6
                        unset($field['builder']);
165 9
                    } elseif (is_string($field)) {
166 1
                        $fieldBuilderName = $field;
167 1
                    }
168
169 9
                    $builderConfig = [];
170 9
                    if (isset($field['builderConfig'])) {
171 5
                        if (is_array($field['builderConfig'])) {
172 5
                            $builderConfig = $field['builderConfig'];
173 5
                        }
174 5
                        unset($field['builderConfig']);
175 5
                    }
176
177 9
                    if ($fieldBuilderName) {
178 6
                        if (!($fieldBuilder = $this->getFieldBuilder($fieldBuilderName))) {
179
                            throw new InvalidConfigurationException(sprintf('Field builder "%s" not found.', $fieldBuilderName));
180
                        }
181 6
                        $buildField = $fieldBuilder->toMappingDefinition($builderConfig);
182 6
                        $field = is_array($field) ? array_merge($buildField, $field) : $buildField;
183 6
                    }
184
185 9
                    return $field;
186 15
                })
187 15
            ->end();
188
189
        $prototype
190 15
            ->children()
191 15
                ->append($this->typeSelection())
192 15
                ->arrayNode('args')
193 15
                    ->info('Array of possible type arguments. Each entry is expected to be an array with following keys: name (string), type')
194 15
                    ->useAttributeAsKey('name', false)
195 15
                    ->prototype('array')
196 15
                        ->children()
197 15
                            ->append($this->typeSelection(true))
198 15
                            ->scalarNode('description')->end()
199 15
                            ->append($this->defaultValueSection())
200 15
                        ->end()
201 15
                    ->end()
202 15
                ->end()
203 15
                ->variableNode('resolve')
204 15
                    ->info('Value resolver (expression language can be use here)')
205 15
                ->end()
206 15
                ->append($this->descriptionSection())
207 15
                ->append($this->deprecationReasonSelection())
208 15
                ->variableNode('access')
209 15
                    ->info('Access control to field (expression language can be use here)')
210 15
                ->end()
211 15
                ->variableNode('complexity')
212 15
                    ->info('Custom complexity calculator.')
213 15
                ->end()
214 15
                ->variableNode('map')->end()
215 15
            ->end();
216
217 15
        if ($withAccess) {
218
            $prototype
219 15
                ->children()
220 15
                    ->variableNode('access')
221 15
                        ->info('Access control to field (expression language can be use here)')
222 15
                    ->end()
223 15
                ->end();
224 15
        }
225
226 15
        return $node;
227
    }
228
}
229