PluginProbe
AI / trunk
AI vtrunk
1.3.0 1.2.0 1.1.0 1.0.2 1.0.1 1.0.0 0.9.0 trunk 0.1.1 0.2.0 0.2.1 0.3.0 0.3.1 0.4.0 0.4.1 0.5.0 0.6.0 0.7.0 0.8.0
ai / includes / Requirements.php

Requirements.php in AI trunk, at includes/Requirements.php

216 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Checks for external dependencies and requirements.
4 *
5 * @package WordPress\AI
6 */
7
8 declare(strict_types=1);
9
10 namespace WordPress\AI;
11
12 // Exit if accessed directly.
13 defined( 'ABSPATH' ) || exit;
14
15 /**
16 * Class - Requirements
17 *
18 * This class checks for external dependencies and requirements for the plugin.
19 *
20 * @internal This class should not be used outside the plugin and there is no guarantee of backwards compatibility.
21 */
22 final class Requirements {
23 /**
24 * The minimum PHP version.
25 */
26 private const MIN_PHP_VERSION = '7.4';
27
28 /**
29 * The minimum WordPress version.
30 */
31 private const MIN_WP_VERSION = '7.0';
32
33 /**
34 * The resolved requirement checks, keyed by requirement slug.
35 *
36 * The value is true if the requirement is met, or the error message if not.
37 * Messages are stored as a callable so they can be translated after the checks are run.
38 *
39 * @since 0.8.0
40 *
41 * @var array<string,(true|callable():string)> $requirements An array of requirement slugs and their check results.
42 */
43 private $requirements = array();
44
45 /**
46 * Runs the plugin requirements checks.
47 *
48 * @return bool True if all requirements are met, false otherwise.
49 */
50 public function are_requirements_met(): bool {
51 foreach ( $this->get_requirements() as $slug => $check_callback ) {
52 if ( isset( $this->requirements[ $slug ] ) ) {
53 continue;
54 }
55
56 $success = $check_callback['check']();
57
58 // The callback is stored, but only triggered inside the admin notice callback so strings can be translated.
59 $this->requirements[ $slug ] = $success ? true : $check_callback['error_message'];
60 }
61
62 if ( ! $this->meets_all_requirements() ) {
63 $this->display_admin_notice();
64 return false;
65 }
66
67 return true;
68 }
69
70 /**
71 * Returns the plugin requirements to check, keyed by a slug for easy retrieval.
72 *
73 * Items are checked during initialization by the order they are defined in this array.
74 *
75 * Each requirement should have a 'check' callback that returns true if the requirement is met, and an 'error_message' callback that returns a string error message if the requirement is not met.
76 *
77 * The error message is stored as a callback to allow for proper translation after the checks are run, since some checks may rely on functions that are not available until later in the WordPress load process.
78 *
79 * @return array<string,array{
80 * check:callable():bool,
81 * error_message:callable():string
82 * }> The requirements to check.
83 */
84 private function get_requirements(): array {
85 return array(
86 'php' => array(
87 'check' => static fn() => version_compare( PHP_VERSION, self::MIN_PHP_VERSION, '>=' ),
88 'error_message' => static fn() => sprintf(
89 // translators: %s: Minimum PHP version, %s: Current PHP version.
90 esc_html__( 'PHP version %1$s or higher is required. You are running PHP version %2$s.', 'ai' ),
91 esc_html( self::MIN_PHP_VERSION ),
92 esc_html( PHP_VERSION )
93 ),
94 ),
95 'wp' => array(
96 'check' => static fn() => is_wp_version_compatible( self::MIN_WP_VERSION ),
97 'error_message' => static fn() => sprintf(
98 // translators: %s: Minimum WordPress version.
99 esc_html__( 'WordPress version %s or higher is required.', 'ai' ),
100 esc_html( self::MIN_WP_VERSION )
101 ),
102 ),
103 'assets' => array(
104 'check' => static function () {
105 // PHPUnit tests may not have the asset built.
106 if ( defined( 'WPAI_IS_TEST' ) && WPAI_IS_TEST ) {
107 return true;
108 }
109
110 $asset_file = WPAI_PLUGIN_DIR . 'build/build.php';
111 if ( file_exists( $asset_file ) ) {
112 require_once $asset_file; // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
113 return true;
114 }
115
116 return false;
117 },
118 'error_message' => static fn() => esc_html__( 'The plugin assets are not built. This is most likely because you downloaded the plugin from the GitHub repository without building the assets. Please run `nvm use && npm ci && npm run build` to build the assets.', 'ai' ),
119 ),
120 'ai_support' => array(
121 'check' => static fn() => wp_supports_ai(),
122 'error_message' => static fn() => esc_html__( 'Your WordPress environment has AI functionality disabled. The AI plugin will not work until AI support is enabled.', 'ai' ),
123 ),
124 );
125 }
126
127 /**
128 * Checks the dependencies and display an admin notice if any are not met.
129 *
130 * @since 0.8.0
131 */
132 private function display_admin_notice(): void {
133 $hooks = array(
134 'admin_notices',
135 'network_admin_notices',
136 );
137
138 // Store a local copy to pass to the static callback.
139 $requirements = $this->requirements;
140
141 foreach ( $hooks as $hook ) {
142 add_action(
143 $hook,
144 static function () use ( $requirements ) {
145 // Messages are generated inside the hook to ensure the translation functions are available.
146 $error_message = self::get_admin_notice_message_html( $requirements );
147
148 wp_admin_notice(
149 wp_kses(
150 $error_message,
151 array(
152 'br' => array(),
153 'ul' => array(),
154 'li' => array(),
155 )
156 ),
157 array(
158 'type' => 'error',
159 )
160 );
161 }
162 );
163 }
164 }
165
166 /**
167 * Checks if all requirements are met.
168 *
169 * @since 0.8.0
170 *
171 * @return bool True if all requirements are met, false otherwise.
172 */
173 private function meets_all_requirements(): bool {
174 foreach ( $this->requirements as $result ) {
175 if ( true !== $result ) {
176 return false;
177 }
178 }
179
180 return true;
181 }
182
183 /**
184 * Gets the admin notice message based on the failed requirements.
185 *
186 * Needs to be sanitized.
187 *
188 * @since 0.8.0
189 *
190 * @param array<string,(true|callable():string)> $requirements The requirements check results, keyed by requirement slug.
191 *
192 * @return string The admin notice message.
193 */
194 private static function get_admin_notice_message_html( array $requirements ): string {
195 $error_messages = array_map(
196 static function ( $result ) {
197 if ( is_callable( $result ) ) {
198 return $result();
199 }
200 return null;
201 },
202 $requirements
203 );
204 $error_messages = array_filter( $error_messages );
205
206 if ( count( $error_messages ) === 1 ) {
207 return reset( $error_messages );
208 }
209
210 return __( 'AI plugin cannot run due to the following issues:', 'ai' )
211 . '<br><ul><li>'
212 . implode( '</li><li>', $error_messages )
213 . '</li></ul>';
214 }
215 }
216