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++; } } 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 : ''); }