Passed
Push — main ( 925020...e6a094 )
by smiley
11:47
created

URLExtractor::extract()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 4
nop 1
dl 0
loc 22
rs 9.6111
c 0
b 0
f 0
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
17
use function array_key_last;
18
use function array_pop;
19
use function count;
20
use function in_array;
21
22
/**
23
 * A client that follows redirects until it reaches a non-30x response, e.g. to extract shortened URLs
24
 *
25
 * The given HTTP client needs to be set up accordingly:
26
 *
27
 *   - CURLOPT_FOLLOWLOCATION  must be set to false so that we can intercept the 30x responses
28
 *   - CURLOPT_MAXREDIRS       should be set to a value > 1
29
 */
30
class URLExtractor implements ClientInterface{
31
32
	/** @var \Psr\Http\Message\ResponseInterface[] */
33
	protected array $responses = [];
34
35
	protected ClientInterface $http;
36
37
	protected RequestFactoryInterface $requestFactory;
38
39
	/**
40
	 * URLExtractor constructor.
41
	 */
42
	public function __construct(ClientInterface $http, RequestFactoryInterface $requestFactory = null){
43
		$this->http           = $http;
44
		$this->requestFactory = $requestFactory ?? new RequestFactory;
45
	}
46
47
	/**
48
	 * @inheritDoc
49
	 */
50
	public function sendRequest(RequestInterface $request):ResponseInterface{
51
52
		do{
53
			// fetch the response for the current request
54
			$response          = $this->http->sendRequest($request);
55
			$this->responses[] = $response;
56
			// set up a new request to the location header of the last response
57
			$request           = $this->requestFactory->createRequest($request->getMethod(), $response->getHeaderLine('location'));
58
		}
59
		while(in_array($response->getStatusCode(), [301, 302, 303, 307, 308], true));
60
61
		return $response;
62
	}
63
64
	/**
65
	 * extract the given URL and return the last valid location header
66
	 */
67
	public function extract(UriInterface|string $shortURL):?string{
68
		$request  = $this->requestFactory->createRequest('GET', $shortURL);
69
		$response = $this->sendRequest($request);
70
71
		if($response->getStatusCode() !== 200 || empty($this->responses)){
72
			return null;
73
		}
74
75
		$count = count($this->responses) - 2;
76
77
		while($count >= 0){
78
79
			$url = $this->responses[$count]?->getHeaderLine('location');
80
81
			if(!empty($url)){
82
				return $url;
83
			}
84
85
			$count--;
86
		}
87
88
		return null;
89
	}
90
91
	/**
92
	 * @return \Psr\Http\Message\ResponseInterface[]
93
	 */
94
	public function getResponses():array{
95
		return $this->responses;
96
	}
97
98
}
99