137 lines
4.1 KiB
PHP
137 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support;
|
|
|
|
/**
|
|
* RouteArgumentParser handles parsing URL arguments in format: "page-name--h:HASHKEY" or "page-name--e:ENCODED_PAYLOAD"
|
|
*/
|
|
class RouteArgumentParser
|
|
{
|
|
/**
|
|
* Parse a route argument string into its components
|
|
*
|
|
* @param string $argument The route argument string (e.g., "edituser--h:USER_HASH123")
|
|
* @return array {slug: string, type?: 'hash'|'payload', value?: string} Array with parsed components
|
|
*/
|
|
public function parseArgument($argument)
|
|
{
|
|
if (!is_string($argument) || empty($argument)) {
|
|
return [
|
|
'slug' => $argument,
|
|
'type' => null,
|
|
'value' => null
|
|
];
|
|
}
|
|
|
|
// Check for hash format: --h:HASHKEY
|
|
if (preg_match('/^(.*?)--h:(.*)$/', $argument, $matches)) {
|
|
return [
|
|
'slug' => $matches[1],
|
|
'type' => 'hash',
|
|
'value' => $this->decodeHashValue($matches[2])
|
|
];
|
|
}
|
|
|
|
// Check for payload format: --e:ENCODED_PAYLOAD
|
|
if (preg_match('/^(.*?)--e:(.*)$/', $argument, $matches)) {
|
|
return [
|
|
'slug' => $matches[1],
|
|
'type' => 'payload',
|
|
'value' => $this->decodePayloadValue($matches[2])
|
|
];
|
|
}
|
|
|
|
// Backward compatibility: Check for hash format without colon: --hHASHKEY
|
|
if (preg_match('/^(.*?)--h([^:].*)$/', $argument, $matches)) {
|
|
return [
|
|
'slug' => $matches[1],
|
|
'type' => 'hash',
|
|
'value' => $this->decodeHashValue($matches[2])
|
|
];
|
|
}
|
|
|
|
// No hash/payload found - just return the slug
|
|
return [
|
|
'slug' => $argument,
|
|
'type' => null,
|
|
'value' => null
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Decode a hash value from URL format (base64 encoded)
|
|
*/
|
|
private function decodeHashValue($encodedValue)
|
|
{
|
|
if (!is_string($encodedValue) || empty($encodedValue)) {
|
|
return $encodedValue;
|
|
}
|
|
|
|
try {
|
|
// Remove base64 encoding and decode
|
|
$decoded = base64_decode($encodedValue, true);
|
|
|
|
// If it's valid UTF-8 string representation of the hashkey
|
|
if (is_string($decoded) && mb_check_encoding($decoded, 'utf-8')) {
|
|
// Frontend encodes as btoa(encodeURIComponent(hashkey)),
|
|
// so we need to urldecode after base64_decode
|
|
return urldecode($decoded);
|
|
}
|
|
|
|
return $encodedValue; // Return original if decoding fails
|
|
} catch (\Exception $e) {
|
|
error_log('[RouteArgumentParser] Error decoding hash: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decode a payload value from URL format (base64 encoded JSON)
|
|
*/
|
|
private function decodePayloadValue($encodedValue)
|
|
{
|
|
if (!is_string($encodedValue) || empty($encodedValue)) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
// Remove base64 encoding and decode
|
|
$decoded = base64_decode($encodedValue, true);
|
|
|
|
// Decode URI components if it was encoded on frontend
|
|
if ($decoded) {
|
|
$decoded = urldecode($decoded);
|
|
}
|
|
|
|
// Parse JSON
|
|
$jsonData = json_decode($decoded, true);
|
|
|
|
if (json_last_error() === JSON_ERROR_NONE) {
|
|
return $jsonData;
|
|
}
|
|
|
|
return null;
|
|
} catch (\Exception $e) {
|
|
error_log('[RouteArgumentParser] Error decoding payload: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if argument contains hash format
|
|
*/
|
|
public function isHashFormat($argument)
|
|
{
|
|
return (bool) preg_match('/^(.*?)--h:?(.*)$/', $argument, $matches);
|
|
}
|
|
|
|
/**
|
|
* Check if argument contains payload format
|
|
*/
|
|
public function isPayloadFormat($argument)
|
|
{
|
|
return (bool) preg_match('/^(.*?)--e:(.*)$/', $argument, $matches);
|
|
}
|
|
} |