aspirecloud/app/Http/Controllers/API/WpOrg/Themes/ThemeController.php
2025-10-25 13:15:51 -06:00

121 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\API\WpOrg\Themes;
use App\Exceptions\NotFoundException;
use App\Http\Controllers\Controller;
use App\Http\Middleware\Hacks\InlineFairMetadata;
use App\Services\Themes\FeatureListService;
use App\Services\Themes\QueryThemesService;
use App\Services\Themes\ThemeHotTagsService;
use App\Services\Themes\ThemeInformationService;
use App\Utils\Regex;
use App\Values\WpOrg\Themes\QueryThemesRequest;
use App\Values\WpOrg\Themes\QueryThemesResponse;
use App\Values\WpOrg\Themes\ThemeInformationRequest;
use App\Values\WpOrg\Themes\ThemeResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use function Safe\preg_match;
class ThemeController extends Controller
{
public function __construct(
private readonly QueryThemesService $queryThemes,
private readonly ThemeInformationService $themeInfo,
private readonly ThemeHotTagsService $hotTags,
private readonly FeatureListService $featureList,
)
{
// @mago-expect lint:middleware-in-routes
config('feature.underscore_fair_hack') and $this->middleware(InlineFairMetadata::class);
}
public function info(Request $request): JsonResponse|Response
{
$action = (string)$request->query('action'); // @mago-expect analysis:array-to-string-conversion
try {
return match ($action) {
'query_themes' => $this->doQueryThemes($request),
'theme_information' => $this->doThemeInformation($request),
'hot_tags' => $this->doHotTags($request),
'feature_list' => $this->doFeatureList($request),
default => $this->unknownAction(),
};
} catch (ValidationException $e) {
// Handle validation errors and return a custom response
$firstErrorMessage = collect($e->errors())->flatten()->first();
return $this->sendResponse(['error' => $firstErrorMessage], 400);
} catch (NotFoundException $e) {
return $this->sendResponse(['error' => $e->getMessage()], 404);
}
}
private function doQueryThemes(Request $request): JsonResponse|Response
{
$req = QueryThemesRequest::from($request);
$themes = $this->queryThemes->queryThemes($req);
return $this->sendResponse($themes);
}
private function doThemeInformation(Request $request): JsonResponse|Response
{
// NOTE: upstream requires slug query parameter to be request[slug], just slug is not recognized
$req = ThemeInformationRequest::fromRequest($request);
$response = $this->themeInfo->info($req);
return $this->sendResponse($response);
}
private function doHotTags(Request $request): JsonResponse|Response
{
$tags = $this->hotTags->getHotTags((int) $request->query('number', '-1'));
return $this->sendResponse($tags);
}
private function doFeatureList(Request $request): JsonResponse|Response
{
$wpVersion = $this->getWpVersion($request);
$tags = $this->featureList->getFeatureList($wpVersion);
return $this->sendResponse($tags);
}
private function unknownAction(): JsonResponse
{
return $this->sendResponse(
['error' => 'Action not implemented. <a href="https://codex.wordpress.org/WordPress.org_API">API Docs</a>";}'],
404,
);
}
/**
* Send response based on API version.
*
* @param array<string,mixed>|QueryThemesResponse|ThemeResponse $response
*/
private function sendResponse(
array|QueryThemesResponse|ThemeResponse $response,
int $statusCode = 200,
): JsonResponse|Response {
$version = request()->route('version');
if ($version === '1.0') {
return response(serialize((object) $response), $statusCode);
}
return response()->json($response, $statusCode);
}
private function getWpVersion(Request $request): ?string
{
$version = $request->route('version') ?? '1.2';
if (version_compare($version, '1.2', '>=')) {
return $request->query('wp_version');
} elseif ($matches = Regex::match('|WordPress/([^;]+)|', $request->server('HTTP_USER_AGENT'))) {
// Get version from user agent since it's not explicitly sent to feature_list requests in older API branches.
return $matches[1];
}
return null;
}
}