Middleware   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 74
dl 0
loc 185
rs 10
c 0
b 0
f 0
wmc 26

5 Methods

Rating   Name   Duplication   Size   Complexity  
B set_curl_options() 0 48 7
A handle_media() 0 20 5
A create_curl_handler() 0 23 6
A stream_write() 0 18 3
A stream_read() 0 27 5
1
<?php
2
3
namespace EasyHttp;
4
5
use EasyHttp\Contracts\CommonsContract;
6
use EasyHttp\Exceptions\ConnectionException;
7
use EasyHttp\Model\HttpOptions;
8
use EasyHttp\Utils\Toolkit;
9
10
/**
11
 * Middleware class
12
 *
13
 * @link    https://github.com/shahradelahi/easy-http
14
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
15
 * @license https://github.com/shahradelahi/easy-http/blob/master/LICENSE (MIT License)
16
 */
17
class Middleware
18
{
19
20
	/**
21
	 * Create curl handler.
22
	 *
23
	 * @param ?string $method
24
	 * @param string $uri
25
	 * @param array|HttpOptions $options
26
	 *
27
	 * @return \CurlHandle|false
28
	 */
29
	public static function create_curl_handler(?string $method, string $uri, array|HttpOptions $options = []): \CurlHandle|false
30
	{
31
		$handler = curl_init();
32
		if (is_resource($handler) || !$handler) {
33
			return false;
34
		}
35
36
		if (gettype($options) === 'array') {
37
			$options = new HttpOptions($options);
38
		}
39
40
		if (count($options->getQuery()) > 0) {
41
			if (!str_contains($uri, '?')) {
42
				$uri .= '?';
43
			}
44
			$uri .= $options->getQueryString();
45
		}
46
47
		curl_setopt($handler, CURLOPT_URL, $uri);
48
49
		self::set_curl_options($method, $handler, $options);
50
51
		return $handler;
52
	}
53
54
	/**
55
	 * Setup curl options based on the given method and our options.
56
	 *
57
	 * @param \CurlHandle $cHandler
58
	 * @param ?string $method
59
	 * @param HttpOptions $options
60
	 *
61
	 * @return void
62
	 */
63
	public static function set_curl_options(?string $method, \CurlHandle $cHandler, HttpOptions $options): void
64
	{
65
		curl_setopt($cHandler, CURLOPT_HEADER, true);
66
		curl_setopt($cHandler, CURLOPT_CUSTOMREQUEST, $method ?? 'GET');
67
68
		# Fetch the header
69
		$fetchedHeaders = [];
70
		foreach ($options->getHeaders() as $header => $value) {
71
			$fetchedHeaders[] = $header . ': ' . $value;
72
		}
73
74
		# Set headers
75
		curl_setopt($cHandler, CURLOPT_HTTPHEADER, $fetchedHeaders ?? []);
76
77
		# Add body if we have one.
78
		if ($options->getBody()) {
79
			curl_setopt($cHandler, CURLOPT_CUSTOMREQUEST, $method ?? 'POST');
80
			curl_setopt($cHandler, CURLOPT_POSTFIELDS, $options->getBody());
81
			curl_setopt($cHandler, CURLOPT_POST, true);
82
		}
83
84
		# Check for a proxy
85
		if ($options->getProxy() != null) {
86
			curl_setopt($cHandler, CURLOPT_PROXY, $options->getProxy()->getHost());
87
			curl_setopt($cHandler, CURLOPT_PROXYUSERPWD, $options->getProxy()->getAuth());
88
			if ($options->getProxy()->type !== null) {
89
				curl_setopt($cHandler, CURLOPT_PROXYTYPE, $options->getProxy()->type);
90
			}
91
		}
92
93
		curl_setopt($cHandler, CURLOPT_RETURNTRANSFER, true);
94
		curl_setopt($cHandler, CURLOPT_FOLLOWLOCATION, true);
95
96
		# Add and override the custom curl options.
97
		foreach ($options->getCurlOptions() as $option => $value) {
98
			curl_setopt($cHandler, $option, $value);
99
		}
100
101
		# if we have a timeout, set it.
102
		curl_setopt($cHandler, CURLOPT_TIMEOUT, $options->getTimeout());
103
104
		# If self-signed certs are allowed, set it.
105
		if ((bool)getenv('HAS_SELF_SIGNED_CERT') === true) {
106
			curl_setopt($cHandler, CURLOPT_SSL_VERIFYPEER, false);
107
			curl_setopt($cHandler, CURLOPT_SSL_VERIFYHOST, false);
108
		}
109
110
		(new Middleware())->handle_media($cHandler, $options);
111
	}
112
113
	/**
114
	 * Handle the media
115
	 *
116
	 * @param \CurlHandle $handler
117
	 * @param HttpOptions $options
118
	 * @return void
119
	 */
120
	private function handle_media(\CurlHandle $handler, HttpOptions $options): void
121
	{
122
		if (count($options->getMultipart()) > 0) {
123
			curl_setopt($handler, CURLOPT_POST, true);
124
			curl_setopt($handler, CURLOPT_CUSTOMREQUEST, 'POST');
125
126
			$form_data = new FormData();
127
			foreach ($options->getMultipart() as $key => $value) {
128
				$form_data->addFile($key, $value);
129
			}
130
131
			$headers = [];
132
			foreach ($options->getHeaders() as $header => $value) {
133
				if (Toolkit::insensitiveString($header, 'content-type')) continue;
134
				$headers[] = $header . ': ' . $value;
135
			}
136
			$headers[] = 'Content-Type: multipart/form-data';
137
138
			curl_setopt($handler, CURLOPT_HTTPHEADER, $headers);
139
			curl_setopt($handler, CURLOPT_POSTFIELDS, $form_data->getFiles());
140
		}
141
	}
142
143
	/**
144
	 * @param mixed $socket
145
	 * @param int $len
146
	 * @return string|null
147
	 * @throws ConnectionException
148
	 */
149
	public static function stream_read(mixed $socket, int $len): string|null
150
	{
151
		if (!is_resource($socket)) {
152
			throw new ConnectionException(sprintf(
153
				'%s is not a valid resource. Datatype: %s', $socket, gettype($socket)
154
			));
155
		}
156
157
		$data = '';
158
		while (($dataLen = strlen($data)) < $len) {
159
			$buff = fread($socket, $len - $dataLen);
160
161
			if ($buff === false) {
162
				return null;
163
			}
164
165
			if ($buff === '') {
166
				$metadata = stream_get_meta_data($socket);
167
				throw new ConnectionException(
168
					sprintf('Empty read; connection dead?  Stream state: %s', json_encode($metadata)),
169
					CommonsContract::CLIENT_EMPTY_READ
170
				);
171
			}
172
			$data .= $buff;
173
		}
174
175
		return $data;
176
	}
177
178
	/**
179
	 * @param mixed $socket
180
	 * @param string $data
181
	 * @return bool
182
	 * @throws ConnectionException
183
	 */
184
	public static function stream_write(mixed $socket, string $data): bool
185
	{
186
		if (!is_resource($socket)) {
187
			throw new ConnectionException(sprintf(
188
				'%s is not a valid resource. Datatype: %s', $socket, gettype($socket)
189
			));
190
		}
191
192
		$written = fwrite($socket, $data);
193
194
		if ($written < strlen($data)) {
195
			throw new ConnectionException(
196
				sprintf('Could only write %s out of %s bytes.', $written, strlen($data)),
197
				CommonsContract::CLIENT_COULD_ONLY_WRITE_LESS
198
			);
199
		}
200
201
		return true;
202
	}
203
204
	/**
205
	 * Stream connect
206
	 */
207
208
}