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 (#96)
by Jérémiah
04:31
created

TypeWithOutputFieldsDefinition::getBuilder()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4.0058

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
ccs 13
cts 14
cp 0.9286
rs 8.5806
cc 4
eloc 14
nc 4
nop 2
crap 4.0058
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
    const RELAY_BUILDERS_PREFIX = 'Relay::';
22
23
    const BUILDER_FIELD_TYPE = 'field';
24
    const BUILDER_ARGS_TYPE = 'args';
25
26
    /**
27
     * @var MappingInterface[]
28
     */
29
    private static $argsBuilderClassMap = [
30
        self::RELAY_BUILDERS_PREFIX.'ForwardConnection' => 'Overblog\GraphQLBundle\Relay\Connection\ForwardConnectionArgsDefinition',
31
        self::RELAY_BUILDERS_PREFIX.'BackwardConnection' => 'Overblog\GraphQLBundle\Relay\Connection\BackwardConnectionArgsDefinition',
32
        self::RELAY_BUILDERS_PREFIX.'Connection' => 'Overblog\GraphQLBundle\Relay\Connection\ConnectionArgsDefinition',
33
    ];
34
35
    /**
36
     * @var MappingInterface[]
37
     */
38
    private static $fieldBuilderClassMap = [
39
        self::RELAY_BUILDERS_PREFIX.'Mutation' => 'Overblog\GraphQLBundle\Relay\Mutation\MutationFieldDefinition',
40
        self::RELAY_BUILDERS_PREFIX.'GlobalId' => 'Overblog\GraphQLBundle\Relay\Node\GlobalIdFieldDefinition',
41
        self::RELAY_BUILDERS_PREFIX.'Node' => 'Overblog\GraphQLBundle\Relay\Node\NodeFieldDefinition',
42
        self::RELAY_BUILDERS_PREFIX.'PluralIdentifyingRoot' => 'Overblog\GraphQLBundle\Relay\Node\PluralIdentifyingRootFieldDefinition',
43
    ];
44
45 1
    public static function addArgsBuilderClass($name, $argBuilderClass)
46
    {
47 1
        self::checkBuilderClass($argBuilderClass, 'args');
48
49 1
        self::$argsBuilderClassMap[$name] = $argBuilderClass;
50 1
    }
51
52 1
    public static function addFieldBuilderClass($name, $fieldBuilderClass)
53
    {
54 1
        self::checkBuilderClass($fieldBuilderClass, 'field');
55
56 1
        self::$fieldBuilderClassMap[$name] = $fieldBuilderClass;
57 1
    }
58
59 1
    protected static function checkBuilderClass($builderClass, $type)
60
    {
61 1
        $interface = 'Overblog\\GraphQLBundle\\Definition\\Builder\\MappingInterface';
62
63 1
        if (!is_string($builderClass)) {
64
            throw new \InvalidArgumentException(
65
                sprintf('%s builder class should be string, but "%s" given.', ucfirst($type), gettype($builderClass))
66
            );
67
        }
68
69 1
        if (!class_exists($builderClass)) {
70
            throw new \InvalidArgumentException(
71
                sprintf('%s builder class "%s" not found.', ucfirst($type), $builderClass)
72
            );
73
        }
74
75 1
        if (!is_subclass_of($builderClass, $interface)) {
76
            throw new \InvalidArgumentException(
77
                sprintf(
78
                    '%s builder class should be instance of "%s", but "%s" given.',
79
                    ucfirst($type),
80
                    $interface,
81
                    $builderClass
82
                )
83
            );
84
        }
85 1
    }
86
87
    /**
88
     * @param string $name
89
     * @param string $type
90
     *
91
     * @return MappingInterface
92
     *
93
     * @throws InvalidConfigurationException if builder class not define
94
     */
95 8
    protected function getBuilder($name, $type)
96
    {
97 8
        static $builders = [];
98 8
        if (isset($builders[$type][$name])) {
99 5
            return $builders[$type][$name];
100
        }
101
102 6
        $builderClassMap = self::${$type.'BuilderClassMap'};
103
104 6
        if (isset($builderClassMap[$name])) {
105 5
            return $builders[$type][$name] = new $builderClassMap[$name]();
106
        }
107
        // deprecated relay builder name ?
108 1
        $newName = static::RELAY_BUILDERS_PREFIX.rtrim($name, 'Args');
109 1
        if (isset($builderClassMap[$newName])) {
110 1
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
111 1
                sprintf('The "%s" %s builder is deprecated as of 0.7 and will be removed in 0.8. Use "%s" instead.', $name, $type, $newName),
112
                E_USER_DEPRECATED
113 1
            );
114
115 1
            return $builders[$type][$newName] = new $builderClassMap[$newName]();
116
        }
117
118
        throw new InvalidConfigurationException(sprintf('%s builder "%s" not found.', ucfirst($type), $name));
119
    }
120
121 18
    protected function outputFieldsSelection($name)
122
    {
123 18
        $builder = new TreeBuilder();
124 18
        $node = $builder->root($name);
125
        $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...
126 18
            ->isRequired()
127 18
            ->requiresAtLeastOneElement();
128
129
        /* @var ArrayNodeDefinition $prototype */
130 18
        $prototype = $node->useAttributeAsKey('name', false)->prototype('array');
131
132
        $prototype
133
            // build args if argsBuilder exists
134 18
            ->beforeNormalization()
135
                ->ifTrue(function ($field) {
136 12
                    return isset($field['argsBuilder']);
137 18
                })
138
                ->then(function ($field) {
139 4
                    $argsBuilderName = null;
140
141 4
                    if (is_string($field['argsBuilder'])) {
142 4
                        $argsBuilderName = $field['argsBuilder'];
143 4
                    } elseif (isset($field['argsBuilder']['builder']) && is_string($field['argsBuilder']['builder'])) {
144 1
                        $argsBuilderName = $field['argsBuilder']['builder'];
145 1
                    }
146
147 4
                    if ($argsBuilderName) {
148 4
                        $args = $this->getBuilder($argsBuilderName, static::BUILDER_ARGS_TYPE)->toMappingDefinition([]);
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 18
                })
156 18
            ->end()
157
            // build field if builder exists
158 18
            ->beforeNormalization()
159
                ->always(function ($field) {
160 12
                    $fieldBuilderName = null;
161
162 12
                    if (isset($field['builder']) && is_string($field['builder'])) {
163 6
                        $fieldBuilderName = $field['builder'];
164 6
                        unset($field['builder']);
165 12
                    } elseif (is_string($field)) {
166 2
                        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
167
                            'The builder short syntax (Field: Builder => Field: {builder: Builder}) is deprecated as of 0.7 and will be removed in 0.8. '.
168 2
                            'It will be replaced by the field type short syntax (Field: Type => Field: {type: Type})',
169
                            E_USER_DEPRECATED
170 2
                        );
171 2
                        $fieldBuilderName = $field;
172 2
                    }
173
174 12
                    $builderConfig = [];
175 12
                    if (isset($field['builderConfig'])) {
176 5
                        if (is_array($field['builderConfig'])) {
177 5
                            $builderConfig = $field['builderConfig'];
178 5
                        }
179 5
                        unset($field['builderConfig']);
180 5
                    }
181
182 12
                    if ($fieldBuilderName) {
183 6
                        $buildField = $this->getBuilder($fieldBuilderName, static::BUILDER_FIELD_TYPE)->toMappingDefinition($builderConfig);
184 6
                        $field = is_array($field) ? array_merge($buildField, $field) : $buildField;
185 6
                    }
186
187 12
                    return $field;
188 18
                })
189 18
            ->end();
190
191
        $prototype
0 ignored issues
show
Bug introduced by
The method useAttributeAsKey() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. Did you maybe mean attribute()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
192 18
            ->children()
193 18
                ->append($this->typeSelection())
194 18
                ->arrayNode('args')
195 18
                    ->info('Array of possible type arguments. Each entry is expected to be an array with following keys: name (string), type')
196 18
                    ->useAttributeAsKey('name', false)
197 18
                    ->prototype('array')
198
                        // Allow arg type short syntax (Arg: Type => Arg: {type: Type})
199 18
                        ->beforeNormalization()
200
                            ->ifTrue(function ($options) {
201 9
                                return is_string($options);
202 18
                            })
203 18
                            ->then(function ($options) {
204 1
                                return ['type' => $options];
205 18
                            })
206 18
                        ->end()
207 18
                        ->children()
208 18
                            ->append($this->typeSelection(true))
209 18
                            ->append($this->descriptionSection())
210 18
                            ->append($this->defaultValueSection())
211 18
                        ->end()
212 18
                    ->end()
213 18
                ->end()
214 18
                ->variableNode('resolve')
215 18
                    ->info('Value resolver (expression language can be use here)')
216 18
                ->end()
217 18
                ->append($this->descriptionSection())
218 18
                ->append($this->deprecationReasonSelection())
219 18
                ->variableNode('access')
220 18
                    ->info('Access control to field (expression language can be use here)')
221 18
                ->end()
222 18
                ->variableNode('complexity')
223 18
                    ->info('Custom complexity calculator.')
224 18
                ->end()
225 18
            ->end();
226
227 18
        return $node;
228
    }
229
}
230