diff --git a/AGENTS.md b/AGENTS.md index b00f7d7..61154f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -490,6 +490,12 @@ deliberately does not change is the hook boundary — WordPress's own files are calling our typed methods still coerces weakly, and a callback that receives a loose type from a filter behaves exactly as it did. +**Every `_doing_it_wrong()` names `Class::method`, never a bare class name.** WordPress prints it as +"Function %s was called incorrectly", so it is the only part of a report that says where in this +library the sentence came from, and a class alone cannot separate two reports made by the same one. +Written `self::class . '::method'` rather than `__METHOD__`, because `__METHOD__` inside a trait +names the trait rather than the class that actually stood down. + **Comments describe behaviour, not the plan.** Never reference a task or plan-step number in a code comment; the code outlives the plan. Comments earn their place by explaining *why*, especially where a plausible alternative is wrong. diff --git a/src/Boot/Scheduler.php b/src/Boot/Scheduler.php index 4320b20..f69645a 100644 --- a/src/Boot/Scheduler.php +++ b/src/Boot/Scheduler.php @@ -262,7 +262,7 @@ private static function load( ContainerInterface $container ): void { */ private static function report_a_step_that_threw( string $step, string $consequence, Throwable $thrown ): void { _doing_it_wrong( - self::class, + self::class . '::report_a_step_that_threw', sprintf( 'The %s threw, so %s: %s', $step, $consequence, $thrown->getMessage() ), '1.0.0' ); diff --git a/src/Conflict/Resolver.php b/src/Conflict/Resolver.php index ddebdc6..ed3d2b1 100644 --- a/src/Conflict/Resolver.php +++ b/src/Conflict/Resolver.php @@ -138,7 +138,7 @@ public function resolve_all(): void { } } catch ( Throwable $thrown ) { _doing_it_wrong( - self::class, + self::class . '::resolve_all', sprintf( 'The conflict for "%s" threw while being resolved, so it was abandoned: %s', $sub_plugin->get_slug(), diff --git a/src/Loader.php b/src/Loader.php index e26ef2f..a4e299d 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -98,7 +98,7 @@ public function load_all(): void { $this->load( $sub_plugin ); } catch ( Throwable $thrown ) { _doing_it_wrong( - self::class, + self::class . '::load_all', sprintf( 'The sub-plugin "%s" threw while loading, so it was abandoned: %s', $sub_plugin->get_slug(), @@ -149,7 +149,7 @@ private function load( Sub_Plugin $sub_plugin ): void { if ( ! is_file( $file ) || ! is_readable( $file ) ) { _doing_it_wrong( - self::class, + self::class . '::load', sprintf( 'The bundled plugin file for "%s" is missing or unreadable: %s', $sub_plugin->get_slug(), diff --git a/src/Registry/Reader.php b/src/Registry/Reader.php index 7788764..d860465 100644 --- a/src/Registry/Reader.php +++ b/src/Registry/Reader.php @@ -171,7 +171,7 @@ public function flush(): void { // duplicate" rather than by path alone, because the two registrations may well name // the same file, and a bare path then reads as if the surviving one went too. _doing_it_wrong( - self::class, + self::class . '::flush', sprintf( '%1$s The original registration was kept; the duplicate %2$s was discarded.', $exception->getMessage(), diff --git a/src/Traits/Guards_Hook_Prefix.php b/src/Traits/Guards_Hook_Prefix.php index 9057e22..735bfb6 100644 --- a/src/Traits/Guards_Hook_Prefix.php +++ b/src/Traits/Guards_Hook_Prefix.php @@ -35,7 +35,7 @@ private static function has_hook_prefix(): bool { try { Config::get_hook_prefix(); } catch ( Config_Exception $exception ) { - _doing_it_wrong( self::class, $exception->getMessage(), '1.0.0' ); + _doing_it_wrong( self::class . '::has_hook_prefix', $exception->getMessage(), '1.0.0' ); return false; } diff --git a/tests/_support/Traits/WithIncorrectUsage.php b/tests/_support/Traits/WithIncorrectUsage.php index b21ab99..65adfc4 100644 --- a/tests/_support/Traits/WithIncorrectUsage.php +++ b/tests/_support/Traits/WithIncorrectUsage.php @@ -120,6 +120,16 @@ protected function assert_the_library_reported_incorrect_usage(): void { $report, 'The report has to name this library, or the host goes looking in WordPress.' ); + + // The member, not just the class. Which method reported is the only thing separating two + // reports from the same class -- the facade makes two, from separate trampolines -- and + // pinning the shape rather than the name is what keeps this assertion loose enough to + // survive a rename while still refusing a bare class name. + $this->assertStringContainsString( + '::', + $report, + 'The report has to name the member it was made from, not just the class.' + ); } }