Files
2026-01-24 12:16:10 +02:00

151 lines
4.5 KiB
PHP

<?php
namespace App\Jobs;
use App\Models\Notification;
use App\Models\Sitemap;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class CheckSitemap implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct(
public Sitemap $sitemap
) {
}
/**
* Execute the job.
*/
public function handle(): void
{
try {
// Fetch the sitemap URL
$response = Http::timeout(30)
->withHeaders([
'User-Agent' => 'Mozilla/5.0 (compatible; SitemapMonitor/1.0)',
])
->get($this->sitemap->url);
if (!$response->successful()) {
$this->sitemap->markAsInvalid(
"HTTP {$response->status()}: Failed to fetch sitemap"
);
$this->sendAlert("Sitemap is inaccessible (HTTP {$response->status()})");
return;
}
// Get the response body
$xmlContent = $response->body();
// Validate XML structure using simplexml_load_string
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlContent);
if ($xml === false) {
$errors = libxml_get_errors();
$errorMessages = array_map(fn($error) => trim($error->message), $errors);
libxml_clear_errors();
$this->sitemap->markAsInvalid(
'Invalid XML: ' . implode('; ', $errorMessages)
);
$this->sendAlert('Sitemap contains invalid XML');
return;
}
// Check if it's a valid sitemap (has urlset or sitemapindex)
$namespaces = $xml->getNamespaces(true);
$isUrlset = isset($xml->url) || isset($namespaces['']);
$isSitemapIndex = isset($xml->sitemap);
if (!$isUrlset && !$isSitemapIndex) {
$this->sitemap->markAsInvalid(
'Invalid sitemap format: Missing <urlset> or <sitemapindex> root element'
);
$this->sendAlert('Sitemap has invalid format');
return;
}
// Count URLs in the sitemap
$urlCount = $this->countUrls($xml);
// Mark as valid
$this->sitemap->markAsValid($urlCount);
Log::info('Sitemap validated successfully', [
'sitemap_id' => $this->sitemap->id,
'url' => $this->sitemap->url,
'url_count' => $urlCount,
]);
} catch (\Exception $e) {
Log::error('Error checking sitemap', [
'sitemap_id' => $this->sitemap->id,
'url' => $this->sitemap->url,
'error' => $e->getMessage(),
]);
$this->sitemap->markAsInvalid(
'Exception: ' . $e->getMessage()
);
$this->sendAlert('Sitemap check failed: ' . $e->getMessage());
throw $e; // Re-throw to allow job retry
}
}
/**
* Count URLs in the sitemap
*/
protected function countUrls(\SimpleXMLElement $xml): int
{
$count = 0;
// Register namespaces
$xml->registerXPathNamespace('sm', 'http://www.sitemaps.org/schemas/sitemap/0.9');
// Check for regular sitemap
$urls = $xml->xpath('//sm:url') ?: $xml->xpath('//url');
if ($urls !== false) {
$count = count($urls);
}
// If it's a sitemap index, count the sitemaps instead
if ($count === 0) {
$sitemaps = $xml->xpath('//sm:sitemap') ?: $xml->xpath('//sitemap');
if ($sitemaps !== false) {
$count = count($sitemaps);
}
}
return $count;
}
/**
* Send alert notification for invalid sitemap
*/
protected function sendAlert(string $message): void
{
Notification::create([
'user_id' => $this->sitemap->site->user_id,
'type' => 'sitemap_invalid',
'message' => "{$message}: {$this->sitemap->url}",
'data' => json_encode([
'sitemap_id' => $this->sitemap->id,
'site_id' => $this->sitemap->site_id,
'url' => $this->sitemap->url,
'error' => $this->sitemap->last_error,
]),
'read_at' => null,
]);
}
}