GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 28fbd7...75c995 )
by Cees-Jan
06:12
created

Client::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 11
nc 2
nop 4
crap 2
1
<?php declare(strict_types=1);
2
3
namespace ApiClients\Foundation\Transport;
4
5
use ApiClients\Foundation\Middleware\Locator\Locator;
6
use ApiClients\Foundation\Middleware\MiddlewareRunner;
7
use ApiClients\Foundation\Transport\CommandBus;
8
use Clue\React\Buzz\Browser;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use React\EventLoop\LoopInterface;
12
use React\Promise\PromiseInterface;
13
use RingCentral\Psr7\Uri;
14
use Throwable;
15
use function ApiClients\Foundation\options_merge;
16
use function React\Promise\reject;
17
use function React\Promise\resolve;
18
19
final class Client implements ClientInterface
20
{
21
    const DEFAULT_OPTIONS = [
22
        Options::SCHEMA => 'https',
23
        Options::PATH => '/',
24
        Options::HEADERS => [],
25
    ];
26
27
    /**
28
     * @var LoopInterface
29
     */
30
    protected $loop;
31
32
    /**
33
     * @var Locator
34
     */
35
    protected $locator;
36
37
    /**
38
     * @var Browser
39
     */
40
    protected $browser;
41
42
    /**
43
     * @var array
44
     */
45
    protected $options = [];
46
47
    /**
48
     * @var string[]
49
     */
50
    protected $middleware = [];
51
52
    /**
53
     * @param LoopInterface $loop
54
     * @param Locator $locator
55
     * @param Browser $buzz
56
     * @param array $options
57
     */
58 17
    public function __construct(
59
        LoopInterface $loop,
60
        Locator $locator,
61
        Browser $buzz,
62
        array $options = []
63
    ) {
64 17
        $this->loop = $loop;
65 17
        $this->locator = $locator;
66 17
        $this->browser = $buzz;
67 17
        $this->options = $options + self::DEFAULT_OPTIONS;
68
69 17
        if (isset($this->options[Options::MIDDLEWARE])) {
70 16
            $this->middleware = $this->options[Options::MIDDLEWARE];
71
        }
72 17
    }
73
74 16
    protected function constructMiddlewares(array $options): MiddlewareRunner
75
    {
76 16
        $set = $this->middleware;
77
78 16
        if (isset($options[Options::MIDDLEWARE])) {
79
            $set = $this->combinedMiddlewares($options[Options::MIDDLEWARE]);
80
        }
81
82 16
        $args = [];
83 16
        $args[] = $options;
84 16
        foreach ($set as $middleware) {
85 16
            $args[] = $this->locator->get($middleware);
86
        }
87
88 16
        return new MiddlewareRunner(...$args);
89
    }
90
91
    protected function combinedMiddlewares(array $extraMiddlewares): array
92
    {
93
        $set = $this->middleware;
94
95
        foreach ($extraMiddlewares as $middleware) {
96
            if (in_array($middleware, $set)) {
97
                continue;
98
            }
99
100
            $set[] = $middleware;
101
        }
102
103
        return $set;
104
    }
105
106
    /**
107
     * @param RequestInterface $request
108
     * @param array $options
109
     * @return PromiseInterface
110
     */
111 16
    public function request(RequestInterface $request, array $options = []): PromiseInterface
112
    {
113 16
        $options = $this->applyRequestOptions($options);
114 16
        $request = $this->applyApiSettingsToRequest($request, $options);
115 16
        $executioner = $this->constructMiddlewares($options);
116
117
        return $executioner->pre($request)->then(function ($request) use ($options) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface React\Promise\PromiseInterface as the method otherwise() does only exist in the following implementations of said interface: React\Promise\FulfilledPromise, React\Promise\LazyPromise, React\Promise\Promise, React\Promise\RejectedPromise.

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...
118 16
            return resolve($this->browser->send(
119 16
                $request
120
            ));
121
        }, function (ResponseInterface $response) {
122
            return resolve($response);
123
        })->then(function (ResponseInterface $response) use ($executioner) {
124 8
            return $executioner->post($response);
125 16
        })->otherwise(function (Throwable $throwable) use ($executioner) {
126 8
            return reject($executioner->error($throwable));
127 16
        });
128
    }
129
130 16
    protected function applyApiSettingsToRequest(RequestInterface $request, array $options): RequestInterface
131
    {
132 16
        $options = array_replace_recursive($this->options, $options);
133 16
        $uri = $request->getUri();
134 16
        if (strpos((string)$uri, '://') === false) {
135 4
            $uri = Uri::resolve(
136 4
                new Uri(
137 4
                    $options[Options::SCHEMA] .
138 4
                    '://' .
139 4
                    $options[Options::HOST] .
140 4
                    $options[Options::PATH]
141
                ),
142 4
                $request->getUri()
143
            );
144
        }
145
146 16
        foreach ($options[Options::HEADERS] as $key => $value) {
147 10
            $request = $request->withAddedHeader($key, $value);
148
        }
149
150 16
        return $request->withUri($uri);
151
    }
152
153 16
    public function applyRequestOptions(array $options): array
154
    {
155 16
        if (!isset($this->options[Options::DEFAULT_REQUEST_OPTIONS])) {
156 16
            return $options;
157
        }
158
159
        return array_merge_recursive(
160
            $this->options[Options::DEFAULT_REQUEST_OPTIONS],
161
            $options
162
        );
163
    }
164
}
165