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

Passed
Push — master ( 861825...4090a2 )
by Jérémiah
16:36 queued 10s
created

Executor::disableIntrospectionQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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 97
    public function __construct(
42
        ExecutorInterface $executor,
43
        EventDispatcherInterface $dispatcher,
44
        PromiseAdapter $promiseAdapter,
45
        callable $defaultFieldResolver = null
46
    ) {
47 97
        $this->executor = $executor;
48 97
        $this->dispatcher = $dispatcher;
49 97
        $this->promiseAdapter = $promiseAdapter;
50 97
        $this->defaultFieldResolver = $defaultFieldResolver;
51 97
    }
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 89
    public function addSchema($name, Schema $schema)
74
    {
75 89
        $this->schemas[$name] = $schema;
76
77 89
        return $this;
78
    }
79
80
    /**
81
     * @param string|null $name
82
     *
83
     * @return Schema
84
     */
85 85
    public function getSchema($name = null)
86
    {
87 85
        if (empty($this->schemas)) {
88 1
            throw new \RuntimeException('At least one schema should be declare.');
89
        }
90
91 84
        if (null === $name) {
92 81
            $schema = array_values($this->schemas)[0];
93
        } else {
94 3
            if (!isset($this->schemas[$name])) {
95 1
                throw new NotFoundHttpException(sprintf('Could not found "%s" schema.', $name));
96
            }
97 2
            $schema = $this->schemas[$name];
98
        }
99
100 83
        return $schema;
101
    }
102
103 93
    public function setMaxQueryDepth($maxQueryDepth)
104
    {
105
        /** @var QueryDepth $queryDepth */
106 93
        $queryDepth = DocumentValidator::getRule('QueryDepth');
107 93
        $queryDepth->setMaxQueryDepth($maxQueryDepth);
108 93
    }
109
110 93
    public function setMaxQueryComplexity($maxQueryComplexity)
111
    {
112
        /** @var QueryComplexity $queryComplexity */
113 93
        $queryComplexity = DocumentValidator::getRule('QueryComplexity');
114 93
        $queryComplexity->setMaxQueryComplexity($maxQueryComplexity);
115 93
    }
116
117 1
    public function disableIntrospectionQuery()
118
    {
119 1
        DocumentValidator::addRule(new DisableIntrospection());
120 1
    }
121
122
    /**
123
     * @param null|string                    $schemaName
124
     * @param array                          $request
125
     * @param null|array|\ArrayObject|object $rootValue
126
     *
127
     * @return ExecutionResult
128
     */
129 83
    public function execute($schemaName, array $request, $rootValue = null)
130
    {
131 83
        $executorArgumentsEvent = $this->preExecute(
132 83
            $this->getSchema($schemaName),
133 82
            isset($request[ParserInterface::PARAM_QUERY]) ? $request[ParserInterface::PARAM_QUERY] : null,
134 82
            new \ArrayObject(),
135 82
            $rootValue,
136 82
            $request[ParserInterface::PARAM_VARIABLES],
137 82
            isset($request[ParserInterface::PARAM_OPERATION_NAME]) ? $request[ParserInterface::PARAM_OPERATION_NAME] : null
138
        );
139
140 81
        $executorArgumentsEvent->getSchema()->processExtensions();
141
142 81
        $result = $this->executor->execute(
143 81
            $executorArgumentsEvent->getSchema(),
144 81
            $executorArgumentsEvent->getRequestString(),
145 81
            $executorArgumentsEvent->getRootValue(),
146 81
            $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 81
            $executorArgumentsEvent->getVariableValue(),
148 81
            $executorArgumentsEvent->getOperationName()
149
        );
150
151 81
        $result = $this->postExecute($result);
152
153 77
        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 82
    private function preExecute(
167
        Schema $schema,
168
        $requestString,
169
        \ArrayObject $contextValue,
170
        $rootValue = null,
171
        array $variableValue = null,
172
        $operationName = null
173
    ) {
174 82
        $this->checkPromiseAdapter();
175
176 81
        $this->executor->setPromiseAdapter($this->promiseAdapter);
177
        // this is needed when not using only generated types
178 81
        if ($this->defaultFieldResolver) {
179 79
            $this->executor->setDefaultFieldResolver($this->defaultFieldResolver);
180
        }
181 81
        $this->dispatcher->dispatch(Events::EXECUTOR_CONTEXT, new ExecutorContextEvent($contextValue));
182
183 81
        return $this->dispatcher->dispatch(
184 81
            Events::PRE_EXECUTOR,
185 81
            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 81
    private function postExecute($result)
195
    {
196 81
        if ($result instanceof Promise) {
197 79
            $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 81
        $this->checkExecutionResult($result);
201
202 79
        return  $this->dispatcher->dispatch(Events::POST_EXECUTOR, new ExecutorResultEvent($result))->getResult();
203
    }
204
205 82
    private function checkPromiseAdapter()
206
    {
207 82
        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 81
    }
217
218 81
    private function checkExecutionResult($result)
219
    {
220 81
        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 79
    }
226
}
227