URLExtractor   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 66
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 23
dl 0
loc 66
rs 10
c 2
b 0
f 0
wmc 9

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A sendRequest() 0 12 2
A getResponses() 0 2 1
A extract() 0 22 5
1
<?php
2
/**
3
 * Class URLExtractor
4
 *
5
 * @created      15.08.2019
6
 * @author       smiley <[email protected]>
7
 * @copyright    2019 smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\HTTP\Psr18;
12
13
use chillerlan\HTTP\Psr17\RequestFactory;
14
use Psr\Http\Client\ClientInterface;
15
use Psr\Http\Message\{RequestFactoryInterface, RequestInterface, ResponseInterface, UriInterface};
16
use function count;
17
use function in_array;
18
19
/**
20
 * A client that follows redirects until it reaches a non-30x response, e.g. to extract shortened URLs
21
 *
22
 * The given HTTP client needs to be set up accordingly:
23
 *
24
 *   - CURLOPT_FOLLOWLOCATION  must be set to false so that we can intercept the 30x responses
25
 *   - CURLOPT_MAXREDIRS       should be set to a value > 1
26
 */
27
class URLExtractor implements ClientInterface{
28
29
	/** @var \Psr\Http\Message\ResponseInterface[] */
30
	protected array $responses = [];
31
32
	protected ClientInterface $http;
33
34
	protected RequestFactoryInterface $requestFactory;
35
36
	/**
37
	 * URLExtractor constructor.
38
	 */
39
	public function __construct(ClientInterface $http, RequestFactoryInterface $requestFactory = null){
40
		$this->http           = $http;
41
		$this->requestFactory = $requestFactory ?? new RequestFactory;
42
	}
43
44
	/**
45
	 * @inheritDoc
46
	 */
47
	public function sendRequest(RequestInterface $request):ResponseInterface{
48
49
		do{
50
			// fetch the response for the current request
51
			$response          = $this->http->sendRequest($request);
52
			$this->responses[] = $response;
53
			// set up a new request to the location header of the last response
54
			$request           = $this->requestFactory->createRequest($request->getMethod(), $response->getHeaderLine('location'));
55
		}
56
		while(in_array($response->getStatusCode(), [301, 302, 303, 307, 308], true));
57
58
		return $response;
59
	}
60
61
	/**
62
	 * extract the given URL and return the last valid location header
63
	 */
64
	public function extract(UriInterface|string $shortURL):?string{
65
		$request  = $this->requestFactory->createRequest('GET', $shortURL);
66
		$response = $this->sendRequest($request);
67
68
		if($response->getStatusCode() !== 200 || empty($this->responses)){
69
			return null;
70
		}
71
72
		$count = count($this->responses) - 2;
73
74
		while($count >= 0){
75
76
			$url = $this->responses[$count]?->getHeaderLine('location');
77
78
			if(!empty($url)){
79
				return $url;
80
			}
81
82
			$count--;
83
		}
84
85
		return null;
86
	}
87
88
	/**
89
	 * @return \Psr\Http\Message\ResponseInterface[]
90
	 */
91
	public function getResponses():array{
92
		return $this->responses;
93
	}
94
95
}
96