friendship ended with social-app. php is my new best friend
1<?php
2/**
3 * Class URLExtractor
4 *
5 * @created 15.08.2019
6 * @author smiley <smiley@chillerlan.net>
7 * @copyright 2019 smiley
8 * @license MIT
9 */
10declare(strict_types=1);
11
12namespace chillerlan\HTTP\Utils\Client;
13
14use Psr\Http\Client\ClientInterface;
15use Psr\Http\Message\{RequestFactoryInterface, RequestInterface, ResponseInterface, UriInterface};
16use function array_reverse, in_array;
17
18/**
19 * A client that follows redirects until it reaches a non-30x response, e.g. to extract shortened URLs
20 *
21 * The given HTTP client needs to be set up accordingly:
22 *
23 * - CURLOPT_FOLLOWLOCATION must be set to false so that we can intercept the 30x responses
24 * - CURLOPT_MAXREDIRS should be set to a value > 1
25 */
26class URLExtractor implements ClientInterface{
27
28 /** @var \Psr\Http\Message\ResponseInterface[] */
29 protected array $responses = [];
30 protected ClientInterface $http;
31 protected RequestFactoryInterface $requestFactory;
32
33 /**
34 * URLExtractor constructor.
35 */
36 public function __construct(ClientInterface $http, RequestFactoryInterface $requestFactory){
37 $this->http = $http;
38 $this->requestFactory = $requestFactory;
39 }
40
41 public function sendRequest(RequestInterface $request):ResponseInterface{
42
43 do{
44 // fetch the response for the current request
45 $response = $this->http->sendRequest($request);
46 $location = $response->getHeaderLine('location');
47 $this->responses[] = $response;
48
49 if($location === ''){
50 break;
51 }
52
53 // set up a new request to the location header of the last response
54 $request = $this->requestFactory->createRequest($request->getMethod(), $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|null{
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 foreach(array_reverse($this->responses) as $r){
73 $url = $r->getHeaderLine('location');
74
75 if(!empty($url)){
76 return $url;
77 }
78 }
79
80 return null;
81 }
82
83 /**
84 * @return \Psr\Http\Message\ResponseInterface[]
85 */
86 public function getResponses():array{
87 return $this->responses;
88 }
89
90}