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 (#137)
by Jérémiah
08:18
created

Executor::getSchema()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.432

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 7
cts 10
cp 0.7
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
nop 1
crap 4.432
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\Request;
13
14
use GraphQL\Executor\ExecutionResult;
15
use GraphQL\Executor\Promise\PromiseAdapter;
16
use GraphQL\Schema;
17
use GraphQL\Validator\DocumentValidator;
18
use GraphQL\Validator\Rules\QueryComplexity;
19
use GraphQL\Validator\Rules\QueryDepth;
20
use Overblog\GraphQLBundle\Error\ErrorHandler;
21
use Overblog\GraphQLBundle\Event\Events;
22
use Overblog\GraphQLBundle\Event\ExecutorContextEvent;
23
use Overblog\GraphQLBundle\Executor\ExecutorInterface;
24
use Overblog\GraphQLBundle\Executor\Promise\PromiseAdapterInterface;
25
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
27
28
class Executor
29
{
30
    const PROMISE_ADAPTER_SERVICE_ID = 'overblog_graphql.promise_adapter';
31
32
    /**
33
     * @var Schema[]
34
     */
35
    private $schemas;
36
37
    /**
38
     * @var EventDispatcherInterface|null
39
     */
40
    private $dispatcher;
41
42
    /** @var bool */
43
    private $throwException;
44
45
    /** @var ErrorHandler|null */
46
    private $errorHandler;
47
48
    /** @var bool */
49
    private $hasDebugInfo;
50
51
    /**
52
     * @var ExecutorInterface
53
     */
54
    private $executor;
55
56
    /**
57
     * @var PromiseAdapter
58
     */
59
    private $promiseAdapter;
60
61 6
    public function __construct(
62
        ExecutorInterface $executor,
63
        EventDispatcherInterface $dispatcher = null,
64
        $throwException = false,
65
        ErrorHandler $errorHandler = null,
66
        $hasDebugInfo = false,
67
        PromiseAdapter $promiseAdapter = null
68
    ) {
69 6
        $this->executor = $executor;
70 6
        $this->dispatcher = $dispatcher;
71 6
        $this->throwException = (bool) $throwException;
72 6
        $this->errorHandler = $errorHandler;
73 6
        $hasDebugInfo ? $this->enabledDebugInfo() : $this->disabledDebugInfo();
74 6
        $this->promiseAdapter = $promiseAdapter;
75 6
    }
76
77 2
    public function setExecutor(ExecutorInterface $executor)
78
    {
79 2
        $this->executor = $executor;
80
81 2
        return $this;
82
    }
83
84 1
    public function setPromiseAdapter(PromiseAdapter $promiseAdapter = null)
85
    {
86 1
        $this->promiseAdapter = $promiseAdapter;
87
88 1
        return $this;
89
    }
90
91 6
    public function addSchema($name, Schema $schema)
92
    {
93 6
        $this->schemas[$name] = $schema;
94
95 6
        return $this;
96
    }
97
98 1
    public function enabledDebugInfo()
99
    {
100 1
        $this->hasDebugInfo = true;
101
102 1
        return $this;
103
    }
104
105 6
    public function disabledDebugInfo()
106
    {
107 6
        $this->hasDebugInfo = false;
108
109 6
        return $this;
110
    }
111
112 2
    public function hasDebugInfo()
113
    {
114 2
        return $this->hasDebugInfo;
115
    }
116
117
    public function setMaxQueryDepth($maxQueryDepth)
118
    {
119
        /** @var QueryDepth $queryDepth */
120
        $queryDepth = DocumentValidator::getRule('QueryDepth');
121
        $queryDepth->setMaxQueryDepth($maxQueryDepth);
122
    }
123
124
    public function setMaxQueryComplexity($maxQueryComplexity)
125
    {
126
        /** @var QueryComplexity $queryComplexity */
127
        $queryComplexity = DocumentValidator::getRule('QueryComplexity');
128
        $queryComplexity->setMaxQueryComplexity($maxQueryComplexity);
129
    }
130
131
    /**
132
     * @param bool $throwException
133
     *
134
     * @return $this
135
     */
136
    public function setThrowException($throwException)
137
    {
138
        $this->throwException = (bool) $throwException;
139
140
        return $this;
141
    }
142
143 5
    public function execute(array $data, array $context = [], $schemaName = null)
144
    {
145 5
        if (null !== $this->dispatcher) {
146
            $event = new ExecutorContextEvent($context);
147
            $this->dispatcher->dispatch(Events::EXECUTOR_CONTEXT, $event);
148
            $context = $event->getExecutorContext();
149
        }
150
151 5
        if ($this->promiseAdapter) {
152 1
            if (!$this->promiseAdapter instanceof PromiseAdapterInterface && !is_callable([$this->promiseAdapter, 'wait'])) {
153 1
                throw new \RuntimeException(
154 1
                    sprintf(
155 1
                        'PromiseAdapter should be an object instantiating "%s" or "%s" with a "wait" method.',
156 1
                        PromiseAdapterInterface::class,
157
                        PromiseAdapter::class
158 1
                    )
159 1
                );
160
            }
161
        }
162
163 4
        $schema = $this->getSchema($schemaName);
164
165 4
        $startTime = microtime(true);
166 4
        $startMemoryUsage = memory_get_usage(true);
167
168 4
        $this->executor->setPromiseAdapter($this->promiseAdapter);
169
170 4
        $result = $this->executor->execute(
171 4
            $schema,
172 4
            isset($data[ParserInterface::PARAM_QUERY]) ? $data[ParserInterface::PARAM_QUERY] : null,
173 4
            $context,
174 4
            $context,
175 4
            $data[ParserInterface::PARAM_VARIABLES],
176 4
            isset($data[ParserInterface::PARAM_OPERATION_NAME]) ? $data[ParserInterface::PARAM_OPERATION_NAME] : null
177 4
        );
178
179 4
        if ($this->promiseAdapter && $this->promiseAdapter->isThenable($result)) {
180
            $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...
181
        }
182
183 4
        if (!is_object($result) || !$result instanceof ExecutionResult) {
184 2
            throw new \RuntimeException(
185 2
                sprintf('Execution result should be an object instantiating "%s".', ExecutionResult::class)
186 2
            );
187
        }
188
189 2
        return $this->prepareResult($result, $startTime, $startMemoryUsage);
190
    }
191
192 2
    private function prepareResult($result, $startTime, $startMemoryUsage)
193
    {
194 2
        if ($this->hasDebugInfo()) {
195 1
            $result->extensions['debug'] = [
196 1
                'executionTime' => sprintf('%d ms', round(microtime(true) - $startTime, 3) * 1000),
197 1
                'memoryUsage' => sprintf('%.2F MiB', (memory_get_usage(true) - $startMemoryUsage) / 1024 / 1024),
198
            ];
199 1
        }
200
201 2
        if (null !== $this->errorHandler) {
202
            $this->errorHandler->handleErrors($result, $this->throwException);
203
        }
204
205 2
        return $result;
206
    }
207
208
    /**
209
     * @param string|null $name
210
     *
211
     * @return Schema
212
     */
213 5
    public function getSchema($name = null)
214
    {
215 5
        if (empty($this->schemas)) {
216 1
            throw new \RuntimeException('At least one schema should be declare.');
217
        }
218
219 4
        if (null === $name) {
220 4
            $schema = array_values($this->schemas)[0];
221 4
        } else {
222
            if (!isset($this->schemas[$name])) {
223
                throw new NotFoundHttpException(sprintf('Could not found "%s" schema.', $name));
224
            }
225
            $schema = $this->schemas[$name];
226
        }
227
228 4
        return $schema;
229
    }
230
}
231