diff --git a/build.php b/build.php index 1b98227..61b0728 100644 --- a/build.php +++ b/build.php @@ -2,30 +2,53 @@ declare(strict_types=1); -require __DIR__ . '/src/GitHubApi.php'; -require __DIR__ . '/src/LatestPhpVersion.php'; -require __DIR__ . '/src/PhpUsagePercentage.php'; -require __DIR__ . '/src/WordpressUsagePercentage.php'; - -$stars = (new Crawler\GitHubApi(getenv('GITHUB_TOKEN'))) - ->addRepository('laravel', 'laravel/laravel') - ->addRepository('slim', 'slimphp/Slim') - ->addRepository('symfony', 'symfony/symfony') - ->addRepository('composer', 'composer/composer') - ->addRepository('awesomePhp', 'ziadoz/awesome-php') - ->addOrganization('phpLeague', 'thephpleague') - ->addOrganization('laminas', 'laminas') - ->run(); - -[$latestPhpVersion, $latestPhpVersionReleaseDate] = (new Crawler\LatestPhpVersion( - 'https://www.php.net/releases/active.php', -))(); -$phpUsagePercentage = (new Crawler\PhpUsagePercentage( - 'https://w3techs.com/technologies/history_overview/programming_language', -))(); -[$wordpressCmsUsagePercentage, $wordpressTotalUsagePercentage] = (new Crawler\WordpressUsagePercentage( - 'https://w3techs.com/technologies/details/cm-wordpress', -))(); +require __DIR__ . '/vendor/autoload.php'; + +$run = function (string $description, callable $execute): mixed { + try { + echo "\e[0m[ \e[36m..\e[0m ] \e[36m{$description}\e[0m"; + $response = $execute(); + echo "\r\e[0m[ \e[1;32mOK\e[0m ] \e[36m{$description}\e[0m\n"; + return $response; + } catch (\Throwable $t) { + echo "\r\e[0m[ \e[1;31mERR\e[0m ] \e[36m{$description}\e[0m\n"; + echo " \e[31m{$t->getMessage()}\e[0m\n"; + die(1); + } +}; + +$stars = $run( + 'Estatísticas do GitHub', + (new App\Crawler\GitHubApi(getenv('GITHUB_TOKEN'))) + ->addRepository('laravel', 'laravel/laravel') + ->addRepository('slim', 'slimphp/Slim') + ->addRepository('symfony', 'symfony/symfony') + ->addRepository('composer', 'composer/composer') + ->addRepository('awesomePhp', 'ziadoz/awesome-php') + ->addOrganization('phpLeague', 'thephpleague') + ->addOrganization('laminas', 'laminas') +); + +[$latestPhpVersion, $latestPhpVersionReleaseDate] = $run( + 'Última versão do PHP', + new App\Crawler\LatestPhpVersion( + 'https://www.php.net/releases/active.php', + ) +); + +$phpUsagePercentage = $run( + 'Uso do PHP no w3techs.com', + new App\Crawler\PhpUsagePercentage( + 'https://w3techs.com/technologies/history_overview/programming_language', + ) +); + +[$wordpressCmsUsagePercentage, $wordpressTotalUsagePercentage] = $run( + 'Uso do WordPress no w3techs.com', + new App\Crawler\WordpressUsagePercentage( + 'https://w3techs.com/technologies/details/cm-wordpress', + ) +); ob_start(); require __DIR__ . '/template/index.phtml'; diff --git a/src/GitHubApi.php b/src/Crawler/GitHubApi.php similarity index 78% rename from src/GitHubApi.php rename to src/Crawler/GitHubApi.php index a18b1f0..cdd12ca 100644 --- a/src/GitHubApi.php +++ b/src/Crawler/GitHubApi.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Crawler; +namespace App\Crawler; use CurlHandle; use RuntimeException; @@ -37,29 +37,30 @@ public function addOrganization(string $entry, string $organization): self return $this; } - public function run(): stdClass + public function __invoke(): stdClass { - $curlMultiHandler = curl_multi_init(); + $curlMultiHandler = \curl_multi_init(); foreach ($this->handlers as $handler) { - curl_multi_add_handle($curlMultiHandler, $handler); + \curl_multi_add_handle($curlMultiHandler, $handler); } + $active = true; do { - $status = curl_multi_exec($curlMultiHandler, $active); + $status = \curl_multi_exec($curlMultiHandler, $active); if ($active) { - curl_multi_select($curlMultiHandler); + \curl_multi_select($curlMultiHandler); } } while ($active && $status == CURLM_OK); foreach ($this->handlers as $handler) { - curl_multi_remove_handle($curlMultiHandler, $handler); + \curl_multi_remove_handle($curlMultiHandler, $handler); } - curl_multi_close($curlMultiHandler); + \curl_multi_close($curlMultiHandler); $response = new stdClass(); foreach ($this->handlers as $entry => $handler) { - $json = curl_multi_getcontent($handler); + $json = \curl_multi_getcontent($handler); $response->$entry = $this->parseJson($entry, $json); } @@ -70,11 +71,11 @@ public function run(): stdClass private function parseJson(string $entry, string $json): string { if (empty($json)) { - throw new RuntimeException("Erro ao buscar dados de {$entry}"); + throw new RuntimeException("Erro ao buscar dados de {$entry}: resposta vazia"); } - $json = json_decode($json); + $json = \json_decode($json); if (empty($json)) { - throw new RuntimeException("Erro ao buscar dados de {$entry}"); + throw new RuntimeException("Erro ao buscar dados de {$entry}: JSON inválido"); } if (empty($this->isOrganization[$entry])) { @@ -102,10 +103,10 @@ private function fetchByOrganization(string $organization): CurlHandle private function makeRequest(string $url): CurlHandle { - $curlHandle = curl_init(); - curl_setopt_array($curlHandle, [ + $curlHandle = \curl_init(); + \curl_setopt_array($curlHandle, [ CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => 1, + CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'vcampitelli', CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$this->token}", @@ -125,6 +126,6 @@ private function shortNumber(int $num): string for ($i = 0; $num >= 1000; $i++) { $num /= 1000; } - return number_format($num, 1, ',', '.') . $units[$i]; + return \number_format($num, 1, ',', '.') . $units[$i]; } } diff --git a/src/LatestPhpVersion.php b/src/Crawler/LatestPhpVersion.php similarity index 75% rename from src/LatestPhpVersion.php rename to src/Crawler/LatestPhpVersion.php index 6fa6026..db46d1d 100644 --- a/src/LatestPhpVersion.php +++ b/src/Crawler/LatestPhpVersion.php @@ -2,8 +2,9 @@ declare(strict_types=1); -namespace Crawler; +namespace App\Crawler; +use App\CurlExec; use DateTimeImmutable; class LatestPhpVersion @@ -14,8 +15,8 @@ public function __construct(private readonly string $url) public function __invoke(): ?array { - $json = file_get_contents($this->url); - $json = json_decode($json, true); + $json = (new CurlExec())->fetchAsString($this->url); + $json = \json_decode($json, true); $latestMajor = end($json); $latestMinor = end($latestMajor); $date = DateTimeImmutable::createFromFormat('d M Y', $latestMinor['date']); diff --git a/src/PhpUsagePercentage.php b/src/Crawler/PhpUsagePercentage.php similarity index 74% rename from src/PhpUsagePercentage.php rename to src/Crawler/PhpUsagePercentage.php index 414fcc5..be0dd8b 100644 --- a/src/PhpUsagePercentage.php +++ b/src/Crawler/PhpUsagePercentage.php @@ -2,8 +2,9 @@ declare(strict_types=1); -namespace Crawler; +namespace App\Crawler; +use App\CurlExec; use DOMDocument; use DOMXPath; @@ -15,11 +16,7 @@ public function __construct(private readonly string $url) public function __invoke(): ?float { - $htmlString = file_get_contents($this->url); - libxml_use_internal_errors(true); - $doc = new DOMDocument(); - $doc->loadHTML($htmlString); - $xpath = new DOMXPath($doc); + $xpath = (new CurlExec())->fetchAsXpath($this->url); $languages = $xpath->query('//table[@class="hist"]/tr'); foreach ($languages as $language) { diff --git a/src/WordpressUsagePercentage.php b/src/Crawler/WordpressUsagePercentage.php similarity index 77% rename from src/WordpressUsagePercentage.php rename to src/Crawler/WordpressUsagePercentage.php index 7d3811a..7ff5796 100644 --- a/src/WordpressUsagePercentage.php +++ b/src/Crawler/WordpressUsagePercentage.php @@ -2,8 +2,9 @@ declare(strict_types=1); -namespace Crawler; +namespace App\Crawler; +use App\CurlExec; use DOMDocument; use DOMXPath; @@ -15,11 +16,7 @@ public function __construct(private readonly string $url) public function __invoke(): ?array { - $htmlString = file_get_contents($this->url); - libxml_use_internal_errors(true); - $doc = new DOMDocument(); - $doc->loadHTML($htmlString); - $xpath = new DOMXPath($doc); + $xpath = (new CurlExec())->fetchAsXpath($this->url); $languages = $xpath->query('//p[@class="surv"]'); foreach ($languages as $language) { diff --git a/src/CurlExec.php b/src/CurlExec.php new file mode 100644 index 0000000..97e94f4 --- /dev/null +++ b/src/CurlExec.php @@ -0,0 +1,38 @@ + $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_USERAGENT => 'vcampitelli', + ]); + + $response = \curl_exec($curlHandle); + if ($response === false) { + throw new RuntimeException(\curl_error($curlHandle)); + } + + return $response; + } + + public function fetchAsXpath(string $url): DOMXPath + { + $htmlString = $this->fetchAsString($url); + \libxml_use_internal_errors(true); + $doc = new DOMDocument(); + $doc->loadHTML($htmlString); + return new DOMXPath($doc); + } +}