Initial commit
This commit is contained in:
		
						commit
						e02e43217c
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					/var/
 | 
				
			||||||
 | 
					/vendor/
 | 
				
			||||||
 | 
					/.idea/
 | 
				
			||||||
							
								
								
									
										47
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					# `curl-http2`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What is this?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `curl` wrapper for PHP that uses `HTTP/2` to send requests over a single TCP connection.  
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					Provided are:
 | 
				
			||||||
 | 
					- The main class `JoopSchilder\Http2\Http2`
 | 
				
			||||||
 | 
					- A simple class for requests supporting `curl` options
 | 
				
			||||||
 | 
					- A simple class for responses
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What is this not?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A production-ready library.  
 | 
				
			||||||
 | 
					I mean, you _are_ of course allowed to use it if you think it's useful.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There is some example code in `bin/app.php`.  
 | 
				
			||||||
 | 
					In it's most basic form, usage of this library might look like this:
 | 
				
			||||||
 | 
					```php
 | 
				
			||||||
 | 
					use JoopSchilder\Http2\Http2;
 | 
				
			||||||
 | 
					use JoopSchilder\Http2\Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$http2 = new Http2();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Might also be an implementation of the ResponseHandler interface
 | 
				
			||||||
 | 
					$http2->onResponse(function(Response $response) {
 | 
				
			||||||
 | 
					    var_dump($response);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This creates a request with sensible defaults
 | 
				
			||||||
 | 
					$request = $http2->createRequest('https://www.twitter.com/');
 | 
				
			||||||
 | 
					$request->setOptions([CURLOPT_USERAGENT => 'AppleTV6,2/11.1']);
 | 
				
			||||||
 | 
					$http2->addRequest($request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Requests are not executed until this method is called
 | 
				
			||||||
 | 
					$http2->execute();
 | 
				
			||||||
 | 
					```  
 | 
				
			||||||
 | 
					It's a good idea to use a different instance of `Http2` for every host you plan to make a request to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## What's next?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Add more control to dynamically add requests to an `Http2` instance
 | 
				
			||||||
 | 
					- Make an `Http2` instance coupled to a domain (as this is its intended use) 
 | 
				
			||||||
 | 
					- Use [`PSR-7`](https://www.php-fig.org/psr/psr-7/) HTTP message interfaces
 | 
				
			||||||
 | 
					- Add a factory for requests
 | 
				
			||||||
							
								
								
									
										37
									
								
								bin/app.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										37
									
								
								bin/app.php
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use JoopSchilder\Http2\Http2;
 | 
				
			||||||
 | 
					use JoopSchilder\Http2\Response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require_once __DIR__ . '/../vendor/autoload.php';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$http2 = new Http2();
 | 
				
			||||||
 | 
					$http2->onResponse(function (Response $response) {
 | 
				
			||||||
 | 
						$statusLine = substr($response->getHeader(), 0, strpos($response->getHeader(), "\r"));
 | 
				
			||||||
 | 
						$statusLine = str_pad($statusLine, 14, ' ', STR_PAD_RIGHT);
 | 
				
			||||||
 | 
						print("$statusLine {$response->getOriginalUrl()}\n");
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add urls
 | 
				
			||||||
 | 
					$urls = [
 | 
				
			||||||
 | 
						'https://twitter.com/survivetheark',
 | 
				
			||||||
 | 
						'https://twitter.com/missingpeople',
 | 
				
			||||||
 | 
						'https://twitter.com/elainecrowley',
 | 
				
			||||||
 | 
						'https://twitter.com/2020comms',
 | 
				
			||||||
 | 
						'https://twitter.com/goal',
 | 
				
			||||||
 | 
						'https://twitter.com/cydarmedical',
 | 
				
			||||||
 | 
						'https://twitter.com/cloakzy',
 | 
				
			||||||
 | 
						'https://twitter.com/cllrandrewkelly',
 | 
				
			||||||
 | 
						'https://twitter.com/youranonnews',
 | 
				
			||||||
 | 
						'https://twitter.com/trickyjabs',
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					foreach ($urls as $url) {
 | 
				
			||||||
 | 
						$http2->addRequest($http2->createRequest($url));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Execute the requests
 | 
				
			||||||
 | 
					print("Note: it might take some time to establish the TCP connection.\n");
 | 
				
			||||||
 | 
					print("The benefits of HTTP/2 become more clear if you send more requests to the same server.\n\n");
 | 
				
			||||||
 | 
					$startTime = microtime(true);
 | 
				
			||||||
 | 
					$http2->execute();
 | 
				
			||||||
 | 
					printf("Requests: %d, time(s): %.3f\n", count($urls), microtime(true) - $startTime);
 | 
				
			||||||
							
								
								
									
										19
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "joopschilder/php-http2",
 | 
				
			||||||
 | 
					  "authors": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "name": "Joop Schilder",
 | 
				
			||||||
 | 
					      "email": "jnmschilder@protonmail.com"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "autoload": {
 | 
				
			||||||
 | 
					    "psr-4": {
 | 
				
			||||||
 | 
					      "JoopSchilder\\Http2\\": [
 | 
				
			||||||
 | 
					        "src"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "require": {
 | 
				
			||||||
 | 
					    "ext-curl": "*"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								composer.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								composer.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "_readme": [
 | 
				
			||||||
 | 
					        "This file locks the dependencies of your project to a known state",
 | 
				
			||||||
 | 
					        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
				
			||||||
 | 
					        "This file is @generated automatically"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "content-hash": "5b1e133742e667bcab8f73ed237d3adc",
 | 
				
			||||||
 | 
					    "packages": [],
 | 
				
			||||||
 | 
					    "packages-dev": [],
 | 
				
			||||||
 | 
					    "aliases": [],
 | 
				
			||||||
 | 
					    "minimum-stability": "stable",
 | 
				
			||||||
 | 
					    "stability-flags": [],
 | 
				
			||||||
 | 
					    "prefer-stable": false,
 | 
				
			||||||
 | 
					    "prefer-lowest": false,
 | 
				
			||||||
 | 
					    "platform": {
 | 
				
			||||||
 | 
					        "ext-curl": "*"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "platform-dev": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										117
									
								
								src/Http2.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/Http2.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace JoopSchilder\Http2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use RuntimeException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class Http2
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Http2
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/** @var resource */
 | 
				
			||||||
 | 
						private $multihandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** @var callable */
 | 
				
			||||||
 | 
						private $responseHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Http2 constructor.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->multihandle = curl_multi_init();
 | 
				
			||||||
 | 
							curl_multi_setopt($this->multihandle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
 | 
				
			||||||
 | 
							curl_multi_setopt($this->multihandle, CURLMOPT_MAX_HOST_CONNECTIONS, 1);
 | 
				
			||||||
 | 
							curl_multi_setopt($this->multihandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Http2 destructor.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __destruct()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (is_resource($this->multihandle)) {
 | 
				
			||||||
 | 
								curl_multi_close($this->multihandle);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param string $url
 | 
				
			||||||
 | 
						 * @param bool   $priorKnowledge
 | 
				
			||||||
 | 
						 * @return Request
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function createRequest(string $url, bool $priorKnowledge = true): Request
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return (new Request($url))->setOptions([
 | 
				
			||||||
 | 
								CURLOPT_AUTOREFERER    => 1,
 | 
				
			||||||
 | 
								CURLOPT_FOLLOWLOCATION => 1,
 | 
				
			||||||
 | 
								CURLOPT_HEADER         => 1,
 | 
				
			||||||
 | 
								CURLOPT_HTTP_VERSION   => ($priorKnowledge ? CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE : CURL_HTTP_VERSION_2),
 | 
				
			||||||
 | 
								CURLOPT_MAXREDIRS      => 5,
 | 
				
			||||||
 | 
								CURLOPT_PIPEWAIT       => 1,
 | 
				
			||||||
 | 
								CURLOPT_RETURNTRANSFER => 1,
 | 
				
			||||||
 | 
								CURLOPT_SSL_VERIFYPEER => 0,
 | 
				
			||||||
 | 
								CURLOPT_TIMEOUT        => 10,
 | 
				
			||||||
 | 
							]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param Request $request
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function addRequest(Request $request): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$handle = curl_init($request->getUri());
 | 
				
			||||||
 | 
							curl_setopt_array($handle, $request->getOptions());
 | 
				
			||||||
 | 
							curl_multi_add_handle($this->multihandle, $handle);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param callable $responseHandler
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function onResponse(callable $responseHandler): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->responseHandler = $responseHandler;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function execute(): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->guardNoResponseHandler();
 | 
				
			||||||
 | 
							do {
 | 
				
			||||||
 | 
								curl_multi_exec($this->multihandle, $stillRunning);
 | 
				
			||||||
 | 
								while (false !== ($message = curl_multi_info_read($this->multihandle))) {
 | 
				
			||||||
 | 
									$handle = $message['handle'];
 | 
				
			||||||
 | 
									$content = curl_multi_getcontent($handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									$header = substr($content, 0, curl_getinfo($handle, CURLINFO_HEADER_SIZE));
 | 
				
			||||||
 | 
									$content = str_replace($header, '', $content);
 | 
				
			||||||
 | 
									$url = curl_getinfo($handle, CURLINFO_EFFECTIVE_URL);
 | 
				
			||||||
 | 
									call_user_func($this->responseHandler, new Response($url, $header, $content));
 | 
				
			||||||
 | 
									curl_multi_remove_handle($this->multihandle, $handle);
 | 
				
			||||||
 | 
									curl_close($handle);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								usleep(5);
 | 
				
			||||||
 | 
							} while ($stillRunning > 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private function guardNoResponseHandler(): void
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!is_callable($this->responseHandler)) {
 | 
				
			||||||
 | 
								throw new RuntimeException('No valid response handler found');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										57
									
								
								src/Request.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Request.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace JoopSchilder\Http2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class Request
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Request
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/** @var string */
 | 
				
			||||||
 | 
						private $uri;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** @var array */
 | 
				
			||||||
 | 
						private $options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Request constructor.
 | 
				
			||||||
 | 
						 * @param string $uri
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct(string $uri)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->uri = $uri;
 | 
				
			||||||
 | 
							$this->options = [];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @return string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function getUri(): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->uri;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @return array
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function getOptions(): array
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->options;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param array $options
 | 
				
			||||||
 | 
						 * @return Request
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function setOptions(array $options)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->options = array_replace($this->options, $options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return $this;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										60
									
								
								src/Response.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/Response.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace JoopSchilder\Http2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class Response
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Response
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/** @var string */
 | 
				
			||||||
 | 
						private $originalUrl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** @var string */
 | 
				
			||||||
 | 
						private $header;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** @var string */
 | 
				
			||||||
 | 
						private $content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Response constructor.
 | 
				
			||||||
 | 
						 * @param string $originalUrl
 | 
				
			||||||
 | 
						 * @param string $header
 | 
				
			||||||
 | 
						 * @param string $content
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function __construct(string $originalUrl, string $header, string $content)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							$this->originalUrl = trim($originalUrl);
 | 
				
			||||||
 | 
							$this->header = trim($header);
 | 
				
			||||||
 | 
							$this->content = trim($content);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @return string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function getOriginalUrl(): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->originalUrl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @return string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function getHeader(): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->header;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @return string
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public function getContent(): string
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return $this->content;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/ResponseHandler.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/ResponseHandler.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace JoopSchilder\Http2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface ResponseHandler
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					interface ResponseHandler
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param Response $response
 | 
				
			||||||
 | 
						 * @return mixed
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						function __invoke(Response $response): void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user