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; } }