[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-entity-actions-registry.php (source)

   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' ),
  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` shows on no screen. Such a value is
 234       * 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[] {
 408       *     The script modules, in registration order. Empty when the entity
 409       *     is invalid.
 410       *
 411       *     @type array ...$0 {
 412       *         @type string   $id      The id of the script module.
 413       *         @type string[] $actions The ids of the actions the module
 414       *                                 applies to, in registration order.
 415       *     }
 416       * }
 417       */
 418  	public function get_registered_action_modules( $kind, $name ) {
 419          if ( ! $this->validate_entity( __METHOD__, $kind, $name ) ) {
 420              return array();
 421          }
 422          $this->initialize();
 423          $modules = array();
 424          foreach ( $this->action_modules[ $kind ][ $name ] ?? array() as $module => $action_ids ) {
 425              $modules[] = array(
 426                  // A numeric module id is an integer as an array key.
 427                  'id'      => (string) $module,
 428                  'actions' => $action_ids,
 429              );
 430          }
 431          return $modules;
 432      }
 433  
 434      /**
 435       * Returns the ids of the script modules registered for every entity.
 436       *
 437       * @since 7.2.0
 438       *
 439       * @return array<string, array<string, string[]>> The lists of module ids,
 440       *                                                keyed by kind, then by
 441       *                                                name.
 442       */
 443  	public function get_all_registered_action_modules() {
 444          $this->initialize();
 445          return array_map(
 446              static function ( $entities ) {
 447                  return array_map(
 448                      static function ( $modules ) {
 449                          // A numeric module id is an integer as an array key.
 450                          return array_map( 'strval', array_keys( $modules ) );
 451                      },
 452                      $entities
 453                  );
 454              },
 455              $this->action_modules
 456          );
 457      }
 458  
 459      /**
 460       * Fires the `wp_entity_actions_api_init` action the first time the registry
 461       * is read.
 462       *
 463       * The read is refused until `init` has completed. The registry holds the
 464       * actions of post types, and post types are not known until then: core
 465       * registers its own on `init` at priority 0, plugins theirs at the
 466       * default priority, and supports are added and removed on `init` too.
 467       * The actions of a post type can derive from its supports, and the
 468       * action fires once, so a read before or during `init` would fix them
 469       * from the post types registered so far for the rest of the request.
 470       *
 471       * did_action() is true from the first `init` callback on, hence the
 472       * doing_action() check for the reads made while it runs.
 473       *
 474       * @since 7.2.0
 475       */
 476  	protected function initialize() {
 477          if ( $this->initialized ) {
 478              return;
 479          }
 480  
 481          if ( ! did_action( 'init' ) || doing_action( 'init' ) ) {
 482              _doing_it_wrong(
 483                  __METHOD__,
 484                  __( '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`.' ),
 485                  '7.2.0'
 486              );
 487              return;
 488          }
 489  
 490          // Set before firing, so a callback reading the registry to inspect
 491          // the actions it unregisters does not fire the action again.
 492          $this->initialized = true;
 493  
 494          $this->initializing = true;
 495          try {
 496              /**
 497               * Fires the first time the registered actions are read, after `init`.
 498               *
 499               * Register or remove actions here, with wp_register_entity_actions() and
 500               * wp_unregister_entity_actions(): it is the only time they run.
 501               *
 502               * The first read happens wherever the actions are needed: while
 503               * handling a REST request as well as on `admin_footer` of the pages
 504               * that load the editor script. A callback should register actions
 505               * and nothing else; script modules, styles, and their enqueue hooks
 506               * belong on `init`.
 507               *
 508               * @since 7.2.0
 509               */
 510              do_action( 'wp_entity_actions_api_init' );
 511          } finally {
 512              $this->initializing = false;
 513          }
 514      }
 515  
 516      /**
 517       * Checks that the registry is firing the `wp_entity_actions_api_init` action,
 518       * as register() and unregister() require.
 519       *
 520       * Outside the action a registration would either come before the
 521       * registrations made on it, and take the ids they register, or after
 522       * the actions have been read and the import map of the editor script
 523       * built from them. Refusing keeps the registry immutable once read,
 524       * even when the action is fired again with do_action(), which is why
 525       * the registry tracks its own firing rather than check doing_action().
 526       *
 527       * @since 7.2.0
 528       *
 529       * @param string $method The calling method, for the notice.
 530       * @return bool Whether the action is firing.
 531       */
 532  	protected function doing_entity_actions_api_init( $method ) {
 533          if ( $this->initializing ) {
 534              return true;
 535          }
 536  
 537          _doing_it_wrong(
 538              $method,
 539              __( 'Actions can only be registered and unregistered on the `wp_entity_actions_api_init` action.' ),
 540              '7.2.0'
 541          );
 542          return false;
 543      }
 544  
 545      /**
 546       * Validates the arguments of register().
 547       *
 548       * @since 7.2.0
 549       *
 550       * @param string $method        The calling method, for the notice.
 551       * @param mixed  $origin        The origin.
 552       * @param mixed  $kind          The entity kind.
 553       * @param mixed  $name          The entity name.
 554       * @param mixed  $actions       The action definitions.
 555       * @param mixed  $script_module The script module id, if any.
 556       * @return bool Whether the call may proceed: the `wp_entity_actions_api_init`
 557       *              action is firing and the arguments are valid.
 558       */
 559  	protected function validate_arguments( $method, $origin, $kind, $name, $actions, $script_module ) {
 560          if ( ! $this->doing_entity_actions_api_init( $method ) ) {
 561              return false;
 562          }
 563  
 564          if ( ! is_string( $origin ) || '' === $origin ) {
 565              _doing_it_wrong(
 566                  $method,
 567                  __( 'The origin must be a non-empty string.' ),
 568                  '7.2.0'
 569              );
 570              return false;
 571          }
 572  
 573          if ( ! $this->validate_entity( $method, $kind, $name ) ) {
 574              return false;
 575          }
 576  
 577          if ( ! is_array( $actions ) || empty( $actions ) || ! array_is_list( $actions ) ) {
 578              _doing_it_wrong(
 579                  $method,
 580                  __( 'The actions must be a list of non-empty action definitions.' ),
 581                  '7.2.0'
 582              );
 583              return false;
 584          }
 585  
 586          if ( null !== $script_module && ( ! is_string( $script_module ) || '' === $script_module ) ) {
 587              _doing_it_wrong(
 588                  $method,
 589                  __( 'The script module must be the id of a script module.' ),
 590                  '7.2.0'
 591              );
 592              return false;
 593          }
 594  
 595          return true;
 596      }
 597  
 598      /**
 599       * Checks that the entity kind and name are non-empty strings, reporting
 600       * them with _doing_it_wrong() otherwise.
 601       *
 602       * @since 7.2.0
 603       *
 604       * @param string $method The calling method, for the notice.
 605       * @param mixed  $kind   The entity kind.
 606       * @param mixed  $name   The entity name.
 607       * @return bool Whether both are valid.
 608       */
 609  	protected function validate_entity( $method, $kind, $name ) {
 610          foreach ( array( $kind, $name ) as $argument ) {
 611              if ( ! is_string( $argument ) || '' === $argument ) {
 612                  _doing_it_wrong(
 613                      $method,
 614                      __( 'The entity kind and the entity name must be non-empty strings.' ),
 615                      '7.2.0'
 616                  );
 617                  return false;
 618              }
 619          }
 620          return true;
 621      }
 622  
 623      /**
 624       * Drops the definitions that are not an array with a non-empty string
 625       * `id`, reporting their positions in the call with _doing_it_wrong():
 626       * like a duplicated action, an invalid definition does not cost the call
 627       * its other actions.
 628       *
 629       * @since 7.2.0
 630       *
 631       * @param string  $method  The calling method, for the notice.
 632       * @param string  $kind    The entity kind.
 633       * @param string  $name    The entity name.
 634       * @param array[] $actions The list of action definitions.
 635       * @return array[] The valid definitions, in the order of the call.
 636       */
 637  	protected function skip_invalid_definitions( $method, $kind, $name, $actions ) {
 638          $valid   = array();
 639          $invalid = array();
 640          foreach ( $actions as $position => $action ) {
 641              if ( is_array( $action ) && isset( $action['id'] ) && is_string( $action['id'] ) && '' !== $action['id'] ) {
 642                  $valid[] = $action;
 643              } else {
 644                  $invalid[] = '#' . ( $position + 1 );
 645              }
 646          }
 647  
 648          if ( $invalid ) {
 649              $message = sprintf(
 650                  /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of positions, e.g. #2, #4. */
 651                  __( 'Every action definition must be an array with a non-empty string `id`. These definitions of %1$s "%2$s" are skipped: %3$s.' ),
 652                  $kind,
 653                  $name,
 654                  implode( ', ', $invalid )
 655              );
 656              if ( $valid ) {
 657                  $message .= ' ' . __( 'The rest of the actions of the call are stored.' );
 658              }
 659              _doing_it_wrong( $method, $message, '7.2.0' );
 660          }
 661  
 662          return $valid;
 663      }
 664  
 665      /**
 666       * Drops the ids that are not a non-empty string, reporting their
 667       * positions in the call with _doing_it_wrong(): like an invalid
 668       * definition, an invalid id does not cost the call its other ids.
 669       *
 670       * @since 7.2.0
 671       *
 672       * @param string $method The calling method, for the notice.
 673       * @param string $kind   The entity kind.
 674       * @param string $name   The entity name.
 675       * @param array  $ids    The ids of the actions.
 676       * @return string[] The valid ids, in the order of the call.
 677       */
 678  	protected function skip_invalid_ids( $method, $kind, $name, $ids ) {
 679          $valid   = array();
 680          $invalid = array();
 681          foreach ( array_values( $ids ) as $position => $id ) {
 682              if ( is_string( $id ) && '' !== $id ) {
 683                  $valid[] = $id;
 684              } else {
 685                  $invalid[] = '#' . ( $position + 1 );
 686              }
 687          }
 688  
 689          if ( $invalid ) {
 690              $message = sprintf(
 691                  /* translators: 1: Entity kind, e.g. postType. 2: Entity name, e.g. page. 3: Comma-separated list of positions, e.g. #2, #4. */
 692                  __( 'Every action id must be a non-empty string. These ids of %1$s "%2$s" are skipped: %3$s.' ),
 693                  $kind,
 694                  $name,
 695                  implode( ', ', $invalid )
 696              );
 697              if ( $valid ) {
 698                  $message .= ' ' . __( 'The rest of the actions of the call are unregistered.' );
 699              }
 700              _doing_it_wrong( $method, $message, '7.2.0' );
 701          }
 702  
 703          return $valid;
 704      }
 705  
 706      /**
 707       * Applies a script module to actions of an entity, after the actions it
 708       * already applies to.
 709       *
 710       * @since 7.2.0
 711       *
 712       * @param string      $kind          The entity kind.
 713       * @param string      $name          The entity name.
 714       * @param string[]    $ids           The ids of the actions.
 715       * @param string|null $script_module The id of the script module, if any.
 716       */
 717  	protected function add_action_module( $kind, $name, $ids, $script_module ) {
 718          if ( null === $script_module ) {
 719              return;
 720          }
 721  
 722          $module_ids = $this->action_modules[ $kind ][ $name ][ $script_module ] ?? array();
 723          foreach ( $ids as $id ) {
 724              if ( ! in_array( $id, $module_ids, true ) ) {
 725                  $module_ids[] = $id;
 726              }
 727          }
 728          $this->action_modules[ $kind ][ $name ][ $script_module ] = $module_ids;
 729      }
 730  
 731      /**
 732       * Forgets an entity left without actions or script modules, and its kind
 733       * once no entity of the kind is left, so the registry only lists
 734       * entities that have some.
 735       *
 736       * @since 7.2.0
 737       *
 738       * @param string $kind The entity kind.
 739       * @param string $name The entity name.
 740       */
 741  	protected function forget_empty_entity( $kind, $name ) {
 742          if ( empty( $this->actions[ $kind ][ $name ] ) ) {
 743              unset( $this->actions[ $kind ][ $name ] );
 744          }
 745          if ( empty( $this->actions[ $kind ] ) ) {
 746              unset( $this->actions[ $kind ] );
 747          }
 748          if ( empty( $this->action_modules[ $kind ][ $name ] ) ) {
 749              unset( $this->action_modules[ $kind ][ $name ] );
 750          }
 751          if ( empty( $this->action_modules[ $kind ] ) ) {
 752              unset( $this->action_modules[ $kind ] );
 753          }
 754      }
 755  }


Generated : Mon Nov 9 08:20:01 2026 Cross-referenced by PHPXref