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
Pull Request — master (#275)
by Hugo
15:12
created

Executor::enabledDebugInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
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\QueryComplexity;
11
use GraphQL\Validator\Rules\QueryDepth;
12
use Overblog\GraphQLBundle\Event\Events;
13
use Overblog\GraphQLBundle\Event\ExecutorArgumentsEvent;
14
use Overblog\GraphQLBundle\Event\ExecutorContextEvent;
15
use Overblog\GraphQLBundle\Event\ExecutorResultEvent;
16
use Overblog\GraphQLBundle\Executor\ExecutorInterface;
17
use Overblog\GraphQLBundle\Executor\Promise\PromiseAdapterInterface;
18
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
20
21
class Executor
22
{
23
    const PROMISE_ADAPTER_SERVICE_ID = 'overblog_graphql.promise_adapter';
24
25
    /** @var Schema[] */
26
    private $schemas;
27
28
    /** @var EventDispatcherInterface|null */
29
    private $dispatcher;
30
31
    /** @var ExecutorInterface */
32
    private $executor;
33
34
    /** @var PromiseAdapter */
35
    private $promiseAdapter;
36
37
    /** @var callable|null */
38
    private $defaultFieldResolver;
39
40 77
    public function __construct(
41
        ExecutorInterface $executor,
42
        EventDispatcherInterface $dispatcher,
43
        PromiseAdapter $promiseAdapter,
44
        callable $defaultFieldResolver = null
45
    ) {
46 77
        $this->executor = $executor;
47 77
        $this->dispatcher = $dispatcher;
48 77
        $this->promiseAdapter = $promiseAdapter;
49 77
        $this->defaultFieldResolver = $defaultFieldResolver;
50 77
    }
51
52 2
    public function setExecutor(ExecutorInterface $executor)
53
    {
54 2
        $this->executor = $executor;
55
56 2
        return $this;
57
    }
58
59 1
    public function setPromiseAdapter(PromiseAdapter $promiseAdapter)
60
    {
61 1
        $this->promiseAdapter = $promiseAdapter;
62
63 1
        return $this;
64
    }
65
66
    /**
67
     * @param string $name
68
     * @param Schema $schema
69
     *
70
     * @return $this
71
     */
72 69
    public function addSchema($name, Schema $schema)
73
    {
74 69
        $this->schemas[$name] = $schema;
75
76 69
        return $this;
77
    }
78
79
    /**
80
     * @param string|null $name
81
     *
82
     * @return Schema
83
     */
84 65
    public function getSchema($name = null)
85
    {
86 65
        if (empty($this->schemas)) {
87 1
            throw new \RuntimeException('At least one schema should be declare.');
88
        }
89
90 64
        if (null === $name) {
91 61
            $schema = array_values($this->schemas)[0];
92
        } else {
93 3
            if (!isset($this->schemas[$name])) {
94 1
                throw new NotFoundHttpException(sprintf('Could not found "%s" schema.', $name));
95
            }
96 2
            $schema = $this->schemas[$name];
97
        }
98
99 63
        return $schema;
100
    }
101
102 73
    public function setMaxQueryDepth($maxQueryDepth)
103
    {
104
        /** @var QueryDepth $queryDepth */
105 73
        $queryDepth = DocumentValidator::getRule('QueryDepth');
106 73
        $queryDepth->setMaxQueryDepth($maxQueryDepth);
107 73
    }
108
109 73
    public function setMaxQueryComplexity($maxQueryComplexity)
110
    {
111
        /** @var QueryComplexity $queryComplexity */
112 73
        $queryComplexity = DocumentValidator::getRule('QueryComplexity');
113 73
        $queryComplexity->setMaxQueryComplexity($maxQueryComplexity);
114 73
    }
115
116
    /**
117
     * @param null|string                    $schemaName
118
     * @param array                          $request
119
     * @param null|array|\ArrayObject|object $rootValue
120
     *
121
     * @return ExecutionResult
122
     */
123 63
    public function execute($schemaName, array $request, $rootValue = null)
124
    {
125 63
        $executorArgumentsEvent = $this->preExecute(
126 63
            $this->getSchema($schemaName),
127 62
            isset($request[ParserInterface::PARAM_QUERY]) ? $request[ParserInterface::PARAM_QUERY] : null,
128 62
            new \ArrayObject(),
129 62
            $rootValue,
130 62
            $request[ParserInterface::PARAM_VARIABLES],
131 62
            isset($request[ParserInterface::PARAM_OPERATION_NAME]) ? $request[ParserInterface::PARAM_OPERATION_NAME] : null
132
        );
133
134 61
        $result = $this->executor->execute(
135 61
            $executorArgumentsEvent->getSchema(),
136 61
            $executorArgumentsEvent->getRequestString(),
137 61
            $executorArgumentsEvent->getRootValue(),
138 61
            $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...
139 61
            $executorArgumentsEvent->getVariableValue(),
140 61
            $executorArgumentsEvent->getOperationName()
141
        );
142
143 61
        $result = $this->postExecute($result);
144
145 59
        return $result;
146
    }
147
148
    /**
149
     * @param Schema       $schema
150
     * @param string       $requestString
151
     * @param \ArrayObject $contextValue
152
     * @param mixed        $rootValue
153
     * @param array|null   $variableValue
154
     * @param string|null  $operationName
155
     *
156
     * @return ExecutorArgumentsEvent
157
     */
158 62
    private function preExecute(
159
        Schema $schema,
160
        $requestString,
161
        \ArrayObject $contextValue,
162
        $rootValue = null,
163
        array $variableValue = null,
164
        $operationName = null
165
    ) {
166 62
        $this->checkPromiseAdapter();
167
168 61
        $this->executor->setPromiseAdapter($this->promiseAdapter);
169
        // this is needed when not using only generated types
170 61
        if ($this->defaultFieldResolver) {
171 59
            $this->executor->setDefaultFieldResolver($this->defaultFieldResolver);
172
        }
173 61
        $this->dispatcher->dispatch(Events::EXECUTOR_CONTEXT, new ExecutorContextEvent($contextValue));
174
175 61
        return $this->dispatcher->dispatch(
176 61
            Events::PRE_EXECUTOR,
177 61
            ExecutorArgumentsEvent::create($schema, $requestString, $contextValue, $rootValue, $variableValue, $operationName)
178
        );
179
    }
180
181
    /**
182
     * @param ExecutionResult|Promise $result
183
     *
184
     * @return ExecutionResult
185
     */
186 61
    private function postExecute($result)
187
    {
188 61
        if ($result instanceof Promise) {
189 59
            $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...
190
        }
191
192 61
        $this->checkExecutionResult($result);
193
194 59
        return  $this->dispatcher->dispatch(Events::POST_EXECUTOR, new ExecutorResultEvent($result))->getResult();
195
    }
196
197 62
    private function checkPromiseAdapter()
198
    {
199 62
        if ($this->promiseAdapter && !$this->promiseAdapter instanceof PromiseAdapterInterface && !is_callable([$this->promiseAdapter, 'wait'])) {
200 1
            throw new \RuntimeException(
201 1
                sprintf(
202 1
                    'PromiseAdapter should be an object instantiating "%s" or "%s" with a "wait" method.',
203 1
                    PromiseAdapterInterface::class,
204 1
                    PromiseAdapter::class
205
                )
206
            );
207
        }
208 61
    }
209
210 61
    private function checkExecutionResult($result)
211
    {
212 61
        if (!is_object($result) || !$result instanceof ExecutionResult) {
213 2
            throw new \RuntimeException(
214 2
                sprintf('Execution result should be an object instantiating "%s".', ExecutionResult::class)
215
            );
216
        }
217 59
    }
218
}
219