Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .tools/psalm/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1819,6 +1819,12 @@
<code><![CDATA[gzgets($fpIn, 1024 * 512)]]></code>
</PossiblyFalseOperand>
</file>
<file src="src/ClassDiscovery.php">
<MixedReturnStatement>
<code><![CDATA[array_first(ClassLoader::getRegisteredLoaders())
?? throw new RuntimeException('Composer ClassLoader not found.')]]></code>
</MixedReturnStatement>
</file>
<file src="src/Config.php">
<ArgumentTypeCoercion>
<code><![CDATA[$cfg->getValue('value')]]></code>
Expand Down
2 changes: 0 additions & 2 deletions addons/debug/boot.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

use function Redaxo\Core\View\escape;

ApiFunction::register('debug', rex_api_debug::class);

if (!rex_debug_clockwork::isRexDebugEnabled() || 'debug' === Request::get(ApiFunction::REQ_CALL_PARAM)) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions addons/debug/lib/api_debug.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<?php

use Redaxo\Core\ApiFunction\ApiFunction;
use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Core;
use Redaxo\Core\Http\Response;

/**
* @internal
*/
#[AsApiFunction('debug')]
class rex_api_debug extends ApiFunction
{
public function execute()
Expand Down
2 changes: 2 additions & 0 deletions src/Addon/ApiFunction/AddonOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Redaxo\Core\Addon\Addon as BaseAddon;
use Redaxo\Core\Addon\AddonManager;
use Redaxo\Core\ApiFunction\ApiFunction;
use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Core;
Expand All @@ -18,6 +19,7 @@
/**
* @internal
*/
#[AsApiFunction('addon_operation')]
final class AddonOperation extends ApiFunction
{
#[Override]
Expand Down
60 changes: 26 additions & 34 deletions src/ApiFunction/ApiFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@
namespace Redaxo\Core\ApiFunction;

use BadMethodCallException;
use Redaxo\Core\Addon\ApiFunction\AddonOperation;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\Base\FactoryTrait;
use Redaxo\Core\Content\ApiFunction as ContentApiFunction;
use Redaxo\Core\ClassDiscovery;
use Redaxo\Core\Core;
use Redaxo\Core\Exception\LogicException;
use Redaxo\Core\Http\Context;
use Redaxo\Core\Http\Exception\HttpException;
use Redaxo\Core\Http\Exception\NotFoundHttpException;
use Redaxo\Core\Http\Request;
use Redaxo\Core\Http\Response;
use Redaxo\Core\MetaInfo\ApiFunction\DefaultFieldsCreate;
use Redaxo\Core\Security\ApiFunction as SecurityApiFunction;
use Redaxo\Core\Security\CsrfToken;
use Redaxo\Core\Security\Login;
use Redaxo\Core\Translation\I18n;
Expand Down Expand Up @@ -65,36 +62,11 @@ abstract class ApiFunction
protected $result;

/**
* Explicitly registered api functions.
* Discovered and registered api functions.
*
* @var array<string, class-string<ApiFunction>>
* @var array<string, class-string<self>>|null
*/
private static $functions = [
'addon_operation' => AddonOperation::class,
'article_add' => ContentApiFunction\ArticleAdd::class,
'article_copy' => ContentApiFunction\ArticleCopy::class,
'article_delete' => ContentApiFunction\ArticleDelete::class,
'article_edit' => ContentApiFunction\ArticleEdit::class,
'article_move' => ContentApiFunction\ArticleMove::class,
'article_slice_move' => ContentApiFunction\ArticleSliceMove::class,
'article_slice_status_change' => ContentApiFunction\ArticleSliceStatusChange::class,
'article_status_change' => ContentApiFunction\ArticleStatusChange::class,
'article_to_category' => ContentApiFunction\ArticleToCategory::class,
'article_to_startarticle' => ContentApiFunction\ArticleToStartArticle::class,
'category_add' => ContentApiFunction\CategoryAdd::class,
'category_delete' => ContentApiFunction\CategoryDelete::class,
'category_edit' => ContentApiFunction\CategoryEdit::class,
'category_move' => ContentApiFunction\CategoryMove::class,
'category_status_change' => ContentApiFunction\CategoryStatusChange::class,
'category_to_article' => ContentApiFunction\CategoryToArticle::class,
'content_copy' => ContentApiFunction\ContentCopy::class,
'metainfo_default_fields_create' => DefaultFieldsCreate::class,
'user_has_session' => SecurityApiFunction\UserHasSession::class,
'user_impersonate' => SecurityApiFunction\UserImpersonate::class,
'user_remove_auth_method' => SecurityApiFunction\UserRemoveAuthMethod::class,
'user_remove_session' => SecurityApiFunction\UserRemoveSession::class,
'user_session_status' => SecurityApiFunction\UserSessionStatus::class,
];
private static ?array $functions = null;

/**
* The api function which is bound to the current request.
Expand Down Expand Up @@ -127,7 +99,11 @@ public static function factory(): ?self
$api = Request::request(self::REQ_CALL_PARAM, 'string');

if ($api) {
$apiClass = self::$functions[$api];
$apiClass = self::loadFunctions()[$api] ?? null;

if (null === $apiClass) {
throw new NotFoundHttpException('API function "' . $api . '" is not registered.');
}

if (class_exists($apiClass)) {
$apiImpl = new $apiClass();
Expand All @@ -146,6 +122,7 @@ public static function factory(): ?self
/** @param class-string<ApiFunction> $class */
public static function register(string $name, string $class): void
{
self::loadFunctions();
self::$functions[$name] = $class;
}

Expand Down Expand Up @@ -312,9 +289,24 @@ protected function requiresCsrfProtection()
return false;
}

/** @return array<string, class-string<ApiFunction>> */
private static function loadFunctions(): array
{
if (null !== self::$functions) {
return self::$functions;
}

$functions = [];
foreach (ClassDiscovery::getInstance()->discoverByAttribute(AsApiFunction::class, self::class) as $class => $attribute) {
$functions[$attribute->name] = $class;
}

return self::$functions = $functions;
}

private static function getName(string $class): string
{
$name = array_search($class, self::$functions, true);
$name = array_search($class, self::loadFunctions(), true);
if (false !== $name) {
return $name;
}
Expand Down
13 changes: 13 additions & 0 deletions src/ApiFunction/AsApiFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Redaxo\Core\ApiFunction;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS)]
final readonly class AsApiFunction
{
public function __construct(
public string $name,
) {}
}
Loading
Loading