PHP code example of dr2gsistemas / xpress

1. Go to this page and download the library: Download dr2gsistemas/xpress library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

dr2gsistemas / xpress example snippets




press\XRouter;

$router = new XRouter();
$router->setBasePath('/api/v1');




use Xpress\XRouter;
use Xpress\XRequest;
use Xpress\XResponse;
use Xpress\Attributes\{XRoute, XMiddleware, XGroup};

$router = xpress();

// Rutas simples con closures
$router->get('/hello', function (XRequest $req) {
    return json(['message' => '¡Hola Mundo!']);
});

// Rutas con parámetros
$router->get('/users/{id}', function (XRequest $req, array $params) {
    return json(['user_id' => $params['id']]);
});

// Rutas con query parameters
$router->get('/search', function (XRequest $req) {
    $query = $req->getQuery('q', '');
    $page = $req->getQuery('page', 1);
    return json(['query' => $query, 'page' => (int)$page]);
});

// Rutas POST con body JSON
$router->post('/users', function (XRequest $req) {
    $data = $req->getJson();
    return json(['created' => $data], 201);
});

// Middleware inline
$router->get('/admin', function () {
    return json(['secret' => 'data']);
}, [function (XRequest $req, callable $next) {
    if ($req->getHeader('X-Admin-Token') !== 'secret123') {
        return json(['error' => 'Unauthorized'], 401);
    }
    return $next($req);
}]);


// src/Controllers/UserController.php

namespace App\Controllers;

use Xpress\XRouter;
use Xpress\XRequest;
use Xpress\XResponse;
use Xpress\Attributes\{XRoute, XMiddleware, XGroup};

#[XGroup('/users')]
class UserController
{
    #[XRoute('', 'GET')]
    public function index(XRequest $request): XResponse
    {
        $users = [
            ['id' => 1, 'name' => 'Juan Pérez', 'email' => '[email protected]'],
            ['id' => 2, 'name' => 'María García', 'email' => '[email protected]'],
        ];

        return (new XResponse())->json([
            'data' => $users,
            'total' => count($users)
        ]);
    }

    #[XRoute('/{id}', 'GET')]
    public function show(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];

        $user = $this->findUser($id);

        if (!$user) {
            return (new XResponse())->json([
                'error' => 'Usuario no encontrado'
            ], 404);
        }

        return (new XResponse())->json([
            'data' => $user
        ]);
    }

    #[XRoute('', 'POST')]
    #[XMiddleware(AuthMiddleware::class)]
    public function store(XRequest $request): XResponse
    {
        $data = $request->getJson();

        if (empty($data['name']) || empty($data['email'])) {
            return (new XResponse())->json([
                'error' => 'Nombre y email son requeridos'
            ], 422);
        }

        $user = [
            'id' => random_int(100, 9999),
            'name' => $data['name'],
            'email' => $data['email'],
            'created_at' => date('Y-m-d H:i:s')
        ];

        return (new XResponse())->json([
            'data' => $user,
            'message' => 'Usuario creado exitosamente'
        ], 201);
    }

    #[XRoute('/{id}', 'PUT')]
    #[XMiddleware([AuthMiddleware::class, ValidateMiddleware::class])]
    public function update(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];
        $data = $request->getJson();

        return (new XResponse())->json([
            'data' => [
                'id' => $id,
                'name' => $data['name'] ?? 'Nombre actualizado',
                'email' => $data['email'] ?? '[email protected]'
            ],
            'message' => 'Usuario actualizado'
        ]);
    }

    #[XRoute('/{id}', 'DELETE')]
    #[XMiddleware(AuthMiddleware::class)]
    public function destroy(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];

        return (new XResponse())->json([
            'message' => "Usuario {$id} eliminado"
        ]);
    }

    private function findUser(int $id): ?array
    {
        $users = [
            1 => ['id' => 1, 'name' => 'Juan Pérez', 'email' => '[email protected]'],
            2 => ['id' => 2, 'name' => 'María García', 'email' => '[email protected]'],
        ];

        return $users[$id] ?? null;
    }
}


// public/index.php

pp\Controllers\UserController;
use App\Controllers\PostController;
use App\Middleware\CorsMiddleware;

$router = new XRouter();

// Middleware global (se ejecuta en todas las rutas)
$router->use(CorsMiddleware::class);

// Rutas con parámetros (orden importante - específico primero)
$router->get('/users/{id}/posts/{postId}', [PostController::class, 'getComment']);

// Rutas normales
$router->get('/users', [UserController::class, 'index']);
$router->get('/users/{id}', [UserController::class, 'show']);
$router->post('/users', [UserController::class, 'store']);
$router->put('/users/{id}', [UserController::class, 'update']);
$router->delete('/users/{id}', [UserController::class, 'destroy']);

// O simplemente registrar todos los controladores
$router->registerControllers([
    UserController::class,
    PostController::class
]);

$response = $router->dispatch();
$response->send();


// src/Middleware/AuthMiddleware.php

namespace App\Middleware;

use Xpress\XRequest;
use Xpress\XResponse;

class AuthMiddleware
{
    public function handle(XRequest $request, callable $next): XResponse
    {
        $authHeader = $request->getHeader('Authorization');

        if (empty($authHeader)) {
            return (new XResponse())->json([
                'error' => 'Autenticación requerida',
                'message' => 'Por favor proporcione el header Authorization'
            ], 401);
        }

        if (!str_starts_with($authHeader, 'Bearer ')) {
            return (new XResponse())->json([
                'error' => 'Token inválido',
                'message' => 'El token debe comenzar con "Bearer "'
            ], 401);
        }

        $token = substr($authHeader, 7);

        if (!$this->validateToken($token)) {
            return (new XResponse())->json([
                'error' => 'Token expirado o inválido'
            ], 401);
        }

        $request = $request->withAttribute('user_id', $this->getUserId($token));

        return $next($request);
    }

    private function validateToken(string $token): bool
    {
        // Lógica de validación de token
        return strlen($token) > 10;
    }

    private function getUserId(string $token): ?int
    {
        return 1; // Retornar ID del usuario del token
    }
}


// src/Middleware/LoggerMiddleware.php

namespace App\Middleware;

use Xpress\XRequest;
use Xpress\XResponse;

class LoggerMiddleware
{
    public function handle(XRequest $request, callable $next): XResponse
    {
        $startTime = microtime(true);
        $method = $request->getMethod();
        $path = $request->getPath();

        $response = $next($request);

        $duration = round((microtime(true) - $startTime) * 1000, 2);

        $log = sprintf(
            "[%s] %s %s - %d (%sms)",
            date('Y-m-d H:i:s'),
            $method,
            $path,
            $response->getStatusCode(),
            $duration
        );

        error_log($log);

        return $response->withHeader('X-Response-Time', "{$duration}ms");
    }
}


// src/Middleware/RateLimitMiddleware.php

namespace App\Middleware;

use Xpress\XRequest;
use Xpress\XResponse;

class RateLimitMiddleware
{
    private static array $requests = [];

    public function handle(XRequest $request, callable $next): XResponse
    {
        $ip = $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown';
        $key = $ip . ':' . date('Y-m-d:H:i');

        if (!isset(self::$requests[$key])) {
            self::$requests[$key] = 0;
        }

        self::$requests[$key]++;
        $limit = 60;
        $remaining = max(0, $limit - self::$requests[$key]);

        if (self::$requests[$key] > $limit) {
            return (new XResponse())->json([
                'error' => 'Límite de requests excedido',
                'retry_after' => 60
            ], 429)
            ->withHeader('X-RateLimit-Limit', (string) $limit)
            ->withHeader('X-RateLimit-Remaining', '0');
        }

        $response = $next($request);

        return $response
            ->withHeader('X-RateLimit-Limit', (string) $limit)
            ->withHeader('X-RateLimit-Remaining', (string) $remaining);
    }
}


// src/Middleware/CorsMiddleware.php

namespace App\Middleware;

use Xpress\XRequest;
use Xpress\XResponse;

class CorsMiddleware
{
    public function handle(XRequest $request, callable $next): XResponse
    {
        if ($request->getMethod() === 'OPTIONS') {
            return (new XResponse(204))
                ->withHeader('Access-Control-Allow-Origin', '*')
                ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD')
                ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept, Origin')
                ->withHeader('Access-Control-Max-Age', '86400');
        }

        $response = $next($request);

        return $response
            ->withHeader('Access-Control-Allow-Origin', '*')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD')
            ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept, Origin');
    }
}


// Uso global
$router->use(LoggerMiddleware::class);
$router->use(CorsMiddleware::class);

// Uso en ruta específica
$router->get('/profile', function () {
    return json(['user' => ['name' => 'Juan']]);
}, [AuthMiddleware::class]);

// Múltiples middlewares en ruta
$router->post('/admin/users', function () {
    return json(['created' => true]);
}, [
    AuthMiddleware::class,
    RateLimitMiddleware::class,
    AdminMiddleware::class
]);

// Middleware inline
$router->delete('/items/{id}', function (XRequest $req, array $params) {
    return json(['deleted' => $params['id']]);
}, [function (XRequest $req, callable $next) {
    if ($req->getRouteParam('id') === '0') {
        return json(['error' => 'ID inválido'], 400);
    }
    return $next($req);
}]);


// src/Controllers/Api/V1/ProductController.php

namespace App\Controllers\Api\V1;

use Xpress\XRequest;
use Xpress\XResponse;
use Xpress\Attributes\{XRoute, XMiddleware, XGroup};

#[XGroup('/products')]
class ProductController
{
    #[XRoute('', 'GET')]
    public function index(XRequest $request): XResponse
    {
        return (new XResponse())->json([
            'products' => [
                ['id' => 1, 'name' => 'Producto A', 'price' => 29.99],
                ['id' => 2, 'name' => 'Producto B', 'price' => 49.99],
            ]
        ]);
    }

    #[XRoute('/{id}', 'GET')]
    public function show(XRequest $request, array $params): XResponse
    {
        return (new XResponse())->json([
            'product' => [
                'id' => (int) $params['id'],
                'name' => 'Producto Demo',
                'price' => 99.99,
                'stock' => 100
            ]
        ]);
    }

    #[XRoute('', 'POST')]
    #[XMiddleware(AuthMiddleware::class)]
    public function store(XRequest $request): XResponse
    {
        $data = $request->getJson();

        return (new XResponse())->json([
            'product' => [
                'id' => random_int(1000, 9999),
                'name' => $data['name'] ?? 'Nuevo Producto',
                'price' => $data['price'] ?? 0,
                'created_at' => date('Y-m-d H:i:s')
            ]
        ], 201);
    }

    #[XRoute('/search', 'GET')]
    public function search(XRequest $request): XResponse
    {
        $q = $request->getQuery('q', '');
        $category = $request->getQuery('category', 'all');
        $minPrice = $request->getQuery('min_price', 0);
        $maxPrice = $request->getQuery('max_price', PHP_INT_MAX);

        return (new XResponse())->json([
            'query' => $q,
            'filters' => [
                'category' => $category,
                'min_price' => (float) $minPrice,
                'max_price' => (float) $maxPrice
            ],
            'results' => [
                ['id' => 1, 'name' => 'Resultado 1', 'price' => 25.00],
                ['id' => 2, 'name' => 'Resultado 2', 'price' => 35.00],
            ],
            'total' => 2
        ]);
    }
}


// src/Controllers/AdminController.php

namespace App\Controllers;

use Xpress\XRequest;
use Xpress\XResponse;
use Xpress\Attributes\{XRoute, XMiddleware, XGroup};

#[XGroup('/admin', [AuthMiddleware::class, AdminMiddleware::class])]
class AdminController
{
    #[XRoute('/dashboard', 'GET')]
    public function dashboard(): XResponse
    {
        return (new XResponse())->json([
            'stats' => [
                'users' => 1250,
                'orders' => 342,
                'revenue' => 45890.50
            ]
        ]);
    }

    #[XRoute('/settings', 'GET')]
    public function settings(): XResponse
    {
        return (new XResponse())->json([
            'settings' => [
                'site_name' => 'Mi Tienda',
                'timezone' => 'America/Mexico_City',
                'currency' => 'MXN'
            ]
        ]);
    }
}

$router = new XRouter();

// Configuración
$router->setBasePath('/api/v1');  // Prefijo base para todas las rutas

// Middleware global
$router->use(CorsMiddleware::class);
$router->use(LoggerMiddleware::class);

// Métodos HTTP
$router->get('/ruta', $handler);
$router->post('/ruta', $handler);
$router->put('/ruta', $handler);
$router->patch('/ruta', $handler);
$router->delete('/ruta', $handler);
$router->options('/ruta', $handler);
$router->head('/ruta', $handler);

// Múltiples métodos
$router->any(['GET', 'POST'], '/ruta', $handler);

// Con middlewares
$router->get('/protegida', $handler, [AuthMiddleware::class]);
$router->post('/recurso', $handler, [AuthMiddleware::class, ValidateMiddleware::class]);

// Registrar controladores con atributos
$router->registerControllers([UserController::class, ProductController::class]);

// Dispatch
$response = $router->dispatch();              // Desde globals
$response = $router->dispatch($request);     // Con request custom

// Crear desde globals
$request = XRequest::fromGlobals();

// Crear desde URI (útil para testing)
$request = new XRequest(new ServerRequest('GET', new Uri('/users/123')));

// Métodos HTTP
$request->getMethod();                           // 'GET', 'POST', etc.
$request->getPath();                             // '/users/123'
$request->getUri();                              // UriInterface

// Query parameters
$request->getQueryParams();                      // ['page' => 1, 'limit' => 10]
$request->getQuery('page');                      // '1'
$request->getQuery('page', 1);                   // 1 (default)
$request->getQuery('q', 'default');              // 'default'

// Body y JSON
$request->getBody();                             // string raw
$request->getParsedBody();                        // parsed form data
$request->getJson();                              // ['key' => 'value'] o null
$request->isJson();                              // true/false

// Headers
$request->getHeader('Authorization');            // 'Bearer token' o null
$request->getHeader('Content-Type');              // 'application/json' o null
$request->getHeaders();                          // todos los headers
$request->hasHeader('X-Custom');                 // true/false

// Parámetros de ruta (capturados de {param})
$request->getRouteParams();                       // ['id' => '123', 'action' => 'edit']
$request->getRouteParam('id');                    // '123'
$request->getRouteParam('id', 0);                // 0 (default)
$request->setRouteParams(['id' => '123']);       // setear params

// Attributes (para pasar datos entre middlewares)
$request->getAttribute('user_id');               // valor o null
$request->withAttribute('user_id', 123);         // nuevo request con attribute

// Cookies
$request->getCookies();                          // ['session' => 'abc123']
$request->getCookie('session');                  // 'abc123'

// Server params
$request->getServerParams();                     // $_SERVER
$request->getProtocolVersion();                   // '1.1'

// Files upload
$request->getUploadedFiles();                    // uploaded files array

// Constructor básico
$response = new XResponse();
$response = new XResponse(200);
$response = new XResponse(404, ['Content-Type' => 'text/plain'], 'Not Found');

// Crear con contenido
(new XResponse())->json(['key' => 'value']);
(new XResponse())->text('Hello World');
(new XResponse())->html('<h1>Título</h1>');
(new XResponse())->xml('<root><item>valor</item></root>');

// Con código de estado
(new XResponse())->json(['error' => 'Not found'], 404);
(new XResponse())->text('Created', 201);

// Helpers estáticos
XResponse::ok();                                 // 200
XResponse::created(['id' => 1]);                  // 201
XResponse::noContent();                          // 204
XResponse::notFound();                           // 404
XResponse::unauthorized();                       // 401
XResponse::forbidden();                         // 403
XResponse::badRequest();                        // 400
XResponse::error('Server error', 500);           // 500

// Métodos fluidos
$response
    ->withHeader('X-Custom-Header', 'value')
    ->withHeader('X-Another', 'another')
    ->withStatus(200, 'OK');

// Redirección
$response->redirect('/new-location');
$response->redirect('/new-location', 301);       // permanent
$response->redirect('/new-location', 307);       // temporary

// CORS
$response->cors([
    'origin' => '*',                              // o dominio específico
    'methods' => 'GET, POST, PUT',
    'headers' => 'Content-Type, Authorization',
    'expose_headers' => 'X-Custom',
    'max_age' => 86400,
    'credentials' => false
]);

// Cookie
$response->cookie('session', 'abc123');
$response->cookie('token', 'xyz', [
    'expires' => time() + 3600,
    'path' => '/',
    'domain' => '',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);

// Enviar respuesta
$response->send();                               // headers + body

// Getters
$response->getStatusCode();                       // 200
$response->getReasonPhrase();                     // 'OK'
$response->getHeaders();                         // todos los headers
$response->getHeaderLine('Content-Type');         // 'application/json'
$response->getBody();                            // StreamInterface
$response->getProtocolVersion();                  // '1.1'

// Instancia global del router
xpress();

// Respuestas rápidas
json(['key' => 'value']);
json(['error' => 'msg'], 400);
text('Hello');
html('<h1>Title</h1>');

// Redirecciones y errores
redirect('/new-path');
redirect('/login', 302);
notFound();
unauthorized();
badRequest();
error('Server error');
error('Custom error', 500);

// Helpers con XResponse
json()    → (new XResponse())->json()
text()    → (new XResponse())->text()
html()    → (new XResponse())->html()
redirect() → (new XResponse())->redirect()
notFound() → XResponse::notFound()->text()


// public/index.php

press\XRequest;
use Xpress\XResponse;
use Xpress\Attributes\{XRoute, XMiddleware, XGroup};

class Database
{
    private static array $users = [];
    private static int $lastId = 0;

    public static function users(): array { return self::$users; }

    public static function findUser(int $id): ?array
    {
        return self::$users[$id] ?? null;
    }

    public static function createUser(array $data): array
    {
        self::$users[++self::$lastId] = [
            'id' => self::$lastId,
            'name' => $data['name'] ?? '',
            'email' => $data['email'] ?? '',
            'created_at' => date('Y-m-d H:i:s')
        ];
        return self::$users[self::$lastId];
    }

    public static function updateUser(int $id, array $data): ?array
    {
        if (!isset(self::$users[$id])) return null;
        self::$users[$id] = array_merge(self::$users[$id], $data);
        return self::$users[$id];
    }

    public static function deleteUser(int $id): bool
    {
        if (!isset(self::$users[$id])) return false;
        unset(self::$users[$id]);
        return true;
    }
}

#[XGroup('/api')]
class ApiController
{
    #[XRoute('/health', 'GET')]
    public function health(): XResponse
    {
        return (new XResponse())->json([
            'status' => 'healthy',
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => '1.0.0'
        ]);
    }
}

#[XGroup('/api/users')]
class UserController
{
    #[XRoute('', 'GET')]
    public function index(XRequest $request): XResponse
    {
        $page = max(1, (int) $request->getQuery('page', 1));
        $limit = min(100, max(1, (int) $request->getQuery('limit', 10)));
        $search = $request->getQuery('search', '');

        $users = array_values(Database::users());

        if ($search) {
            $users = array_filter($users, fn($u) =>
                stripos($u['name'], $search) !== false ||
                stripos($u['email'], $search) !== false
            );
        }

        $total = count($users);
        $offset = ($page - 1) * $limit;
        $users = array_slice($users, $offset, $limit);

        return (new XResponse())->json([
            'data' => $users,
            'pagination' => [
                'page' => $page,
                'limit' => $limit,
                'total' => $total,
                'pages' => ceil($total / $limit)
            ]
        ]);
    }

    #[XRoute('/{id}', 'GET')]
    public function show(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];
        $user = Database::findUser($id);

        if (!$user) {
            return (new XResponse())->json([
                'error' => 'Usuario no encontrado'
            ], 404);
        }

        return (new XResponse())->json(['data' => $user]);
    }

    #[XRoute('', 'POST')]
    public function store(XRequest $request): XResponse
    {
        $data = $request->getJson();

        $errors = [];
        if (empty($data['name'])) {
            $errors['name'] = 'El nombre es requerido';
        }
        if (empty($data['email'])) {
            $errors['email'] = 'El email es requerido';
        } elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            $errors['email'] = 'El email no es válido';
        }

        if (!empty($errors)) {
            return (new XResponse())->json([
                'error' => 'Datos de validación fallidos',
                'errors' => $errors
            ], 422);
        }

        $user = Database::createUser($data);

        return (new XResponse())->json([
            'data' => $user,
            'message' => 'Usuario creado exitosamente'
        ], 201);
    }

    #[XRoute('/{id}', 'PUT')]
    public function update(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];
        $data = $request->getJson();

        $user = Database::updateUser($id, $data);

        if (!$user) {
            return (new XResponse())->json([
                'error' => 'Usuario no encontrado'
            ], 404);
        }

        return (new XResponse())->json([
            'data' => $user,
            'message' => 'Usuario actualizado'
        ]);
    }

    #[XRoute('/{id}', 'DELETE')]
    public function destroy(XRequest $request, array $params): XResponse
    {
        $id = (int) $params['id'];

        if (!Database::deleteUser($id)) {
            return (new XResponse())->json([
                'error' => 'Usuario no encontrado'
            ], 404);
        }

        return (new XResponse())->json([
            'message' => 'Usuario eliminado'
        ]);
    }
}

// Configurar router
$router = new XRouter();
$router->use(function (XRequest $req, callable $next) {
    $response = $next($req);
    return $response->withHeader('X-API-Version', '1.0');
});
$router->registerControllers([ApiController::class, UserController::class]);

// Ejecutar
$response = $router->dispatch();
$response->send();

use Xpress\Result\XResult;
use Xpress\Result\XError;

// Retornar éxito
$result = XResult::ok(['user' => ['id' => 1, 'name' => 'Juan']]);

// Retornar error
$result = XResult::fail('Usuario no encontrado', 404);

// Con datos adicionales
$result = XResult::fail('Validación fallida', 422, ['errors' => ['email' => 'inválido']]);

use Xpress\Result\XError;

XError::badRequest('Datos inválidos');          // 400
XError::unauthorized('No autenticado');         // 401
XError::forbidden('Sin permisos');              // 403
XError::notFound('Recurso no encontrado');     // 404
XError::conflict('Conflicto de datos');         // 409
XError::unprocessable('Entidad no procesable'); // 422
XError::validation(['email' => 'inválido']);     // 422 con errores
XError::internal('Error interno');              // 500

$result = XResult::ok(['data' => 'value']);

// Verificación
$result->isSuccess();      // true
$result->isFailure();      // false
$result->isNotFound();     // false
$result->isUnauthorized(); // false

// Extracción de valores
$result->getValue();                // ['data' => 'value']
$result->getValueOr('default');    // 'default' si es error
$result->unwrap();                  // Lanza excepción si es error
$result->unwrapOr('default');       // 'default' si es error

// Manejo de errores
$result->getError();          // null (éxito) o XError
$result->getErrorMessage();   // string del error
$result->getErrorCode();      // código HTTP del error
$result->getErrorData();      // datos adicionales del error

// Modificación
$result->withCode(201);       // Cambiar código HTTP a 201
$result->withHttpCode(203);  // Similar, más semántico

$result = XResult::ok(['user_id' => 1])
    ->andThen(fn($data) => findUserById($data['user_id']))
    ->andThen(fn($user) => $user->isActive() 
        ? XResult::ok($user) 
        : XResult::fail('Usuario inactivo', 403))
    ->map(fn($user) => $user->toArray());

// map: transforma el valor si es éxito
// mapError: transforma el error si es fallo
// andThen: encadena operaciones que retornan Result
// orElse: maneja errores y puede recuperarlos

$result = XResult::ok(['user' => $user]);

// Directamente a Response
$result->toResponse()->send();

// O usando send()
$result->send();

// Convertir a array
$result->toArray();
// [
//     'success' => true,
//     'data' => ['user' => ...],
//     'error' => null
// ]


use Xpress\XRequest;
use Xpress\Result\XResult;
use Xpress\Result\XResultController;

class UserController
{
    use XResultController;

    public function show(XRequest $request, array $params): XResult
    {
        return $this->try(function() use ($params) {
            $id = (int) $params['id'];
            $user = $this->findUser($id);
            
            if (!$user) {
                return $this->notFound('Usuario no encontrado');
            }
            
            return $this->ok($user->toArray());
        });
    }

    public function store(XRequest $request): XResult
    {
        return $this->try(function() use ($request) {
            $data = $request->getJson();
            
            $this->validate($data, [
                'name' => '

// En helpers.php
result_ok(['data' => 'value']);              // XResult::ok()
result_fail('Error', 500, ['key' => 'val']); // XResult::fail()
result_error(XError::notFound());            // XResult::error()


use Xpress\XRouter;
use Xpress\XRequest;
use Xpress\Result\XResult;
use Xpress\Result\XResultController;

class ProductController
{
    use XResultController;

    #[XRoute('/products', 'GET')]
    public function index(XRequest $request): XResult
    {
        return $this->try(function() use ($request) {
            $page = (int) $request->getQuery('page', 1);
            $limit = min(100, (int) $request->getQuery('limit', 10));
            
            $products = $this->getProductRepository()->paginate($page, $limit);
            
            return $this->ok([
                'products' => $products['data'],
                'pagination' => $products['pagination']
            ]);
        });
    }

    #[XRoute('/products/{id}', 'GET')]
    public function show(XRequest $request, array $params): XResult
    {
        return $this->try(function() use ($params) {
            $product = $this->getProductRepository()->find((int) $params['id']);
            
            if (!$product) {
                return $this->notFound('Producto no encontrado');
            }
            
            return $this->ok($product->toArray());
        });
    }

    #[XRoute('/products', 'POST')]
    public function store(XRequest $request): XResult
    {
        return $this->try(function() use ($request) {
            $data = $request->getJson();
            
            $errors = $this->validate($data, [
                'name' => '

public function index(): XResponse  // XResponse directo
{
    return (new XResponse())->json(['data' => []]);
}

public function show(): XResult  // Con Result pattern
{
    return $this->ok(['data' => []]);
}

// Ambos funcionan en el router
apache
# Xpress Router - Apache Configuration
RewriteEngine On

# Redirigir requests al index.php
RewriteCond %{REQUEST_URI} -f [OR]
RewriteCond %{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ index.php [QSA,L]

# Security headers
<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-XSS-Protection "1; mode=block"
</IfModule>

# Prevenir listing de directorios
Options -Indexes

# Cache estáticos
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>

├── public/
│   ├── .htaccess
│   └── index.php
├── src/
│   ├── controllers/
│   └── middleware/
├── bootstrap.php
├── .env.example
└── routes/
    └── web.php

mi-api/
├── composer.json
├── public/
│   └── index.php          # Entry point
├── src/
│   ├── Controllers/
│   │   └── UserController.php
│   └── Middlewares/
│       └── AuthMiddleware.php
└── vendor/