false, 'error' => 'A server error occurred' ]); } }); ini_set('display_errors', 0); error_reporting(E_ALL); // Custom error handler. Only genuine errors abort the request; notices, // warnings and deprecations (e.g. new deprecations on a PHP upgrade) are // logged but must not take the endpoint down with a 500. set_error_handler(function ($errno, $errstr, $errfile, $errline) { // Respect the @-operator / error_reporting. if (!(error_reporting() & $errno)) { return false; } error_log("PHP Error in ticket_dependencies.php: $errstr in $errfile:$errline"); if (!in_array($errno, [E_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_PARSE], true)) { // Non-fatal: log and continue. return true; } ob_end_clean(); http_response_code(500); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'error' => 'A server error occurred' ]); exit; }); // Custom exception handler set_exception_handler(function ($e) { // Log detailed error server-side error_log('Exception in ticket_dependencies.php: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); ob_end_clean(); http_response_code(500); header('Content-Type: application/json'); echo json_encode([ 'success' => false, 'error' => 'A server error occurred' ]); exit; }); // Apply rate limiting (also starts session) require_once dirname(__DIR__) . '/middleware/RateLimitMiddleware.php'; RateLimitMiddleware::apply('api'); // Ensure session is started if (session_status() === PHP_SESSION_NONE) { session_start(); } require_once dirname(__DIR__) . '/config/config.php'; require_once dirname(__DIR__) . '/helpers/Database.php'; require_once dirname(__DIR__) . '/services/DependencyService.php'; require_once dirname(__DIR__) . '/helpers/ResponseHelper.php'; header('Content-Type: application/json'); // Check authentication if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) { ResponseHelper::unauthorized(); } $currentUser = $_SESSION['user']; // CSRF Protection for POST/DELETE if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'DELETE') { require_once dirname(__DIR__) . '/middleware/CsrfMiddleware.php'; $csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; if (!CsrfMiddleware::validateToken($csrfToken)) { ResponseHelper::error('Invalid CSRF token', 403, ['csrf_token' => CsrfMiddleware::getToken()]); } } // Use centralized database connection $conn = Database::getConnection(); // Check if ticket_dependencies table exists $tableCheck = $conn->query("SHOW TABLES LIKE 'ticket_dependencies'"); if ($tableCheck->num_rows === 0) { ResponseHelper::serverError('Ticket dependencies feature not available. The ticket_dependencies table does not exist. Please run the migration.'); } $method = $_SERVER['REQUEST_METHOD']; try { switch ($method) { case 'GET': // Get dependencies for a ticket $result = DependencyService::list($conn, $currentUser, $_GET['ticket_id'] ?? null); if (!$result['success']) { ResponseHelper::error($result['error'], $result['http_status']); } ResponseHelper::success([ 'dependencies' => $result['dependencies'], 'dependents' => $result['dependents'] ]); break; case 'POST': // Add a new dependency $data = json_decode(file_get_contents('php://input'), true); if (!is_array($data)) { ResponseHelper::error('Invalid JSON'); } $result = DependencyService::add($conn, $currentUser, $data); if (!$result['success']) { ResponseHelper::error($result['error'], $result['http_status']); } ResponseHelper::created($result); break; case 'DELETE': // Remove a dependency, by dependency_id or by ticket IDs + type $data = json_decode(file_get_contents('php://input'), true); if (!is_array($data)) { ResponseHelper::error('Invalid JSON'); } $result = DependencyService::remove($conn, $currentUser, $data); if (!$result['success']) { ResponseHelper::error($result['error'], $result['http_status']); } ResponseHelper::success([], 'Dependency removed'); break; default: ResponseHelper::error('Method not allowed', 405); } } catch (Exception $e) { // Log detailed error server-side error_log('Ticket dependencies API error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); ResponseHelper::serverError('An error occurred while processing the dependency request'); };