PluginProbe
Auto Alt Text / 2.8.2
Auto Alt Text v2.8.2
3.0.3 2.8.2 1.3.1 1.3.2 2.0.0 2.1.0 2.1.1 2.2.0 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.7.0 2.8.0 2.8.1 All 28 releases
auto-alt-text / src / App / Core / Container.php

Container.php in Auto Alt Text 2.8.2, at src/App/Core/Container.php

402 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace AATXT\App\Core;
6
7 use AATXT\App\Admin\MediaLibrary;
8 use AATXT\App\Admin\PluginOptions;
9 use AATXT\App\AIProviders\Anthropic\AnthropicModelsRegistry;
10 use AATXT\App\AIProviders\Anthropic\AnthropicResponse;
11 use AATXT\App\AIProviders\Azure\AzureComputerVisionCaptionsResponse;
12 use AATXT\App\AIProviders\Azure\AzureTranslator;
13 use AATXT\App\AIProviders\OpenAI\OpenAIVision;
14 use AATXT\App\Configuration\AnthropicConfig;
15 use AATXT\App\Configuration\AzureConfig;
16 use AATXT\App\Configuration\OpenAIConfig;
17 use AATXT\App\Infrastructure\Cache\CacheInterface;
18 use AATXT\App\Infrastructure\Cache\WordPressTransientCache;
19 use AATXT\App\Infrastructure\Database\ErrorLogSchema;
20 use AATXT\App\Infrastructure\Http\HttpClientInterface;
21 use AATXT\App\Infrastructure\Http\WordPressHttpClient;
22 use AATXT\App\Infrastructure\Repositories\ConfigRepositoryInterface;
23 use AATXT\App\Infrastructure\Repositories\ErrorLogRepository;
24 use AATXT\App\Infrastructure\Repositories\ErrorLogRepositoryInterface;
25 use AATXT\App\Infrastructure\Repositories\WordPressConfigRepository;
26 use AATXT\App\Logging\DBLogger;
27 use AATXT\App\Services\AltTextGeneratorFactory;
28 use AATXT\App\Services\ConfigBasedGeneratorFactory;
29 use AATXT\App\Services\AltTextService;
30 use AATXT\App\Utilities\AssetsManager;
31 use AATXT\App\AIProviders\Decorators\DecoratorBuilder;
32 use AATXT\App\AltTextGeneratorAi;
33 use AATXT\App\AltTextGeneratorAttachmentTitle;
34 use AATXT\App\AltTextGeneratorParentPostTitle;
35 use AATXT\App\Events\EventDispatcherInterface;
36 use AATXT\App\Events\SimpleEventDispatcher;
37 use AATXT\App\Events\AltTextGenerationFailedEvent;
38 use AATXT\App\Events\Listeners\LogErrorListener;
39 use AATXT\App\Events\Listeners\NotifyAdminListener;
40 use AATXT\Config\Constants;
41 use AATXT\Vendor\DI\Container as DIContainer;
42 use AATXT\Vendor\DI\ContainerBuilder;
43
44 /**
45 * Dependency Injection Container configuration.
46 *
47 * This class sets up and configures the PHP-DI container with all
48 * service bindings for the plugin. It implements the Dependency Inversion
49 * Principle by binding interfaces to concrete implementations.
50 *
51 * Usage:
52 * ```php
53 * $container = Container::make();
54 * $service = $container->get(SomeService::class);
55 * ```
56 */
57 final class Container
58 {
59 private static ?DIContainer $instance = null;
60
61 /**
62 * Private constructor to prevent direct instantiation.
63 */
64 private function __construct()
65 {
66 }
67
68 /**
69 * Get or create the container instance.
70 *
71 * @return DIContainer The configured container
72 * @throws \Exception If container build fails
73 */
74 public static function make(): DIContainer
75 {
76 if (self::$instance === null) {
77 self::$instance = self::build();
78 }
79
80 return self::$instance;
81 }
82
83 /**
84 * Build and configure the container with all service bindings.
85 *
86 * @return DIContainer The configured container
87 * @throws \Exception If container build fails
88 */
89 private static function build(): DIContainer
90 {
91 $builder = new ContainerBuilder();
92
93 // Enable compilation for better performance in production
94 // Note: Disable in development if you need to modify bindings frequently
95 // $builder->enableCompilation(__DIR__ . '/../../../var/cache');
96
97 $builder->addDefinitions(self::getDefinitions());
98
99 return $builder->build();
100 }
101
102 /**
103 * Get all service definitions for the container.
104 *
105 * @return array<string, mixed> Array of service definitions
106 */
107 private static function getDefinitions(): array
108 {
109 return [
110 // WordPress database abstraction
111 // Maps wpdb class to the global WordPress database object
112 \wpdb::class => function () {
113 return $GLOBALS['wpdb'];
114 },
115
116 // Database Schema Management
117 // Manages error logs table schema
118 ErrorLogSchema::class => \AATXT\Vendor\DI\create(ErrorLogSchema::class)
119 ->constructor(\AATXT\Vendor\DI\get(\wpdb::class)),
120
121 // Error Log Repository
122 // Maps interface to concrete implementation for error log persistence
123 ErrorLogRepositoryInterface::class => \AATXT\Vendor\DI\create(ErrorLogRepository::class)
124 ->constructor(
125 \AATXT\Vendor\DI\get(\wpdb::class),
126 \AATXT\Vendor\DI\get(ErrorLogSchema::class)
127 ),
128
129 // Config Repository
130 // Maps interface to WordPress options implementation for configuration management
131 ConfigRepositoryInterface::class => \AATXT\Vendor\DI\create(WordPressConfigRepository::class),
132
133 // Database Logger
134 // Legacy logger refactored to use repository pattern
135 DBLogger::class => \AATXT\Vendor\DI\create(DBLogger::class)
136 ->constructor(
137 \AATXT\Vendor\DI\get(ErrorLogRepositoryInterface::class),
138 \AATXT\Vendor\DI\get(ErrorLogSchema::class)
139 ),
140
141 // HTTP Client abstraction
142 // Maps HttpClientInterface to WordPress HTTP client implementation
143 HttpClientInterface::class => \AATXT\Vendor\DI\create(WordPressHttpClient::class),
144
145 // Cache abstraction
146 // Maps CacheInterface to the WordPress Transients API
147 CacheInterface::class => \AATXT\Vendor\DI\create(WordPressTransientCache::class),
148
149 // Anthropic Models Registry
150 // Fetches the available Claude models from the Anthropic API, with caching
151 AnthropicModelsRegistry::class => function ($container) {
152 return new AnthropicModelsRegistry(
153 $container->get(HttpClientInterface::class),
154 $container->get(CacheInterface::class),
155 PluginOptions::apiKeyAnthropic()
156 );
157 },
158
159 // OpenAI Configuration
160 // Factory that reads configuration from WordPress options
161 OpenAIConfig::class => function () {
162 return new OpenAIConfig(
163 PluginOptions::apiKeyOpenAI(),
164 PluginOptions::openAiPrompt(),
165 PluginOptions::openAiModel()
166 );
167 },
168
169 // Anthropic Configuration
170 // Factory that reads configuration from WordPress options
171 AnthropicConfig::class => function () {
172 return new AnthropicConfig(
173 PluginOptions::apiKeyAnthropic(),
174 PluginOptions::anthropicPrompt(),
175 PluginOptions::anthropicModel()
176 );
177 },
178
179 // Azure Configuration
180 // Factory that reads configuration from WordPress options
181 // Includes both Computer Vision and Translator settings
182 AzureConfig::class => function () {
183 return new AzureConfig(
184 PluginOptions::apiKeyAzureComputerVision(),
185 PluginOptions::endpointAzureComputerVision(),
186 '', // Azure doesn't use a model parameter
187 '', // Azure doesn't use a custom prompt
188 PluginOptions::apiKeyAzureTranslateInstance(),
189 PluginOptions::endpointAzureTranslateInstance(),
190 PluginOptions::regionAzureTranslateInstance(),
191 PluginOptions::languageAzureTranslateInstance()
192 );
193 },
194
195 // OpenAI Vision Provider
196 // Automatically injects HttpClientInterface and OpenAIConfig
197 OpenAIVision::class => \AATXT\Vendor\DI\create(OpenAIVision::class)
198 ->constructor(
199 \AATXT\Vendor\DI\get(HttpClientInterface::class),
200 \AATXT\Vendor\DI\get(OpenAIConfig::class)
201 ),
202
203 // Anthropic Claude Provider
204 // Automatically injects HttpClientInterface, AnthropicConfig and the models registry
205 // (the registry powers the runtime fallback when the configured model is unavailable)
206 AnthropicResponse::class => \AATXT\Vendor\DI\create(AnthropicResponse::class)
207 ->constructor(
208 \AATXT\Vendor\DI\get(HttpClientInterface::class),
209 \AATXT\Vendor\DI\get(AnthropicConfig::class),
210 \AATXT\Vendor\DI\get(AnthropicModelsRegistry::class)
211 ),
212
213 // Azure Translator
214 // Automatically injects HttpClientInterface and AzureConfig
215 AzureTranslator::class => \AATXT\Vendor\DI\create(AzureTranslator::class)
216 ->constructor(
217 \AATXT\Vendor\DI\get(HttpClientInterface::class),
218 \AATXT\Vendor\DI\get(AzureConfig::class)
219 ),
220
221 // Azure Computer Vision Provider
222 // Automatically injects HttpClientInterface, AzureConfig, and AzureTranslator
223 AzureComputerVisionCaptionsResponse::class => \AATXT\Vendor\DI\create(AzureComputerVisionCaptionsResponse::class)
224 ->constructor(
225 \AATXT\Vendor\DI\get(HttpClientInterface::class),
226 \AATXT\Vendor\DI\get(AzureConfig::class),
227 \AATXT\Vendor\DI\get(AzureTranslator::class)
228 ),
229
230 // =============================================
231 // Decorated AI Providers (using Decorator Pattern)
232 // Order: Provider → Cleaning → Validation → Caching
233 // =============================================
234
235 // Decorated OpenAI Vision Provider
236 // Applies cleaning and validation decorators
237 'openai.vision.decorated' => function ($container) {
238 return DecoratorBuilder::wrap($container->get(OpenAIVision::class))
239 ->withCleaning()
240 ->withValidation(false)
241 ->build();
242 },
243
244 // Decorated Anthropic Provider
245 // Applies cleaning and validation decorators
246 'anthropic.decorated' => function ($container) {
247 return DecoratorBuilder::wrap($container->get(AnthropicResponse::class))
248 ->withCleaning()
249 ->withValidation(false)
250 ->build();
251 },
252
253 // Decorated Azure Provider
254 // Applies cleaning and validation decorators
255 // Note: Azure has built-in translation, so cleaning is important
256 'azure.decorated' => function ($container) {
257 return DecoratorBuilder::wrap($container->get(AzureComputerVisionCaptionsResponse::class))
258 ->withCleaning()
259 ->withValidation(false)
260 ->build();
261 },
262
263 // Alt Text Generator Factory
264 // Factory pattern for creating different types of alt text generators
265 // Uses decorated providers for cleaning and validation
266 AltTextGeneratorFactory::class => function ($container) {
267 $factory = new ConfigBasedGeneratorFactory();
268
269 // Register OpenAI Vision generator (decorated)
270 $factory->register(
271 Constants::AATXT_OPTION_TYPOLOGY_CHOICE_OPENAI,
272 function () use ($container) {
273 return AltTextGeneratorAi::make(
274 $container->get('openai.vision.decorated')
275 );
276 }
277 );
278
279 // Register Anthropic generator (decorated)
280 $factory->register(
281 Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ANTHROPIC,
282 function () use ($container) {
283 return AltTextGeneratorAi::make(
284 $container->get('anthropic.decorated')
285 );
286 }
287 );
288
289 // Register Azure generator (decorated)
290 $factory->register(
291 Constants::AATXT_OPTION_TYPOLOGY_CHOICE_AZURE,
292 function () use ($container) {
293 return AltTextGeneratorAi::make(
294 $container->get('azure.decorated')
295 );
296 }
297 );
298
299 // Register Parent Post Title generator
300 $factory->register(
301 Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ARTICLE_TITLE,
302 function () {
303 return AltTextGeneratorParentPostTitle::make();
304 }
305 );
306
307 // Register Attachment Title generator
308 $factory->register(
309 Constants::AATXT_OPTION_TYPOLOGY_CHOICE_ATTACHMENT_TITLE,
310 function () {
311 return AltTextGeneratorAttachmentTitle::make();
312 }
313 );
314
315 return $factory;
316 },
317
318
319 // =============================================
320 // Event System (Observer Pattern)
321 // =============================================
322
323 // Log Error Listener
324 // Listens for AltTextGenerationFailedEvent and logs errors to database
325 LogErrorListener::class => function ($container) {
326 return new LogErrorListener(
327 $container->get(ErrorLogRepositoryInterface::class)
328 );
329 },
330
331 // Notify Admin Listener
332 // Listens for failure events and can send email notifications
333 // Email notifications are disabled by default
334 NotifyAdminListener::class => function () {
335 return new NotifyAdminListener(
336 false, // Email disabled by default
337 5 // Threshold: 5 failures before notification
338 );
339 },
340
341 // Event Dispatcher
342 // Central event dispatcher with pre-registered listeners
343 EventDispatcherInterface::class => function ($container) {
344 $dispatcher = new SimpleEventDispatcher();
345
346 // Register LogErrorListener for failure events
347 // Note: We're using the listener via event system instead of direct logging
348 // This allows for decoupled error handling
349 $logErrorListener = $container->get(LogErrorListener::class);
350 $dispatcher->listen(
351 AltTextGenerationFailedEvent::class,
352 [$logErrorListener, 'handle']
353 );
354
355 // Register NotifyAdminListener for failure events
356 $notifyAdminListener = $container->get(NotifyAdminListener::class);
357 $dispatcher->listen(
358 AltTextGenerationFailedEvent::class,
359 [$notifyAdminListener, 'handleFailure']
360 );
361
362 return $dispatcher;
363 },
364
365 // Alt Text Service
366 // Main service for generating alt text, uses factory and handles errors
367 // Integrated with Event System for decoupled logging
368 AltTextService::class => function ($container) {
369 return new AltTextService(
370 $container->get(AltTextGeneratorFactory::class),
371 $container->get(ConfigRepositoryInterface::class),
372 $container->get(ErrorLogRepositoryInterface::class),
373 $container->get(EventDispatcherInterface::class)
374 );
375 },
376
377 // Assets Manager
378 // Handles Vite manifest loading for versioned assets
379 AssetsManager::class => \AATXT\Vendor\DI\create(AssetsManager::class),
380
381 // Media Library
382 // Handles media library UI customization and AJAX alt text generation
383 MediaLibrary::class => function ($container) {
384 return new MediaLibrary(
385 $container->get(AltTextService::class),
386 $container->get(AssetsManager::class)
387 );
388 },
389 ];
390 }
391
392 /**
393 * Reset the container instance (useful for testing).
394 *
395 * @return void
396 */
397 public static function reset(): void
398 {
399 self::$instance = null;
400 }
401 }
402