HttpClient   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Importance

Changes 11
Bugs 0 Features 0
Metric Value
wmc 16
eloc 40
c 11
b 0
f 0
dl 0
loc 180
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A sendRequest() 0 9 1
A getClient() 0 10 2
A setClient() 0 3 1
A setRequestFactory() 0 3 1
A parseOptions() 0 21 6
A getAnalyticsResponse() 0 3 1
A post() 0 4 1
A getRequestFactory() 0 7 2
A batch() 0 12 1
1
<?php
2
3
namespace TheIconic\Tracking\GoogleAnalytics\Network;
4
5
6
use Http\Discovery\MessageFactoryDiscovery;
7
use TheIconic\Tracking\GoogleAnalytics\AnalyticsResponse;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Http\Client\HttpAsyncClient;
11
use Http\Discovery\HttpAsyncClientDiscovery;
12
use Http\Discovery\Psr17FactoryDiscovery;
13
use Http\Message\RequestFactory;
14
use Http\Promise\Promise;
15
16
use React\Http\Browser;
17
use React\EventLoop\Factory;
18
19
/**
20
 * Class HttpClient
21
 *
22
 * @package TheIconic\Tracking\GoogleAnalytics
23
 */
24
class HttpClient
25
{
26
    /**
27
     * User agent for the client.
28
     */
29
    const PHP_GA_MEASUREMENT_PROTOCOL_USER_AGENT =
30
        'THE ICONIC GA Measurement Protocol PHP Client (https://github.com/theiconic/php-ga-measurement-protocol)';
31
32
    /**
33
     * Timeout in seconds for the request connection and actual request execution.
34
     * Using the same value you can find in Google's PHP Client.
35
     */
36
    const REQUEST_TIMEOUT_SECONDS = 100;
37
38
    /**
39
     * HTTP client.
40
     *
41
     * @var Browser
42
     */
43
    private $client;
44
45
    /**
46
     * @var Factory
47
     */
48
    private $requestFactory = null;
49
50
    /**
51
     * Holds the promises (async responses).
52
     *
53
     * @var Promise[]
54
     */
55
    private static $promises = [];
0 ignored issues
show
introduced by
The private property $promises is not used, and could be removed.
Loading history...
56
57
    /**
58
     * Sets HTTP client.
59
     *
60
     * @internal
61
     * @param Browser $client
62
     */
63
    public function setClient(Browser $client)
64
    {
65
        $this->client = $client;
66
    }
67
68
    /**
69
     * Gets HTTP client for internal class use.
70
     *
71
     * @return Browser
72
     *
73
     * @throws \Http\Discovery\Exception\NotFoundException
74
     */
75
    private function getClient()
76
    {
77
        if ($this->client === null) {
78
            // @codeCoverageIgnoreStart
79
            //$loop = \React\EventLoop\Factory::create();
80
            $this->setClient(new Browser($this->getRequestFactory()));
0 ignored issues
show
Bug introduced by
$this->getRequestFactory() of type React\EventLoop\Factory is incompatible with the type React\EventLoop\LoopInterface expected by parameter $loop of React\Http\Browser::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

80
            $this->setClient(new Browser(/** @scrutinizer ignore-type */ $this->getRequestFactory()));
Loading history...
81
        }
82
        // @codeCoverageIgnoreEnd
83
84
        return $this->client;
85
    }
86
87
    /**
88
     * Sets HTTP request factory.
89
     *
90
     * @param $requestFactory
91
     *
92
     * @internal
93
     */
94
    public function setRequestFactory($requestFactory)
95
    {
96
        $this->requestFactory = $requestFactory;
97
    }
98
99
    /**
100
     * @return Factory|null
101
     *
102
     * @throws \Http\Discovery\Exception\NotFoundException
103
     */
104
    private function getRequestFactory()
105
    {
106
        if (null === $this->requestFactory) {
107
            $this->setRequestFactory(Factory::create());
108
        }
109
110
        return $this->requestFactory;
111
    }
112
113
    /**
114
     * Sends request to Google Analytics.
115
     *
116
     * @internal
117
     * @param string $url
118
     * @param array $options
119
     * @return AnalyticsResponse
120
     *
121
     * @throws \Exception If processing the request is impossible (eg. bad configuration).
122
     * @throws \Http\Discovery\Exception\NotFoundException
123
     */
124
    public function post($url, array $options = [])
125
    {
126
127
        return $this->sendRequest($url, $options);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->sendRequest($url, $options) targeting TheIconic\Tracking\Googl...tpClient::sendRequest() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
128
    }
129
130
    /**
131
     * Sends batch request to Google Analytics.
132
     *
133
     * @internal
134
     * @param string $url
135
     * @param array $batchUrls
136
     * @param array $options
137
     * @return AnalyticsResponse
138
     */
139
    public function batch($url, array $batchUrls, array $options = [])
140
    {
141
        $body = implode(PHP_EOL, $batchUrls);
142
143
        $request = $this->getRequestFactory()->createReques(
0 ignored issues
show
Bug introduced by
The method createReques() does not exist on React\EventLoop\Factory. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
        $request = $this->getRequestFactory()->/** @scrutinizer ignore-call */ createReques(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144
            'POST',
145
            $url,
146
            ['User-Agent' => self::PHP_GA_MEASUREMENT_PROTOCOL_USER_AGENT],
147
            $body
148
        );
149
150
        return $this->sendRequest($request, $options);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->sendRequest($request, $options) targeting TheIconic\Tracking\Googl...tpClient::sendRequest() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
151
    }
152
153
    private function sendRequest($request, array $options = [])
154
    {
155
        $opts = $this->parseOptions($options);
0 ignored issues
show
Unused Code introduced by
The assignment to $opts is dead and can be removed.
Loading history...
156
        $response = $this->getClient()->get( $request )->then(
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
157
          function ($request, ResponseInterface $response) {
158
            return $this->getAnalyticsResponse($request, $response);
159
          });
160
161
          $this->getRequestFactory()->run();
0 ignored issues
show
Bug introduced by
The method run() does not exist on React\EventLoop\Factory. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

161
          $this->getRequestFactory()->/** @scrutinizer ignore-call */ run();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
162
163
    }
164
165
    /**
166
     * Parse the given options and fill missing fields with default values.
167
     *
168
     * @param array $options
169
     * @return array
170
     */
171
    private function parseOptions(array $options)
172
    {
173
        $defaultOptions = [
174
            'timeout' => static::REQUEST_TIMEOUT_SECONDS,
175
            'async' => false,
176
        ];
177
178
        $opts = [];
179
        foreach ($defaultOptions as $option => $value) {
180
            $opts[$option] = isset($options[$option]) ? $options[$option] : $defaultOptions[$option];
181
        }
182
183
        if (!is_int($opts['timeout']) || $opts['timeout'] <= 0) {
184
            throw new \UnexpectedValueException('The timeout must be an integer with a value greater than 0');
185
        }
186
187
        if (!is_bool($opts['async'])) {
188
            throw new \UnexpectedValueException('The async option must be boolean');
189
        }
190
191
        return $opts;
192
    }
193
194
    /**
195
     * Creates an analytics response object.
196
     *
197
     * @param RequestInterface $request
198
     * @param ResponseInterface|PromiseInterface $response
0 ignored issues
show
Bug introduced by
The type TheIconic\Tracking\Googl...etwork\PromiseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
199
     * @return AnalyticsResponse
200
     */
201
    protected function getAnalyticsResponse(RequestInterface $request, $response)
202
    {
203
        return new AnalyticsResponse($request, $response);
204
    }
205
}
206