33namespace Redaxo \Core \ApiFunction ;
44
55use BadMethodCallException ;
6- use Redaxo \Core \Addon \ApiFunction \AddonOperation ;
76use Redaxo \Core \ApiFunction \Exception \ApiFunctionException ;
87use Redaxo \Core \Base \FactoryTrait ;
9- use Redaxo \Core \Content \ ApiFunction as ContentApiFunction ;
8+ use Redaxo \Core \ClassDiscovery ;
109use Redaxo \Core \Core ;
1110use Redaxo \Core \Exception \LogicException ;
1211use Redaxo \Core \Http \Context ;
1312use Redaxo \Core \Http \Exception \HttpException ;
1413use Redaxo \Core \Http \Exception \NotFoundHttpException ;
1514use Redaxo \Core \Http \Request ;
1615use Redaxo \Core \Http \Response ;
17- use Redaxo \Core \MetaInfo \ApiFunction \DefaultFieldsCreate ;
18- use Redaxo \Core \Security \ApiFunction as SecurityApiFunction ;
1916use Redaxo \Core \Security \CsrfToken ;
2017use Redaxo \Core \Security \Login ;
2118use Redaxo \Core \Translation \I18n ;
@@ -65,36 +62,11 @@ abstract class ApiFunction
6562 protected $ result ;
6663
6764 /**
68- * Explicitly registered api functions.
65+ * Discovered and registered api functions.
6966 *
70- * @var array<string, class-string<ApiFunction>>
67+ * @var array<string, class-string<self>>|null
7168 */
72- private static $ functions = [
73- 'addon_operation ' => AddonOperation::class,
74- 'article_add ' => ContentApiFunction \ArticleAdd::class,
75- 'article_copy ' => ContentApiFunction \ArticleCopy::class,
76- 'article_delete ' => ContentApiFunction \ArticleDelete::class,
77- 'article_edit ' => ContentApiFunction \ArticleEdit::class,
78- 'article_move ' => ContentApiFunction \ArticleMove::class,
79- 'article_slice_move ' => ContentApiFunction \ArticleSliceMove::class,
80- 'article_slice_status_change ' => ContentApiFunction \ArticleSliceStatusChange::class,
81- 'article_status_change ' => ContentApiFunction \ArticleStatusChange::class,
82- 'article_to_category ' => ContentApiFunction \ArticleToCategory::class,
83- 'article_to_startarticle ' => ContentApiFunction \ArticleToStartArticle::class,
84- 'category_add ' => ContentApiFunction \CategoryAdd::class,
85- 'category_delete ' => ContentApiFunction \CategoryDelete::class,
86- 'category_edit ' => ContentApiFunction \CategoryEdit::class,
87- 'category_move ' => ContentApiFunction \CategoryMove::class,
88- 'category_status_change ' => ContentApiFunction \CategoryStatusChange::class,
89- 'category_to_article ' => ContentApiFunction \CategoryToArticle::class,
90- 'content_copy ' => ContentApiFunction \ContentCopy::class,
91- 'metainfo_default_fields_create ' => DefaultFieldsCreate::class,
92- 'user_has_session ' => SecurityApiFunction \UserHasSession::class,
93- 'user_impersonate ' => SecurityApiFunction \UserImpersonate::class,
94- 'user_remove_auth_method ' => SecurityApiFunction \UserRemoveAuthMethod::class,
95- 'user_remove_session ' => SecurityApiFunction \UserRemoveSession::class,
96- 'user_session_status ' => SecurityApiFunction \UserSessionStatus::class,
97- ];
69+ private static ?array $ functions = null ;
9870
9971 /**
10072 * The api function which is bound to the current request.
@@ -127,7 +99,11 @@ public static function factory(): ?self
12799 $ api = Request::request (self ::REQ_CALL_PARAM , 'string ' );
128100
129101 if ($ api ) {
130- $ apiClass = self ::$ functions [$ api ];
102+ $ apiClass = self ::loadFunctions ()[$ api ] ?? null ;
103+
104+ if (null === $ apiClass ) {
105+ throw new NotFoundHttpException ('API function " ' . $ api . '" is not registered. ' );
106+ }
131107
132108 if (class_exists ($ apiClass )) {
133109 $ apiImpl = new $ apiClass ();
@@ -146,6 +122,7 @@ public static function factory(): ?self
146122 /** @param class-string<ApiFunction> $class */
147123 public static function register (string $ name , string $ class ): void
148124 {
125+ self ::loadFunctions ();
149126 self ::$ functions [$ name ] = $ class ;
150127 }
151128
@@ -312,9 +289,24 @@ protected function requiresCsrfProtection()
312289 return false ;
313290 }
314291
292+ /** @return array<string, class-string<ApiFunction>> */
293+ private static function loadFunctions (): array
294+ {
295+ if (null !== self ::$ functions ) {
296+ return self ::$ functions ;
297+ }
298+
299+ $ functions = [];
300+ foreach (ClassDiscovery::getInstance ()->discoverByAttribute (AsApiFunction::class, self ::class) as $ class => $ attribute ) {
301+ $ functions [$ attribute ->name ] = $ class ;
302+ }
303+
304+ return self ::$ functions = $ functions ;
305+ }
306+
315307 private static function getName (string $ class ): string
316308 {
317- $ name = array_search ($ class , self ::$ functions , true );
309+ $ name = array_search ($ class , self ::loadFunctions () , true );
318310 if (false !== $ name ) {
319311 return $ name ;
320312 }
0 commit comments