mark_registered_post_types(); } /** * Checks whether WordPress core declares the setting flag natively. * * `register_setting()` applies the `register_setting_args` filter before merging the * caller's arguments over its defaults, so the defaults array is core's own statement of * which arguments it understands. Once `show_in_abilities` appears there, core owns the * flag: it picks the default and each setting opts in through `register_setting()`, the * way `show_in_rest` already works. * * @since 1.2.0 * * @param mixed $defaults The default registration arguments. * @return bool True when core declares `show_in_abilities` as a setting argument. */ private function core_declares_setting_flag( $defaults ): bool { return is_array( $defaults ) && array_key_exists( 'show_in_abilities', $defaults ); } /** * Adds the `show_in_abilities` flag to curated core settings as they are registered. * * Respects an explicit `show_in_abilities` value already present in the registration * arguments, including an explicit `false` opt-out, only filling it in when the key is * absent entirely. Does nothing once core declares the flag natively, so the polyfill * never overrides a default core chose. * * @since 1.1.0 * @since 1.2.0 Respects an explicit falsy value, and stands down once core declares the flag. * * @param mixed $args The setting registration arguments. * @param array $defaults The default registration arguments. * @param string $option_group The settings group. * @param string $option_name The option name. * @return mixed The (possibly amended) registration arguments. */ public function mark_setting( $args, $defaults, $option_group, $option_name ) { if ( ! is_array( $args ) || $this->core_declares_setting_flag( $defaults ) ) { return $args; } $settings = $this->settings_map(); if ( isset( $settings[ $option_name ] ) && ! array_key_exists( 'show_in_abilities', $args ) ) { $args['show_in_abilities'] = $settings[ $option_name ]; } return $args; } /** * Checks whether WordPress core declares the post type flag natively. * * Passing a class name to {@see property_exists()} reports only properties declared by * the class. Passing an object also reports dynamic properties, which is what * {@see self::mark_registered_post_types()} sets, so the two forms answer different * questions and must not be interchanged. * * Once core declares the property, core owns the flag: it decides the default and honors * `register_post_type()` arguments itself, and this polyfill must step aside rather than * try to distinguish "core defaulted it to false" from "a site opted out". * * @since 1.2.0 * * @return bool True when core declares `show_in_abilities` on {@see WP_Post_Type}. */ private function core_declares_post_type_flag(): bool { return property_exists( \WP_Post_Type::class, 'show_in_abilities' ); } /** * Adds the `show_in_abilities` flag to curated core post types as they are registered. * * Respects an explicit `show_in_abilities` value already present in the registration * arguments — including an explicit `false` opt-out — only filling it in when the key is * absent entirely. Normalization happens after registration filters have run, in * {@see self::normalize_post_type()}. Does nothing once core declares the flag natively. * * @since 1.2.0 * * @param array $args The post type registration arguments. * @param string $post_type The post type key. * @return array The (possibly amended) registration arguments. */ public function mark_post_type( array $args, string $post_type ): array { if ( $this->core_declares_post_type_flag() ) { return $args; } $post_types = $this->post_types_map(); if ( isset( $post_types[ $post_type ] ) && ! array_key_exists( 'show_in_abilities', $args ) ) { $args['show_in_abilities'] = $post_types[ $post_type ]; } return $args; } /** * Normalizes exposure after all post type registration argument filters have run. * * Core normalizes inside WP_Post_Type::set_props(), after both argument filters. * Older versions have no hook at that point, so normalize the registered object * before the usual registered_post_type callbacks inspect it. * * @since 1.5.0 * * @param string $post_type Post type key. * @param \WP_Post_Type $post_type_object Registered post type object. */ public function normalize_post_type( string $post_type, \WP_Post_Type $post_type_object ): void { if ( $this->core_declares_post_type_flag() ) { return; } $post_type_object->show_in_abilities = true === ( $post_type_object->show_in_abilities ?? false ); // @phpstan-ignore property.notFound (WP_Post_Type permits dynamic properties.) } /** * Marks curated post types and normalizes any types registered before the polyfill. * * Existing values, including explicit opt-outs, take precedence over curated defaults. * Only true exposes a post type, matching normalization during registration. * Does nothing once core declares the flag natively. * * @since 1.2.0 * @since 1.5.0 Normalizes exposure for all already-registered post types. */ public function mark_registered_post_types(): void { if ( $this->core_declares_post_type_flag() ) { return; } $post_types = $this->post_types_map(); foreach ( get_post_types( array(), 'objects' ) as $post_type => $object ) { if ( isset( $post_types[ $post_type ] ) && ! property_exists( $object, 'show_in_abilities' ) ) { $object->show_in_abilities = $post_types[ $post_type ]; // @phpstan-ignore property.notFound (WP_Post_Type permits dynamic properties.) } $this->normalize_post_type( $post_type, $object ); } } /** * Returns the curated core post types to expose, keyed by post type key. * * Only `true` exposes a post type, as in core. This matches the set marked natively by * WordPress core (`post` and `page`). * * @since 1.2.0 * * @return array Post types map keyed by post type key. */ private function post_types_map(): array { return array( 'post' => true, 'page' => true, ); } /** * Returns the curated core settings to expose, keyed by option name. * * The value is whatever `show_in_abilities` should contain: `true`, or an array with * optional `name` and `schema` keys. With `true`, the setting uses the same name and * schema as in the REST API (from `show_in_rest`). * * This list is kept 1:1 with the settings core flags `show_in_abilities` on in * `register_initial_settings()` (wp-includes/option.php), preserving the same group order. * Keep the two in sync when adding or removing entries. * * @since 1.1.0 * @since 1.5.0 Exposes every setting with `true`, and adds `wp_page_for_privacy_policy`. * * @return array> Settings map keyed by option name. */ private function settings_map(): array { return array( // General. 'blogname' => true, 'blogdescription' => true, 'siteurl' => true, 'admin_email' => true, 'timezone_string' => true, 'date_format' => true, 'time_format' => true, 'start_of_week' => true, 'WPLANG' => true, // Writing. 'use_smilies' => true, 'default_category' => true, 'default_post_format' => true, // Reading. 'posts_per_page' => true, 'show_on_front' => true, 'page_on_front' => true, 'page_for_posts' => true, 'wp_page_for_privacy_policy' => true, // Discussion. 'default_ping_status' => true, 'default_comment_status' => true, ); } }