BaseController.php

9.79 KB
04/08/2025 05:50
PHP
BaseController.php
<?php

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Database;
use App\Core\Validator;

/**
 * Base Controller
 * Provides common functionality for all controllers
 */
class BaseController
{
    /**
     * @var mixed
     */
    protected $db;
    /**
     * @var mixed
     */
    protected $auth;
    /**
     * @var mixed
     */
    protected $currentUser;

    public function __construct()
    {
        // Start session if not already started
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $this->db = Database::getInstance();
        $this->auth = new Auth($this->db, $_ENV['JWT_SECRET'] ?? 'default-secret');

        // Set JSON content type by default
        header('Content-Type: application/json');

        // Handle CORS preflight requests
        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            $this->handleCorsPreflightRequest();
            exit;
        }
    }

    /**
     * Send JSON response
     */
    protected function jsonResponse(array $data, int $statusCode = 200): void
    {
        http_response_code($statusCode);
        echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        exit;
    }

    /**
     * Get JSON input from request body
     */
    protected function getJsonInput(): array
    {
        $input = file_get_contents('php://input');
        $data = json_decode($input, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $this->jsonResponse([
                'success' => false,
                'error' => 'Invalid JSON input'
            ], 400);
        }

        return $data ?? [];
    }

    /**
     * Get form input (POST data)
     */
    protected function getFormInput(): array
    {
        return $_POST ?? [];
    }

    /**
     * Get query parameters
     */
    protected function getQueryParams(): array
    {
        return $_GET ?? [];
    }

    /**
     * Require authentication
     */
    protected function requireAuth(): bool
    {
        $token = $this->getBearerToken();

        if (!$token) {
            $this->jsonResponse([
                'success' => false,
                'error' => 'Authentication required',
                'message' => 'No authentication token provided'
            ], 401);
            return false;
        }

        $user = $this->auth->validateToken($token);

        if (!$user) {
            $this->jsonResponse([
                'success' => false,
                'error' => 'Invalid or expired token',
                'message' => 'Please login again'
            ], 401);
            return false;
        }

        $this->currentUser = $user;
        return true;
    }

    /**
     * Require admin privileges
     */
    protected function requireAdmin(): bool
    {
        if (!$this->currentUser) {
            $this->jsonResponse([
                'success' => false,
                'error' => 'Authentication required'
            ], 401);
            return false;
        }

        if (!in_array($this->currentUser['role'], ['admin', 'staff'])) {
            $this->jsonResponse([
                'success' => false,
                'error' => 'Admin privileges required',
                'message' => 'You do not have permission to perform this action'
            ], 403);
            return false;
        }

        return true;
    }

    /**
     * Get current authenticated user
     */
    protected function getCurrentUser(): ?array
    {
        return $_SESSION['authenticated_user'] ?? $this->currentUser;
    }

    /**
     * Get Bearer token from Authorization header
     */
    private function getBearerToken(): ?string
    {
        $headers = $this->getAuthorizationHeader();

        if (!$headers) {
            return null;
        }

        if (preg_match('/Bearer\s+(.*)$/i', $headers, $matches)) {
            return $matches[1];
        }

        return null;
    }

    /**
     * Get Authorization header
     */
    private function getAuthorizationHeader(): ?string
    {
        $headers = null;

        if (isset($_SERVER['Authorization'])) {
            $headers = trim($_SERVER['Authorization']);
        } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
            $headers = trim($_SERVER['HTTP_AUTHORIZATION']);
        } elseif (function_exists('apache_request_headers')) {
            $requestHeaders = apache_request_headers();
            $requestHeaders = array_combine(
                array_map('ucwords', array_keys($requestHeaders)),
                array_values($requestHeaders)
            );

            if (isset($requestHeaders['Authorization'])) {
                $headers = trim($requestHeaders['Authorization']);
            }
        }

        return $headers;
    }

    /**
     * Validate input data
     */
    protected function validateInput(array $data, array $rules): bool
    {
        $validator = new Validator();
        return $validator->validate($data, $rules);
    }

    /**
     * Get validation errors
     */
    protected function getValidationErrors(array $data, array $rules): array
    {
        $validator = new Validator();
        $validator->validate($data, $rules);
        return $validator->getErrors();
    }

    /**
     * Sanitize input data
     */
    protected function sanitizeInput($input)
    {
        return Validator::sanitizeInput($input);
    }

    /**
     * Check if debug mode is enabled
     */
    protected function isDebugMode(): bool
    {
        return filter_var($_ENV['DEBUG'] ?? false, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * Log error message
     */
    protected function logError(string $message, array $context = []): void
    {
        $logMessage = $message;
        if (!empty($context)) {
            $logMessage .= ' Context: '.json_encode($context);
        }

        error_log($logMessage);
    }

    /**
     * Handle CORS preflight requests
     */
    private function handleCorsPreflightRequest(): void
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '*';

        header("Access-Control-Allow-Origin: $origin");
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');

        http_response_code(200);
    }

    /**
     * Set CORS headers for regular requests
     */
    protected function setCorsHeaders(): void
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '*';

        header("Access-Control-Allow-Origin: $origin");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Expose-Headers: Content-Length, X-JSON');
    }

    /**
     * Handle method override for POST requests
     */
    protected function getRequestMethod(): string
    {
        $method = $_SERVER['REQUEST_METHOD'];

        // Handle method override for POST requests
        if ($method === 'POST' && isset($_POST['_method'])) {
            $overrideMethod = strtoupper($_POST['_method']);
            if (in_array($overrideMethod, ['PUT', 'PATCH', 'DELETE'])) {
                return $overrideMethod;
            }
        }

        return $method;
    }

    /**
     * Get request data based on method
     */
    protected function getRequestData(): array
    {
        $method = $this->getRequestMethod();

        switch ($method) {
            case 'GET':
                return $_GET;
            case 'POST':
                return array_merge($_POST, $this->getJsonInput());
            case 'PUT':
            case 'PATCH':
            case 'DELETE':
                return $this->getJsonInput();
            default:
                return [];
        }
    }

    /**
     * Check if request is AJAX
     */
    protected function isAjaxRequest(): bool
    {
        return isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
        strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
    }

    /**
     * Get client IP address
     */
    protected function getClientIp(): string
    {
        $ipKeys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];

        foreach ($ipKeys as $key) {
            if (!empty($_SERVER[$key])) {
                $ip = $_SERVER[$key];
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                    return $ip;
                }
            }
        }

        return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    }

    /**
     * Get user agent
     */
    protected function getUserAgent(): string
    {
        return $_SERVER['HTTP_USER_AGENT'] ?? '';
    }

    /**
     * Get request parameters (GET, POST, or JSON body)
     */
    protected function getRequestParams(): array
    {
        $params = [];

        // Merge GET parameters
        $params = array_merge($params, $_GET);

        // Merge POST parameters
        $params = array_merge($params, $_POST);

        // Parse JSON body if content type is JSON
        $contentType = $_SERVER['CONTENT_TYPE'] ?? '';
        if (strpos($contentType, 'application/json') !== false) {
            $jsonBody = json_decode(file_get_contents('php://input'), true);
            if (is_array($jsonBody)) {
                $params = array_merge($params, $jsonBody);
            }
        }

        return $params;
    }

    /**
     * Get request body (JSON or POST data)
     */
    protected function getRequestBody(): array
    {
        $contentType = $_SERVER['CONTENT_TYPE'] ?? '';

        if (strpos($contentType, 'application/json') !== false) {
            $jsonBody = json_decode(file_get_contents('php://input'), true);
            return is_array($jsonBody) ? $jsonBody : [];
        }

        return $_POST;
    }
}