Diff: STRATO-apps/wordpress_03/app/wp-includes/abilities-api/class-wp-abilities-registry.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 + /**
3 + * Abilities API
4 + *
5 + * Defines WP_Abilities_Registry class.
6 + *
7 + * @package WordPress
8 + * @subpackage Abilities API
9 + * @since 6.9.0
10 + */
11 +
12 + declare( strict_types = 1 );
13 +
14 + /**
15 + * Manages the registration and lookup of abilities.
16 + *
17 + * @since 6.9.0
18 + * @access private
19 + */
20 + final class WP_Abilities_Registry {
21 + /**
22 + * The singleton instance of the registry.
23 + *
24 + * @since 6.9.0
25 + * @var self|null
26 + */
27 + private static $instance = null;
28 +
29 + /**
30 + * Holds the registered abilities.
31 + *
32 + * @since 6.9.0
33 + * @var WP_Ability[]
34 + */
35 + private $registered_abilities = array();
36 +
37 + /**
38 + * Registers a new ability.
39 + *
40 + * Do not use this method directly. Instead, use the `wp_register_ability()` function.
41 + *
42 + * @since 6.9.0
43 + *
44 + * @see wp_register_ability()
45 + *
46 + * @param string $name The name of the ability. The name must be a string containing a namespace
47 + * prefix, i.e. `my-plugin/my-ability`. It can only contain lowercase
48 + * alphanumeric characters, dashes and the forward slash.
49 + * @param array<string, mixed> $args {
50 + * An associative array of arguments for the ability.
51 + *
52 + * @type string $label The human-readable label for the ability.
53 + * @type string $description A detailed description of what the ability does.
54 + * @type string $category The ability category slug this ability belongs to.
55 + * @type callable $execute_callback A callback function to execute when the ability is invoked.
56 + * Receives optional mixed input and returns mixed result or WP_Error.
57 + * @type callable $permission_callback A callback function to check permissions before execution.
58 + * Receives optional mixed input and returns bool or WP_Error.
59 + * @type array<string, mixed> $input_schema Optional. JSON Schema definition for the ability's input.
60 + * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output.
61 + * @type array<string, mixed> $meta {
62 + * Optional. Additional metadata for the ability.
63 + *
64 + * @type array<string, bool|null> $annotations {
65 + * Optional. Semantic annotations describing the ability's behavioral characteristics.
66 + * These annotations are hints for tooling and documentation.
67 + *
68 + * @type bool|null $readonly Optional. If true, the ability does not modify its environment.
69 + * @type bool|null $destructive Optional. If true, the ability may perform destructive updates to its environment.
70 + * If false, the ability performs only additive updates.
71 + * @type bool|null $idempotent Optional. If true, calling the ability repeatedly with the same arguments
72 + * will have no additional effect on its environment.
73 + * }
74 + * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. Default false.
75 + * }
76 + * @type string $ability_class Optional. Custom class to instantiate instead of WP_Ability.
77 + * }
78 + * @return WP_Ability|null The registered ability instance on success, null on failure.
79 + */
80 + public function register( string $name, array $args ): ?WP_Ability {
81 + if ( ! preg_match( '/^[a-z0-9-]+\/[a-z0-9-]+$/', $name ) ) {
82 + _doing_it_wrong(
83 + __METHOD__,
84 + __(
85 + 'Ability name must be a string containing a namespace prefix, i.e. "my-plugin/my-ability". It can only contain lowercase alphanumeric characters, dashes and the forward slash.'
86 + ),
87 + '6.9.0'
88 + );
89 + return null;
90 + }
91 +
92 + if ( $this->is_registered( $name ) ) {
93 + _doing_it_wrong(
94 + __METHOD__,
95 + /* translators: %s: Ability name. */
96 + sprintf( __( 'Ability "%s" is already registered.' ), esc_html( $name ) ),
97 + '6.9.0'
98 + );
99 + return null;
100 + }
101 +
102 + /**
103 + * Filters the ability arguments before they are validated and used to instantiate the ability.
104 + *
105 + * @since 6.9.0
106 + *
107 + * @param array<string, mixed> $args {
108 + * An associative array of arguments for the ability.
109 + *
110 + * @type string $label The human-readable label for the ability.
111 + * @type string $description A detailed description of what the ability does.
112 + * @type string $category The ability category slug this ability belongs to.
113 + * @type callable $execute_callback A callback function to execute when the ability is invoked.
114 + * Receives optional mixed input and returns mixed result or WP_Error.
115 + * @type callable $permission_callback A callback function to check permissions before execution.
116 + * Receives optional mixed input and returns bool or WP_Error.
117 + * @type array<string, mixed> $input_schema Optional. JSON Schema definition for the ability's input.
118 + * @type array<string, mixed> $output_schema Optional. JSON Schema definition for the ability's output.
119 + * @type array<string, mixed> $meta {
120 + * Optional. Additional metadata for the ability.
121 + *
122 + * @type array<string, bool|string> $annotations Optional. Annotation metadata for the ability.
123 + * @type bool $show_in_rest Optional. Whether to expose this ability in the REST API. Default false.
124 + * }
125 + * @type string $ability_class Optional. Custom class to instantiate instead of WP_Ability.
126 + * }
127 + * @param string $name The name of the ability, with its namespace.
128 + */
129 + $args = apply_filters( 'wp_register_ability_args', $args, $name );
130 +
131 + // Validate ability category exists if provided (will be validated as required in WP_Ability).
132 + if ( isset( $args['category'] ) ) {
133 + if ( ! wp_has_ability_category( $args['category'] ) ) {
134 + _doing_it_wrong(
135 + __METHOD__,
136 + sprintf(
137 + /* translators: %1$s: ability category slug, %2$s: ability name */
138 + __( 'Ability category "%1$s" is not registered. Please register the ability category before assigning it to ability "%2$s".' ),
139 + esc_html( $args['category'] ),
140 + esc_html( $name )
141 + ),
142 + '6.9.0'
143 + );
144 + return null;
145 + }
146 + }
147 +
148 + // The class is only used to instantiate the ability, and is not a property of the ability itself.
149 + if ( isset( $args['ability_class'] ) && ! is_a( $args['ability_class'], WP_Ability::class, true ) ) {
150 + _doing_it_wrong(
151 + __METHOD__,
152 + __( 'The ability args should provide a valid `ability_class` that extends WP_Ability.' ),
153 + '6.9.0'
154 + );
155 + return null;
156 + }
157 +
158 + /** @var class-string<WP_Ability> */
159 + $ability_class = $args['ability_class'] ?? WP_Ability::class;
160 + unset( $args['ability_class'] );
161 +
162 + try {
163 + // WP_Ability::prepare_properties() will throw an exception if the properties are invalid.
164 + $ability = new $ability_class( $name, $args );
165 + } catch ( InvalidArgumentException $e ) {
166 + _doing_it_wrong(
167 + __METHOD__,
168 + $e->getMessage(),
169 + '6.9.0'
170 + );
171 + return null;
172 + }
173 +
174 + $this->registered_abilities[ $name ] = $ability;
175 + return $ability;
176 + }
177 +
178 + /**
179 + * Unregisters an ability.
180 + *
181 + * Do not use this method directly. Instead, use the `wp_unregister_ability()` function.
182 + *
183 + * @since 6.9.0
184 + *
185 + * @see wp_unregister_ability()
186 + *
187 + * @param string $name The name of the registered ability, with its namespace.
188 + * @return WP_Ability|null The unregistered ability instance on success, null on failure.
189 + */
190 + public function unregister( string $name ): ?WP_Ability {
191 + if ( ! $this->is_registered( $name ) ) {
192 + _doing_it_wrong(
193 + __METHOD__,
194 + /* translators: %s: Ability name. */
195 + sprintf( __( 'Ability "%s" not found.' ), esc_html( $name ) ),
196 + '6.9.0'
197 + );
198 + return null;
199 + }
200 +
201 + $unregistered_ability = $this->registered_abilities[ $name ];
202 + unset( $this->registered_abilities[ $name ] );
203 +
204 + return $unregistered_ability;
205 + }
206 +
207 + /**
208 + * Retrieves the list of all registered abilities.
209 + *
210 + * Do not use this method directly. Instead, use the `wp_get_abilities()` function.
211 + *
212 + * @since 6.9.0
213 + *
214 + * @see wp_get_abilities()
215 + *
216 + * @return WP_Ability[] The array of registered abilities.
217 + */
218 + public function get_all_registered(): array {
219 + return $this->registered_abilities;
220 + }
221 +
222 + /**
223 + * Checks if an ability is registered.
224 + *
225 + * Do not use this method directly. Instead, use the `wp_has_ability()` function.
226 + *
227 + * @since 6.9.0
228 + *
229 + * @see wp_has_ability()
230 + *
231 + * @param string $name The name of the registered ability, with its namespace.
232 + * @return bool True if the ability is registered, false otherwise.
233 + */
234 + public function is_registered( string $name ): bool {
235 + return isset( $this->registered_abilities[ $name ] );
236 + }
237 +
238 + /**
239 + * Retrieves a registered ability.
240 + *
241 + * Do not use this method directly. Instead, use the `wp_get_ability()` function.
242 + *
243 + * @since 6.9.0
244 + *
245 + * @see wp_get_ability()
246 + *
247 + * @param string $name The name of the registered ability, with its namespace.
248 + * @return WP_Ability|null The registered ability instance, or null if it is not registered.
249 + */
250 + public function get_registered( string $name ): ?WP_Ability {
251 + if ( ! $this->is_registered( $name ) ) {
252 + _doing_it_wrong(
253 + __METHOD__,
254 + /* translators: %s: Ability name. */
255 + sprintf( __( 'Ability "%s" not found.' ), esc_html( $name ) ),
256 + '6.9.0'
257 + );
258 + return null;
259 + }
260 + return $this->registered_abilities[ $name ];
261 + }
262 +
263 + /**
264 + * Utility method to retrieve the main instance of the registry class.
265 + *
266 + * The instance will be created if it does not exist yet.
267 + *
268 + * @since 6.9.0
269 + *
270 + * @return WP_Abilities_Registry|null The main registry instance, or null when `init` action has not fired.
271 + */
272 + public static function get_instance(): ?self {
273 + if ( ! did_action( 'init' ) ) {
274 + _doing_it_wrong(
275 + __METHOD__,
276 + sprintf(
277 + // translators: %s: init action.
278 + __( 'Ability API should not be initialized before the %s action has fired.' ),
279 + '<code>init</code>'
280 + ),
281 + '6.9.0'
282 + );
283 + return null;
284 + }
285 +
286 + if ( null === self::$instance ) {
287 + self::$instance = new self();
288 +
289 + // Ensure ability category registry is initialized first to allow categories to be registered
290 + // before abilities that depend on them.
291 + WP_Ability_Categories_Registry::get_instance();
292 +
293 + /**
294 + * Fires when preparing abilities registry.
295 + *
296 + * Abilities should be created and register their hooks on this action rather
297 + * than another action to ensure they're only loaded when needed.
298 + *
299 + * @since 6.9.0
300 + *
301 + * @param WP_Abilities_Registry $instance Abilities registry object.
302 + */
303 + do_action( 'wp_abilities_api_init', self::$instance );
304 + }
305 +
306 + return self::$instance;
307 + }
308 +
309 + /**
310 + * Wakeup magic method.
311 + *
312 + * @since 6.9.0
313 + * @throws LogicException If the registry object is unserialized.
314 + * This is a security hardening measure to prevent unserialization of the registry.
315 + */
316 + public function __wakeup(): void {
317 + throw new LogicException( __CLASS__ . ' should never be unserialized.' );
318 + }
319 +
320 + /**
321 + * Sleep magic method.
322 + *
323 + * @since 6.9.0
324 + * @throws LogicException If the registry object is serialized.
325 + * This is a security hardening measure to prevent serialization of the registry.
326 + */
327 + public function __sleep(): array {
328 + throw new LogicException( __CLASS__ . ' should never be serialized.' );
329 + }
330 + }
331 +