From 7fc9fba4a4073ec48bba446a75bb8aa651e99e31 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 11:30:52 -0400 Subject: [PATCH 01/10] refactor: centralize container management in Config class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move container management from Provider to Config class for better separation of concerns. Add Safe_Dynamic_Prefix utility to handle table name length constraints dynamically. Update all references to use Config::get_container() instead of Provider::get_container(). Key changes: - Container now managed by Config class - Added Safe_Dynamic_Prefix utility for dynamic table prefix calculation - Fixed table name length constraints to respect MySQL's 64-character limit - Updated tests to use new container access pattern 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/Abstracts/Table_Abstract.php | 24 +++- src/Abstracts/Task_Model_Abstract.php | 4 +- src/Config.php | 125 ++++++++---------- src/Log.php | 6 +- src/Provider.php | 32 +---- src/Tables/Provider.php | 14 ++ src/Tables/Utility/Safe_Dynamic_Prefix.php | 115 ++++++++++++++++ src/Traits/Loggable.php | 4 +- src/functions.php | 3 +- .../Helper/Traits/With_AS_Assertions.php | 3 +- tests/_support/Helper/test-functions.php | 38 ++++-- .../wpunit/Abstracts/Table_Abstract_Test.php | 3 +- tests/wpunit/Config_Test.php | 59 ++------- tests/wpunit/Provider_Test.php | 12 -- tests/wpunit/Regulator_Test.php | 2 +- tests/wpunit/Tables/Tasks_Test.php | 11 +- .../Utility/Safe_Dynamic_Prefix_Test.php | 47 +++++++ tests/wpunit/Traits/Loggable_Test.php | 4 +- 18 files changed, 324 insertions(+), 182 deletions(-) create mode 100644 src/Tables/Utility/Safe_Dynamic_Prefix.php create mode 100644 tests/wpunit/Tables/Utility/Safe_Dynamic_Prefix_Test.php diff --git a/src/Abstracts/Table_Abstract.php b/src/Abstracts/Table_Abstract.php index 2a26bb1..d789479 100644 --- a/src/Abstracts/Table_Abstract.php +++ b/src/Abstracts/Table_Abstract.php @@ -15,6 +15,7 @@ use StellarWP\DB\DB; use StellarWP\Pigeon\Provider as Pigeon_Main_Controller; use StellarWP\Pigeon\Config; +use StellarWP\Pigeon\Tables\Utility\Safe_Dynamic_Prefix; use StellarWP\Pigeon\Traits\Custom_Table_Query_Methods; use DateTimeInterface; @@ -140,11 +141,10 @@ abstract class Table_Abstract extends Table { */ public function __construct() { $this->db = DB::class; - $this->container = Pigeon_Main_Controller::get_container(); + $this->container = Config::get_container(); // Modify table names to use the hook prefix. - self::$base_table_name = sprintf( self::$base_table_name, Config::get_safe_hook_prefix() ); - self::$schema_slug = sprintf( self::$schema_slug, Config::get_hook_prefix() ); + self::$schema_slug = sprintf( self::$schema_slug, Config::get_hook_prefix() ); } /** @@ -157,7 +157,12 @@ public function __construct() { * @return string The base table name. */ public static function base_table_name(): string { - return sprintf( static::$base_table_name, Config::get_safe_hook_prefix() ); + $container = Config::get_container(); + + return sprintf( + static::$base_table_name, + $container->get( Safe_Dynamic_Prefix::class )->get() + ); } /** @@ -295,4 +300,15 @@ protected function after_update( array $results ) { return $results; } + + /** + * Returns the base table name without the dynamic prefix. + * + * @since TBD + * + * @return string The base table name without the dynamic prefix. + */ + public static function raw_base_table_name(): string { + return static::$base_table_name; + } } diff --git a/src/Abstracts/Task_Model_Abstract.php b/src/Abstracts/Task_Model_Abstract.php index 7c550c4..932ec22 100644 --- a/src/Abstracts/Task_Model_Abstract.php +++ b/src/Abstracts/Task_Model_Abstract.php @@ -14,7 +14,7 @@ use StellarWP\Pigeon\Contracts\Task_Model; use RuntimeException; use StellarWP\Pigeon\Tables\Tasks as Tasks_Table; -use StellarWP\Pigeon\Provider; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Contracts\Task; use StellarWP\Pigeon\Action_Scheduler_Methods; use StellarWP\Pigeon\Exceptions\PigeonTaskAlreadyExistsException; @@ -240,7 +240,7 @@ public function get_args(): array { * @return Table_Abstract The table interface. */ public function get_table_interface(): Table_Abstract { - return Provider::get_container()->get( static::TABLE_INTERFACE ); + return Config::get_container()->get( static::TABLE_INTERFACE ); } /** diff --git a/src/Config.php b/src/Config.php index 5eff6ae..0fbfefd 100644 --- a/src/Config.php +++ b/src/Config.php @@ -10,6 +10,7 @@ namespace StellarWP\Pigeon; use RuntimeException; +use StellarWP\ContainerContract\ContainerInterface; use StellarWP\Pigeon\Contracts\Logger; use StellarWP\Pigeon\Loggers\ActionScheduler_DB_Logger; @@ -21,6 +22,13 @@ * @package StellarWP\Pigeon\Config */ class Config { + /** + * Container object. + * + * @var ContainerInterface + */ + private static $container; + /** * The hook prefix. * @@ -39,15 +47,6 @@ class Config { */ protected static ?Logger $logger = null; - /** - * The maximum safe hook prefix length. - * - * @since TBD - * - * @var ?int - */ - protected static ?int $max_hook_prefix_length = null; - /** * The maximum table name length. * @@ -55,41 +54,19 @@ class Config { * * @var int */ - protected const MAX_TABLE_NAME_LENGTH = 64; - - /** - * The longest table name. - * - * @since TBD - * - * @var string - */ - protected const LONGEST_TABLE_NAME = 'pigeon_%s_task_logs'; + protected static int $max_table_name_length = 64; /** - * Gets the maximum safe hook prefix length. - * - * Calculates the maximum length a hook prefix can be while ensuring - * table names don't exceed MySQL's 64-character limit. - * - * @since TBD + * Get the container. * - * @return int The maximum safe hook prefix length. + * @return ContainerInterface */ - public static function get_max_hook_prefix_length(): int { - if ( null !== static::$max_hook_prefix_length ) { - return static::$max_hook_prefix_length; + public static function get_container() : ContainerInterface { + if ( self::$container === null ) { + throw new \RuntimeException( 'You must provide a container via StellarWP\Pigeon\Config::set_container() before attempting to fetch it.' ); } - global $wpdb; - - $wp_prefix_length = strlen( $wpdb->prefix ); - - $hook_prefix = static::get_hook_prefix(); - - $base_name_length = strlen( sprintf( self::LONGEST_TABLE_NAME, $hook_prefix ) ); - - return strlen( $hook_prefix ) + ( self::MAX_TABLE_NAME_LENGTH - $base_name_length - $wp_prefix_length ); + return self::$container; } /** @@ -111,45 +88,45 @@ public static function get_hook_prefix(): string { } /** - * Gets the safe hook prefix. - * - * Returns the hook prefix trimmed to the maximum safe length - * to ensure table names don't exceed MySQL's limit. + * Gets the logger. * * @since TBD * - * @throws RuntimeException If the hook prefix is not set or the max hook prefix length could not be determined. - * - * @return string The safe hook prefix. + * @return Logger */ - public static function get_safe_hook_prefix(): string { - $prefix = static::get_hook_prefix(); - $max_length = static::get_max_hook_prefix_length(); - - if ( ! $max_length ) { - throw new RuntimeException( 'The max hook prefix could not be determined.' ); - } - - if ( strlen( $prefix ) > $max_length ) { - return substr( $prefix, 0, $max_length ); + public static function get_logger(): Logger { + if ( ! static::$logger ) { + static::$logger = new ActionScheduler_DB_Logger(); } - return $prefix; + return static::$logger; } /** - * Gets the logger. + * Gets the maximum table name length. * - * @since TBD + * @return int + */ + public static function get_max_table_name_length(): int { + return static::$max_table_name_length; + } + + /** + * Returns whether the container has been set. * - * @return Logger + * @return bool */ - public static function get_logger(): Logger { - if ( ! static::$logger ) { - static::$logger = new ActionScheduler_DB_Logger(); - } + public static function has_container() : bool { + return self::$container !== null; + } - return static::$logger; + /** + * Set the container object. + * + * @param ContainerInterface $container Container object. + */ + public static function set_container( ContainerInterface $container ) { + self::$container = $container; } /** @@ -182,14 +159,28 @@ public static function set_logger( ?Logger $logger ): void { static::$logger = $logger; } + /** + * Sets the maximum table name length. + * + * @since TBD + * + * @param int $length The maximum table name length. + * + * @return void + */ + public static function set_max_table_name_length( int $length ): void { + static::$max_table_name_length = $length; + } + /** * Resets the config. * * @return void */ public static function reset(): void { - static::$hook_prefix = ''; - static::$logger = null; - static::$max_hook_prefix_length = null; + static::$container = null; + static::$hook_prefix = ''; + static::$logger = null; + static::$max_table_name_length = 64; } } diff --git a/src/Log.php b/src/Log.php index 4dcb024..5b28920 100644 --- a/src/Log.php +++ b/src/Log.php @@ -11,13 +11,13 @@ namespace StellarWP\Pigeon; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Contracts\Log_Model; use StellarWP\Pigeon\Tables\Task_Logs as Task_Logs_Table; use StellarWP\Pigeon\Tables\AS_Logs as AS_Logs_Table; use StellarWP\Pigeon\Loggers\ActionScheduler_DB_Logger; use StellarWP\Pigeon\Loggers\DB_Logger; use StellarWP\Pigeon\Contracts\Logger; -use StellarWP\Pigeon\Provider; use StellarWP\Pigeon\Abstracts\Model_Abstract; use DateTimeInterface; use StellarWP\Pigeon\Abstracts\Table_Abstract; @@ -287,7 +287,7 @@ public function get_entry(): string { * @throws RuntimeException If the log table interface is invalid. */ public function get_table_interface(): Table_Abstract { - $logger = Provider::get_container()->get( Logger::class ); + $logger = Config::get_container()->get( Logger::class ); $table = null; @@ -305,7 +305,7 @@ public function get_table_interface(): Table_Abstract { throw new RuntimeException( 'Invalid log table interface.' ); } - return Provider::get_container()->get( $table ); + return Config::get_container()->get( $table ); } /** diff --git a/src/Provider.php b/src/Provider.php index b628d02..8c417b4 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -45,15 +45,6 @@ class Provider extends Provider_Abstract { */ protected static string $hook_prefix; - /** - * The container. - * - * @since TBD - * - * @var ?Container - */ - private static ?Container $static_container = null; - /** * Whether the provider has been registered. * @@ -77,9 +68,7 @@ public function register(): void { $this->require_action_scheduler(); - self::$static_container = $this->container; - - Schema_Config::set_container( $this->container ); + Schema_Config::set_container( Config::get_container() ); Schema_Config::set_db( DB::class ); $this->container->singleton( Logger::class, Config::get_logger() ); $this->container->singleton( Tables_Provider::class ); @@ -101,19 +90,6 @@ private function require_action_scheduler(): void { require_once __DIR__ . '/../vendor/woocommerce/action-scheduler/action-scheduler.php'; } - /** - * Sets the container. - * - * @since TBD - * - * @param ?Container $container The container. - * - * @return void - */ - public static function set_container( ?Container $container ): void { - self::$static_container = $container; - } - /** * Resets the registered state. * @@ -135,11 +111,7 @@ public static function reset(): void { * @throws RuntimeException If the container has not been set. */ public static function get_container(): Container { - if ( ! self::$static_container ) { - throw new RuntimeException( 'The container has not been set.' ); - } - - return self::$static_container; + return Config::get_container(); } /** diff --git a/src/Tables/Provider.php b/src/Tables/Provider.php index afab41c..26707bb 100644 --- a/src/Tables/Provider.php +++ b/src/Tables/Provider.php @@ -24,6 +24,16 @@ * @package StellarWP\Pigeon\Tables; */ class Provider extends Provider_Abstract { + /** + * Tables to register. + * + * @var array + */ + private array $tables = [ + Tasks::class, + Task_Logs::class, + ]; + /** * Registers the service provider bindings. * @@ -32,6 +42,10 @@ class Provider extends Provider_Abstract { * @return void The method does not return any value. */ public function register(): void { + // Bind after all tables are registered. + $this->container->singleton( Utility\Safe_Dynamic_Prefix::class ); + $this->container->get( Utility\Safe_Dynamic_Prefix::class )->calculate_longest_table_name( $this->tables ); + Register::table( Tasks::class ); if ( $this->container->get( Logger::class ) instanceof DB_Logger ) { diff --git a/src/Tables/Utility/Safe_Dynamic_Prefix.php b/src/Tables/Utility/Safe_Dynamic_Prefix.php new file mode 100644 index 0000000..304d597 --- /dev/null +++ b/src/Tables/Utility/Safe_Dynamic_Prefix.php @@ -0,0 +1,115 @@ + $tables The tables to calculate the longest table name for. + * + * @return void + */ + public function calculate_longest_table_name( array $tables ): void { + $this->longest_table_name = $this->get_longest_table_name( $tables ); + } + + /** + * Gets the longest table name. + * + * @since TBD + * + * @param array $tables The tables to calculate the longest table name for. + * + * @return string The longest table name. + */ + public function get_longest_table_name( array $tables ): string { + $longest_table_name = ''; + + foreach ( $tables as $table ) { + $raw_base_table_name = $table::raw_base_table_name(); + + if ( strlen( $raw_base_table_name ) > strlen( $longest_table_name ) ) { + $longest_table_name = $raw_base_table_name; + } + } + + return $longest_table_name; + } + + /** + * Gets the maximum safe hook prefix length. + * + * Calculates the maximum length a hook prefix can be while ensuring + * table names don't exceed MySQL's 64-character limit. + * + * @since TBD + * + * @param string|null $longest_table_name The longest table name to calculate the maximum length for. + * + * @return int The maximum safe hook prefix length. + */ + public function get_max_length( ?string $longest_table_name = null ): int { + global $wpdb; + + $wp_prefix_length = strlen( $wpdb->prefix ); + $longest_table_name = $longest_table_name ?? $this->longest_table_name; + $unprefixed_table_name_length = strlen( sprintf( $longest_table_name, '' ) ); + + return Config::get_max_table_name_length() - $unprefixed_table_name_length - $wp_prefix_length; + } + + /** + * Gets the safe hook prefix. + * + * Returns the hook prefix trimmed to the maximum safe length + * to ensure table names don't exceed MySQL's limit. + * + * @since TBD + * + * @param string|null $longest_table_name The longest table name to calculate the maximum length for. + * + * @throws \RuntimeException If the dynamic table prefix is not set or the max dynamic table prefix length could not be determined. + * + * @return string The safe hook prefix. + */ + public function get( ?string $longest_table_name = null ): string { + $prefix = Config::get_hook_prefix(); + $max_length = $this->get_max_length( $longest_table_name ); + + if ( ! $max_length ) { + throw new \RuntimeException( 'The max dynamic table prefix could not be determined.' ); + } + + if ( strlen( $prefix ) > $max_length ) { + return substr( $prefix, 0, $max_length ); + } + + return $prefix; + } +} \ No newline at end of file diff --git a/src/Traits/Loggable.php b/src/Traits/Loggable.php index c106d4c..1cea577 100644 --- a/src/Traits/Loggable.php +++ b/src/Traits/Loggable.php @@ -11,8 +11,8 @@ namespace StellarWP\Pigeon\Traits; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Contracts\Logger; -use StellarWP\Pigeon\Provider; use Psr\Log\LogLevel; /** @@ -41,7 +41,7 @@ trait Loggable { */ private function get_logger(): Logger { if ( ! $this->logger ) { - $this->logger = Provider::get_container()->get( Logger::class ); + $this->logger = Config::get_container()->get( Logger::class ); } return $this->logger; diff --git a/src/functions.php b/src/functions.php index d3a66f5..41212d1 100644 --- a/src/functions.php +++ b/src/functions.php @@ -10,6 +10,7 @@ namespace StellarWP\Pigeon; use RuntimeException; +use StellarWP\Pigeon\Config; /** * Get the Pigeon's Regulator instance. @@ -31,7 +32,7 @@ function pigeon(): Regulator { return $pigeon; } - $pigeon = Provider::get_container()->get( Regulator::class ); + $pigeon = Config::get_container()->get( Regulator::class ); return $pigeon; } diff --git a/tests/_support/Helper/Traits/With_AS_Assertions.php b/tests/_support/Helper/Traits/With_AS_Assertions.php index f955983..6c029ef 100644 --- a/tests/_support/Helper/Traits/With_AS_Assertions.php +++ b/tests/_support/Helper/Traits/With_AS_Assertions.php @@ -7,6 +7,7 @@ use StellarWP\Pigeon\Action_Scheduler_Methods; use ActionScheduler_Action; use ActionScheduler_QueueRunner as Runner; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Provider; use StellarWP\DB\DB; use function StellarWP\Pigeon\pigeon; @@ -17,7 +18,7 @@ trait With_AS_Assertions { * @after */ protected function delete_actions_between_runs(): void { - Provider::get_container()->get( Task_Model_Abstract::TABLE_INTERFACE )->empty_table(); + Config::get_container()->get( Task_Model_Abstract::TABLE_INTERFACE )->empty_table(); DB::query( DB::prepare( "DELETE FROM %i", DB::prefix( 'actionscheduler_actions' ) ) ); } diff --git a/tests/_support/Helper/test-functions.php b/tests/_support/Helper/test-functions.php index 7dc9a37..c1cb2c6 100644 --- a/tests/_support/Helper/test-functions.php +++ b/tests/_support/Helper/test-functions.php @@ -4,8 +4,9 @@ use StellarWP\Pigeon\Tables\Tasks; use StellarWP\Pigeon\Tables\Task_Logs; use StellarWP\DB\DB; -use StellarWP\Pigeon\Provider; use StellarWP\Pigeon\Config; +use StellarWP\Pigeon\Provider; +use StellarWP\Pigeon\Tables; use StellarWP\Schema\Register; /** @@ -14,11 +15,21 @@ * @return void */ function tests_pigeon_drop_tables() { - $tables = [ - Tasks::base_table_name(), - Task_Logs::base_table_name(), + $container = tests_pigeon_get_container(); + $safe_dynamic_prefix = $container->get( Tables\Utility\Safe_Dynamic_Prefix::class ); + + $tables = []; + $table_classes = [ + Tasks::class, + Task_Logs::class, ]; + $longest_table_name = $safe_dynamic_prefix->get_longest_table_name( $table_classes ); + + foreach ( $table_classes as $table_class ) { + $tables[] = sprintf( $table_class::raw_base_table_name(), $safe_dynamic_prefix->get( $longest_table_name ) ); + } + foreach ( $tables as $table ) { DB::query( DB::prepare( 'DROP TABLE IF EXISTS %i', DB::prefix( $table ) ) @@ -54,6 +65,18 @@ function tests_pigeon_raise_auto_increment(): void { } } +/** + * Resets the config. + * + * @return void + */ +function tests_pigeon_reset_config(): void { + Config::reset(); + Config::set_hook_prefix( tests_pigeon_get_hook_prefix() ); + Config::set_container( tests_pigeon_get_container() ); + Config::set_max_table_name_length( 25 ); +} + /** * Get the hook prefix. * @@ -94,12 +117,11 @@ function tests_pigeon_get_dt(): DateTimeInterface { * @return void */ function tests_pigeon_common_bootstrap(): void { - Config::set_hook_prefix( tests_pigeon_get_hook_prefix() ); - - $container = tests_pigeon_get_container(); - + tests_pigeon_reset_config(); tests_pigeon_drop_tables(); + $container = Config::get_container(); + // Bootstrap Pigeon. $container->singleton( Provider::class ); $container->get( Provider::class )->register(); diff --git a/tests/wpunit/Abstracts/Table_Abstract_Test.php b/tests/wpunit/Abstracts/Table_Abstract_Test.php index ade91d9..8ddf510 100644 --- a/tests/wpunit/Abstracts/Table_Abstract_Test.php +++ b/tests/wpunit/Abstracts/Table_Abstract_Test.php @@ -7,6 +7,7 @@ use lucatume\WPBrowser\TestCase\WPTestCase; use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Contracts\Model; +use StellarWP\Pigeon\Tables\Utility\Safe_Dynamic_Prefix; use StellarWP\DB\DB; class Dummy_Table extends Table_Abstract { @@ -81,7 +82,7 @@ public function it_should_trim_long_hook_prefix_to_prevent_exceeding_mysql_table // The table name should use the safe prefix $table_name = Dummy_Table::table_name(); - $safe_prefix = Config::get_safe_hook_prefix(); + $safe_prefix = Config::get_container()->get( Safe_Dynamic_Prefix::class )->get(); // The safe prefix should be trimmed $this->assertLessThan( strlen( $long_prefix ), strlen( $safe_prefix ), 'Safe prefix should be shorter than original' ); diff --git a/tests/wpunit/Config_Test.php b/tests/wpunit/Config_Test.php index df6e83c..ee72623 100644 --- a/tests/wpunit/Config_Test.php +++ b/tests/wpunit/Config_Test.php @@ -6,10 +6,22 @@ use lucatume\WPBrowser\TestCase\WPTestCase; use RuntimeException; +use StellarWP\ContainerContract\ContainerInterface; use StellarWP\Pigeon\Contracts\Logger; use StellarWP\Pigeon\Loggers\ActionScheduler_DB_Logger; class Config_Test extends WPTestCase { + /** + * @test + */ + public function it_should_get_the_container(): void { + $container = tests_pigeon_get_container(); + + $this->assertInstanceOf( ContainerInterface::class, $container ); + $this->assertInstanceOf( ContainerInterface::class, Config::get_container() ); + $this->assertSame( $container, Config::get_container() ); + } + /** * @test */ @@ -51,61 +63,18 @@ public function it_should_set_and_get_logger(): void { $this->assertSame( $mock_logger, Config::get_logger() ); } - /** - * @test - */ - public function it_should_calculate_max_hook_prefix_length(): void { - global $wpdb; - - // Set a known hook prefix - Config::set_hook_prefix( 'test' ); - - // The new calculation is based on: - // strlen($hook_prefix) + (64 - strlen(sprintf('pigeon_%s_task_logs', $hook_prefix)) - strlen($wpdb->prefix)) - $hook_prefix = 'test'; - $base_name_length = strlen( sprintf( 'pigeon_%s_task_logs', $hook_prefix ) ); - $expected = strlen( $hook_prefix ) + ( 64 - $base_name_length - strlen( $wpdb->prefix ) ); - - $this->assertEquals( $expected, Config::get_max_hook_prefix_length() ); - } - - /** - * @test - */ - public function it_should_return_safe_hook_prefix_for_short_prefix(): void { - Config::set_hook_prefix( 'short' ); - - // Short prefix should be returned as-is - $this->assertEquals( 'short', Config::get_safe_hook_prefix() ); - } - - /** - * @test - */ - public function it_should_trim_long_hook_prefix_to_safe_length(): void { - $very_long_prefix = str_repeat( 'a', 100 ); // 100 characters - Config::set_hook_prefix( $very_long_prefix ); - - $safe_prefix = Config::get_safe_hook_prefix(); - $max_length = Config::get_max_hook_prefix_length(); - - // Safe prefix should be trimmed to max length - $this->assertEquals( $max_length, strlen( $safe_prefix ) ); - $this->assertEquals( substr( $very_long_prefix, 0, $max_length ), $safe_prefix ); - } - /** * @before */ public function it_should_set_and_get_logger_when_null(): void { Config::reset(); + Config::set_container( tests_pigeon_get_container() ); } /** * @after */ public function reset(): void { - Config::set_logger( null ); - Config::set_hook_prefix( tests_pigeon_get_hook_prefix() ); + tests_pigeon_reset_config(); } } diff --git a/tests/wpunit/Provider_Test.php b/tests/wpunit/Provider_Test.php index f54bfba..a7ee03d 100644 --- a/tests/wpunit/Provider_Test.php +++ b/tests/wpunit/Provider_Test.php @@ -5,20 +5,8 @@ namespace StellarWP\Pigeon; use lucatume\WPBrowser\TestCase\WPTestCase; -use StellarWP\ContainerContract\ContainerInterface; class Provider_Test extends WPTestCase { - /** - * @test - */ - public function it_should_get_the_container(): void { - $container = tests_pigeon_get_container(); - - $this->assertInstanceOf( ContainerInterface::class, $container ); - $this->assertInstanceOf( ContainerInterface::class, Provider::get_container() ); - $this->assertSame( $container, Provider::get_container() ); - } - /** * @test */ diff --git a/tests/wpunit/Regulator_Test.php b/tests/wpunit/Regulator_Test.php index fbbedb8..ae90056 100644 --- a/tests/wpunit/Regulator_Test.php +++ b/tests/wpunit/Regulator_Test.php @@ -11,7 +11,7 @@ class Regulator_Test extends WPTestCase { * @test */ public function it_should_have_as_hook_registered(): void { - $regulator = Provider::get_container()->get( Regulator::class ); + $regulator = Config::get_container()->get( Regulator::class ); $this->assertSame( 10, has_action( 'pigeon_' . Config::get_hook_prefix() . '_process_task', [ $regulator, 'process_task' ] ) ); } } diff --git a/tests/wpunit/Tables/Tasks_Test.php b/tests/wpunit/Tables/Tasks_Test.php index 277469b..641f585 100644 --- a/tests/wpunit/Tables/Tasks_Test.php +++ b/tests/wpunit/Tables/Tasks_Test.php @@ -9,6 +9,7 @@ use StellarWP\Pigeon\Abstracts\Task_Abstract; use StellarWP\DB\DB; use StellarWP\Pigeon\Config; +use StellarWP\Pigeon\Tables\Utility\Safe_Dynamic_Prefix; use InvalidArgumentException; class Dummy_Task extends Task_Abstract implements Task { @@ -56,8 +57,10 @@ private function insert_dummy_task( array $data ) { * @test */ public function it_should_be_using_the_prefix(): void { + $container = Config::get_container(); + $safe_dynamic_prefix = $container->get( Safe_Dynamic_Prefix::class ); $name = Tasks::base_table_name(); - $this->assertStringContainsString( Config::get_hook_prefix(), $name ); + $this->assertStringContainsString( $safe_dynamic_prefix->get(), $name ); $query = DB::prepare( 'SHOW TABLES LIKE %s', DB::prefix( $name ) ); $tables = DB::get_results( $query ); @@ -124,8 +127,10 @@ public function it_should_handle_very_long_hook_prefix_without_exceeding_mysql_l $this->assertLessThanOrEqual( 64, strlen( $table_name ), 'Tasks table name should not exceed MySQL\'s 64-character limit' ); // The table name should use the safe prefix - $safe_prefix = Config::get_safe_hook_prefix(); - $expected = DB::prefix( 'pigeon_' . $safe_prefix . '_tasks' ); + $container = Config::get_container(); + $safe_dynamic_prefix = $container->get( Safe_Dynamic_Prefix::class ); + $safe_prefix = $safe_dynamic_prefix->get(); + $expected = DB::prefix( 'pigeon_' . $safe_prefix . '_tasks' ); $this->assertEquals( $expected, $table_name ); } diff --git a/tests/wpunit/Tables/Utility/Safe_Dynamic_Prefix_Test.php b/tests/wpunit/Tables/Utility/Safe_Dynamic_Prefix_Test.php new file mode 100644 index 0000000..a99878f --- /dev/null +++ b/tests/wpunit/Tables/Utility/Safe_Dynamic_Prefix_Test.php @@ -0,0 +1,47 @@ +assertEquals( 5, Config::get_container()->get( Safe_Dynamic_Prefix::class )->get_max_length() ); + } + + /** + * @test + */ + public function it_should_return_safe_hook_prefix_for_short_prefix(): void { + Config::set_hook_prefix( 'short' ); + + // Short prefix should be returned as-is + $this->assertEquals( 'short', Config::get_container()->get( Safe_Dynamic_Prefix::class )->get() ); + } + + /** + * @test + */ + public function it_should_trim_long_hook_prefix_to_safe_length(): void { + $very_long_prefix = str_repeat( 'a', 100 ); // 100 characters + Config::set_hook_prefix( $very_long_prefix ); + + // Safe prefix should be trimmed to max length + $this->assertEquals( 'aaaaa', Config::get_container()->get( Safe_Dynamic_Prefix::class )->get() ); + } + + /** + * @after + */ + public function reset(): void { + Config::set_hook_prefix( tests_pigeon_get_hook_prefix() ); + } +} diff --git a/tests/wpunit/Traits/Loggable_Test.php b/tests/wpunit/Traits/Loggable_Test.php index 230204c..1104724 100644 --- a/tests/wpunit/Traits/Loggable_Test.php +++ b/tests/wpunit/Traits/Loggable_Test.php @@ -5,8 +5,8 @@ namespace StellarWP\Pigeon\Traits; use lucatume\WPBrowser\TestCase\WPTestCase; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Contracts\Logger; -use StellarWP\Pigeon\Provider; use Psr\Log\LogLevel; class Dummy_Loggable { @@ -21,7 +21,7 @@ class Loggable_Test extends WPTestCase { */ public function set_up_mock_logger() { $this->mock_logger = $this->createMock( Logger::class ); - $container = Provider::get_container(); + $container = Config::get_container(); $container->singleton( Logger::class, $this->mock_logger ); } From 6670ffb180302d221602b3646cf2d1557ceb33dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Jul 2025 15:39:11 +0000 Subject: [PATCH 02/10] chore: autopublish 2025-07-04T15:39:11Z --- src/Abstracts/Table_Abstract.php | 1 - src/Config.php | 4 ++-- src/Tables/Utility/Safe_Dynamic_Prefix.php | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Abstracts/Table_Abstract.php b/src/Abstracts/Table_Abstract.php index d789479..79597b1 100644 --- a/src/Abstracts/Table_Abstract.php +++ b/src/Abstracts/Table_Abstract.php @@ -13,7 +13,6 @@ use StellarWP\Schema\Tables\Contracts\Table; use StellarWP\DB\DB; -use StellarWP\Pigeon\Provider as Pigeon_Main_Controller; use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Tables\Utility\Safe_Dynamic_Prefix; use StellarWP\Pigeon\Traits\Custom_Table_Query_Methods; diff --git a/src/Config.php b/src/Config.php index 0fbfefd..09810b8 100644 --- a/src/Config.php +++ b/src/Config.php @@ -61,7 +61,7 @@ class Config { * * @return ContainerInterface */ - public static function get_container() : ContainerInterface { + public static function get_container(): ContainerInterface { if ( self::$container === null ) { throw new \RuntimeException( 'You must provide a container via StellarWP\Pigeon\Config::set_container() before attempting to fetch it.' ); } @@ -116,7 +116,7 @@ public static function get_max_table_name_length(): int { * * @return bool */ - public static function has_container() : bool { + public static function has_container(): bool { return self::$container !== null; } diff --git a/src/Tables/Utility/Safe_Dynamic_Prefix.php b/src/Tables/Utility/Safe_Dynamic_Prefix.php index 304d597..9fb2330 100644 --- a/src/Tables/Utility/Safe_Dynamic_Prefix.php +++ b/src/Tables/Utility/Safe_Dynamic_Prefix.php @@ -78,7 +78,7 @@ public function get_max_length( ?string $longest_table_name = null ): int { global $wpdb; $wp_prefix_length = strlen( $wpdb->prefix ); - $longest_table_name = $longest_table_name ?? $this->longest_table_name; + $longest_table_name ??= $this->longest_table_name; $unprefixed_table_name_length = strlen( sprintf( $longest_table_name, '' ) ); return Config::get_max_table_name_length() - $unprefixed_table_name_length - $wp_prefix_length; @@ -112,4 +112,4 @@ public function get( ?string $longest_table_name = null ): string { return $prefix; } -} \ No newline at end of file +} From 13402ac34f975bdb025edc13bdc291c9ed37d738 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 11:42:33 -0400 Subject: [PATCH 03/10] Typehint the container in Config --- src/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.php b/src/Config.php index 0fbfefd..8ca65ee 100644 --- a/src/Config.php +++ b/src/Config.php @@ -27,7 +27,7 @@ class Config { * * @var ContainerInterface */ - private static $container; + private static ?ContainerInterface $container = null; /** * The hook prefix. From 192da3e1590b1e1d6395a2e7b8d2fa28fb1798c7 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 11:46:52 -0400 Subject: [PATCH 04/10] chore: Resolve PHPStan notices --- src/Config.php | 2 +- src/Tables/Provider.php | 2 +- src/Tables/Utility/Safe_Dynamic_Prefix.php | 7 ------- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Config.php b/src/Config.php index 7c0b6c9..f2b295a 100644 --- a/src/Config.php +++ b/src/Config.php @@ -27,7 +27,7 @@ class Config { * * @var ContainerInterface */ - private static ?ContainerInterface $container = null; + protected static ?ContainerInterface $container = null; /** * The hook prefix. diff --git a/src/Tables/Provider.php b/src/Tables/Provider.php index 26707bb..2600ff9 100644 --- a/src/Tables/Provider.php +++ b/src/Tables/Provider.php @@ -27,7 +27,7 @@ class Provider extends Provider_Abstract { /** * Tables to register. * - * @var array + * @var array */ private array $tables = [ Tasks::class, diff --git a/src/Tables/Utility/Safe_Dynamic_Prefix.php b/src/Tables/Utility/Safe_Dynamic_Prefix.php index 9fb2330..71b0c12 100644 --- a/src/Tables/Utility/Safe_Dynamic_Prefix.php +++ b/src/Tables/Utility/Safe_Dynamic_Prefix.php @@ -19,13 +19,6 @@ class Safe_Dynamic_Prefix { */ private string $longest_table_name = ''; - /** - * The calculated safe max dynamic table prefix. - * - * @var string - */ - private string $safe_prefix = ''; - /** * Calculates the longest table name. * From 93fb467dd9d70f9e2374de5cf93c4e09f496877f Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 11:50:47 -0400 Subject: [PATCH 05/10] chore: Resolve phpcs docblock issues --- src/Config.php | 4 ++++ src/Tables/Utility/Safe_Dynamic_Prefix.php | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/Config.php b/src/Config.php index f2b295a..ec358e8 100644 --- a/src/Config.php +++ b/src/Config.php @@ -59,6 +59,10 @@ class Config { /** * Get the container. * + * @since TBD + * + * @throws RuntimeException If the container is not set. + * * @return ContainerInterface */ public static function get_container(): ContainerInterface { diff --git a/src/Tables/Utility/Safe_Dynamic_Prefix.php b/src/Tables/Utility/Safe_Dynamic_Prefix.php index 71b0c12..e3ecbec 100644 --- a/src/Tables/Utility/Safe_Dynamic_Prefix.php +++ b/src/Tables/Utility/Safe_Dynamic_Prefix.php @@ -11,6 +11,13 @@ use StellarWP\Pigeon\Config; +/** + * Safe dynamic table prefix utility class. + * + * @since TBD + * + * @package StellarWP\Pigeon\Tables\Utility; + */ class Safe_Dynamic_Prefix { /** * Longest table name. From 6accd8281e98840f1de7162744ba7edb3a1f417e Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 12:20:22 -0400 Subject: [PATCH 06/10] refactor: Address code review feedback for table prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove schema slug modification from Table_Abstract constructor - Fix container property type hint in Config class - Remove redundant get_container() method from Provider - Use imported RuntimeException instead of fully qualified name 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/Abstracts/Table_Abstract.php | 3 --- src/Config.php | 6 ++++-- src/Provider.php | 13 ------------- src/Tables/Utility/Safe_Dynamic_Prefix.php | 5 +++-- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/Abstracts/Table_Abstract.php b/src/Abstracts/Table_Abstract.php index 79597b1..b29e549 100644 --- a/src/Abstracts/Table_Abstract.php +++ b/src/Abstracts/Table_Abstract.php @@ -141,9 +141,6 @@ abstract class Table_Abstract extends Table { public function __construct() { $this->db = DB::class; $this->container = Config::get_container(); - - // Modify table names to use the hook prefix. - self::$schema_slug = sprintf( self::$schema_slug, Config::get_hook_prefix() ); } /** diff --git a/src/Config.php b/src/Config.php index ec358e8..185f18e 100644 --- a/src/Config.php +++ b/src/Config.php @@ -25,7 +25,9 @@ class Config { /** * Container object. * - * @var ContainerInterface + * @since TBD + * + * @var ?ContainerInterface */ protected static ?ContainerInterface $container = null; @@ -67,7 +69,7 @@ class Config { */ public static function get_container(): ContainerInterface { if ( self::$container === null ) { - throw new \RuntimeException( 'You must provide a container via StellarWP\Pigeon\Config::set_container() before attempting to fetch it.' ); + throw new RuntimeException( 'You must provide a container via StellarWP\Pigeon\Config::set_container() before attempting to fetch it.' ); } return self::$container; diff --git a/src/Provider.php b/src/Provider.php index 8c417b4..ef0fbe8 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -101,19 +101,6 @@ public static function reset(): void { self::$has_registered = false; } - /** - * Gets the container. - * - * @since TBD - * - * @return Container - * - * @throws RuntimeException If the container has not been set. - */ - public static function get_container(): Container { - return Config::get_container(); - } - /** * Checks if Pigeon is registered. * diff --git a/src/Tables/Utility/Safe_Dynamic_Prefix.php b/src/Tables/Utility/Safe_Dynamic_Prefix.php index e3ecbec..0373272 100644 --- a/src/Tables/Utility/Safe_Dynamic_Prefix.php +++ b/src/Tables/Utility/Safe_Dynamic_Prefix.php @@ -9,6 +9,7 @@ namespace StellarWP\Pigeon\Tables\Utility; +use RuntimeException; use StellarWP\Pigeon\Config; /** @@ -94,7 +95,7 @@ public function get_max_length( ?string $longest_table_name = null ): int { * * @param string|null $longest_table_name The longest table name to calculate the maximum length for. * - * @throws \RuntimeException If the dynamic table prefix is not set or the max dynamic table prefix length could not be determined. + * @throws RuntimeException If the dynamic table prefix is not set or the max dynamic table prefix length could not be determined. * * @return string The safe hook prefix. */ @@ -103,7 +104,7 @@ public function get( ?string $longest_table_name = null ): string { $max_length = $this->get_max_length( $longest_table_name ); if ( ! $max_length ) { - throw new \RuntimeException( 'The max dynamic table prefix could not be determined.' ); + throw new RuntimeException( 'The max dynamic table prefix could not be determined.' ); } if ( strlen( $prefix ) > $max_length ) { From b5853c991244e53250e592e0d31568da195d25ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Jul 2025 16:21:46 +0000 Subject: [PATCH 07/10] chore: autopublish 2025-07-04T16:21:46Z --- src/Provider.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Provider.php b/src/Provider.php index ef0fbe8..bdf193f 100644 --- a/src/Provider.php +++ b/src/Provider.php @@ -12,9 +12,7 @@ namespace StellarWP\Pigeon; use StellarWP\Pigeon\Abstracts\Provider_Abstract; -use StellarWP\ContainerContract\ContainerInterface as Container; use StellarWP\Pigeon\Tables\Provider as Tables_Provider; -use RuntimeException; use StellarWP\Schema\Config as Schema_Config; use StellarWP\DB\DB; use StellarWP\Pigeon\Contracts\Logger; From ff06125f4e4d1fde47b9198e490b5c7d8ca917f1 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 12:24:52 -0400 Subject: [PATCH 08/10] test: repoint Provider::get_container() to Config::get_container() --- tests/integration/Regulator_Test.php | 2 +- tests/integration/Tasks/Email_Test.php | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/integration/Regulator_Test.php b/tests/integration/Regulator_Test.php index 30a8382..d550407 100644 --- a/tests/integration/Regulator_Test.php +++ b/tests/integration/Regulator_Test.php @@ -43,7 +43,7 @@ public function freeze(): void { } private function get_logger(): Logger { - return Provider::get_container()->get( Logger::class ); + return Config::get_container()->get( Logger::class ); } /** diff --git a/tests/integration/Tasks/Email_Test.php b/tests/integration/Tasks/Email_Test.php index b39a9e7..cb2b9a0 100644 --- a/tests/integration/Tasks/Email_Test.php +++ b/tests/integration/Tasks/Email_Test.php @@ -5,8 +5,7 @@ use lucatume\WPBrowser\TestCase\WPTestCase; use StellarWP\Pigeon\Contracts\Logger; -use StellarWP\Pigeon\Loggers\DB_Logger; -use StellarWP\Pigeon\Provider; +use StellarWP\Pigeon\Config; use StellarWP\Pigeon\Tests\Traits\With_AS_Assertions; use StellarWP\Pigeon\Tests\Traits\With_Clock_Mock; use StellarWP\Pigeon\Tests\Traits\With_Log_Snapshot; @@ -29,7 +28,7 @@ public function setup(): void { } private function get_logger(): Logger { - return Provider::get_container()->get( Logger::class ); + return Config::get_container()->get( Logger::class ); } /** From fe9b68895e17622bcaa014eb75b380d09a796d5d Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 12:26:59 -0400 Subject: [PATCH 09/10] Update docs to set the container --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index e78cc82..8b08aed 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -88,6 +88,7 @@ $container = get_my_container(); // Register Pigeon as a singleton $container->singleton( Provider::class ); +Config::set_container( $container ); Config::set_hook_prefix( 'my_app' ); // Needs to be set before the provider is initialized. // Initialize Pigeon From c27b5c968c3faba8cf3ed4cd25872ac11e0b71c4 Mon Sep 17 00:00:00 2001 From: Matthew Batchelder Date: Fri, 4 Jul 2025 12:28:18 -0400 Subject: [PATCH 10/10] docs: Add Config::set_container() to the getting started docs --- docs/getting-started.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 9516518..c42d968 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -43,6 +43,9 @@ add_action( 'plugins_loaded', function() { // Register Pigeon as a singleton $container->singleton( Provider::class ); + // Set the container for Pigeon. + Config::set_container( $container ); + // Initialize Pigeon $container->get( Provider::class )->register(); } );