['Content-Type' => 'application/json'], self::TYPE_RAW => ['Content-Type' => 'text/html; charset=UTF-8'], ]; /** * Build response from handler result * * @param mixed $result Handler result * @param string $handler_type Handler type from HandlerFactory * @param array $attributes Method attributes * @return Response */ public static function build($result, $handler_type = null, $attributes = []) { // If already a Response object, return as-is if ($result instanceof Response || $result instanceof JsonResponse || $result instanceof RedirectResponse || $result instanceof BinaryFileResponse || $result instanceof StreamedResponse) { return $result; } // Handle array responses with type hints if (is_array($result) && isset($result['type'])) { return static::__build_typed_response($result); } // Handle based on handler type if ($handler_type !== null) { return static::__build_by_handler_type($result, $handler_type, $attributes); } // Default: return as JSON return static::__build_json_response($result); } /** * Build response from typed result array * * @param array $result * @return Response */ protected static function __build_typed_response($result) { $type = $result['type']; switch ($type) { case self::TYPE_VIEW: return static::__build_view_response($result); case self::TYPE_JSON: return static::__build_json_response( $result['data'] ?? $result, $result['status'] ?? 200, $result['headers'] ?? [] ); case self::TYPE_REDIRECT: return static::__build_redirect_response($result); case self::TYPE_FILE: return static::__build_file_response($result); case self::TYPE_STREAM: return static::__build_stream_response($result); case self::TYPE_ERROR: return static::__build_error_response($result); case self::TYPE_EMPTY: return static::__build_empty_response( $result['status'] ?? 204, $result['headers'] ?? [] ); case self::TYPE_RAW: return static::__build_raw_response($result); default: // Unknown type, return as JSON return static::__build_json_response($result); } } /** * Build response based on handler type * * @param mixed $result * @param string $handler_type * @param array $attributes * @return Response */ protected static function __build_by_handler_type($result, $handler_type, $attributes) { switch ($handler_type) { case HandlerFactory::TYPE_CONTROLLER: // Controllers default to view responses if (is_array($result) && !isset($result['type'])) { $result['type'] = self::TYPE_VIEW; return static::__build_typed_response($result); } break; case HandlerFactory::TYPE_API: // API handlers default to JSON if (!is_array($result) || !isset($result['type'])) { return static::__build_json_response($result); } break; case HandlerFactory::TYPE_FILE: // File handlers default to file responses if (is_array($result) && !isset($result['type'])) { $result['type'] = self::TYPE_FILE; return static::__build_typed_response($result); } break; } // If we have a typed response, build it if (is_array($result) && isset($result['type'])) { return static::__build_typed_response($result); } // Default to JSON return static::__build_json_response($result); } /** * Build view response * * @param array $result * @return Response */ protected static function __build_view_response($result) { $view = $result['view'] ?? 'welcome'; $data = $result['data'] ?? []; $status = $result['status'] ?? 200; $headers = $result['headers'] ?? []; // Check if view exists if (!View::exists($view)) { // Return error response if view not found return static::__build_error_response([ 'code' => 500, 'message' => "View not found: {$view}" ]); } return response()->view($view, $data, $status)->withHeaders($headers); } /** * Build JSON response * * @param mixed $data * @param int $status * @param array $headers * @return JsonResponse */ protected static function __build_json_response($data, $status = 200, $headers = []) { // Merge with default JSON headers $headers = array_merge(static::$default_headers[self::TYPE_JSON], $headers); return response()->json($data, $status, $headers); } /** * Build redirect response * * @param array $result * @return Response */ protected static function __build_redirect_response($result) { $url = $result['url'] ?? '/'; $status = $result['status'] ?? 302; $headers = $result['headers'] ?? []; $secure = $result['secure'] ?? null; // Use Laravel's redirect helper for proper URL generation $response = redirect($url, $status, $headers, $secure); return $response; } /** * Build file response * * @param array $result * @return BinaryFileResponse|Response */ protected static function __build_file_response($result) { $path = $result['path'] ?? null; if (!$path || !file_exists($path)) { return static::__build_error_response([ 'code' => 404, 'message' => 'File not found' ]); } $headers = $result['headers'] ?? []; // Create file response $response = response()->file($path, $headers); // Set download name if provided if (isset($result['name'])) { $disposition = $result['disposition'] ?? 'attachment'; $response->headers->set( 'Content-Disposition', "{$disposition}; filename=\"" . $result['name'] . "\"" ); } // Set MIME type if provided if (isset($result['mime'])) { $response->headers->set('Content-Type', $result['mime']); } return $response; } /** * Build stream response * * @param array $result * @return StreamedResponse */ protected static function __build_stream_response($result) { $callback = $result['callback'] ?? function() { echo ''; }; $status = $result['status'] ?? 200; $headers = $result['headers'] ?? []; return response()->stream($callback, $status, $headers); } /** * Build error response * * @param array $result * @return Response */ protected static function __build_error_response($result) { $code = $result['code'] ?? 500; $message = $result['message'] ?? 'Server Error'; $headers = $result['headers'] ?? []; // Use Laravel's abort for proper error handling abort($code, $message); } /** * Build empty response * * @param int $status * @param array $headers * @return Response */ protected static function __build_empty_response($status = 204, $headers = []) { return response('', $status, $headers); } /** * Build raw response * * @param array $result * @return Response */ protected static function __build_raw_response($result) { $content = $result['content'] ?? ''; $status = $result['status'] ?? 200; $headers = array_merge( static::$default_headers[self::TYPE_RAW], $result['headers'] ?? [] ); return response($content, $status, $headers); } /** * Set CORS headers for response * * @param Response $response * @param array $cors_config * @return Response */ public static function set_cors_headers($response, $cors_config = []) { $default_cors = [ 'Access-Control-Allow-Origin' => '*', 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With', 'Access-Control-Max-Age' => '86400', ]; $cors = array_merge($default_cors, $cors_config); foreach ($cors as $header => $value) { $response->headers->set($header, $value); } return $response; } /** * Set cache headers for response * * @param Response $response * @param int $ttl Time to live in seconds * @param array $options Additional cache options * @return Response */ public static function set_cache_headers($response, $ttl = 3600, $options = []) { if ($ttl > 0) { $response->headers->set('Cache-Control', "public, max-age={$ttl}"); // Set expires header $expires = gmdate('D, d M Y H:i:s', time() + $ttl) . ' GMT'; $response->headers->set('Expires', $expires); // Set ETag if provided if (isset($options['etag'])) { $response->headers->set('ETag', $options['etag']); } // Set Last-Modified if provided if (isset($options['last_modified'])) { $response->headers->set('Last-Modified', $options['last_modified']); } } else { // No cache $response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate'); $response->headers->set('Pragma', 'no-cache'); $response->headers->set('Expires', '0'); } return $response; } /** * Apply response attributes * * @param Response $response * @param array $attributes * @return Response */ public static function apply_attributes($response, $attributes) { // Apply CORS if specified if (isset($attributes['cors'])) { static::set_cors_headers($response, $attributes['cors']); } // Apply cache if specified if (isset($attributes['cache'])) { $cache_config = $attributes['cache']; $ttl = $cache_config['ttl'] ?? 3600; static::set_cache_headers($response, $ttl, $cache_config); } // Apply custom headers if (isset($attributes['headers'])) { foreach ($attributes['headers'] as $header => $value) { $response->headers->set($header, $value); } } return $response; } /** * Build response for asset file * * @param string $path * @param string $base_path * @return Response */ public static function build_asset_response($path, $base_path = null) { // Resolve full path if ($base_path) { $full_path = rtrim($base_path, '/') . '/' . ltrim($path, '/'); } else { $full_path = public_path($path); } if (!file_exists($full_path)) { return static::__build_error_response([ 'code' => 404, 'message' => 'Asset not found' ]); } // Determine MIME type $mime = mime_content_type($full_path); // Build response $response = response()->file($full_path, [ 'Content-Type' => $mime ]); // Set cache headers for assets (1 year for production) if (app()->environment('production')) { static::set_cache_headers($response, 31536000); // 1 year } else { static::set_cache_headers($response, 3600); // 1 hour for dev } return $response; } }