PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / bootstrap / service-locator.php

service-locator.php in 404 Solution trunk, at includes/bootstrap/service-locator.php

238 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 require_once __DIR__ . '/../core/PhpErrorLogFallback.php';
8 require_once __DIR__ . '/../core/ServiceContainer.php';
9
10 /**
11 * Global service-locator helper functions.
12 *
13 * Required from includes/bootstrap.php at production boot and from
14 * tests/bootstrap.php during the test run, so abj_service() is defined
15 * before any caller invokes it.
16 *
17 * The container itself lives in ABJ_404_Solution_ServiceContainer.
18 */
19
20 /**
21 * Helper function to access services from the container.
22 *
23 * This provides a shorter, more convenient syntax than calling
24 * ABJ_404_Solution_ServiceContainer::getInstance()->get().
25 *
26 * The production bootstrap registers every service via
27 * `abj_404_solution_init_services()`. A required lookup rehydrates the
28 * production graph after tests or early boot clear the container.
29 *
30 * @param string $name Service identifier
31 * @return mixed The service instance
32 * @throws ABJ_404_Solution_ServiceNotRegisteredException when the service has
33 * no registered factory.
34 *
35 * @phpstan-return (
36 * $name is 'functions' ? ABJ_404_Solution_Functions : (
37 * $name is 'request_input_normalizer' ? ABJ_404_Solution_RequestInputNormalizer : (
38 * $name is 'mb_string_adapter' ? ABJ_404_Solution_MbStringAdapter : (
39 * $name is 'regex_helper' ? ABJ_404_Solution_RegexHelper : (
40 * $name is 'url_encoder' ? ABJ_404_Solution_UrlEncoder : (
41 * $name is 'sanitizer' ? ABJ_404_Solution_Sanitizer : (
42 * $name is 'query_string_helper' ? ABJ_404_Solution_QueryStringHelper : (
43 * $name is 'logging' ? ABJ_404_Solution_Logging : (
44 * $name is 'logging_state_store' ? ABJ_404_Solution_LoggingStateStore : (
45 * $name is 'clock' ? ABJ_404_Solution_Clock : (
46 * $name is 'error_handler' ? class-string : (
47 * $name is 'db_core' ? ABJ_404_Solution_DatabaseCore : (
48 * $name is 'content_repository' ? ABJ_404_Solution_ContentRepository : (
49 * $name is 'redirects_repository' ? ABJ_404_Solution_RedirectsRepository : (
50 * $name is 'redirects_retention_service' ? ABJ_404_Solution_RedirectsRetentionService : (
51 * $name is 'redirect_dead_destination_checker' ? ABJ_404_Solution_RedirectDeadDestinationChecker : (
52 * $name is 'logs_repository' ? ABJ_404_Solution_LogsRepository : (
53 * $name is 'stats_repository' ? ABJ_404_Solution_StatsRepository : (
54 * $name is 'internal_source_evidence_repository' ? ABJ_404_Solution_InternalSourceEvidenceRepository : (
55 * $name is 'plugin_update_metadata_repository' ? ABJ_404_Solution_PluginUpdateMetadataRepository : (
56 * $name is 'view_read_service' ? ABJ_404_Solution_ViewReadService : (
57 * $name is 'data_access' ? ABJ_404_Solution_DataAccess : (
58 * $name is 'database_upgrades' ? ABJ_404_Solution_DatabaseUpgradesEtc : (
59 * $name is 'permalink_cache' ? ABJ_404_Solution_PermalinkCache : (
60 * $name is 'ngram_extractor' ? ABJ_404_Solution_NGramExtractor : (
61 * $name is 'ngram_coverage_policy' ? ABJ_404_Solution_NGramCoveragePolicy : (
62 * $name is 'ngram_cache_repository' ? ABJ_404_Solution_NGramCacheRepository : (
63 * $name is 'ngram_filter' ? ABJ_404_Solution_NGramFilter : (
64 * $name is 'plugin_logic' ? ABJ_404_Solution_PluginLogic : (
65 * $name is 'request_ignore_normalizer' ? ABJ_404_Solution_RequestIgnoreNormalizer : (
66 * $name is 'spell_checker' ? ABJ_404_Solution_SpellChecker : (
67 * $name is 'old_permalink_structure_store' ? ABJ_404_Solution_OldPermalinkStructureStore : (
68 * $name is 'old_permalink_structure_resolver' ? ABJ_404_Solution_OldPermalinkStructureResolver : (
69 * $name is 'engine_old_permalink_structure' ? ABJ_404_Solution_OldPermalinkStructureEngine : (
70 * $name is 'engine_slug' ? ABJ_404_Solution_SlugMatchingEngine : (
71 * $name is 'engine_url_fix' ? ABJ_404_Solution_UrlFixEngine : (
72 * $name is 'engine_title' ? ABJ_404_Solution_TitleMatchingEngine : (
73 * $name is 'engine_category_tag' ? ABJ_404_Solution_CategoryTagMatchingEngine : (
74 * $name is 'engine_content' ? ABJ_404_Solution_ContentMatchingEngine : (
75 * $name is 'engine_spelling' ? ABJ_404_Solution_SpellingMatchingEngine : (
76 * $name is 'engine_archive_fallback' ? ABJ_404_Solution_ArchiveFallbackEngine : (
77 * $name is 'matching_engines' ? array<int, object> : (
78 * $name is 'near_miss_recorder' ? ABJ_404_Solution_NearMissRecorder : (
79 * $name is 'wordpress_connector' ? ABJ_404_Solution_WordPress_Connector : (
80 * $name is 'slug_change_handler' ? ABJ_404_Solution_SlugChangeHandler : (
81 * $name is 'sync_utils' ? ABJ_404_Solution_SynchronizationUtils : (
82 * $name is 'request_context' ? ABJ_404_Solution_RequestContext : (
83 * $name is 'previous_request_cookie_tracker' ? ABJ_404_Solution_PreviousRequestCookieTracker : (
84 * $name is 'view' ? ABJ_404_Solution_View : (
85 * $name is 'view_suggestions' ? ABJ_404_Solution_View_Suggestions : (
86 * $name is 'shortcode' ? ABJ_404_Solution_ShortCode : (
87 * $name is 'ajax_security_gate' ? ABJ_404_Solution_AjaxSecurityGate : (
88 * $name is 'ajax_failure_logger' ? ABJ_404_Solution_AjaxFailureLogger : (
89 * $name is 'version_upgrade' ? ABJ_404_Solution_PluginLogicVersionUpgrader : (
90 * $name is 'options_repository' ? ABJ_404_Solution_PluginLogicOptionsResolver : (
91 * $name is 'admin_access_policy' ? ABJ_404_Solution_PluginAdminAccessPolicy : (
92 * $name is 'settings_mode_preference' ? ABJ_404_Solution_SettingsModePreference : (
93 * $name is 'not_found_response' ? ABJ_404_Solution_NotFoundResponseService :
94 * mixed
95 * ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
96 */
97 function abj_service($name) {
98 if ($name === 'logging' && class_exists('ABJ_404_Solution_Logging', false)) {
99 $logger = ABJ_404_Solution_Logging::peekInstance();
100 if ($logger !== null) {
101 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
102 return $logger;
103 }
104 }
105 if ($name === 'database_upgrades' && class_exists('ABJ_404_Solution_DatabaseUpgradesEtc', false)) {
106 $upgrades = ABJ_404_Solution_DatabaseUpgradesEtc::peekInstance();
107 if ($upgrades !== null) {
108 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
109 return $upgrades;
110 }
111 }
112
113 $container = ABJ_404_Solution_ServiceContainer::getInstance();
114 if (!$container->has($name) && function_exists('abj_404_solution_init_services')) {
115 $container->beginPreservingExistingRegistrations();
116 try {
117 abj_404_solution_init_services();
118 } finally {
119 $container->endPreservingExistingRegistrations();
120 }
121 $container = ABJ_404_Solution_ServiceContainer::getInstance();
122 }
123 if (!$container->has($name)) {
124 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
125 throw new ABJ_404_Solution_ServiceNotRegisteredException(
126 $name,
127 'ABJ_404_Solution_RuntimeServiceRegistration'
128 );
129 }
130
131 $service = $container->get($name);
132 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
133 return $service;
134 }
135
136 /**
137 * Optional service lookup for bootstrap probes and degraded-mode fallbacks.
138 *
139 * Required dependencies must use abj_service() so missing registrations fail
140 * with a named service. This helper is only for call sites where null is an
141 * explicit, handled outcome.
142 *
143 * @param string $name Service identifier
144 * @return mixed The service instance, or null when unavailable
145 *
146 * @phpstan-return (
147 * $name is 'logging' ? ABJ_404_Solution_Logging|null : (
148 * $name is 'options_repository' ? ABJ_404_Solution_PluginLogicOptionsResolver|null : (
149 * $name is 'ajax_failure_logger' ? ABJ_404_Solution_AjaxFailureLogger|null : (
150 * $name is 'ajax_security_gate' ? ABJ_404_Solution_AjaxSecurityGate|null : (
151 * $name is 'view' ? ABJ_404_Solution_View|null : (
152 * $name is 'database_upgrades' ? ABJ_404_Solution_DatabaseUpgradesEtc|null : (
153 * $name is 'cron_scheduler' ? ABJ_404_Solution_CronScheduler|null : (
154 * $name is 'cron_recurrence_migration' ? ABJ_404_Solution_CronRecurrenceMigration|null : (
155 * $name is 'clock' ? ABJ_404_Solution_Clock|null : (
156 * $name is 'not_found_response' ? ABJ_404_Solution_NotFoundResponseService|null : (
157 * $name is 'request_ignore_normalizer' ? ABJ_404_Solution_RequestIgnoreNormalizer|null : (
158 * $name is 'previous_request_cookie_tracker' ? ABJ_404_Solution_PreviousRequestCookieTracker|null : (
159 * $name is 'admin_access_policy' ? ABJ_404_Solution_PluginAdminAccessPolicy|null : (
160 * $name is 'pii_redactor' ? ABJ_404_Solution_PiiRedactor|null : (
161 * $name is 'view_read_service' ? ABJ_404_Solution_ViewReadService|null : (
162 * $name is 'rebuild_health' ? ABJ_404_Solution_RebuildHealthState|null :
163 * mixed|null
164 * ))))))))))))))))
165 */
166 function abj_service_optional($name) {
167 $container = ABJ_404_Solution_ServiceContainer::getInstance();
168 if (!$container->has($name)) {
169 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
170 return null;
171 }
172
173 try {
174 $service = $container->get($name);
175 ABJ_404_Solution_ServiceContainer::clearLastSuppressedError();
176 return $service;
177 } catch (\Throwable $e) {
178 ABJ_404_Solution_ServiceContainer::recordSuppressedErrorPublic(
179 'abj_service_optional(' . $name . ')',
180 $e
181 );
182 return null;
183 }
184 }
185
186 /**
187 * Typed accessor for the WP-Cron scheduler service.
188 *
189 * @return ABJ_404_Solution_CronScheduler
190 */
191 function abj_cron_scheduler(): ABJ_404_Solution_CronScheduler {
192 $scheduler = abj_service_optional('cron_scheduler');
193 if ($scheduler instanceof ABJ_404_Solution_CronScheduler) {
194 return $scheduler;
195 }
196 $clock = abj_service_optional('clock');
197 $logger = abj_service_optional('logging');
198 return new ABJ_404_Solution_CronScheduler(
199 $clock instanceof ABJ_404_Solution_Clock ? $clock : new ABJ_404_Solution_SystemClock(),
200 $logger instanceof ABJ_404_Solution_Logging ? $logger : null
201 );
202 }
203
204 /**
205 * Typed accessor for the daily-recurrence cadence policy.
206 *
207 * @return ABJ_404_Solution_CronRecurrenceMigration
208 */
209 function abj_cron_recurrence_migration(): ABJ_404_Solution_CronRecurrenceMigration {
210 $migration = abj_service_optional('cron_recurrence_migration');
211 if ($migration instanceof ABJ_404_Solution_CronRecurrenceMigration) {
212 return $migration;
213 }
214 $logger = abj_service_optional('logging');
215 return new ABJ_404_Solution_CronRecurrenceMigration(
216 abj_cron_scheduler(),
217 new ABJ_404_Solution_ScheduledEventInspector(),
218 $logger instanceof ABJ_404_Solution_Logging ? $logger : null
219 );
220 }
221
222 /**
223 * Typed accessor for the project clock service.
224 *
225 * Production resolves the container's SystemClock; tests can bind a
226 * FrozenClock to the same service and drive time-sensitive behavior without
227 * direct wall-clock calls.
228 *
229 * @return ABJ_404_Solution_Clock
230 */
231 function abj_clock(): ABJ_404_Solution_Clock {
232 $clock = abj_service_optional('clock');
233 if ($clock instanceof ABJ_404_Solution_Clock) {
234 return $clock;
235 }
236 return new ABJ_404_Solution_SystemClock();
237 }
238