<?php
declare(strict_types=1);
/**************************************************************************************
*
* Catalyst PHP Framework
* PHP Version 8.3 (Required).
*
* @package Catalyst
* @subpackage Public
* @see https://github.com/arcanisgk/catalyst
*
* @author Walter Nuñez (arcanisgk/original founder) <[email protected]>
* @copyright 2023 - 2025
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*
* @note This program is distributed in the hope that it will be useful
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
*
* @category Framework
* @filesource
*
* @link https://catalyst.dock Local development URL
*
*/
use Catalyst\Framework\Core\Exceptions\RouteNotFoundException;
use Catalyst\Framework\Core\Response\JsonResponse;
use Catalyst\Framework\Core\Response\RedirectResponse;
use Catalyst\Framework\Core\Response\Response;
use Catalyst\Framework\Core\Response\ViewResponse;
use Catalyst\Framework\Core\Route\Router;
use Random\RandomException;
if (!function_exists('route')) {
/**
* Generate a URL to a named route
*
* @param string $name The name of the route
* @param array $parameters Parameters for the route
* @param bool $absolute Whether to generate an absolute URL
* @return string The generated URL
* @throws RouteNotFoundException If the route doesn't exist
*/
function route(string $name, array $parameters = [], bool $absolute = false): string
{
return Router::getInstance()->url($name, $parameters, $absolute);
}
}
if (!function_exists('redirect')) {
/**
* Create a redirect response to the given URL
*
* @param string $url The URL to redirect to
* @param int $status The HTTP status code (default: 302)
* @param array $headers Additional headers
* @return RedirectResponse
*/
function redirect(string $url, int $status = 302, array $headers = []): RedirectResponse
{
return Response::redirect($url, $status, $headers);
}
}
if (!function_exists('redirect_to_route')) {
/**
* Create a redirect response to a named route
*
* @param string $name The name of the route
* @param array $parameters Parameters for the route
* @param int $status The HTTP status code
* @param array $headers Additional headers
* @return RedirectResponse
* @throws RouteNotFoundException If the route doesn't exist
*/
function redirect_to_route(
string $name,
array $parameters = [],
int $status = 302,
array $headers = []
): RedirectResponse
{
$url = route($name, $parameters);
return redirect($url, $status, $headers);
}
}
if (!function_exists('route_is')) {
/**
* Determine if the current request URL matches a pattern
*
* @param string $pattern Pattern to check against
* @return bool
*/
function route_is(string $pattern): bool
{
$request = $_SERVER['REQUEST_URI'] ?? '/';
// Remove query string
if (($pos = strpos($request, '?')) !== false) {
$request = substr($request, 0, $pos);
}
// Simple wildcard matching
$pattern = str_replace('*', '.*', preg_quote($pattern, '/'));
return (bool)preg_match('/^' . $pattern . '$/', $request);
}
}
if (!function_exists('view')) {
/**
* Create a view response
*
* @param string $view View name
* @param array $data View data
* @param int $status HTTP status code
* @param array $headers Response headers
* @return ViewResponse
*/
function view(
string $view,
array $data = [],
int $status = 200,
array $headers = []
): ViewResponse
{
return new ViewResponse($view, $data, $status, $headers);
}
}
if (!function_exists('view_with_layout')) {
/**
* Create a view response with a layout
*
* @param string $view View name
* @param array $data View data
* @param string $layout Layout name
* @param int $status HTTP status code
* @param array $headers Response headers
* @return ViewResponse
*/
function view_with_layout(
string $view,
array $data = [],
string $layout = 'default',
int $status = 200,
array $headers = []
): ViewResponse
{
return ViewResponse::withLayout($view, $data, $layout, $status, $headers);
}
}
if (!function_exists('json')) {
/**
* Create a JSON response
*
* @param mixed $data The data to encode as JSON
* @param int $status The HTTP status code
* @param array $headers Array of HTTP headers
* @param int $options JSON encoding options
* @return JsonResponse
*/
function json(
mixed $data = null,
int $status = 200,
array $headers = [],
int $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
): JsonResponse
{
return new JsonResponse($data, $status, $headers, $options);
}
}
if (!function_exists('json_success')) {
/**
* Create a JSON success response
*
* @param mixed $data The data payload
* @param string|null $message Optional success message
* @param int $status HTTP status code
* @param array $headers HTTP headers
* @return JsonResponse
*/
function json_success(
mixed $data = null,
?string $message = null,
int $status = 200,
array $headers = []
): JsonResponse
{
return JsonResponse::api($data, true, $message, $status, $headers);
}
}
if (!function_exists('json_error')) {
/**
* Create a JSON error response
*
* @param string $message Error message
* @param mixed $errors Detailed error information
* @param int $status HTTP status code
* @param array $headers HTTP headers
* @return JsonResponse
*/
function json_error(
string $message,
mixed $errors = null,
int $status = 400,
array $headers = []
): JsonResponse
{
return JsonResponse::error($message, $errors, $status, $headers);
}
}
if (!function_exists('route_url')) {
/**
* Generate a URL by concatenating segments
*
* @param string ...$segments URL segments
* @return string The generated URL
*/
function route_url(string ...$segments): string
{
$url = '';
foreach ($segments as $segment) {
$segment = trim($segment, '/');
$url .= ($segment ? "/$segment" : '');
}
return $url ?: '/';
}
}
if (!function_exists('current_route_url')) {
/**
* Get the current URL
*
* @param bool $withQueryString Include query string
* @return string Current URL
*/
function current_route_url(bool $withQueryString = false): string
{
$url = $_SERVER['REQUEST_URI'] ?? '/';
if (!$withQueryString && ($pos = strpos($url, '?')) !== false) {
$url = substr($url, 0, $pos);
}
return $url;
}
}
if (!function_exists('csrf_token')) {
/**
* Generate or retrieve a CSRF token for the current session
*
* @return string The CSRF token
* @throws RandomException
*/
function csrf_token(): string
{
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Generate a new token if one doesn't exist
if (!isset($_SESSION['_csrf_token'])) {
$_SESSION['_csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['_csrf_token'];
}
}
if (!function_exists('csrf_field')) {
/**
* Generate a hidden input field containing the CSRF token
*
* @return string HTML input element with CSRF token
* @throws RandomException
*/
function csrf_field(): string
{
return '<input type="hidden" name="_token" value="' . csrf_token() . '">';
}
}
if (!function_exists('csrf_verify')) {
/**
* Verify that the provided token matches the stored CSRF token
*
* @param string|null $token The token to verify
* @return bool Whether the token is valid
*/
function csrf_verify(?string $token = null): bool
{
// Get token from request if not provided
if ($token === null) {
$token = $_POST['_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null;
}
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Compare tokens
return $token !== null &&
isset($_SESSION['_csrf_token']) &&
hash_equals($_SESSION['_csrf_token'], $token);
}
}
if (!function_exists('asset')) {
/**
* Generate URL for an asset file
*
* @param string $path Path to the asset file
* @param bool $absolute Whether to return an absolute URL
* @return string The asset URL
*/
function asset(string $path, bool $absolute = false): string
{
// Remove leading slash if present
$path = ltrim($path, '/');
// Base path for assets
$basePath = '/assets/';
// Build the URL
$url = $basePath . $path;
// Add domain for absolute URLs
if ($absolute) {
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$url = $protocol . '://' . $host . $url;
}
return $url;
}
}
|