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 (#333)
by Jérémiah
08:28 queued 05:48
created

Executor::setMaxQueryComplexity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Overblog\GraphQLBundle\Request;
4
5
use GraphQL\Executor\ExecutionResult;
6
use GraphQL\Executor\Promise\Promise;
7
use GraphQL\Executor\Promise\PromiseAdapter;
8
use GraphQL\Type\Schema;
9
use GraphQL\Validator\DocumentValidator;
10
use GraphQL\Validator\Rules\DisableIntrospection;
11
use GraphQL\Validator\Rules\QueryComplexity;
12
use GraphQL\Validator\Rules\QueryDepth;
13
use Overblog\GraphQLBundle\Event\Events;
14
use Overblog\GraphQLBundle\Event\ExecutorArgumentsEvent;
15
use Overblog\GraphQLBundle\Event\ExecutorContextEvent;
16
use Overblog\GraphQLBundle\Event\ExecutorResultEvent;
17
use Overblog\GraphQLBundle\Executor\ExecutorInterface;
18
use Overblog\GraphQLBundle\Executor\Promise\PromiseAdapterInterface;
19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
20
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21
22
class Executor
23
{
24
    const PROMISE_ADAPTER_SERVICE_ID = 'overblog_graphql.promise_adapter';
25
26
    /** @var Schema[] */
27
    private $schemas = [];
28
29
    /** @var EventDispatcherInterface|null */
30
    private $dispatcher;
31
32
    /** @var ExecutorInterface */
33
    private $executor;
34
35
    /** @var PromiseAdapter */
36
    private $promiseAdapter;
37
38
    /** @var callable|null */
39
    private $defaultFieldResolver;
40
41 4
    public function __construct(
42
        ExecutorInterface $executor,
43
        EventDispatcherInterface $dispatcher,
44
        PromiseAdapter $promiseAdapter,
45
        callable $defaultFieldResolver = null
46
    ) {
47 4
        $this->executor = $executor;
48 4
        $this->dispatcher = $dispatcher;
49 4
        $this->promiseAdapter = $promiseAdapter;
50 4
        $this->defaultFieldResolver = $defaultFieldResolver;
51 4
    }
52
53 2
    public function setExecutor(ExecutorInterface $executor)
54
    {
55 2
        $this->executor = $executor;
56
57 2
        return $this;
58
    }
59
60 1
    public function setPromiseAdapter(PromiseAdapter $promiseAdapter)
61
    {
62 1
        $this->promiseAdapter = $promiseAdapter;
63
64 1
        return $this;
65
    }
66
67
    /**
68
     * @param string $name
69
     * @param Schema $schema
70
     *
71
     * @return $this
72
     */
73 4
    public function addSchema($name, Schema $schema)
74
    {
75 4
        $this->schemas[$name] = $schema;
76
77 4
        return $this;
78
    }
79
80
    /**
81
     * @param string|null $name
82
     *
83
     * @return Schema
84
     */
85 4
    public function getSchema($name = null)
86
    {
87 4
        if (empty($this->schemas)) {
88 1
            throw new \RuntimeException('At least one schema should be declare.');
89
        }
90
91 3
        if (null === $name) {
92 3
            $schema = array_values($this->schemas)[0];
93
        } else {
94
            if (!isset($this->schemas[$name])) {
95
                throw new NotFoundHttpException(sprintf('Could not found "%s" schema.', $name));
96
            }
97
            $schema = $this->schemas[$name];
98
        }
99
100 3
        return $schema;
101
    }
102
103
    public function setMaxQueryDepth($maxQueryDepth)
104
    {
105
        /** @var QueryDepth $queryDepth */
106
        $queryDepth = DocumentValidator::getRule('QueryDepth');
107
        $queryDepth->setMaxQueryDepth($maxQueryDepth);
108
    }
109
110
    public function setMaxQueryComplexity($maxQueryComplexity)
111
    {
112
        /** @var QueryComplexity $queryComplexity */
113
        $queryComplexity = DocumentValidator::getRule('QueryComplexity');
114
        $queryComplexity->setMaxQueryComplexity($maxQueryComplexity);
115
    }
116
117
    public function disableIntrospectionQuery()
118
    {
119
        DocumentValidator::addRule(new DisableIntrospection());
120
    }
121
122
    /**
123
     * @param null|string                    $schemaName
124
     * @param array                          $request
125
     * @param null|array|\ArrayObject|object $rootValue
126
     *
127
     * @return ExecutionResult
128
     */
129 3
    public function execute($schemaName, array $request, $rootValue = null)
130
    {
131 3
        $executorArgumentsEvent = $this->preExecute(
132 3
            $this->getSchema($schemaName),
133 3
            isset($request[ParserInterface::PARAM_QUERY]) ? $request[ParserInterface::PARAM_QUERY] : null,
134 3
            new \ArrayObject(),
135 3
            $rootValue,
136 3
            $request[ParserInterface::PARAM_VARIABLES],
137 3
            isset($request[ParserInterface::PARAM_OPERATION_NAME]) ? $request[ParserInterface::PARAM_OPERATION_NAME] : null
138
        );
139
140 2
        $executorArgumentsEvent->getSchema()->processExtensions();
141
142 2
        $result = $this->executor->execute(
143 2
            $executorArgumentsEvent->getSchema(),
144 2
            $executorArgumentsEvent->getRequestString(),
145 2
            $executorArgumentsEvent->getRootValue(),
146 2
            $executorArgumentsEvent->getContextValue(),
0 ignored issues
show
Documentation introduced by
$executorArgumentsEvent->getContextValue() is of type object<ArrayObject>, but the function expects a null|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
147 2
            $executorArgumentsEvent->getVariableValue(),
148 2
            $executorArgumentsEvent->getOperationName()
149
        );
150
151 2
        $result = $this->postExecute($result);
152
153
        return $result;
154
    }
155
156
    /**
157
     * @param Schema       $schema
158
     * @param string       $requestString
159
     * @param \ArrayObject $contextValue
160
     * @param mixed        $rootValue
161
     * @param array|null   $variableValue
162
     * @param string|null  $operationName
163
     *
164
     * @return ExecutorArgumentsEvent
165
     */
166 3
    private function preExecute(
167
        Schema $schema,
168
        $requestString,
169
        \ArrayObject $contextValue,
170
        $rootValue = null,
171
        array $variableValue = null,
172
        $operationName = null
173
    ) {
174 3
        $this->checkPromiseAdapter();
175
176 2
        $this->executor->setPromiseAdapter($this->promiseAdapter);
177
        // this is needed when not using only generated types
178 2
        if ($this->defaultFieldResolver) {
179
            $this->executor->setDefaultFieldResolver($this->defaultFieldResolver);
180
        }
181 2
        $this->dispatcher->dispatch(Events::EXECUTOR_CONTEXT, new ExecutorContextEvent($contextValue));
182
183 2
        return $this->dispatcher->dispatch(
184 2
            Events::PRE_EXECUTOR,
185 2
            ExecutorArgumentsEvent::create($schema, $requestString, $contextValue, $rootValue, $variableValue, $operationName)
0 ignored issues
show
Compatibility introduced by
$schema of type object<GraphQL\Type\Schema> is not a sub-type of object<Overblog\GraphQLB...\Type\ExtensibleSchema>. It seems like you assume a child class of the class GraphQL\Type\Schema to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
186
        );
187
    }
188
189
    /**
190
     * @param ExecutionResult|Promise $result
191
     *
192
     * @return ExecutionResult
193
     */
194 2
    private function postExecute($result)
195
    {
196 2
        if ($result instanceof Promise) {
197
            $result = $this->promiseAdapter->wait($result);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface GraphQL\Executor\Promise\PromiseAdapter as the method wait() does only exist in the following implementations of said interface: GraphQL\Executor\Promise...pter\SyncPromiseAdapter, Overblog\GraphQLBundle\E...ter\ReactPromiseAdapter.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
198
        }
199
200 2
        $this->checkExecutionResult($result);
201
202
        return  $this->dispatcher->dispatch(Events::POST_EXECUTOR, new ExecutorResultEvent($result))->getResult();
203
    }
204
205 3
    private function checkPromiseAdapter()
206
    {
207 3
        if ($this->promiseAdapter && !$this->promiseAdapter instanceof PromiseAdapterInterface && !is_callable([$this->promiseAdapter, 'wait'])) {
208 1
            throw new \RuntimeException(
209 1
                sprintf(
210 1
                    'PromiseAdapter should be an object instantiating "%s" or "%s" with a "wait" method.',
211 1
                    PromiseAdapterInterface::class,
212 1
                    PromiseAdapter::class
213
                )
214
            );
215
        }
216 2
    }
217
218 2
    private function checkExecutionResult($result)
219
    {
220 2
        if (!is_object($result) || !$result instanceof ExecutionResult) {
221 2
            throw new \RuntimeException(
222 2
                sprintf('Execution result should be an object instantiating "%s".', ExecutionResult::class)
223
            );
224
        }
225
    }
226
}
227