fix: compatibilidad PHP 7.4 — str_starts_with, str_contains, match, never, mixed

This commit is contained in:
Lizandro Guarnizo
2026-06-04 20:04:37 -05:00
parent 943e629120
commit 43f337ee34
5 changed files with 54 additions and 50 deletions
+18 -18
View File
@@ -332,26 +332,26 @@ HTML;
private static function typeLabel(string $field, string $type): string private static function typeLabel(string $field, string $type): string
{ {
if ($field === 'statuses') { if ($field === 'statuses') {
$cls = match ($type) { switch ($type) {
'sent' => 'badge-blue', case 'sent': $cls = 'badge-blue'; break;
'delivered' => 'badge-green', case 'delivered': $cls = 'badge-green'; break;
'read' => 'badge-teal', case 'read': $cls = 'badge-teal'; break;
'failed' => 'badge-red', case 'failed': $cls = 'badge-red'; break;
default => 'badge-gray', default: $cls = 'badge-gray'; break;
}; }
return '<span class="badge ' . $cls . '">' . self::h($type) . '</span>'; return '<span class="badge ' . $cls . '">' . self::h($type) . '</span>';
} }
return match ($type) { switch ($type) {
'text' => '💬 Texto', case 'text': return '&#128172; Texto';
'image' => '🖼️ Imagen', case 'image': return '&#128247; Imagen';
'audio' => '🎵 Audio', case 'audio': return '&#127925; Audio';
'video' => '🎥 Video', case 'video': return '&#127916; Video';
'document' => '📄 Documento', case 'document': return '&#128196; Documento';
'location' => '📍 Ubicación', case 'location': return '&#128205; Ubicaci&oacute;n';
'interactive' => '🔘 Interactivo', case 'interactive': return '&#128280; Interactivo';
'button' => '🔲 Botón', case 'button': return '&#128306; Bot&oacute;n';
default => self::h($type), default: return self::h($type);
}; }
} }
// ─── Render ─────────────────────────────────────────────────────────────── // ─── Render ───────────────────────────────────────────────────────────────
+29 -25
View File
@@ -92,11 +92,13 @@ class WpWebhook
$field = $change['field'] ?? ''; $field = $change['field'] ?? '';
$value = $change['value'] ?? []; $value = $change['value'] ?? [];
match ($field) { if ($field === 'messages' || $field === 'conversations') {
'messages', 'conversations' => self::handleMessages($value), self::handleMessages($value);
'statuses' => self::handleStatuses($value), } elseif ($field === 'statuses') {
default => self::log('INFO', "Campo no manejado: $field"), self::handleStatuses($value);
}; } else {
self::log('INFO', "Campo no manejado: $field");
}
} }
} }
@@ -140,19 +142,19 @@ class WpWebhook
'display_phone' => $displayPhone, 'display_phone' => $displayPhone,
]; ];
match ($type) { switch ($type) {
'text' => self::handleText($msg, $context), case 'text': self::handleText($msg, $context); break;
'image' => self::handleMedia($msg, $context, 'image'), case 'image': self::handleMedia($msg, $context, 'image'); break;
'audio' => self::handleMedia($msg, $context, 'audio'), case 'audio': self::handleMedia($msg, $context, 'audio'); break;
'video' => self::handleMedia($msg, $context, 'video'), case 'video': self::handleMedia($msg, $context, 'video'); break;
'document' => self::handleMedia($msg, $context, 'document'), case 'document': self::handleMedia($msg, $context, 'document'); break;
'sticker' => self::handleMedia($msg, $context, 'sticker'), case 'sticker': self::handleMedia($msg, $context, 'sticker'); break;
'location' => self::handleLocation($msg, $context), case 'location': self::handleLocation($msg, $context); break;
'interactive' => self::handleInteractive($msg, $context), case 'interactive': self::handleInteractive($msg, $context); break;
'button' => self::handleButton($msg, $context), case 'button': self::handleButton($msg, $context); break;
'reaction' => self::handleReaction($msg, $context), case 'reaction': self::handleReaction($msg, $context); break;
default => self::log('INFO', "Tipo de mensaje no manejado: $type | from=$from"), default: self::log('INFO', "Tipo de mensaje no manejado: $type | from=$from"); break;
}; }
} }
// Estados que pueden venir dentro del mismo field 'messages' // Estados que pueden venir dentro del mismo field 'messages'
@@ -198,11 +200,13 @@ class WpWebhook
private static function handleInteractive(array $msg, array $ctx): void private static function handleInteractive(array $msg, array $ctx): void
{ {
$iType = $msg['interactive']['type'] ?? ''; $iType = $msg['interactive']['type'] ?? '';
$reply = match ($iType) { if ($iType === 'button_reply') {
'button_reply' => $msg['interactive']['button_reply'] ?? [], $reply = $msg['interactive']['button_reply'] ?? [];
'list_reply' => $msg['interactive']['list_reply'] ?? [], } elseif ($iType === 'list_reply') {
default => [], $reply = $msg['interactive']['list_reply'] ?? [];
}; } else {
$reply = [];
}
$preview = $iType . ': ' . json_encode($reply); $preview = $iType . ': ' . json_encode($reply);
self::log('MSG', "[INTERACTIVE/$iType] {$ctx['from']} | reply=" . json_encode($reply)); self::log('MSG', "[INTERACTIVE/$iType] {$ctx['from']} | reply=" . json_encode($reply));
self::saveWebhookLog('messages', $ctx['from'], $ctx['name'], 'interactive', $preview); self::saveWebhookLog('messages', $ctx['from'], $ctx['name'], 'interactive', $preview);
@@ -262,7 +266,7 @@ class WpWebhook
} }
$sigHeader = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? ''; $sigHeader = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
if (!str_starts_with($sigHeader, 'sha256=')) { if (strpos($sigHeader, 'sha256=') !== 0) {
self::respond(401, ['error' => 'Firma ausente']); self::respond(401, ['error' => 'Firma ausente']);
} }
@@ -366,7 +370,7 @@ class WpWebhook
} }
} }
private static function respond(int $code, array $body): never private static function respond(int $code, array $body): void
{ {
http_response_code($code); http_response_code($code);
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
+3 -3
View File
@@ -15,10 +15,10 @@ declare(strict_types=1);
foreach ($lines as $line) { foreach ($lines as $line) {
$line = trim($line); $line = trim($line);
// Ignorar comentarios // Ignorar comentarios
if ($line === '' || str_starts_with($line, '#')) { if ($line === '' || strpos($line, '#') === 0) {
continue; continue;
} }
if (!str_contains($line, '=')) { if (strpos($line, '=') === false) {
continue; continue;
} }
[$key, $value] = explode('=', $line, 2); [$key, $value] = explode('=', $line, 2);
@@ -32,7 +32,7 @@ declare(strict_types=1);
} }
})(); })();
function env(string $key, mixed $default = null): mixed function env(string $key, $default = null)
{ {
return $_ENV[$key] ?? getenv($key) ?: $default; return $_ENV[$key] ?? getenv($key) ?: $default;
} }
+2 -2
View File
@@ -5,13 +5,13 @@ $base = '';
if (file_exists(__DIR__ . '/.env')) { if (file_exists(__DIR__ . '/.env')) {
foreach (file(__DIR__ . '/.env') as $line) { foreach (file(__DIR__ . '/.env') as $line) {
$line = trim($line); $line = trim($line);
if (str_starts_with($line, 'APP_BASE=')) { if (strpos($line, 'APP_BASE=') === 0) {
$base = rtrim(trim(explode('=', $line, 2)[1]), '/'); $base = rtrim(trim(explode('=', $line, 2)[1]), '/');
break; break;
} }
} }
} }
if ($base !== '' && str_starts_with($_SERVER['REQUEST_URI'], $base)) { if ($base !== '' && strpos($_SERVER['REQUEST_URI'], $base) === 0) {
$_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], strlen($base)) ?: '/'; $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], strlen($base)) ?: '/';
} }
require_once __DIR__ . '/public/index.php'; require_once __DIR__ . '/public/index.php';
+2 -2
View File
@@ -19,7 +19,7 @@ class Auth
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
?? ''; ?? '';
if (!str_starts_with($header, 'Bearer ')) { if (strpos($header, 'Bearer ') !== 0) {
self::abort(401, 'Token de autorización requerido.'); self::abort(401, 'Token de autorización requerido.');
} }
@@ -78,7 +78,7 @@ class Auth
return base64_decode(strtr($data, '-_', '+/')); return base64_decode(strtr($data, '-_', '+/'));
} }
private static function abort(int $code, string $message): never private static function abort(int $code, string $message): void
{ {
http_response_code($code); http_response_code($code);
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');