PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.0
28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 19.1 All 128 releases
wordpress-seo / src / loader.php

loader.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.0, at src/loader.php

312 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO;
4
5 use Throwable;
6 use WP_CLI;
7 use YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerInterface;
8
9 /**
10 * Class that manages loading integrations if and only if all their conditionals are met.
11 */
12 class Loader {
13
14 /**
15 * The registered integrations.
16 *
17 * @var string[]
18 */
19 protected $integrations = [];
20
21 /**
22 * The registered integrations.
23 *
24 * @var string[]
25 */
26 protected $initializers = [];
27
28 /**
29 * The registered routes.
30 *
31 * @var string[]
32 */
33 protected $routes = [];
34
35 /**
36 * The registered commands.
37 *
38 * @var string[]
39 */
40 protected $commands = [];
41
42 /**
43 * The registered migrations.
44 *
45 * @var string[]
46 */
47 protected $migrations = [];
48
49 /**
50 * The dependency injection container.
51 *
52 * @var ContainerInterface
53 */
54 protected $container;
55
56 /**
57 * Loader constructor.
58 *
59 * @param ContainerInterface $container The dependency injection container.
60 */
61 public function __construct( ContainerInterface $container ) {
62 $this->container = $container;
63 }
64
65 /**
66 * Registers an integration.
67 *
68 * @param string $integration_class The class name of the integration to be loaded.
69 *
70 * @return void
71 */
72 public function register_integration( $integration_class ) {
73 $this->integrations[] = $integration_class;
74 }
75
76 /**
77 * Registers an initializer.
78 *
79 * @param string $initializer_class The class name of the initializer to be loaded.
80 *
81 * @return void
82 */
83 public function register_initializer( $initializer_class ) {
84 $this->initializers[] = $initializer_class;
85 }
86
87 /**
88 * Registers a route.
89 *
90 * @param string $route_class The class name of the route to be loaded.
91 *
92 * @return void
93 */
94 public function register_route( $route_class ) {
95 $this->routes[] = $route_class;
96 }
97
98 /**
99 * Registers a command.
100 *
101 * @param string $command_class The class name of the command to be loaded.
102 *
103 * @return void
104 */
105 public function register_command( $command_class ) {
106 $this->commands[] = $command_class;
107 }
108
109 /**
110 * Registers a migration.
111 *
112 * @param string $plugin The plugin the migration belongs to.
113 * @param string $version The version of the migration.
114 * @param string $migration_class The class name of the migration to be loaded.
115 *
116 * @return void
117 */
118 public function register_migration( $plugin, $version, $migration_class ) {
119 if ( ! \array_key_exists( $plugin, $this->migrations ) ) {
120 $this->migrations[ $plugin ] = [];
121 }
122
123 $this->migrations[ $plugin ][ $version ] = $migration_class;
124 }
125
126 /**
127 * Loads all registered classes if their conditionals are met.
128 *
129 * @return void
130 */
131 public function load() {
132 $this->load_initializers();
133
134 if ( ! \did_action( 'init' ) ) {
135 \add_action( 'init', [ $this, 'load_integrations' ] );
136 }
137 else {
138 $this->load_integrations();
139 }
140
141 \add_action( 'rest_api_init', [ $this, 'load_routes' ] );
142
143 if ( \defined( 'WP_CLI' ) && \WP_CLI ) {
144 $this->load_commands();
145 }
146 }
147
148 /**
149 * Returns all registered migrations.
150 *
151 * @param string $plugin The plugin to get the migrations for.
152 *
153 * @return string[]|false The registered migrations. False if no migrations were registered.
154 */
155 public function get_migrations( $plugin ) {
156 if ( ! \array_key_exists( $plugin, $this->migrations ) ) {
157 return false;
158 }
159
160 return $this->migrations[ $plugin ];
161 }
162
163 /**
164 * Loads all registered commands.
165 *
166 * Commands that opt in to conditional loading by implementing
167 * Loadable_Interface are skipped when their conditionals are not met.
168 *
169 * @return void
170 */
171 protected function load_commands() {
172 foreach ( $this->commands as $class ) {
173 if ( \is_subclass_of( $class, Loadable_Interface::class )
174 && ! $this->conditionals_are_met( $class )
175 ) {
176 continue;
177 }
178
179 $command = $this->get_class( $class );
180
181 if ( $command === null ) {
182 continue;
183 }
184
185 WP_CLI::add_command( $class::get_namespace(), $command );
186 }
187 }
188
189 /**
190 * Loads all registered initializers if their conditionals are met.
191 *
192 * @return void
193 */
194 protected function load_initializers() {
195 foreach ( $this->initializers as $class ) {
196 if ( ! $this->conditionals_are_met( $class ) ) {
197 continue;
198 }
199
200 $initializer = $this->get_class( $class );
201
202 if ( $initializer === null ) {
203 continue;
204 }
205
206 $initializer->initialize();
207 }
208 }
209
210 /**
211 * Loads all registered integrations if their conditionals are met.
212 *
213 * @return void
214 */
215 public function load_integrations() {
216 foreach ( $this->integrations as $class ) {
217 if ( ! $this->conditionals_are_met( $class ) ) {
218 continue;
219 }
220
221 $integration = $this->get_class( $class );
222
223 if ( $integration === null ) {
224 continue;
225 }
226
227 $integration->register_hooks();
228 }
229 }
230
231 /**
232 * Loads all registered routes if their conditionals are met.
233 *
234 * @return void
235 */
236 public function load_routes() {
237 foreach ( $this->routes as $class ) {
238 if ( ! $this->conditionals_are_met( $class ) ) {
239 continue;
240 }
241
242 $route = $this->get_class( $class );
243
244 if ( $route === null ) {
245 continue;
246 }
247
248 $route->register_routes();
249 }
250 }
251
252 /**
253 * Checks if all conditionals of a given loadable are met.
254 *
255 * @param string $loadable_class The class name of the loadable.
256 *
257 * @return bool Whether all conditionals of the loadable are met.
258 */
259 protected function conditionals_are_met( $loadable_class ) {
260 // In production environments do not fatal if the class does not exist but log and fail gracefully.
261 if ( \YOAST_ENVIRONMENT === 'production' && ! \class_exists( $loadable_class ) ) {
262 if ( \defined( 'WP_DEBUG' ) && \WP_DEBUG ) {
263 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
264 \error_log(
265 \sprintf(
266 /* translators: %1$s expands to Yoast SEO, %2$s expands to the name of the class that could not be found. */
267 \__( '%1$s attempted to load the class %2$s but it could not be found.', 'wordpress-seo' ),
268 'Yoast SEO',
269 $loadable_class,
270 ),
271 );
272 }
273 return false;
274 }
275
276 $conditionals = $loadable_class::get_conditionals();
277 foreach ( $conditionals as $class ) {
278 $conditional = $this->get_class( $class );
279 if ( $conditional === null || ! $conditional->is_met() ) {
280 return false;
281 }
282 }
283
284 return true;
285 }
286
287 /**
288 * Gets a class from the container.
289 *
290 * @param string $class_name The class name.
291 *
292 * @return object|null The class or, in production environments, null if it does not exist.
293 *
294 * @throws Throwable If the class does not exist in development environments.
295 */
296 protected function get_class( $class_name ) {
297 try {
298 return $this->container->get( $class_name );
299 } catch ( Throwable $e ) {
300 // In production environments do not fatal if the class could not be constructed but log and fail gracefully.
301 if ( \YOAST_ENVIRONMENT === 'production' ) {
302 if ( \defined( 'WP_DEBUG' ) && \WP_DEBUG ) {
303 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
304 \error_log( $e->getMessage() );
305 }
306 return null;
307 }
308 throw $e;
309 }
310 }
311 }
312