1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright © Thomas Klein, All rights reserved. |
4
|
|
|
* See LICENSE bundled with this library for license details. |
5
|
|
|
*/ |
6
|
|
|
declare(strict_types=1); |
7
|
|
|
|
8
|
|
|
namespace Zoho\Desk\Client; |
9
|
|
|
|
10
|
|
|
use Zoho\OAuth\Utility\ZohoOAuthConstants; |
11
|
|
|
use Zoho\Desk\Api\Metadata; |
12
|
|
|
use Zoho\Desk\Exception\Exception; |
13
|
|
|
use Zoho\Desk\Exception\InvalidArgumentException; |
14
|
|
|
use Zoho\Desk\OAuth\ClientInterface; |
15
|
|
|
use function array_merge; |
16
|
|
|
use function curl_init; |
17
|
|
|
use function curl_setopt; |
18
|
|
|
use function http_build_query; |
19
|
|
|
use function is_array; |
20
|
|
|
use function is_numeric; |
21
|
|
|
use function json_encode; |
22
|
|
|
use function sprintf; |
23
|
|
|
use const CURLOPT_CUSTOMREQUEST; |
24
|
|
|
use const CURLOPT_HEADER; |
25
|
|
|
use const CURLOPT_HTTPHEADER; |
26
|
|
|
use const CURLOPT_URL; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @api |
30
|
|
|
*/ |
31
|
|
|
final class RequestBuilder |
32
|
|
|
{ |
33
|
|
|
public const HTTP_GET = 'GET'; |
34
|
|
|
public const HTTP_POST = 'POST'; |
35
|
|
|
public const HTTP_PATCH = 'PATCH'; |
36
|
|
|
public const HTTP_DELETE = 'DELETE'; |
37
|
|
|
|
38
|
|
|
private const MANDATORY_FIELDS = ['entityType', 'method']; |
39
|
|
|
|
40
|
|
|
private ClientInterface $client; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var resource |
44
|
|
|
*/ |
45
|
|
|
private $curl; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var string[] |
49
|
|
|
*/ |
50
|
|
|
private array $mandatoryData; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
private array $data; |
56
|
|
|
|
57
|
|
|
public function __construct(ClientInterface $client, array $mandatoryData = []) |
58
|
|
|
{ |
59
|
|
|
$this->client = $client; |
60
|
|
|
$this->mandatoryData = array_merge(self::MANDATORY_FIELDS, $mandatoryData); |
61
|
|
|
$this->data = []; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
public function setEntityType(string $entityType): self |
65
|
|
|
{ |
66
|
|
|
$this->data['entityType'] = $entityType; |
67
|
|
|
|
68
|
|
|
return $this; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
public function setMethod(string $method): self |
72
|
|
|
{ |
73
|
|
|
$this->data['method'] = $method; |
74
|
|
|
|
75
|
|
|
return $this; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
public function setArguments(array $arguments): self |
79
|
|
|
{ |
80
|
|
|
$this->data['arguments'] = $arguments; |
81
|
|
|
|
82
|
|
|
return $this; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
public function setQueryParameters(array $params): self |
86
|
|
|
{ |
87
|
|
|
$this->data['queryParameters'] = $params; |
88
|
|
|
|
89
|
|
|
return $this; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
public function setFields(array $fields): self |
93
|
|
|
{ |
94
|
|
|
$this->data['fields'] = $fields; |
95
|
|
|
|
96
|
|
|
return $this; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @return RequestInterface |
101
|
|
|
* @throws InvalidArgumentException|Exception |
102
|
|
|
*/ |
103
|
|
|
public function create(): RequestInterface |
104
|
|
|
{ |
105
|
|
|
foreach ($this->mandatoryData as $mandatory) { |
106
|
|
|
if (!isset($this->data[$mandatory])) { |
107
|
|
|
throw InvalidArgumentException::createMandatoryFieldException($mandatory); |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$this->curl = curl_init(); |
|
|
|
|
112
|
|
|
curl_setopt($this->curl, CURLOPT_URL, $this->buildUrl()); |
113
|
|
|
curl_setopt($this->curl, CURLOPT_COOKIESESSION, true); |
114
|
|
|
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); |
115
|
|
|
curl_setopt($this->curl, CURLOPT_HEADER, 1); |
116
|
|
|
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $this->buildHeaders()); |
117
|
|
|
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $this->data['method']); |
118
|
|
|
|
119
|
|
|
if ($this->data['method'] !== self::HTTP_GET) { |
120
|
|
|
curl_setopt($this->curl, CURLOPT_POST, true); |
121
|
|
|
if ($this->isMultipart()) { |
122
|
|
|
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->data['fields']); |
123
|
|
|
} else { |
124
|
|
|
curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($this->data['fields'])); |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
$request = new Request($this->curl); |
129
|
|
|
|
130
|
|
|
$this->curl = null; |
131
|
|
|
$this->data = []; |
132
|
|
|
|
133
|
|
|
return $request; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @return string[] |
138
|
|
|
* @throws Exception |
139
|
|
|
*/ |
140
|
|
|
private function buildHeaders(): array |
141
|
|
|
{ |
142
|
|
|
$headers = [ |
143
|
|
|
ZohoOAuthConstants::AUTHORIZATION . ':' . ZohoOAuthConstants::OAUTH_HEADER_PREFIX . $this->client->getAccessToken(), |
144
|
|
|
'Content-Type: ' . ($this->isMultipart() ? 'multipart/form-data' : 'application/json'), |
145
|
|
|
]; |
146
|
|
|
|
147
|
|
|
if ($this->client->getOrgId()) { |
148
|
|
|
$headers[] = Metadata::ORG_ID . ':' . $this->client->getOrgId(); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
return $headers; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
private function isMultipart() { |
155
|
|
|
return isset($this->data['fields']['file']) && $this->data['fields']['file'] instanceof \CURLFile; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
private function buildUrl(): string |
159
|
|
|
{ |
160
|
|
|
$url = sprintf( |
161
|
|
|
'https://%s/%s/%s', |
162
|
|
|
$this->client->getApiBaseUrl(), |
163
|
|
|
$this->client->getApiVersion(), |
164
|
|
|
$this->data['entityType'] |
165
|
|
|
); |
166
|
|
|
|
167
|
|
|
if (isset($this->data['arguments']) && is_array($this->data['arguments'])) { |
168
|
|
|
foreach ($this->data['arguments'] as $key => $argument) { |
169
|
|
|
if (!is_numeric($key)) { |
170
|
|
|
$url .= '/' . $key; |
171
|
|
|
} |
172
|
|
|
$url .= '/' . $argument; |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
if (isset($this->data['queryParameters']) && is_array($this->data['queryParameters'])) { |
176
|
|
|
$url .= '?' . http_build_query($this->data['queryParameters']); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return $url; |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.