160 lines
4.1 KiB
PHP
160 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
function usage(): void
|
|
{
|
|
$script = basename(__FILE__);
|
|
|
|
fwrite(STDERR, <<<TXT
|
|
Usage:
|
|
php scripts/{$script} --base=https://skinbase.org /@gregor /categories /wallpapers
|
|
php scripts/{$script} https://skinbase.org/@gregor https://skinbase.org/categories
|
|
|
|
Classifications:
|
|
INERTIA_SSR id="app" + data-page + non-empty app HTML
|
|
INERTIA_NO_SSR id="app" + data-page + empty app HTML
|
|
NOT_INERTIA missing id="app" or missing data-page
|
|
|
|
TXT);
|
|
}
|
|
|
|
function isAbsoluteUrl(string $value): bool
|
|
{
|
|
return (bool) preg_match('~^https?://~i', $value);
|
|
}
|
|
|
|
function joinUrl(string $baseUrl, string $path): string
|
|
{
|
|
return rtrim($baseUrl, '/') . '/' . ltrim($path, '/');
|
|
}
|
|
|
|
function resolveTargets(array $arguments): array
|
|
{
|
|
$baseUrl = null;
|
|
$targets = [];
|
|
|
|
foreach ($arguments as $argument) {
|
|
if (str_starts_with($argument, '--base=')) {
|
|
$baseUrl = substr($argument, 7);
|
|
continue;
|
|
}
|
|
|
|
if ($argument === '--help' || $argument === '-h') {
|
|
usage();
|
|
exit(0);
|
|
}
|
|
|
|
$targets[] = $argument;
|
|
}
|
|
|
|
if ($targets === []) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
return array_map(static function (string $target) use ($baseUrl): string {
|
|
if (isAbsoluteUrl($target)) {
|
|
return $target;
|
|
}
|
|
|
|
if ($baseUrl === null || $baseUrl === '') {
|
|
fwrite(STDERR, "Relative path '{$target}' requires --base=...\n");
|
|
exit(1);
|
|
}
|
|
|
|
return joinUrl($baseUrl, $target);
|
|
}, $targets);
|
|
}
|
|
|
|
function fetchUrl(string $url): array
|
|
{
|
|
if (function_exists('curl_init')) {
|
|
$handle = curl_init($url);
|
|
curl_setopt_array($handle, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_MAXREDIRS => 5,
|
|
CURLOPT_TIMEOUT => 20,
|
|
CURLOPT_CONNECTTIMEOUT => 10,
|
|
CURLOPT_USERAGENT => 'Skinbase Inertia Checker/1.0',
|
|
CURLOPT_HEADER => true,
|
|
]);
|
|
|
|
$raw = curl_exec($handle);
|
|
if ($raw === false) {
|
|
$error = curl_error($handle);
|
|
curl_close($handle);
|
|
|
|
throw new RuntimeException($error !== '' ? $error : 'Unknown cURL error');
|
|
}
|
|
|
|
$status = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
|
|
$headerSize = (int) curl_getinfo($handle, CURLINFO_HEADER_SIZE);
|
|
curl_close($handle);
|
|
|
|
return [
|
|
'status' => $status,
|
|
'body' => substr($raw, $headerSize),
|
|
];
|
|
}
|
|
|
|
$context = stream_context_create([
|
|
'http' => [
|
|
'method' => 'GET',
|
|
'follow_location' => 1,
|
|
'max_redirects' => 5,
|
|
'timeout' => 20,
|
|
'header' => "User-Agent: Skinbase Inertia Checker/1.0\r\n",
|
|
'ignore_errors' => true,
|
|
],
|
|
]);
|
|
|
|
$body = @file_get_contents($url, false, $context);
|
|
if ($body === false) {
|
|
$error = error_get_last();
|
|
throw new RuntimeException($error['message'] ?? 'HTTP request failed');
|
|
}
|
|
|
|
$headers = $http_response_header ?? [];
|
|
$status = 0;
|
|
|
|
foreach ($headers as $header) {
|
|
if (preg_match('~^HTTP/\S+\s+(\d{3})~', $header, $matches)) {
|
|
$status = (int) $matches[1];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'status' => $status,
|
|
'body' => $body,
|
|
];
|
|
}
|
|
|
|
function classifyHtml(string $html): string
|
|
{
|
|
$hasApp = str_contains($html, 'id="app"');
|
|
$hasDataPage = str_contains($html, 'data-page="');
|
|
|
|
if (! $hasApp || ! $hasDataPage) {
|
|
return 'NOT_INERTIA';
|
|
}
|
|
|
|
if (! preg_match('~<div id="app"[^>]*>(.*?)</div>~si', $html, $matches)) {
|
|
return 'INERTIA_UNKNOWN';
|
|
}
|
|
|
|
return trim($matches[1]) === '' ? 'INERTIA_NO_SSR' : 'INERTIA_SSR';
|
|
}
|
|
|
|
$targets = resolveTargets(array_slice($argv, 1));
|
|
|
|
foreach ($targets as $url) {
|
|
try {
|
|
$response = fetchUrl($url);
|
|
$classification = classifyHtml($response['body']);
|
|
printf("%d\t%s\t%s\n", $response['status'], $classification, $url);
|
|
} catch (Throwable $exception) {
|
|
printf("ERROR\t%s\t%s\n", $exception->getMessage(), $url);
|
|
}
|
|
} |