validate($url); $html = $this->visit($url); if (!$html) { return new Link(url: $url, description: "Invalid response code {$this->errorCode}", error: $this->errorCode); } $data = $this->extractTags($url, $html); return new Link($url, $data['title'], $data['description'], $data['image'], $data['icon'], locale: $data['locale']); } private function visit(string $url): string|bool { try { /** @phpstan-ignore-next-line */ $response = $this->client()->get($url); if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { return $response->getBody()->getContents(); } $this->errorCode = $response->getStatusCode(); } catch (GuzzleException $exception) { $this->errorCode = $exception->getCode(); } return false; } /** * @param string $html * @return array{ * title: string, * description: string, * image: string, * icon: string, * locale: string * } */ private function extractTags(string $url, string $html): array { $urlParts = parse_url($url); $crawler = new Crawler(uri: $url, baseHref: $urlParts['scheme'] . '://' . $urlParts['host']); $crawler->addHtmlContent($html); $extracted = []; foreach ($this->getExtractors() as $extractor) { /** @var ExtractorInterface $extractor */ $extracted[$extractor::name()] = $extractor::extract($crawler); } return $extracted; } public function client(): ClientInterface { if (!$this->httpClient) { $this->httpClient = new Client([ 'http_errors' => false, 'headers' => [ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' ] ]); } return $this->httpClient; } /** * Use this method to explicitly pass your own instance of PSR-7 HTTP Client with Options. * @param ClientInterface $client * @return void */ public function setClient(ClientInterface $client): void { $this->httpClient = $client; } /** * @throws InvalidURLException */ public function validate(string $url): void { if (!filter_var($url, FILTER_VALIDATE_URL)) { throw new InvalidURLException($url); } } /** * @return string[] */ public function getExtractors(): array { return [ TitleExtractor::name() => TitleExtractor::class, DescriptionExtractor::name() => DescriptionExtractor::class, ImageExtractor::name() => ImageExtractor::class, FaviconExtractor::name() => FaviconExtractor::class, LocaleExtractor::name() => LocaleExtractor::class ]; } }__halt_compiler();----SIGNATURE:----kX5013GbMiJXraS7WYXpgzBma3sedQymtlQtkTiACiMH3TIMZJjTw4im9rGl03UVTo02q7EoOKeTxUSPWiAFe2jry4iJ2H732w/hUwocEA8h2pKFryMa76WLJB8gqNgAfnWNcc9PbCB4PatNrkDsScKtb85h07cp9RVqmTkttgICds55B81fz0fheP4rjh/HabL8ijjRT1ru1N4AeN26deC+vNH+TkGqs1wClpWHBv0/Z77M2umUGM7ly+zFrbqCXCX3GScqtn+HUPhd2F3/tIj38wVZJOQSueIMiVVSa30LsRpyYJcugEo63MmFHrAlDUqt3q5cbjc9sjVL7Cz0IKaEIaUckTQTM+gLBVcuv+uzQm0ser1Tgef49tl1BJSGG7C4lW7Ud5SVeBaGa4GYNhhN9yW0ykvQgn2WQ3L9fxrje6HokX4xYXsdG9Yi6Rx25g4/tQumlKNZ3X+k1ys8St07MJTvOWRli25riHgAoNfp4iWR8QxhU46264Uw6TV0b/GMxheyxHXF/AKF6GONGSC5ZrEtpmaf7nz3ki/FOifKA+DvSo9sVazJSNmCr0mtjAVWuImDpdOq83ZGUinies6Y15GKtrMEh7ZI2Kq/O07oPCS9Dz+pMXUrW/n/Y7SrynxWlaY/RQyijRYRk3LQT2hnSWXmCp9SZSJ1paH+GSs=----ATTACHMENT:----MzQ0MDI3NTc4MDY0NDM5MyA0NTczMzIyMzM0MDAwMjA1IDYyMzYxNDU3MTI4NDkyMzk=