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}