2026-03-04 15:56:58 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
use MintyPHP\Http\Input\FormErrors;
|
|
|
|
|
use MintyPHP\Http\Input\RequestInput;
|
|
|
|
|
use MintyPHP\Http\Input\RequestInputFactory;
|
2026-03-11 23:32:11 +01:00
|
|
|
use MintyPHP\Http\Request;
|
2026-03-04 15:56:58 +01:00
|
|
|
use MintyPHP\Support\Flash;
|
|
|
|
|
|
|
|
|
|
function requestInput(): RequestInput
|
|
|
|
|
{
|
|
|
|
|
static $requestInput = null;
|
|
|
|
|
if ($requestInput instanceof RequestInput) {
|
|
|
|
|
return $requestInput;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$factory = app(RequestInputFactory::class);
|
|
|
|
|
$requestInput = $factory->create();
|
|
|
|
|
return $requestInput;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formErrors(): FormErrors
|
|
|
|
|
{
|
|
|
|
|
return new FormErrors();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formErrorsFrom(array|FormErrors $errors): FormErrors
|
|
|
|
|
{
|
|
|
|
|
return formErrors()->merge($errors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function flashFormErrors(FormErrors $errors, ?string $scope = null, string $keyPrefix = 'form_error'): void
|
|
|
|
|
{
|
|
|
|
|
$index = 0;
|
|
|
|
|
foreach ($errors->toFlatList() as $message) {
|
|
|
|
|
$key = $keyPrefix . '_' . $index;
|
|
|
|
|
Flash::error((string) $message, $scope, $key);
|
|
|
|
|
$index++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-11 23:32:11 +01:00
|
|
|
|
2026-04-15 18:45:45 +02:00
|
|
|
/**
|
|
|
|
|
* Consume and remove flash messages matching any provided scope.
|
|
|
|
|
*
|
|
|
|
|
* @param array<int, string> $scopes
|
|
|
|
|
* @return array<int, array<string, mixed>>
|
|
|
|
|
*/
|
|
|
|
|
function flashConsumeForScopes(array $scopes): array
|
|
|
|
|
{
|
|
|
|
|
return Flash::consumeForScopes($scopes);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 23:32:11 +01:00
|
|
|
function requestExtractNestedReturnValue(mixed $value): string
|
|
|
|
|
{
|
|
|
|
|
if (is_string($value)) {
|
|
|
|
|
return trim($value);
|
|
|
|
|
}
|
|
|
|
|
if (!is_array($value)) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ($i = count($value) - 1; $i >= 0; $i--) {
|
|
|
|
|
$item = $value[$i] ?? null;
|
|
|
|
|
if (is_string($item) && trim($item) !== '') {
|
|
|
|
|
return trim($item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestLooksLikeDetailPath(string $path): bool
|
|
|
|
|
{
|
|
|
|
|
$path = trim($path, '/');
|
|
|
|
|
if ($path === '') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$segments = array_values(array_filter(explode('/', $path), static fn (string $segment): bool => $segment !== ''));
|
|
|
|
|
return in_array('edit', $segments, true) || in_array('create', $segments, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestNormalizeSafeInternalTarget(string $target, int $maxDepth = 5): string
|
|
|
|
|
{
|
|
|
|
|
$target = trim($target);
|
|
|
|
|
if ($target === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parts = parse_url($target);
|
|
|
|
|
if (
|
|
|
|
|
!is_array($parts)
|
|
|
|
|
|| ($parts['scheme'] ?? '') !== ''
|
|
|
|
|
|| ($parts['host'] ?? '') !== ''
|
|
|
|
|
) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$safeTarget = Request::safeReturnTarget($target);
|
|
|
|
|
if ($safeTarget === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$safeParts = parse_url($safeTarget);
|
|
|
|
|
if (!is_array($safeParts)) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$path = Request::stripLocale(Request::stripBasePath((string) ($safeParts['path'] ?? '')));
|
|
|
|
|
if ($path === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$queryParams = [];
|
|
|
|
|
parse_str((string) ($safeParts['query'] ?? ''), $queryParams);
|
|
|
|
|
$nestedReturnTarget = requestExtractNestedReturnValue($queryParams['return'] ?? null);
|
|
|
|
|
unset($queryParams['return']);
|
|
|
|
|
$query = http_build_query($queryParams);
|
|
|
|
|
$normalizedOuterTarget = $path . ($query !== '' ? '?' . $query : '');
|
|
|
|
|
|
|
|
|
|
if ($maxDepth > 0 && $nestedReturnTarget !== '' && requestLooksLikeDetailPath($path)) {
|
|
|
|
|
$resolvedNestedTarget = requestNormalizeSafeInternalTarget($nestedReturnTarget, $maxDepth - 1);
|
|
|
|
|
if ($resolvedNestedTarget !== '') {
|
|
|
|
|
return $resolvedNestedTarget;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $normalizedOuterTarget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestResolveReturnTargetFromInput(RequestInput $input, string $fallback = ''): string
|
|
|
|
|
{
|
|
|
|
|
$normalizedFallback = requestNormalizeSafeInternalTarget($fallback);
|
|
|
|
|
|
|
|
|
|
$candidate = $input->body('return', '');
|
|
|
|
|
if (!is_string($candidate)) {
|
|
|
|
|
$candidate = '';
|
|
|
|
|
}
|
|
|
|
|
$candidate = trim($candidate);
|
|
|
|
|
|
|
|
|
|
if ($candidate === '') {
|
|
|
|
|
$candidate = $input->query('return', '');
|
|
|
|
|
if (!is_string($candidate)) {
|
|
|
|
|
$candidate = '';
|
|
|
|
|
}
|
|
|
|
|
$candidate = trim($candidate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($candidate !== '') {
|
|
|
|
|
$resolved = requestNormalizeSafeInternalTarget($candidate);
|
|
|
|
|
if ($resolved !== '') {
|
|
|
|
|
if ($normalizedFallback === '') {
|
|
|
|
|
return $resolved;
|
|
|
|
|
}
|
|
|
|
|
$resolvedPath = (string) parse_url($resolved, PHP_URL_PATH);
|
|
|
|
|
if ($resolvedPath !== '' && !requestLooksLikeDetailPath($resolvedPath)) {
|
|
|
|
|
return $resolved;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $normalizedFallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestResolveReturnTarget(string $fallback = ''): string
|
|
|
|
|
{
|
|
|
|
|
return requestResolveReturnTargetFromInput(requestInput(), $fallback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function requestPathWithReturnTarget(string $path, string $returnTarget = ''): string
|
|
|
|
|
{
|
|
|
|
|
$resolvedPath = requestNormalizeSafeInternalTarget($path);
|
|
|
|
|
if ($resolvedPath === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$returnTarget = trim($returnTarget);
|
|
|
|
|
if ($returnTarget === '') {
|
|
|
|
|
return $resolvedPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$normalizedReturnTarget = requestNormalizeSafeInternalTarget($returnTarget);
|
|
|
|
|
if ($normalizedReturnTarget === '') {
|
|
|
|
|
return $resolvedPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parts = parse_url($resolvedPath);
|
|
|
|
|
if (!is_array($parts)) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
$pathValue = Request::stripLocale(Request::stripBasePath((string) ($parts['path'] ?? '')));
|
|
|
|
|
if ($pathValue === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
$queryParams = [];
|
|
|
|
|
parse_str((string) ($parts['query'] ?? ''), $queryParams);
|
|
|
|
|
unset($queryParams['return']);
|
|
|
|
|
$queryParams['return'] = $normalizedReturnTarget;
|
|
|
|
|
$query = http_build_query($queryParams);
|
|
|
|
|
|
|
|
|
|
return $pathValue . ($query !== '' ? '?' . $query : '');
|
|
|
|
|
}
|