0, 'cache_hits' => 0, 'factory_calls' => 0, ); /** * Get singleton instance of a class * * @param string $class_name Full class name. * @param array $args Constructor arguments. * @return object Instance. */ public static function get_instance( string $class_name, array $args = array() ): object { // Check cache first. $cache_key = self::get_cache_key( $class_name, $args ); if ( isset( self::$instances[ $cache_key ] ) ) { ++self::$stats['cache_hits']; accessibility_audio_tts_debug_log( "Lazy loader cache hit: {$class_name}" ); return self::$instances[ $cache_key ]; } // Create new instance. $instance = self::create_instance( $class_name, $args ); // Cache the instance. self::$instances[ $cache_key ] = $instance; ++self::$stats['loaded']; accessibility_audio_tts_debug_log( "Lazy loaded instance: {$class_name}" ); return $instance; } /** * Register factory function for lazy loading * * @param string $key Factory key. * @param callable $factory Factory function. * @param array $config Configuration options. * @return void. */ public static function register_factory( string $key, callable $factory, array $config = array() ): void { self::$factories[ $key ] = $factory; self::$configurations[ $key ] = array_merge( array( 'singleton' => true, 'cache_ttl' => 0, // 0 = no expiration. 'lazy' => true, ), $config ); accessibility_audio_tts_debug_log( "Registered lazy factory: {$key}" ); } /** * Get instance from factory * * @param string $key Factory key. * @param array $args Arguments to pass to factory. * @return object Instance. * @throws \Exception If factory not registered. */ public static function get_from_factory( string $key, array $args = array() ): object { if ( ! isset( self::$factories[ $key ] ) ) { throw new \Exception( sprintf( "Factory '%s' not registered", esc_html( $key ) ) ); } $config = self::$configurations[ $key ]; $cache_key = $config['singleton'] ? $key : self::get_cache_key( $key, $args ); // Check cache if singleton or caching enabled. if ( $config['singleton'] && isset( self::$instances[ $cache_key ] ) ) { ++self::$stats['cache_hits']; accessibility_audio_tts_debug_log( "Factory cache hit: {$key}" ); return self::$instances[ $cache_key ]; } // Call factory function. $factory = self::$factories[ $key ]; $instance = $factory( ...$args ); ++self::$stats['factory_calls']; // Cache if singleton. if ( $config['singleton'] ) { self::$instances[ $cache_key ] = $instance; } accessibility_audio_tts_debug_log( "Factory created instance: {$key}" ); return $instance; } /** * Register component for lazy loading * * @param string $key Component key. * @param string $class_name Class name. * @param array $args Default constructor arguments. * @param array $config Configuration options. * @return void. */ public static function register_component( string $key, string $class_name, array $args = array(), array $config = array() ): void { self::register_factory( $key, function () use ( $class_name, $args ) { return self::create_instance( $class_name, $args ); }, $config ); } /** * Get component instance * * @param string $key Component key. * @param array $override_args Override constructor arguments. * @return object Instance. */ public static function get_component( string $key, array $override_args = array() ): object { return self::get_from_factory( $key, $override_args ); } /** * Create instance with error handling * * @param string $class_name Class name. * @param array $args Constructor arguments. * @return object Instance. * @throws \Exception If class not found. */ private static function create_instance( string $class_name, array $args = array() ): object { if ( ! class_exists( $class_name ) ) { throw new \Exception( sprintf( "Class '%s' not found for lazy loading", esc_html( $class_name ) ) ); } try { // Handle singleton classes that have get_instance() method. if ( method_exists( $class_name, 'get_instance' ) ) { return $class_name::get_instance(); } // Regular class instantiation. if ( empty( $args ) ) { return new $class_name(); } else { return new $class_name( ...$args ); } } catch ( \Throwable $e ) { accessibility_audio_tts_error_log( "Error creating instance of {$class_name}: " . $e->getMessage() ); throw new \Exception( sprintf( "Failed to create instance of '%s': %s", esc_html( $class_name ), esc_html( $e->getMessage() ) ) ); } } /** * Generate cache key * * @param string $identifier Class name or factory key. * @param array $args Arguments. * @return string Cache key. */ private static function get_cache_key( string $identifier, array $args = array() ): string { if ( empty( $args ) ) { return $identifier; } return $identifier . '_' . md5( serialize( $args ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Safe use for cache key generation only. } /** * Check if instance is cached * * @param string $class_name Class name. * @param array $args Constructor arguments. * @return bool True if cached. */ public static function is_cached( string $class_name, array $args = array() ): bool { $cache_key = self::get_cache_key( $class_name, $args ); return isset( self::$instances[ $cache_key ] ); } /** * Check if factory is registered * * @param string $key Factory key. * @return bool True if registered. */ public static function has_factory( string $key ): bool { return isset( self::$factories[ $key ] ); } /** * Remove instance from cache * * @param string $class_name Class name. * @param array $args Constructor arguments. * @return bool True if removed. */ public static function remove_instance( string $class_name, array $args = array() ): bool { $cache_key = self::get_cache_key( $class_name, $args ); if ( isset( self::$instances[ $cache_key ] ) ) { unset( self::$instances[ $cache_key ] ); accessibility_audio_tts_debug_log( "Removed cached instance: {$class_name}" ); return true; } return false; } /** * Clear all cached instances * * @return void. */ public static function clear_cache(): void { $count = count( self::$instances ); self::$instances = array(); accessibility_audio_tts_debug_log( "Cleared {$count} cached instances from lazy loader" ); } /** * Clear specific factory * * @param string $key Factory key. * @return bool True if removed. */ public static function remove_factory( string $key ): bool { if ( isset( self::$factories[ $key ] ) ) { unset( self::$factories[ $key ] ); unset( self::$configurations[ $key ] ); // Remove cached instance if singleton. if ( isset( self::$instances[ $key ] ) ) { unset( self::$instances[ $key ] ); } accessibility_audio_tts_debug_log( "Removed factory: {$key}" ); return true; } return false; } /** * Get loading statistics * * @return array Statistics. */ public static function get_stats(): array { return array_merge( self::$stats, array( 'cached_instances' => count( self::$instances ), 'registered_factories' => count( self::$factories ), 'cache_efficiency' => self::$stats['loaded'] > 0 ? round( ( self::$stats['cache_hits'] / ( self::$stats['loaded'] + self::$stats['cache_hits'] ) ) * 100, 2 ) : 0, ) ); } /** * Get list of cached instances * * @return array Cached instance keys. */ public static function get_cached_instances(): array { return array_keys( self::$instances ); } /** * Get list of registered factories * * @return array Factory keys with configurations. */ public static function get_registered_factories(): array { $factories = array(); foreach ( self::$factories as $key => $factory ) { $factories[ $key ] = self::$configurations[ $key ] ?? array(); } return $factories; } /** * Initialize common plugin components * * Registers frequently used components for lazy loading. * * @return void. */ public static function init_common_components(): void { // Admin components. self::register_component( 'admin_secure', '\Accessibility_Audio_TTS\Admin\Admin_Secure' ); self::register_component( 'asset_manager', '\Accessibility_Audio_TTS\Admin\Asset_Manager' ); self::register_component( 'conversion_manager', '\Accessibility_Audio_TTS\Admin\Conversion_Manager' ); // API components. self::register_component( 'elevenlabs_client', '\Accessibility_Audio_TTS\API\ElevenLabs\ElevenLabs_Client' ); self::register_component( 'audio_file_manager', '\Accessibility_Audio_TTS\API\Audio\Audio_File_Manager' ); self::register_component( 'text_processor', '\Accessibility_Audio_TTS\API\Text\Text_Processor' ); // Frontend components. self::register_component( 'player', '\Accessibility_Audio_TTS\Frontend\Player' ); self::register_component( 'player_renderer', '\Accessibility_Audio_TTS\Frontend\Player_Renderer' ); // Handlers. self::register_component( 'audio_ajax_handler', '\Accessibility_Audio_TTS\Handlers\Audio_Ajax_Handler' ); self::register_component( 'cron_ajax_handler', '\Accessibility_Audio_TTS\Handlers\Cron_Ajax_Handler' ); accessibility_audio_tts_debug_log( 'Initialized common components for lazy loading' ); } /** * Preload critical components * * Loads components that are always needed to avoid lazy loading overhead. * * @param array $component_keys Component keys to preload. * @return void. */ public static function preload_components( array $component_keys ): void { foreach ( $component_keys as $key ) { if ( self::has_factory( $key ) ) { try { self::get_component( $key ); accessibility_audio_tts_debug_log( "Preloaded component: {$key}" ); } catch ( \Exception $e ) { accessibility_audio_tts_error_log( "Failed to preload component {$key}: " . $e->getMessage() ); } } } } /** * Reset loader state (for testing) * * @return void. */ public static function reset(): void { self::$instances = array(); self::$factories = array(); self::$configurations = array(); self::$stats = array( 'loaded' => 0, 'cache_hits' => 0, 'factory_calls' => 0, ); accessibility_audio_tts_debug_log( 'Lazy loader reset' ); } }