Skip to content

Commit b7e311c

Browse files
authored
Fix fatal error in Abilities API registration when instance is unavailable (#435) (#436)
* Add regression test for abilities registration with unavailable instance (code-writer) * Bail registration callbacks when internal post type instance unavailable (code-writer) * Harden schema merge against missing properties key (code-writer) Treat a missing properties key on either the entity or internal schema as an empty array so the merge no longer emits an undefined-key warning or fatals with array_merge(): Argument #1 must be of type array, null given. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add changelog entry for abilities registration crash fix (doc-writer) * Avoid caching unavailable abilities instances --------- Props cbravobernal, dhenriet.
1 parent b9015f7 commit b7e311c

3 files changed

Lines changed: 78 additions & 4 deletions

File tree

‎includes/abilities/class-scf-internal-post-type-abilities.php‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,19 @@ public function __construct() {
7878
/**
7979
* Gets the internal post type instance.
8080
*
81-
* @return ACF_Internal_Post_Type
81+
* @return ACF_Internal_Post_Type|false
8282
*/
8383
private function instance() {
84-
if ( null === $this->instance ) {
85-
$this->instance = acf_get_internal_post_type_instance( $this->internal_post_type );
84+
if ( ! $this->instance ) {
85+
$instance = acf_get_internal_post_type_instance( $this->internal_post_type );
86+
87+
if ( $instance ) {
88+
$this->instance = $instance;
89+
}
90+
91+
return $instance;
8692
}
93+
8794
return $this->instance;
8895
}
8996

@@ -199,7 +206,10 @@ private function get_internal_fields_schema() {
199206
private function get_entity_with_internal_fields_schema() {
200207
$schema = $this->get_entity_schema();
201208
$internal = $this->get_internal_fields_schema();
202-
$schema['properties'] = array_merge( $schema['properties'], $internal['properties'] );
209+
$schema['properties'] = array_merge(
210+
$schema['properties'] ?? array(),
211+
$internal['properties'] ?? array()
212+
);
203213
return $schema;
204214
}
205215

@@ -211,6 +221,10 @@ private function get_entity_with_internal_fields_schema() {
211221
* @return void
212222
*/
213223
public function register_categories() {
224+
if ( ! $this->instance() ) {
225+
return;
226+
}
227+
214228
wp_register_ability_category(
215229
$this->ability_category(),
216230
array(
@@ -234,6 +248,10 @@ public function register_categories() {
234248
* @return void
235249
*/
236250
public function register_abilities() {
251+
if ( ! $this->instance() ) {
252+
return;
253+
}
254+
237255
$this->register_list_ability();
238256
$this->register_get_ability();
239257
$this->register_create_ability();

‎readme.txt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ This plugin builds upon and is a fork of the previous work done by the contribut
5151

5252

5353
== Changelog ==
54+
= Next =
55+
*Release Date TBD*
56+
57+
*Fixes*
58+
59+
- SCF's Abilities API integration for its internal post types no longer triggers PHP warnings, notices, or a fatal error (500) on block editor and REST API requests when another active plugin builds the WordPress abilities registry earlier in the request; registration is skipped cleanly in that case and normal abilities behavior is otherwise unchanged.
60+
5461
= 6.8.6 =
5562
*Release Date 27th May 2026*
5663

‎tests/php/includes/abilities/test-scf-internal-post-type-abilities.php‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,55 @@ public function test_ability_name_uses_singular_for_get() {
801801
$this->assertEquals( 'scf/get-taxonomy', $method->invoke( $this->abilities, 'get' ) );
802802
}
803803

804+
/**
805+
* Test registration skips cleanly when the internal post type instance is unavailable.
806+
*
807+
* Reproduces the issue's crash path: when the internal post type instance lookup
808+
* resolves to false (e.g. the post type is not registered yet), the abilities and
809+
* categories registration must not attempt to dereference the missing instance.
810+
* Asserts that nothing is registered and that no warning, notice, deprecation, or
811+
* fatal TypeError is emitted (the suite promotes such emissions to test failures).
812+
*/
813+
public function test_registration_skips_cleanly_when_instance_unavailable() {
814+
global $mock_registered_abilities, $mock_registered_ability_categories;
815+
$mock_registered_abilities = array();
816+
$mock_registered_ability_categories = array();
817+
818+
$store = acf_get_store( 'internal-post-types' );
819+
$original_instance = $store ? $store->get( 'acf-taxonomy' ) : null;
820+
821+
$reflection = new ReflectionClass( SCF_Internal_Post_Type_Abilities::class );
822+
$property = $reflection->getProperty( 'instance' );
823+
$property->setAccessible( true );
824+
$property->setValue( $this->abilities, null );
825+
826+
if ( $store ) {
827+
$store->remove( 'acf-taxonomy' );
828+
}
829+
830+
try {
831+
$this->abilities->register_categories();
832+
$this->abilities->register_abilities();
833+
834+
$this->assertEmpty(
835+
$mock_registered_ability_categories,
836+
'No categories should be registered when the instance is unavailable'
837+
);
838+
$this->assertEmpty(
839+
$mock_registered_abilities,
840+
'No abilities should be registered when the instance is unavailable'
841+
);
842+
$this->assertNull(
843+
$property->getValue( $this->abilities ),
844+
'Unavailable instances should not be cached so later registration attempts can retry'
845+
);
846+
} finally {
847+
if ( $store && $original_instance ) {
848+
$store->set( 'acf-taxonomy', $original_instance );
849+
}
850+
}
851+
}
852+
804853
/**
805854
* Test instance() method caches result
806855
*/

0 commit comments

Comments
 (0)