Completed
Pull Request — master (#135)
by
unknown
11:01
created

ProfileClient::getStopwatchEventName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Http\HttplugBundle\Collector;
4
5
use Http\Client\Common\FlexibleHttpClient;
6
use Http\Client\Exception\HttpException;
7
use Http\Client\HttpAsyncClient;
8
use Http\Client\HttpClient;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Symfony\Component\Stopwatch\Stopwatch;
12
use Symfony\Component\Stopwatch\StopwatchEvent;
13
14
/**
15
 * The ProfileClient decorates any client that implement both HttpClient and HttpAsyncClient interfaces to gather target
16
 * url and response status code.
17
 *
18
 * @author Fabien Bourigault <[email protected]>
19
 *
20
 * @internal
21
 */
22
class ProfileClient implements HttpClient, HttpAsyncClient
23
{
24
    /**
25
     * @var HttpClient|HttpAsyncClient
26
     */
27
    private $client;
28
29
    /**
30
     * @var Collector
31
     */
32
    private $collector;
33
34
    /**
35
     * @var Formatter
36
     */
37
    private $formatter;
38
39
    /**
40
     * @var Stopwatch
41
     */
42
    private $stopwatch;
43 6
44
    /**
45 6
     * @param HttpClient|HttpAsyncClient $client    The client to profile. Client must implement both HttpClient and
46
     *                                              HttpAsyncClient interfaces.
47
     * @param Collector                  $collector
48
     * @param Formatter                  $formatter
49
     * @param Stopwatch                  $stopwatch
50
     */
51
    public function __construct($client, Collector $collector, Formatter $formatter, Stopwatch $stopwatch)
52
    {
53
        if (!($client instanceof HttpClient && $client instanceof HttpAsyncClient)) {
54
            throw new \RuntimeException(sprintf(
55
                '%s first argument must implement %s and %s. Consider using %s.',
56
                    __METHOD__,
57
                    HttpClient::class,
58
                    HttpAsyncClient::class,
59
                    FlexibleHttpClient::class
60
            ));
61
        }
62
        $this->client = $client;
63
        $this->collector = $collector;
64
        $this->formatter = $formatter;
65
        $this->stopwatch = $stopwatch;
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function sendAsyncRequest(RequestInterface $request)
72
    {
73
        $stack = $this->collector->getCurrentStack();
74
        $this->collectRequestInformations($request, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 73 can also be of type boolean; however, Http\HttplugBundle\Colle...ctRequestInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
75
        $event = $this->stopwatch->start($this->getStopwatchEventName($request));
76
77
        return $this->client->sendAsyncRequest($request)->then(
0 ignored issues
show
Bug introduced by
The method sendAsyncRequest does only exist in Http\Client\HttpAsyncClient, but not in Http\Client\HttpClient.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
78
            function (ResponseInterface $response) use ($event, $stack) {
79
                $event->stop();
80
                $this->collectResponseInformations($response, $event, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 73 can also be of type boolean; however, Http\HttplugBundle\Colle...tResponseInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
81
82
                return $response;
83
            }, function (\Exception $exception) use ($event, $stack) {
84
                $this->collectExceptionInformations($exception, $event, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 73 can also be of type boolean; however, Http\HttplugBundle\Colle...ExceptionInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85
86
                throw $exception;
87
            }
88
        );
89
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function sendRequest(RequestInterface $request)
95
    {
96
        $stack = $this->collector->getCurrentStack();
97
        $this->collectRequestInformations($request, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 96 can also be of type boolean; however, Http\HttplugBundle\Colle...ctRequestInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
98
        $event = $this->stopwatch->start($this->getStopwatchEventName($request));
99
100
        try {
101
            $response = $this->client->sendRequest($request);
0 ignored issues
show
Bug introduced by
The method sendRequest does only exist in Http\Client\HttpClient, but not in Http\Client\HttpAsyncClient.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
102
            $event->stop();
103
104
            $this->collectResponseInformations($response, $event, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 96 can also be of type boolean; however, Http\HttplugBundle\Colle...tResponseInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
105
106
            return $response;
107
        } catch (\Exception $e) {
108
            $event->stop();
109
            $this->collectExceptionInformations($e, $event, $stack);
0 ignored issues
show
Bug introduced by
It seems like $stack defined by $this->collector->getCurrentStack() on line 96 can also be of type boolean; however, Http\HttplugBundle\Colle...ExceptionInformations() does only seem to accept null|object<Http\HttplugBundle\Collector\Stack>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
110
111
            throw $e;
112
        }
113
    }
114
115
    /**
116
     * @param RequestInterface $request
117
     * @param Stack|null       $stack
118
     */
119
    private function collectRequestInformations(RequestInterface $request, Stack $stack = null)
120
    {
121
        if (!$stack) {
122
            return;
123
        }
124
125
        $stack->setRequestTarget($request->getRequestTarget());
126
        $stack->setRequestMethod($request->getMethod());
127
        $stack->setRequestScheme($request->getUri()->getScheme());
128
        $stack->setRequestHost($request->getUri()->getHost());
129
        $stack->setClientRequest($this->formatter->formatRequest($request));
130
    }
131
132
    /**
133
     * @param ResponseInterface $response
134
     * @param StopwatchEvent    $event
135
     * @param Stack|null        $stack
136
     */
137
    private function collectResponseInformations(ResponseInterface $response, StopwatchEvent $event, Stack $stack = null)
138
    {
139
        if (!$stack) {
140
            return;
141
        }
142
143
        $stack->setDuration($event->getDuration());
144
        $stack->setResponseCode($response->getStatusCode());
145
        $stack->setClientResponse($this->formatter->formatResponse($response));
146
    }
147
148
    /**
149
     * @param \Exception     $exception
150
     * @param StopwatchEvent $event
151
     * @param Stack|null     $stack
152
     */
153
    private function collectExceptionInformations(\Exception $exception, StopwatchEvent $event, Stack $stack = null)
154
    {
155
        if ($exception instanceof HttpException) {
156
            $this->collectResponseInformations($exception->getResponse(), $event, $stack);
157
        }
158
159
        if (!$stack) {
160
            return;
161
        }
162
163
        $stack->setDuration($event->getDuration());
164
        $stack->setClientException($this->formatter->formatException($exception));
165
    }
166
167
    /**
168
     * Generates the event name.
169
     *
170
     * @param RequestInterface $request
171
     *
172
     * @return string
173
     */
174
    private function getStopwatchEventName(RequestInterface $request)
175
    {
176
        return sprintf('%s %s', $request->getMethod(), $request->getUri()->__toString());
177
    }
178
}
179