| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Entity Actions API: WP_Entity_Actions_Registry class 4 * 5 * @package WordPress 6 * @subpackage Entity Actions 7 * @since 7.2.0 8 */ 9 10 /** 11 * Holds the actions registered for entities. 12 * 13 * The registry maps an entity, identified by its kind and name, to its 14 * registered action definitions keyed by id, in registration order, each 15 * with the origin that registered it, and to the script modules registered 16 * for it, each with the ids of the actions it applies to. register() adds 17 * actions and validates the definitions before storing them: an action 18 * that cannot be stored is reported and skipped, and the rest of the call 19 * is stored. To change a registered action, unregister it and register it 20 * again. 21 * 22 * Actions are registered on the `wp_entity_actions_api_init` action, and only 23 * there, with wp_register_entity_actions() and wp_unregister_entity_actions(): 24 * register() and unregister() refuse to run while the action is not 25 * firing. 26 * 27 * The registry is filled lazily: the first time its actions are read, it 28 * fires the `wp_entity_actions_api_init` action, on which the actions of every 29 * entity are registered. A read before `init` has completed is refused, 30 * see initialize(). 31 * 32 * Once the action has fired the registry does not change: every reader of 33 * a request, the REST controller as well as the import map of the editor 34 * script, sees the same actions. 35 * 36 * @since 7.2.0 37 * 38 * @access private 39 */ 40 class WP_Entity_Actions_Registry { 41 42 /** 43 * The values DataViews supports for the properties of an action that 44 * take one of a set of values, see packages/dataviews/src/types/dataviews.ts. 45 * 46 * @since 7.2.0 47 * 48 * @var array<string, string[]> 49 */ 50 const SUPPORTED_VALUES = array( 51 'context' => array( 'list', 'single' ), 52 'modalSize' => array( 'small', 'medium', 'large', 'fill' ), 53 ); 54 55 /** 56 * Registered actions, as kind => name => array of action id => definition. 57 * 58 * Kept nested rather than under a joined `{$kind}/{$name}` key: both parts 59 * may contain any character, so no separator keeps two entities apart. 60 * 61 * @since 7.2.0 62 * 63 * @var array<string, array<string, array<string, array>>> 64 */ 65 protected $actions = array(); 66 67 /** 68 * Registered script modules, as kind => name => array of module id => 69 * list of the ids of the actions the module applies to, in registration 70 * order. 71 * 72 * @since 7.2.0 73 * 74 * @var array<string, array<string, array<string, string[]>>> 75 */ 76 protected $action_modules = array(); 77 78 /** 79 * Whether the `wp_entity_actions_api_init` action has fired since the registry 80 * was created. 81 * 82 * @since 7.2.0 83 * 84 * @var bool 85 */ 86 protected $initialized = false; 87 88 /** 89 * Whether the registry is firing the `wp_entity_actions_api_init` action, the 90 * only time register() and unregister() run. 91 * 92 * @since 7.2.0 93 * 94 * @var bool 95 */ 96 protected $initializing = false; 97 98 /** 99 * The singleton instance. 100 * 101 * @since 7.2.0 102 * 103 * @var WP_Entity_Actions_Registry|null 104 */ 105 protected static $instance = null; 106 107 /** 108 * Returns the singleton instance. 109 * 110 * @since 7.2.0 111 * 112 * @return WP_Entity_Actions_Registry The registry. 113 */ 114 public static function get_instance() { 115 if ( null === self::$instance ) { 116 self::$instance = new self(); 117 } 118 119 return self::$instance; 120 } 121 122 /** 123 * Registers actions for an entity. 124 * 125 * The actions must be new: an action with the id of a registered action 126 * is reported and skipped, and so is every definition after the first of 127 * an id that appears more than once in the call. The rest of the actions 128 * of the call are registered: an action another plugin got to first does 129 * not cost a plugin the others. A definition that is not an array with a 130 * non-empty string `id` is reported and skipped the same way. To replace 131 * a registered action, unregister it first. The script module, if any, 132 * applies to every action the call registers. 133 * 134 * A value DataViews does not support is reported, and a property only a 135 * script module can provide is reported and removed, see 136 * report_invalid_values(). 137 * 138 * The origin is stored as the `origin` property of each action, as 139 * `registeredBy`, replacing any `origin` the definition sets. 140 * 141 * @since 7.2.0 142 * 143 * @param string $origin Who registers the actions: `core`, or 144 * the slug of the plugin or theme. 145 * @param string $kind The entity kind (e.g. `postType`). 146 * @param string $name The entity name (e.g. `page`). 147 * @param array[] $actions The list of action definitions, each with an `id`. 148 * @param string|null $script_module The id of the script module providing 149 * the JavaScript parts of the actions, if any. 150 * @return string[] The ids of the actions registered, in the order of the 151 * call. Empty when none is, when called outside the 152 * `wp_entity_actions_api_init` action, or when an argument is 153 * invalid. 154 */ 155 public function register( $origin, $kind, $name, $actions, $script_module = null ) { 156 if ( ! $this->validate_arguments( __METHOD__, $origin, $kind, $name, $actions, $script_module ) ) { 157 return array(); 158 } 159 $actions = $this->skip_invalid_definitions( __METHOD__, $kind, $name, $actions ); 160 if ( ! $actions ) { 161 return array(); 162 } 163 164 $new_actions = array(); 165 $duplicated = array(); 166 $registered_ids = array(); 167 foreach ( $actions as $action ) { 168 $id = $action['id']; 169 if ( isset( $this->actions[ $kind ][ $name ][ $id ] ) ) { 170 $registered_ids[ $id ] = $id; 171 } elseif ( isset( $new_actions[ $id ] ) ) { 172 $duplicated[ $id ] = $id; 173 } else { 174 $new_actions[ $id ] = $action; 175 } 176 } 177 178 if ( $duplicated ) { 179 _doing_it_wrong( 180 __METHOD__, 181 sprintf( 182 /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of action ids. */ 183 __( 'An action can only be registered once. These actions of %1$s "%2$s" appear more than once in the same call: %3$s. Only their first definition is registered.' ), 184 $kind, 185 $name, 186 implode( ', ', $duplicated ) 187 ), 188 '7.2.0' 189 ); 190 } 191 192 if ( $registered_ids ) { 193 _doing_it_wrong( 194 __METHOD__, 195 sprintf( 196 /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of action ids. */ 197 __( 'An action can only be registered once. These actions of %1$s "%2$s" are skipped because they are already registered: %3$s. Unregister an action first to replace it.' ), 198 $kind, 199 $name, 200 implode( ', ', $registered_ids ) 201 ), 202 '7.2.0' 203 ); 204 } 205 206 if ( ! $new_actions ) { 207 return array(); 208 } 209 $new_actions = $this->report_invalid_values( __METHOD__, $kind, $name, $new_actions ); 210 211 foreach ( $new_actions as $action ) { 212 $this->actions[ $kind ][ $name ][ $action['id'] ] = array_merge( 213 $action, 214 array( 215 'origin' => array( 216 'registeredBy' => $origin, 217 ), 218 ) 219 ); 220 } 221 $ids = array_column( $new_actions, 'id' ); 222 $this->add_action_module( $kind, $name, $ids, $script_module ); 223 224 return $ids; 225 } 226 227 /** 228 * Reports the values of action definitions that DataViews does not 229 * support, and the properties only a script module can provide, with 230 * _doing_it_wrong(). 231 * 232 * A value DataViews does not support is otherwise silent: an action 233 * whose `context` is not `list` or `single` shows on no screen. Such a 234 * value is kept, like the field types the fields registry reports. 235 * `callback`, `RenderModal`, and `isEligible` are functions and 236 * components, which only a script module can provide: a value set on 237 * the server would be called on the client and fail, so it is removed. 238 * 239 * @since 7.2.0 240 * 241 * @param string $method The calling method, for the notice. 242 * @param string $kind The entity kind. 243 * @param string $name The entity name. 244 * @param array[] $actions The action definitions, keyed by id. 245 * @return array[] The definitions, without the properties only a script 246 * module can provide. 247 */ 248 protected function report_invalid_values( $method, $kind, $name, $actions ) { 249 $invalid = array(); 250 foreach ( $actions as $id => $action ) { 251 foreach ( self::SUPPORTED_VALUES as $property => $values ) { 252 if ( array_key_exists( $property, $action ) && ! in_array( $action[ $property ], $values, true ) ) { 253 $invalid[] = sprintf( 254 '%1$s (%2$s)', 255 $id, 256 wp_sprintf( 257 /* translators: 1: Property name, e.g. context. 2: List of the values DataViews supports. */ 258 __( '%1$s is not one of %2$l.' ), 259 $property, 260 $values 261 ) 262 ); 263 } 264 } 265 foreach ( array( 'callback', 'RenderModal', 'isEligible' ) as $property ) { 266 if ( array_key_exists( $property, $action ) ) { 267 unset( $actions[ $id ][ $property ] ); 268 $invalid[] = sprintf( 269 /* translators: 1: Action id. 2: Property name, e.g. callback. */ 270 __( '%1$s (%2$s can only come from a script module, so it is removed.)' ), 271 $id, 272 $property 273 ); 274 } 275 } 276 } 277 if ( $invalid ) { 278 _doing_it_wrong( 279 $method, 280 sprintf( 281 /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of action ids, each with the error of a value. */ 282 __( 'These actions of %1$s "%2$s" have values DataViews does not support: %3$s' ), 283 $kind, 284 $name, 285 implode( ', ', $invalid ) 286 ), 287 '7.2.0' 288 ); 289 } 290 291 return $actions; 292 } 293 294 /** 295 * Unregisters actions of an entity. 296 * 297 * Unregistering an action removes its registered definition and drops 298 * it from the script modules that applied to it (a module left with no 299 * action is forgotten). Unregistering every action forgets the entity: 300 * its registered actions and script modules. 301 * 302 * Like register(), it only runs on the `wp_entity_actions_api_init` action, and 303 * an invalid entity is reported and unregisters nothing. An id that is 304 * not a non-empty string is reported and skipped, like a definition 305 * register() cannot store. 306 * 307 * @since 7.2.0 308 * 309 * @param string $kind The entity kind (e.g. `postType`). 310 * @param string $name The entity name (e.g. `page`). 311 * @param string[]|null $ids The ids of the actions to unregister. 312 * Default null, every action of the entity. 313 * @return array[] The list of the definitions unregistered, in 314 * registration order, as get_registered() returns them. 315 * Empty when none of the actions is registered, when 316 * called outside the `wp_entity_actions_api_init` action, or 317 * when the entity is invalid. 318 */ 319 public function unregister( $kind, $name, $ids = null ) { 320 if ( ! $this->doing_entity_actions_api_init( __METHOD__ ) || ! $this->validate_entity( __METHOD__, $kind, $name ) ) { 321 return array(); 322 } 323 324 if ( null === $ids ) { 325 $unregistered = array_values( $this->actions[ $kind ][ $name ] ?? array() ); 326 unset( $this->actions[ $kind ][ $name ], $this->action_modules[ $kind ][ $name ] ); 327 $this->forget_empty_entity( $kind, $name ); 328 return $unregistered; 329 } 330 331 $ids = $this->skip_invalid_ids( __METHOD__, $kind, $name, (array) $ids ); 332 if ( ! $ids ) { 333 return array(); 334 } 335 336 $unregistered = array_intersect_key( $this->actions[ $kind ][ $name ] ?? array(), array_flip( $ids ) ); 337 if ( empty( $unregistered ) ) { 338 return array(); 339 } 340 341 // A script module only ever applies to registered actions: register() 342 // adds it for the actions it stores, and actions leave both lists 343 // together here. So the actions removed are all the registry loses, 344 // and the modules are not returned: the ones that applied to them are 345 // listed by get_registered_action_modules(), read beforehand. 346 $ids = array_keys( $unregistered ); 347 foreach ( $ids as $id ) { 348 unset( $this->actions[ $kind ][ $name ][ $id ] ); 349 } 350 foreach ( $this->action_modules[ $kind ][ $name ] ?? array() as $module => $module_ids ) { 351 $remaining = array_values( array_diff( $module_ids, $ids ) ); 352 if ( empty( $remaining ) ) { 353 unset( $this->action_modules[ $kind ][ $name ][ $module ] ); 354 } else { 355 $this->action_modules[ $kind ][ $name ][ $module ] = $remaining; 356 } 357 } 358 $this->forget_empty_entity( $kind, $name ); 359 360 return array_values( $unregistered ); 361 } 362 363 /** 364 * Returns the actions registered for an entity. 365 * 366 * @since 7.2.0 367 * 368 * @param string $kind The entity kind (e.g. `postType`). 369 * @param string $name The entity name (e.g. `page`). 370 * @return array[] The list of action definitions, in registration order. 371 * Empty when the entity is invalid. 372 */ 373 public function get_registered( $kind, $name ) { 374 if ( ! $this->validate_entity( __METHOD__, $kind, $name ) ) { 375 return array(); 376 } 377 $this->initialize(); 378 return array_values( $this->actions[ $kind ][ $name ] ?? array() ); 379 } 380 381 /** 382 * Returns the actions registered for every entity. 383 * 384 * @since 7.2.0 385 * 386 * @return array<string, array<string, array[]>> The lists of action 387 * definitions, keyed by 388 * kind, then by name. 389 */ 390 public function get_all_registered() { 391 $this->initialize(); 392 return array_map( 393 static function ( $entities ) { 394 return array_map( 'array_values', $entities ); 395 }, 396 $this->actions 397 ); 398 } 399 400 /** 401 * Returns the script modules registered for an entity. 402 * 403 * @since 7.2.0 404 * 405 * @param string $kind The entity kind (e.g. `postType`). 406 * @param string $name The entity name (e.g. `page`). 407 * @return array<string, string[]> The ids of the actions each module 408 * applies to, keyed by module id, in 409 * registration order. Empty when the 410 * entity is invalid. 411 */ 412 public function get_registered_action_modules( $kind, $name ) { 413 if ( ! $this->validate_entity( __METHOD__, $kind, $name ) ) { 414 return array(); 415 } 416 $this->initialize(); 417 return $this->action_modules[ $kind ][ $name ] ?? array(); 418 } 419 420 /** 421 * Returns the ids of the script modules registered for every entity. 422 * 423 * @since 7.2.0 424 * 425 * @return array<string, array<string, string[]>> The lists of module ids, 426 * keyed by kind, then by 427 * name. 428 */ 429 public function get_all_registered_action_modules() { 430 $this->initialize(); 431 return array_map( 432 static function ( $entities ) { 433 return array_map( 434 static function ( $modules ) { 435 // A numeric module id is an integer as an array key. 436 return array_map( 'strval', array_keys( $modules ) ); 437 }, 438 $entities 439 ); 440 }, 441 $this->action_modules 442 ); 443 } 444 445 /** 446 * Fires the `wp_entity_actions_api_init` action the first time the registry 447 * is read. 448 * 449 * The read is refused until `init` has completed. The registry holds the 450 * actions of post types, and post types are not known until then: core 451 * registers its own on `init` at priority 0, plugins theirs at the 452 * default priority, and supports are added and removed on `init` too. 453 * The actions of a post type can derive from its supports, and the 454 * action fires once, so a read before or during `init` would fix them 455 * from the post types registered so far for the rest of the request. 456 * 457 * did_action() is true from the first `init` callback on, hence the 458 * doing_action() check for the reads made while it runs. 459 * 460 * @since 7.2.0 461 */ 462 protected function initialize() { 463 if ( $this->initialized ) { 464 return; 465 } 466 467 if ( ! did_action( 'init' ) || doing_action( 'init' ) ) { 468 _doing_it_wrong( 469 __METHOD__, 470 __( 'The registered actions cannot be read until the `init` action has completed: the post types and supports they derive from are registered on `init`. Read them once `init` has completed, or hook `wp_entity_actions_api_init`.' ), 471 '7.2.0' 472 ); 473 return; 474 } 475 476 // Set before firing, so a callback reading the registry to inspect 477 // the actions it unregisters does not fire the action again. 478 $this->initialized = true; 479 480 $this->initializing = true; 481 try { 482 /** 483 * Fires the first time the registered actions are read, after `init`. 484 * 485 * Register or remove actions here, with wp_register_entity_actions() and 486 * wp_unregister_entity_actions(): it is the only time they run. 487 * 488 * The first read happens wherever the actions are needed: while 489 * handling a REST request as well as on `admin_footer` of the pages 490 * that load the editor script. A callback should register actions 491 * and nothing else; script modules, styles, and their enqueue hooks 492 * belong on `init`. 493 * 494 * @since 7.2.0 495 */ 496 do_action( 'wp_entity_actions_api_init' ); 497 } finally { 498 $this->initializing = false; 499 } 500 } 501 502 /** 503 * Checks that the registry is firing the `wp_entity_actions_api_init` action, 504 * as register() and unregister() require. 505 * 506 * Outside the action a registration would either come before the 507 * registrations made on it, and take the ids they register, or after 508 * the actions have been read and the import map of the editor script 509 * built from them. Refusing keeps the registry immutable once read, 510 * even when the action is fired again with do_action(), which is why 511 * the registry tracks its own firing rather than check doing_action(). 512 * 513 * @since 7.2.0 514 * 515 * @param string $method The calling method, for the notice. 516 * @return bool Whether the action is firing. 517 */ 518 protected function doing_entity_actions_api_init( $method ) { 519 if ( $this->initializing ) { 520 return true; 521 } 522 523 _doing_it_wrong( 524 $method, 525 __( 'Actions can only be registered and unregistered on the `wp_entity_actions_api_init` action.' ), 526 '7.2.0' 527 ); 528 return false; 529 } 530 531 /** 532 * Validates the arguments of register(). 533 * 534 * @since 7.2.0 535 * 536 * @param string $method The calling method, for the notice. 537 * @param mixed $origin The origin. 538 * @param mixed $kind The entity kind. 539 * @param mixed $name The entity name. 540 * @param mixed $actions The action definitions. 541 * @param mixed $script_module The script module id, if any. 542 * @return bool Whether the call may proceed: the `wp_entity_actions_api_init` 543 * action is firing and the arguments are valid. 544 */ 545 protected function validate_arguments( $method, $origin, $kind, $name, $actions, $script_module ) { 546 if ( ! $this->doing_entity_actions_api_init( $method ) ) { 547 return false; 548 } 549 550 if ( ! is_string( $origin ) || '' === $origin ) { 551 _doing_it_wrong( 552 $method, 553 __( 'The origin must be a non-empty string.' ), 554 '7.2.0' 555 ); 556 return false; 557 } 558 559 if ( ! $this->validate_entity( $method, $kind, $name ) ) { 560 return false; 561 } 562 563 if ( ! is_array( $actions ) || empty( $actions ) || ! array_is_list( $actions ) ) { 564 _doing_it_wrong( 565 $method, 566 __( 'The actions must be a list of non-empty action definitions.' ), 567 '7.2.0' 568 ); 569 return false; 570 } 571 572 if ( null !== $script_module && ( ! is_string( $script_module ) || '' === $script_module ) ) { 573 _doing_it_wrong( 574 $method, 575 __( 'The script module must be the id of a script module.' ), 576 '7.2.0' 577 ); 578 return false; 579 } 580 581 return true; 582 } 583 584 /** 585 * Checks that the entity kind and name are non-empty strings, reporting 586 * them with _doing_it_wrong() otherwise. 587 * 588 * @since 7.2.0 589 * 590 * @param string $method The calling method, for the notice. 591 * @param mixed $kind The entity kind. 592 * @param mixed $name The entity name. 593 * @return bool Whether both are valid. 594 */ 595 protected function validate_entity( $method, $kind, $name ) { 596 foreach ( array( $kind, $name ) as $argument ) { 597 if ( ! is_string( $argument ) || '' === $argument ) { 598 _doing_it_wrong( 599 $method, 600 __( 'The entity kind and the entity name must be non-empty strings.' ), 601 '7.2.0' 602 ); 603 return false; 604 } 605 } 606 return true; 607 } 608 609 /** 610 * Drops the definitions that are not an array with a non-empty string 611 * `id`, reporting their positions in the call with _doing_it_wrong(): 612 * like a duplicated action, an invalid definition does not cost the call 613 * its other actions. 614 * 615 * @since 7.2.0 616 * 617 * @param string $method The calling method, for the notice. 618 * @param string $kind The entity kind. 619 * @param string $name The entity name. 620 * @param array[] $actions The list of action definitions. 621 * @return array[] The valid definitions, in the order of the call. 622 */ 623 protected function skip_invalid_definitions( $method, $kind, $name, $actions ) { 624 $valid = array(); 625 $invalid = array(); 626 foreach ( $actions as $position => $action ) { 627 if ( is_array( $action ) && isset( $action['id'] ) && is_string( $action['id'] ) && '' !== $action['id'] ) { 628 $valid[] = $action; 629 } else { 630 $invalid[] = '#' . ( $position + 1 ); 631 } 632 } 633 634 if ( $invalid ) { 635 $message = sprintf( 636 /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of positions, e.g. #2, #4. */ 637 __( 'Every action definition must be an array with a non-empty string `id`. These definitions of %1$s "%2$s" are skipped: %3$s.' ), 638 $kind, 639 $name, 640 implode( ', ', $invalid ) 641 ); 642 if ( $valid ) { 643 $message .= ' ' . __( 'The rest of the actions of the call are stored.' ); 644 } 645 _doing_it_wrong( $method, $message, '7.2.0' ); 646 } 647 648 return $valid; 649 } 650 651 /** 652 * Drops the ids that are not a non-empty string, reporting their 653 * positions in the call with _doing_it_wrong(): like an invalid 654 * definition, an invalid id does not cost the call its other ids. 655 * 656 * @since 7.2.0 657 * 658 * @param string $method The calling method, for the notice. 659 * @param string $kind The entity kind. 660 * @param string $name The entity name. 661 * @param array $ids The ids of the actions. 662 * @return string[] The valid ids, in the order of the call. 663 */ 664 protected function skip_invalid_ids( $method, $kind, $name, $ids ) { 665 $valid = array(); 666 $invalid = array(); 667 foreach ( array_values( $ids ) as $position => $id ) { 668 if ( is_string( $id ) && '' !== $id ) { 669 $valid[] = $id; 670 } else { 671 $invalid[] = '#' . ( $position + 1 ); 672 } 673 } 674 675 if ( $invalid ) { 676 $message = sprintf( 677 /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of positions, e.g. #2, #4. */ 678 __( 'Every action id must be a non-empty string. These ids of %1$s "%2$s" are skipped: %3$s.' ), 679 $kind, 680 $name, 681 implode( ', ', $invalid ) 682 ); 683 if ( $valid ) { 684 $message .= ' ' . __( 'The rest of the actions of the call are unregistered.' ); 685 } 686 _doing_it_wrong( $method, $message, '7.2.0' ); 687 } 688 689 return $valid; 690 } 691 692 /** 693 * Applies a script module to actions of an entity, after the actions it 694 * already applies to. 695 * 696 * @since 7.2.0 697 * 698 * @param string $kind The entity kind. 699 * @param string $name The entity name. 700 * @param string[] $ids The ids of the actions. 701 * @param string|null $script_module The id of the script module, if any. 702 */ 703 protected function add_action_module( $kind, $name, $ids, $script_module ) { 704 if ( null === $script_module ) { 705 return; 706 } 707 708 $module_ids = $this->action_modules[ $kind ][ $name ][ $script_module ] ?? array(); 709 foreach ( $ids as $id ) { 710 if ( ! in_array( $id, $module_ids, true ) ) { 711 $module_ids[] = $id; 712 } 713 } 714 $this->action_modules[ $kind ][ $name ][ $script_module ] = $module_ids; 715 } 716 717 /** 718 * Forgets an entity left without actions or script modules, and its kind 719 * once no entity of the kind is left, so the registry only lists 720 * entities that have some. 721 * 722 * @since 7.2.0 723 * 724 * @param string $kind The entity kind. 725 * @param string $name The entity name. 726 */ 727 protected function forget_empty_entity( $kind, $name ) { 728 if ( empty( $this->actions[ $kind ][ $name ] ) ) { 729 unset( $this->actions[ $kind ][ $name ] ); 730 } 731 if ( empty( $this->actions[ $kind ] ) ) { 732 unset( $this->actions[ $kind ] ); 733 } 734 if ( empty( $this->action_modules[ $kind ][ $name ] ) ) { 735 unset( $this->action_modules[ $kind ][ $name ] ); 736 } 737 if ( empty( $this->action_modules[ $kind ] ) ) { 738 unset( $this->action_modules[ $kind ] ); 739 } 740 } 741 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Oct 20 08:20:34 2026 | Cross-referenced by PHPXref |