From a0b9a26dbfc0cbcc90c1e4a0f0240e6479abf002 Mon Sep 17 00:00:00 2001 From: Renan Bernordi Date: Thu, 5 Dec 2024 11:41:08 -0300 Subject: [PATCH] Revert "criada a classe curl para otimizar e simplificar o codigo" This reverts commit 38e89e19b0e72f0aebfc69a64b2cd514851d0e9d. # Conflicts: # app/inc/Curl.php # app/inc/URLAnalyzer.php --- app/inc/Curl.php | 372 ---------------------------------------- app/inc/URLAnalyzer.php | 244 ++++++++++++++++---------- 2 files changed, 156 insertions(+), 460 deletions(-) delete mode 100644 app/inc/Curl.php diff --git a/app/inc/Curl.php b/app/inc/Curl.php deleted file mode 100644 index c9838ca..0000000 --- a/app/inc/Curl.php +++ /dev/null @@ -1,372 +0,0 @@ -defaultOptions = [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_MAXREDIRS => 5, - CURLOPT_TIMEOUT => 30, - CURLOPT_ENCODING => '', - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_COOKIESESSION => true, - CURLOPT_FRESH_CONNECT => true, - ]; - - $this->setDefaultHeaders(); - } - - /** - * Define os cabeçalhos HTTP padrão - * Configura os headers básicos que serão utilizados em todas as requisições - */ - protected function setDefaultHeaders() - { - $this->headers = [ - 'Accept' => 'text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8', - 'Accept-Language' => 'pt-BR, pt;q=0.9, en-US;q=0.8, en;q=0.7', - 'Cache-Control' => 'no-cache', - 'Pragma' => 'no-cache' - ]; - } - - /** - * Define o agente do usuário para as requisições - * - * @param string $userAgent String que identifica o cliente - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setUserAgent($userAgent) - { - if (is_string($userAgent)) { - $this->userAgent = $userAgent; - } - return $this; - } - - /** - * Define cabeçalhos HTTP personalizados - * - * @param array $headers Array associativo com os cabeçalhos - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setHeaders(array $headers) - { - // Redefine os headers para o padrão primeiro - $this->setDefaultHeaders(); - - // Adiciona os novos headers - foreach ($headers as $name => $value) { - if (!is_string($name) || !is_string($value)) { - continue; - } - $this->headers[trim($name)] = trim($value); - } - return $this; - } - - /** - * Define cookies para a requisição - * - * @param array $cookies Array associativo com os cookies - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setCookies(array $cookies) - { - $this->cookies = []; - foreach ($cookies as $name => $value) { - if (is_string($name) && $value !== null && is_string($value)) { - $this->cookies[] = trim($name) . '=' . trim($value); - } - } - return $this; - } - - /** - * Configura um proxy para as requisições - * - * @param string $host Endereço do servidor proxy - * @param int $port Porta do servidor proxy - * @param string|null $username Nome de usuário para autenticação no proxy - * @param string|null $password Senha para autenticação no proxy - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setProxy($host, $port, $username = null, $password = null) - { - if (is_string($host) && is_numeric($port)) { - $this->proxy = [ - 'host' => $host, - 'port' => (int)$port, - 'username' => is_string($username) ? $username : null, - 'password' => is_string($password) ? $password : null - ]; - } - return $this; - } - - /** - * Define o número máximo de tentativas para uma requisição - * - * @param int $maxRetries Número máximo de tentativas - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setMaxRetries($maxRetries) - { - $this->maxRetries = max(1, (int)$maxRetries); - return $this; - } - - /** - * Define o intervalo entre tentativas de requisição - * - * @param int $microseconds Tempo em microssegundos - * @return $this Retorna a instância atual para encadeamento de métodos - */ - public function setRetryDelay($microseconds) - { - $this->retryDelay = max(0, (int)$microseconds); - return $this; - } - - /** - * Prepara as opções do cURL para a requisição - * - * @param string $url URL da requisição - * @param array $additionalOptions Opções adicionais do cURL - * @return array Retorna array com todas as opções configuradas - * @throws InvalidArgumentException Se a URL não for uma string válida - */ - protected function prepareOptions($url, array $additionalOptions = []) - { - if (!is_string($url)) { - throw new InvalidArgumentException('A URL deve ser uma string'); - } - - $options = []; - - // Adiciona opções padrão - foreach ($this->defaultOptions as $key => $value) { - if (is_int($key)) { - $options[$key] = $value; - } - } - - // Define a URL - $options[CURLOPT_URL] = $url; - - // Define o User Agent - if ($this->userAgent) { - $options[CURLOPT_USERAGENT] = $this->userAgent; - } - - // Converte array de headers para o formato do cURL - $headerLines = []; - foreach ($this->headers as $name => $value) { - $headerLines[] = $name . ': ' . $value; - } - if (!empty($headerLines)) { - $options[CURLOPT_HTTPHEADER] = $headerLines; - } - - // Define os Cookies - if (!empty($this->cookies)) { - $cookieStr = implode('; ', array_filter($this->cookies, 'is_string')); - if (!empty($cookieStr)) { - $options[CURLOPT_COOKIE] = $cookieStr; - } - } - - // Define o Proxy - if (!empty($this->proxy)) { - $options[CURLOPT_PROXY] = $this->proxy['host'] . ':' . $this->proxy['port']; - if (!empty($this->proxy['username']) && !empty($this->proxy['password'])) { - $options[CURLOPT_PROXYUSERPWD] = $this->proxy['username'] . ':' . $this->proxy['password']; - } - } - - // Adiciona opções adicionais - foreach ($additionalOptions as $key => $value) { - if (is_int($key)) { - $options[$key] = $value; - } - } - - return $options; - } - - /** - * Executa uma requisição HTTP - * - * @param string $url URL da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - * @throws Exception Se a requisição falhar após todas as tentativas - */ - protected function execute($url, array $options = []) - { - $attempts = 0; - $lastError = null; - - while ($attempts < $this->maxRetries) { - $ch = curl_init(); - $curlOptions = $this->prepareOptions($url, $options); - - if (!curl_setopt_array($ch, $curlOptions)) { - $error = curl_error($ch); - curl_close($ch); - throw new Exception("Falha ao definir opções do cURL: " . $error); - } - - $content = curl_exec($ch); - $error = curl_error($ch); - $info = curl_getinfo($ch); - - curl_close($ch); - - if ($content !== false && empty($error)) { - return [ - 'content' => $content, - 'info' => $info - ]; - } - - $lastError = $error ?: 'HTTP ' . $info['http_code']; - $attempts++; - - if ($attempts < $this->maxRetries) { - usleep($this->retryDelay); - } - } - - throw new Exception("Falha após {$this->maxRetries} tentativas. Último erro: " . $lastError); - } - - /** - * Executa uma requisição GET - * - * @param string $url URL da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - */ - public function get($url, array $options = []) - { - return $this->execute($url, $options); - } - - /** - * Executa uma requisição HEAD - * - * @param string $url URL da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - */ - public function head($url, array $options = []) - { - $options[CURLOPT_NOBODY] = true; - return $this->execute($url, $options); - } - - /** - * Executa uma requisição POST - * - * @param string $url URL da requisição - * @param mixed $data Dados a serem enviados no corpo da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - */ - public function post($url, $data = null, array $options = []) - { - $options[CURLOPT_POST] = true; - - if ($data !== null) { - $options[CURLOPT_POSTFIELDS] = is_array($data) ? http_build_query($data) : $data; - } - - return $this->execute($url, $options); - } - - /** - * Executa uma requisição PUT - * - * @param string $url URL da requisição - * @param mixed $data Dados a serem enviados no corpo da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - */ - public function put($url, $data = null, array $options = []) - { - $options[CURLOPT_CUSTOMREQUEST] = 'PUT'; - - if ($data !== null) { - $options[CURLOPT_POSTFIELDS] = is_array($data) ? http_build_query($data) : $data; - } - - return $this->execute($url, $options); - } - - /** - * Executa uma requisição DELETE - * - * @param string $url URL da requisição - * @param array $options Opções adicionais do cURL - * @return array Retorna array com o conteúdo e informações da requisição - */ - public function delete($url, array $options = []) - { - $options[CURLOPT_CUSTOMREQUEST] = 'DELETE'; - return $this->execute($url, $options); - } -} diff --git a/app/inc/URLAnalyzer.php b/app/inc/URLAnalyzer.php index 55002c8..0643414 100644 --- a/app/inc/URLAnalyzer.php +++ b/app/inc/URLAnalyzer.php @@ -14,39 +14,33 @@ require_once 'Rules.php'; require_once 'Cache.php'; -require_once 'Curl.php'; class URLAnalyzer { /** * @var array Lista de User Agents disponíveis para requisições */ - protected $userAgents; + private $userAgents; /** * @var int Número máximo de tentativas para obter conteúdo */ - protected $maxAttempts; + private $maxAttempts; /** * @var array Lista de servidores DNS para resolução */ - protected $dnsServers; + private $dnsServers; /** * @var Rules Instância da classe de regras */ - protected $rules; + private $rules; /** * @var Cache Instância da classe de cache */ - protected $cache; - - /** - * @var Curl Instância da classe de curl - */ - protected $curl; + private $cache; /** * Construtor da classe @@ -59,7 +53,6 @@ class URLAnalyzer $this->dnsServers = explode(',', DNS_SERVERS); $this->rules = new Rules(); $this->cache = new Cache(); - $this->curl = new Curl(); } /** @@ -70,22 +63,29 @@ class URLAnalyzer */ public function checkRedirects($url) { - $this->curl->setUserAgent($this->userAgents[array_rand($this->userAgents)]['user_agent']); - - try { - $response = $this->curl->head($url); - return [ - 'finalUrl' => $response['info']['url'], - 'hasRedirect' => ($response['info']['url'] !== $url), - 'httpCode' => $response['info']['http_code'] - ]; - } catch (Exception $e) { - return [ - 'finalUrl' => $url, - 'hasRedirect' => false, - 'httpCode' => 0 - ]; - } + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_NOBODY => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_TIMEOUT => 5, + CURLOPT_USERAGENT => $this->userAgents[array_rand($this->userAgents)]['user_agent'], + CURLOPT_SSL_VERIFYPEER => false + ]); + + $response = curl_exec($ch); + $finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + return [ + 'finalUrl' => $finalUrl, + 'hasRedirect' => ($finalUrl !== $url), + 'httpCode' => $httpCode + ]; } /** @@ -153,6 +153,7 @@ class URLAnalyzer * Tenta obter o conteúdo da URL com múltiplas tentativas * * @param string $url URL para buscar conteúdo + * @param string $resolvedIp IP resolvido do domínio * @return string Conteúdo obtido * @throws Exception Se todas as tentativas falharem */ @@ -165,27 +166,13 @@ class URLAnalyzer $userAgentKeys = array_keys($this->userAgents); $totalUserAgents = count($userAgentKeys); - $this->curl->setMaxRetries($this->maxAttempts); - $this->curl->setRetryDelay(500000); // 0.5 segundo entre tentativas - while ($attempts < $this->maxAttempts) { try { // Seleciona um user agent de forma rotativa $currentUserAgentKey = $userAgentKeys[$attempts % $totalUserAgents]; - $result = $this->fetchContent($url, $currentUserAgentKey); - - // Se o HTTP code não for 200 - if ($result['http_code'] !== 200) { - try { - $content = $this->fetchFromWaybackMachine($url); - if (!empty($content)) { - return $content; - } - } catch (Exception $e) { - $errors[] = "Wayback Machine: " . $e->getMessage(); - } - } else if (!empty($result['content'])) { - return $result['content']; + $content = $this->fetchContent($url, $currentUserAgentKey); + if (!empty($content)) { + return $content; } } catch (Exception $e) { $errors[] = $e->getMessage(); @@ -195,7 +182,7 @@ class URLAnalyzer usleep(500000); // 0.5 segundo de espera entre tentativas } - // Se todas as tentativas falharem, tenta buscar do Wayback Machine uma última vez + // Se todas as tentativas falharem, tenta buscar do Wayback Machine try { $content = $this->fetchFromWaybackMachine($url); if (!empty($content)) { @@ -222,40 +209,73 @@ class URLAnalyzer // Primeiro, verifica a disponibilidade de snapshots $availabilityUrl = "https://archive.org/wayback/available?url=" . urlencode($cleanUrl); - try { - $this->curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'); - $response = $this->curl->get($availabilityUrl); - - $data = json_decode($response['content'], true); - if (!isset($data['archived_snapshots']['closest']['url'])) { - return null; - } + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $availabilityUrl, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_TIMEOUT => 10, + CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + CURLOPT_SSL_VERIFYPEER => false + ]); - // Obtém o snapshot mais recente - $archiveUrl = $data['archived_snapshots']['closest']['url']; - - $response = $this->curl->get($archiveUrl); - $content = $response['content']; - - if (empty($content)) { - return null; - } + $response = curl_exec($ch); + $error = curl_error($ch); + curl_close($ch); - // Remove o toolbar do Wayback Machine - $content = preg_replace('/.*?/s', '', $content); - - return $content; - } catch (Exception $e) { + if ($error || empty($response)) { return null; } + + $data = json_decode($response, true); + if (!isset($data['archived_snapshots']['closest']['url'])) { + return null; + } + + // Obtém o snapshot mais recente + $archiveUrl = $data['archived_snapshots']['closest']['url']; + + // Busca o conteúdo do snapshot + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $archiveUrl, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 2, + CURLOPT_TIMEOUT => 30, + CURLOPT_ENCODING => '', + CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_HTTPHEADER => [ + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language: en-US,en;q=0.5', + 'Cache-Control: no-cache', + 'Pragma: no-cache' + ] + ]); + + $content = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error || $httpCode >= 400 || empty($content)) { + return null; + } + + // Remove o toolbar do Wayback Machine + $content = preg_replace('/.*?/s', '', $content); + + return $content; } /** * Realiza requisição HTTP usando cURL * * @param string $url URL para requisição + * @param string $resolvedIp IP resolvido do domínio * @param string $userAgentKey Chave do user agent a ser utilizado - * @return array Array com o conteúdo e código HTTP + * @return string Conteúdo obtido * @throws Exception Em caso de erro na requisição */ private function fetchContent($url, $userAgentKey) @@ -267,34 +287,82 @@ class URLAnalyzer // Obtém a configuração do user agent $userAgentConfig = $this->userAgents[$userAgentKey]; - - // Configura o curl - $this->curl->setUserAgent($userAgentConfig['user_agent']); - - // Adiciona headers específicos do user agent + $userAgent = $userAgentConfig['user_agent']; + + $curlOptions = [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 2, + CURLOPT_TIMEOUT => 10, + CURLOPT_ENCODING => '', + CURLOPT_USERAGENT => $userAgent, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_DNS_SERVERS => implode(',', $this->dnsServers) + ]; + + // Prepara os headers + $headers = [ + 'Host: ' . $host, + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7', + 'Cache-Control: no-cache', + 'Pragma: no-cache' + ]; + + // Adiciona os headers específicos do user agent if (isset($userAgentConfig['headers'])) { - $this->curl->setHeaders($userAgentConfig['headers']); + foreach ($userAgentConfig['headers'] as $headerName => $headerValue) { + $headers[] = $headerName . ': ' . $headerValue; + } } - // Adiciona headers específicos do domínio - if ($domainRules !== null && isset($domainRules['headers'])) { - $this->curl->setHeaders($domainRules['headers']); + // Adiciona headers específicos do domínio se existirem + if ($domainRules !== null && isset($domainRules['userAgent'])) { + $curlOptions[CURLOPT_USERAGENT] = $domainRules['userAgent']; } - // Adiciona cookies específicos do domínio + // Adiciona headers específicos do domínio se existirem + if ($domainRules !== null && isset($domainRules['customHeaders'])) { + foreach ($domainRules['customHeaders'] as $headerName => $headerValue) { + $headers[] = $headerName . ': ' . $headerValue; + } + } + + $curlOptions[CURLOPT_HTTPHEADER] = $headers; + $curlOptions[CURLOPT_COOKIESESSION] = true; + $curlOptions[CURLOPT_FRESH_CONNECT] = true; + if ($domainRules !== null && isset($domainRules['cookies'])) { - $this->curl->setCookies($domainRules['cookies']); + $cookies = []; + foreach ($domainRules['cookies'] as $name => $value) { + if ($value !== null) { + $cookies[] = $name . '=' . $value; + } + } + if (!empty($cookies)) { + $curlOptions[CURLOPT_COOKIE] = implode('; ', $cookies); + } } - try { - $response = $this->curl->get($url); - return [ - 'content' => $response['content'], - 'http_code' => $response['info']['http_code'] - ]; - } catch (Exception $e) { - throw new Exception("Erro ao obter conteúdo: " . $e->getMessage()); + $ch = curl_init(); + curl_setopt_array($ch, $curlOptions); + + $content = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + + curl_close($ch); + + if ($error) { + throw new Exception("Erro CURL: " . $error); } + + if ($httpCode >= 400) { + throw new Exception("Erro HTTP: " . $httpCode); + } + + return $content; } /**