diff --git a/.tools/psalm/baseline.xml b/.tools/psalm/baseline.xml
index 3f1b5b74b2..6bbb488fd0 100644
--- a/.tools/psalm/baseline.xml
+++ b/.tools/psalm/baseline.xml
@@ -1819,6 +1819,12 @@
+
+
+
+
+
getValue('value')]]>
diff --git a/addons/debug/boot.php b/addons/debug/boot.php
index d58280bcea..3f7c0db0d4 100644
--- a/addons/debug/boot.php
+++ b/addons/debug/boot.php
@@ -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;
}
diff --git a/addons/debug/lib/api_debug.php b/addons/debug/lib/api_debug.php
index 82b4c2f2aa..8aeb96e80e 100644
--- a/addons/debug/lib/api_debug.php
+++ b/addons/debug/lib/api_debug.php
@@ -1,6 +1,7 @@
>
+ * @var array>|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.
@@ -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();
@@ -146,6 +122,7 @@ public static function factory(): ?self
/** @param class-string $class */
public static function register(string $name, string $class): void
{
+ self::loadFunctions();
self::$functions[$name] = $class;
}
@@ -312,9 +289,24 @@ protected function requiresCsrfProtection()
return false;
}
+ /** @return array> */
+ 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;
}
diff --git a/src/ApiFunction/AsApiFunction.php b/src/ApiFunction/AsApiFunction.php
new file mode 100644
index 0000000000..01a6ce349a
--- /dev/null
+++ b/src/ApiFunction/AsApiFunction.php
@@ -0,0 +1,13 @@
+|null */
+ private ?array $relevantPaths = null;
+
+ /** @var array|object>>|null */
+ private ?array $cacheData = null;
+
+ private function __construct(
+ private readonly ClassLoader $classLoader,
+ ) {}
+
+ public static function getInstance(): self
+ {
+ return self::$instance ??= new self(self::findClassLoader());
+ }
+
+ /**
+ * Discovers all non-abstract classes that carry the given attribute.
+ *
+ * @template TAttribute of object
+ * @template TParent of object
+ * @param class-string $attributeClass
+ * @param class-string|null $parentClass Only include classes extending/implementing this type
+ * @return array, TAttribute> Map of class name to attribute instance
+ */
+ public function discoverByAttribute(string $attributeClass, ?string $parentClass = null): array
+ {
+ $cacheKey = $attributeClass . ($parentClass ? '|' . $parentClass : '');
+ $cacheData = $this->loadCacheData();
+
+ if (isset($cacheData[$cacheKey])) {
+ // Reconstruct attribute instances from cached arrays (JSON roundtrip converts objects to arrays)
+ $result = [];
+ foreach ($cacheData[$cacheKey] as $class => $entry) {
+ /**
+ * @var TAttribute $attribute
+ * @psalm-suppress MixedMethodCall
+ */
+ $attribute = is_array($entry) ? new $attributeClass(...$entry) : $entry;
+ $result[$class] = $attribute;
+ }
+ /** @var array, TAttribute> */
+ return $result;
+ }
+
+ $result = [];
+
+ foreach ($this->getRelevantClasses() as $class) {
+ if (!class_exists($class)) {
+ continue;
+ }
+
+ $reflection = new ReflectionClass($class);
+
+ if ($reflection->isAbstract()) {
+ continue;
+ }
+
+ $attributes = $reflection->getAttributes($attributeClass);
+
+ if ([] === $attributes) {
+ continue;
+ }
+
+ if (null !== $parentClass && !$reflection->isSubclassOf($parentClass)) {
+ continue;
+ }
+
+ /** @var class-string $class */
+ $result[$class] = $attributes[0]->newInstance();
+ }
+
+ $this->saveCacheData($cacheKey, $result);
+
+ return $result;
+ }
+
+ public static function clearCache(): void
+ {
+ File::delete(self::getCacheFile());
+ }
+
+ /** @return list */
+ private function getRelevantClasses(): array
+ {
+ $relevantPaths = $this->getRelevantPaths();
+ $classes = [];
+
+ // 1. Classes from Composer's classmap (available for all autoload types when optimized)
+ foreach ($this->classLoader->getClassMap() as $class => $file) {
+ $realFile = (string) realpath($file);
+ if ($this->isRelevantPath($realFile, $relevantPaths)) {
+ $classes[] = $class;
+ }
+ }
+
+ // 2. For PSR-4 prefixes, scan directories not yet covered by the classmap.
+ // With an optimized/authoritative classmap, all classes are already in the classmap above.
+ if ($this->classLoader->isClassMapAuthoritative()) {
+ return $classes;
+ }
+
+ foreach ($this->classLoader->getPrefixesPsr4() as $namespace => $dirs) {
+ foreach ($dirs as $dir) {
+ $realDir = (string) realpath($dir);
+ if (!is_dir($realDir) || !$this->isRelevantPath($realDir . DIRECTORY_SEPARATOR, $relevantPaths)) {
+ continue;
+ }
+
+ foreach ($this->scanDirectory($realDir, $namespace) as $class) {
+ if (!array_key_exists($class, $this->classLoader->getClassMap())) {
+ $classes[] = $class;
+ }
+ }
+ }
+ }
+
+ return $classes;
+ }
+
+ /** @param list $relevantPaths */
+ private function isRelevantPath(string $path, array $relevantPaths): bool
+ {
+ foreach ($relevantPaths as $relevantPath) {
+ if (str_starts_with($path, $relevantPath)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** @return list */
+ private function getRelevantPaths(): array
+ {
+ if (null !== $this->relevantPaths) {
+ return $this->relevantPaths;
+ }
+
+ $paths = [];
+
+ // PSR-4 directories of the root composer package (project-level code)
+ $rootPath = realpath(InstalledVersions::getRootPackage()['install_path']);
+ if (false !== $rootPath) {
+ $vendorPrefix = $rootPath . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR;
+ foreach ($this->classLoader->getPrefixesPsr4() as $dirs) {
+ foreach ($dirs as $dir) {
+ $realDir = (string) realpath($dir);
+ if ('' !== $realDir && !str_starts_with($realDir, $vendorPrefix)) {
+ $paths[] = $realDir . DIRECTORY_SEPARATOR;
+ }
+ }
+ }
+ }
+
+ // Core source directory (dirname of this file's parent = src/)
+ $paths[] = __DIR__ . DIRECTORY_SEPARATOR;
+
+ // Active addon paths
+ foreach (Addon::getAvailableAddons() as $addon) {
+ $paths[] = $addon->path . DIRECTORY_SEPARATOR;
+ }
+
+ return $this->relevantPaths = $paths;
+ }
+
+ /** @return list */
+ private function scanDirectory(string $dir, string $namespace): array
+ {
+ $classes = [];
+
+ /** @var RecursiveDirectoryIterator $file */
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)) as $file) {
+ if (!$file->isFile() || 'php' !== $file->getExtension()) {
+ continue;
+ }
+
+ if (!$this->fileDefinesClass($file->getPathname())) {
+ continue;
+ }
+
+ $relativePath = substr($file->getPathname(), strlen($dir) + 1);
+ $class = $namespace . str_replace(['/', '.php'], ['\\', ''], $relativePath);
+
+ $classes[] = $class;
+ }
+
+ return $classes;
+ }
+
+ /** Checks whether a PHP file defines a class, interface, enum, or trait. */
+ private function fileDefinesClass(string $filePath): bool
+ {
+ $content = file_get_contents($filePath);
+
+ if (false === $content) {
+ return false;
+ }
+
+ $tokens = token_get_all($content, TOKEN_PARSE);
+
+ foreach ($tokens as $token) {
+ if (!is_array($token)) {
+ continue;
+ }
+
+ if (T_CLASS === $token[0] || T_INTERFACE === $token[0] || T_TRAIT === $token[0] || T_ENUM === $token[0]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** @return array|object>> */
+ private function loadCacheData(): array
+ {
+ if (null !== $this->cacheData) {
+ return $this->cacheData;
+ }
+
+ /** @var array{addons_hash?: string, files_hash?: string, data?: array>>} $cache */
+ $cache = File::getCache(self::getCacheFile());
+
+ if (!isset($cache['addons_hash']) || $cache['addons_hash'] !== $this->getAddonHash()) {
+ return $this->cacheData = [];
+ }
+
+ // In debug mode, check if PHP files were added/removed since cache was built
+ if (Core::isDebugMode()) {
+ if (!isset($cache['files_hash']) || $cache['files_hash'] !== $this->getPhpFilesHash()) {
+ return $this->cacheData = [];
+ }
+ }
+
+ return $this->cacheData = $cache['data'] ?? [];
+ }
+
+ /** @param array|object> $data */
+ private function saveCacheData(string $cacheKey, array $data): void
+ {
+ $this->cacheData = $this->loadCacheData();
+ $this->cacheData[$cacheKey] = $data;
+
+ File::putCache(self::getCacheFile(), [
+ 'addons_hash' => $this->getAddonHash(),
+ 'files_hash' => $this->getPhpFilesHash(),
+ 'data' => $this->cacheData,
+ ]);
+ }
+
+ private function getAddonHash(): string
+ {
+ $parts = [];
+ foreach (Addon::getAvailableAddons() as $addon) {
+ $parts[] = $addon->name . ':' . $addon->getVersion();
+ }
+
+ return hash('xxh128', implode(',', $parts));
+ }
+
+ /**
+ * Builds a hash of all PHP file paths in the relevant directories.
+ * This is cheap (just directory listing, no file reading) and detects
+ * added/removed files so the cache is invalidated automatically in debug mode.
+ */
+ private function getPhpFilesHash(): string
+ {
+ $files = [];
+
+ foreach ($this->getRelevantPaths() as $path) {
+ if (!is_dir($path)) {
+ continue;
+ }
+
+ /** @var RecursiveDirectoryIterator $file */
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $file) {
+ if ($file->isFile() && 'php' === $file->getExtension()) {
+ $files[] = $file->getPathname();
+ }
+ }
+ }
+
+ sort($files, SORT_STRING);
+
+ return hash('xxh128', implode("\n", $files));
+ }
+
+ private static function getCacheFile(): string
+ {
+ return Path::coreCache('class_discovery.cache');
+ }
+
+ private static function findClassLoader(): ClassLoader
+ {
+ return array_first(ClassLoader::getRegisteredLoaders())
+ ?? throw new RuntimeException('Composer ClassLoader not found.');
+ }
+}
diff --git a/src/Content/ApiFunction/ArticleAdd.php b/src/Content/ApiFunction/ArticleAdd.php
index 275872a7a6..70e82306f3 100644
--- a/src/Content/ApiFunction/ArticleAdd.php
+++ b/src/Content/ApiFunction/ArticleAdd.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\ArticleHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_add')]
class ArticleAdd extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleCopy.php b/src/Content/ApiFunction/ArticleCopy.php
index a3f04c6f65..53ef185ada 100644
--- a/src/Content/ApiFunction/ArticleCopy.php
+++ b/src/Content/ApiFunction/ArticleCopy.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Backend\Controller;
@@ -16,6 +17,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_copy')]
class ArticleCopy extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleDelete.php b/src/Content/ApiFunction/ArticleDelete.php
index 15c703ef71..f6f0bc1b10 100644
--- a/src/Content/ApiFunction/ArticleDelete.php
+++ b/src/Content/ApiFunction/ArticleDelete.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\ArticleHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_delete')]
class ArticleDelete extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleEdit.php b/src/Content/ApiFunction/ArticleEdit.php
index b25e2493ff..898bef5033 100644
--- a/src/Content/ApiFunction/ArticleEdit.php
+++ b/src/Content/ApiFunction/ArticleEdit.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\ArticleHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_edit')]
class ArticleEdit extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleMove.php b/src/Content/ApiFunction/ArticleMove.php
index 00b688ac93..e5eea34d94 100644
--- a/src/Content/ApiFunction/ArticleMove.php
+++ b/src/Content/ApiFunction/ArticleMove.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_move')]
class ArticleMove extends ApiFunction
{
/**
diff --git a/src/Content/ApiFunction/ArticleSliceMove.php b/src/Content/ApiFunction/ArticleSliceMove.php
index af425db38b..651260b2c4 100644
--- a/src/Content/ApiFunction/ArticleSliceMove.php
+++ b/src/Content/ApiFunction/ArticleSliceMove.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -15,6 +16,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_slice_move')]
class ArticleSliceMove extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleSliceStatusChange.php b/src/Content/ApiFunction/ArticleSliceStatusChange.php
index 7b83777f4a..89f9cec49c 100644
--- a/src/Content/ApiFunction/ArticleSliceStatusChange.php
+++ b/src/Content/ApiFunction/ArticleSliceStatusChange.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_slice_status_change')]
class ArticleSliceStatusChange extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleStatusChange.php b/src/Content/ApiFunction/ArticleStatusChange.php
index b7da5c0b4e..3f2b8820a3 100644
--- a/src/Content/ApiFunction/ArticleStatusChange.php
+++ b/src/Content/ApiFunction/ArticleStatusChange.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\ArticleHandler;
@@ -13,6 +14,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_status_change')]
class ArticleStatusChange extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleToCategory.php b/src/Content/ApiFunction/ArticleToCategory.php
index a3a7e4d79c..e7bdda30a8 100644
--- a/src/Content/ApiFunction/ArticleToCategory.php
+++ b/src/Content/ApiFunction/ArticleToCategory.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_to_category')]
class ArticleToCategory extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ArticleToStartArticle.php b/src/Content/ApiFunction/ArticleToStartArticle.php
index ec70a5b560..7ab6f54246 100644
--- a/src/Content/ApiFunction/ArticleToStartArticle.php
+++ b/src/Content/ApiFunction/ArticleToStartArticle.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('article_to_startarticle')]
class ArticleToStartArticle extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryAdd.php b/src/Content/ApiFunction/CategoryAdd.php
index 7506a49d84..3e5d99da98 100644
--- a/src/Content/ApiFunction/CategoryAdd.php
+++ b/src/Content/ApiFunction/CategoryAdd.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\CategoryHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_add')]
class CategoryAdd extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryDelete.php b/src/Content/ApiFunction/CategoryDelete.php
index e2801117b1..4b14a17fde 100644
--- a/src/Content/ApiFunction/CategoryDelete.php
+++ b/src/Content/ApiFunction/CategoryDelete.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\CategoryHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_delete')]
class CategoryDelete extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryEdit.php b/src/Content/ApiFunction/CategoryEdit.php
index 17714489e1..7946f9ca20 100644
--- a/src/Content/ApiFunction/CategoryEdit.php
+++ b/src/Content/ApiFunction/CategoryEdit.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\CategoryHandler;
@@ -12,6 +13,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_edit')]
class CategoryEdit extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryMove.php b/src/Content/ApiFunction/CategoryMove.php
index 5436543251..af64c192a6 100644
--- a/src/Content/ApiFunction/CategoryMove.php
+++ b/src/Content/ApiFunction/CategoryMove.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_move')]
class CategoryMove extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryStatusChange.php b/src/Content/ApiFunction/CategoryStatusChange.php
index 60956e7dac..bf312c1660 100644
--- a/src/Content/ApiFunction/CategoryStatusChange.php
+++ b/src/Content/ApiFunction/CategoryStatusChange.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\CategoryHandler;
@@ -13,6 +14,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_status_change')]
class CategoryStatusChange extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/CategoryToArticle.php b/src/Content/ApiFunction/CategoryToArticle.php
index 7333e88f7b..c4a402d5bb 100644
--- a/src/Content/ApiFunction/CategoryToArticle.php
+++ b/src/Content/ApiFunction/CategoryToArticle.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\Article;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('category_to_article')]
class CategoryToArticle extends ApiFunction
{
public function execute()
diff --git a/src/Content/ApiFunction/ContentCopy.php b/src/Content/ApiFunction/ContentCopy.php
index c3768bef34..5a740b6371 100644
--- a/src/Content/ApiFunction/ContentCopy.php
+++ b/src/Content/ApiFunction/ContentCopy.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Content\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\ApiFunction\Result;
use Redaxo\Core\Content\ContentHandler;
@@ -13,6 +14,7 @@
/**
* @internal
*/
+#[AsApiFunction('content_copy')]
class ContentCopy extends ApiFunction
{
/**
diff --git a/src/MetaInfo/ApiFunction/DefaultFieldsCreate.php b/src/MetaInfo/ApiFunction/DefaultFieldsCreate.php
index 7bd5240b6d..6aab4c24f2 100644
--- a/src/MetaInfo/ApiFunction/DefaultFieldsCreate.php
+++ b/src/MetaInfo/ApiFunction/DefaultFieldsCreate.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\MetaInfo\ApiFunction;
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;
@@ -18,6 +19,7 @@
/**
* @internal
*/
+#[AsApiFunction('metainfo_default_fields_create')]
class DefaultFieldsCreate extends ApiFunction
{
public function execute()
diff --git a/src/Security/ApiFunction/UserHasSession.php b/src/Security/ApiFunction/UserHasSession.php
index 326eba1b86..e6ac452394 100644
--- a/src/Security/ApiFunction/UserHasSession.php
+++ b/src/Security/ApiFunction/UserHasSession.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Security\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\Core;
use Redaxo\Core\Http\Request;
@@ -11,6 +12,7 @@
/**
* @internal
*/
+#[AsApiFunction('user_has_session')]
class UserHasSession extends ApiFunction
{
/** @return never */
diff --git a/src/Security/ApiFunction/UserImpersonate.php b/src/Security/ApiFunction/UserImpersonate.php
index 49b151cb87..f6faf455af 100644
--- a/src/Security/ApiFunction/UserImpersonate.php
+++ b/src/Security/ApiFunction/UserImpersonate.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Security\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\ApiFunction\Exception\ApiFunctionException;
use Redaxo\Core\Core;
use Redaxo\Core\Filesystem\Url;
@@ -15,6 +16,7 @@
/**
* @internal
*/
+#[AsApiFunction('user_impersonate')]
class UserImpersonate extends ApiFunction
{
/** @return never */
diff --git a/src/Security/ApiFunction/UserRemoveAuthMethod.php b/src/Security/ApiFunction/UserRemoveAuthMethod.php
index 932ceaaacc..6d6821e416 100644
--- a/src/Security/ApiFunction/UserRemoveAuthMethod.php
+++ b/src/Security/ApiFunction/UserRemoveAuthMethod.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Security\ApiFunction;
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;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('user_remove_auth_method')]
class UserRemoveAuthMethod extends ApiFunction
{
public function execute()
diff --git a/src/Security/ApiFunction/UserRemoveSession.php b/src/Security/ApiFunction/UserRemoveSession.php
index c11b67153a..bec9beec10 100644
--- a/src/Security/ApiFunction/UserRemoveSession.php
+++ b/src/Security/ApiFunction/UserRemoveSession.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Security\ApiFunction;
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;
@@ -14,6 +15,7 @@
/**
* @internal
*/
+#[AsApiFunction('user_remove_session')]
class UserRemoveSession extends ApiFunction
{
public function execute()
diff --git a/src/Security/ApiFunction/UserSessionStatus.php b/src/Security/ApiFunction/UserSessionStatus.php
index 6b219a97c8..b4089b5749 100644
--- a/src/Security/ApiFunction/UserSessionStatus.php
+++ b/src/Security/ApiFunction/UserSessionStatus.php
@@ -3,6 +3,7 @@
namespace Redaxo\Core\Security\ApiFunction;
use Redaxo\Core\ApiFunction\ApiFunction;
+use Redaxo\Core\ApiFunction\AsApiFunction;
use Redaxo\Core\Core;
use Redaxo\Core\Http\Response;
use Redaxo\Core\Security\BackendLogin;
@@ -10,6 +11,7 @@
/**
* @internal
*/
+#[AsApiFunction('user_session_status')]
class UserSessionStatus extends ApiFunction
{
/** @return never */