PluginProbe ʕ •ᴥ•ʔ
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback / trunk
Atarim – AI Agency for WordPress: Edit Pages, Fix Code, Update Plugins, SEO & Client Feedback vtrunk
5.1.3 5.1.2 5.1.1 5.1 5.0 trunk 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2.0 3.2.1 3.22 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.3.0 3.3.1 3.3.2 3.3.2.1 3.3.2.2 3.3.3 3.30 3.31 3.32 3.4 3.4.1 3.4.3 3.4.4 3.5 3.5.1 3.6 3.6.1 3.7 3.8 3.9 3.9.1 3.9.2 3.9.3 3.9.4 3.9.6 3.9.6.1 4.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.2 4.2.1 4.2.2 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.4
atarim-visual-collaboration / doit / abilities / class-avcf-abilities-core.php
atarim-visual-collaboration / doit / abilities Last commit date
class-avcf-abilities-base.php 4 weeks ago class-avcf-abilities-block-navigation.php 4 weeks ago class-avcf-abilities-cache.php 4 weeks ago class-avcf-abilities-content.php 2 weeks ago class-avcf-abilities-core.php 4 days ago class-avcf-abilities-execute-php.php 2 weeks ago class-avcf-abilities-global-styles.php 4 weeks ago class-avcf-abilities-gutenberg.php 2 weeks ago class-avcf-abilities-media.php 2 weeks ago class-avcf-abilities-metadata.php 2 weeks ago class-avcf-abilities-navigation.php 4 weeks ago class-avcf-abilities-patterns.php 4 weeks ago class-avcf-abilities-plugins.php 4 days ago class-avcf-abilities-readonly.php 2 weeks ago class-avcf-abilities-settings.php 4 weeks ago class-avcf-abilities-taxonomies.php 4 weeks ago class-avcf-abilities-templates.php 4 weeks ago class-avcf-abilities-theme-files.php 2 weeks ago class-avcf-abilities-themes.php 4 days ago class-avcf-abilities-users.php 4 weeks ago class-avcf-abilities-wp-cli.php 4 days ago
class-avcf-abilities-core.php
608 lines
1 <?php
2 /**
3 * WordPress core / environment MCP abilities.
4 *
5 * Reports on the host environment: WordPress version and pending updates,
6 * PHP version and WordPress support status, database server version, and
7 * multisite configuration. Built so the AI layer can audit a site before
8 * recommending major operations (upgrades, theme switches, plugin installs)
9 * that depend on the runtime environment.
10 *
11 * Exposed abilities:
12 * atarim/get-core-status WP + PHP + DB + multisite snapshot in one call.
13 * atarim/check-core-updates Force a fresh WordPress.org update check.
14 * atarim/update-core Update core to the latest (or a specific offered) version.
15 * atarim/reinstall-core Re-install the current version to repair core files.
16 *
17 * Note: abilities are exposed to the MCP server automatically (dynamic
18 * enumeration minus the avcf_mcp_blocked_abilities blacklist); no manual
19 * registration list needs editing when adding abilities here.
20 *
21 * @package atarim-visual-collaboration
22 */
23
24 if ( ! defined('ABSPATH') ) {
25 exit;
26 }
27
28 class AVCF_Abilities_Core extends AVCF_Abilities_Base {
29
30 /**
31 * Register all core / environment abilities.
32 * Called from AVCF_MCP::avcf_mcp_register_abilities() on wp_abilities_api_init.
33 */
34 public function register() {
35
36 // ---- get-core-status ----
37 wp_register_ability( 'atarim/get-core-status', [
38 'label' => 'Get Core Status',
39 'description' => 'Returns a snapshot of the WordPress core, PHP, and database environment plus pending core update info and multisite configuration. Use this to audit the site before recommending or attempting major operations like theme switches, large plugin installs, or PHP-dependent upgrades.',
40 'category' => 'atarim',
41 'input_schema' => [
42 'type' => 'object',
43 'properties' => new \stdClass(),
44 'additionalProperties' => false,
45 ],
46 'output_schema' => [
47 'type' => 'object',
48 'properties' => [
49 'wordpress' => [
50 'type' => 'object',
51 'properties' => [
52 'version' => [ 'type' => 'string' ],
53 'latest_version' => [ 'type' => 'string' ],
54 'update_available' => [ 'type' => 'boolean' ],
55 'update_type' => [ 'type' => 'string' ],
56 'automatic_updates_enabled' => [ 'type' => 'boolean' ],
57 'language' => [ 'type' => 'string' ],
58 ],
59 ],
60 'php' => [
61 'type' => 'object',
62 'properties' => [
63 'version' => [ 'type' => 'string' ],
64 'recommended_minimum' => [ 'type' => 'string' ],
65 'is_supported_by_wp' => [ 'type' => 'boolean' ],
66 'memory_limit' => [ 'type' => 'string' ],
67 'max_execution_time' => [ 'type' => 'integer' ],
68 ],
69 ],
70 'database' => [
71 'type' => 'object',
72 'properties' => [
73 'server_version' => [ 'type' => 'string' ],
74 'server_type' => [ 'type' => 'string' ],
75 'charset' => [ 'type' => 'string' ],
76 'collate' => [ 'type' => 'string' ],
77 ],
78 ],
79 'multisite' => [
80 'type' => 'object',
81 'properties' => [
82 'is_multisite' => [ 'type' => 'boolean' ],
83 'is_main_site' => [ 'type' => 'boolean' ],
84 'site_count' => [ 'type' => 'integer' ],
85 'network_id' => [ 'type' => 'integer' ],
86 ],
87 ],
88 'message' => [ 'type' => 'string' ],
89 ],
90 'required' => [ 'wordpress', 'php', 'database', 'multisite' ],
91 ],
92 'execute_callback' => function( $input = [] ) {
93 global $wp_version, $wpdb;
94
95 // --- WordPress core ---
96 if ( ! function_exists( 'get_core_updates' ) ) {
97 require_once ABSPATH . 'wp-admin/includes/update.php';
98 }
99
100 // Without this the cached result reports "Core is up to date" into a
101 // client-facing document on any site with a cold update cache.
102 wp_version_check();
103
104 $core_updates = get_core_updates();
105 $latest_version = $wp_version;
106 $update_available = false;
107 $update_type = 'none';
108
109 if ( is_array( $core_updates ) && ! empty( $core_updates ) ) {
110 foreach ( $core_updates as $update ) {
111 if ( isset( $update->response ) && $update->response === 'upgrade' && isset( $update->current ) ) {
112 $latest_version = $update->current;
113 $update_available = true;
114 // Determine major vs minor vs patch.
115 $current_parts = explode( '.', $wp_version );
116 $latest_parts = explode( '.', $latest_version );
117 if ( isset( $current_parts[0], $latest_parts[0] ) && $current_parts[0] !== $latest_parts[0] ) {
118 $update_type = 'major';
119 } elseif ( isset( $current_parts[1], $latest_parts[1] ) && ( ! isset( $current_parts[1] ) || ! isset( $latest_parts[1] ) || $current_parts[1] !== $latest_parts[1] ) ) {
120 $update_type = 'minor';
121 } else {
122 $update_type = 'patch';
123 }
124 break;
125 }
126 }
127 }
128
129 // WordPress auto-update preferences. WP exposes these via constants and filters.
130 $auto_updates_enabled = ! defined( 'AUTOMATIC_UPDATER_DISABLED' ) || ! AUTOMATIC_UPDATER_DISABLED;
131 // Core minor updates default-on in modern WP; respect explicit user opt-out.
132 if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) {
133 $auto_updates_enabled = (bool) WP_AUTO_UPDATE_CORE;
134 }
135
136 // --- PHP ---
137 // WordPress's recommended PHP minimum, surfaced in admin notices.
138 // Current as of WP 6.x: 7.4 absolute minimum, 8.1+ recommended.
139 // We expose WP's hard-floor (RECOMMENDED_PHP_VERSION) when defined.
140 $php_recommended = '';
141 if ( defined( 'WP_PHP_MIN_VERSION' ) ) {
142 $php_recommended = (string) WP_PHP_MIN_VERSION;
143 } elseif ( function_exists( 'wp_check_php_version' ) ) {
144 // wp_check_php_version() returns a structure with recommended_version
145 $check = wp_check_php_version();
146 if ( is_array( $check ) && isset( $check['recommended_version'] ) ) {
147 $php_recommended = (string) $check['recommended_version'];
148 }
149 }
150 if ( $php_recommended === '' ) {
151 // Fallback to the value WP has used as the floor for several years.
152 $php_recommended = '7.4';
153 }
154
155 $php_supported = version_compare( PHP_VERSION, $php_recommended, '>=' );
156
157 // --- Database ---
158 $db_version = '';
159 $db_type = '';
160 if ( $wpdb && method_exists( $wpdb, 'db_server_info' ) ) {
161 $db_version = (string) $wpdb->db_version();
162 $raw_info = (string) $wpdb->db_server_info();
163 $db_type = ( stripos( $raw_info, 'mariadb' ) !== false ) ? 'MariaDB' : 'MySQL';
164 } elseif ( $wpdb ) {
165 $db_version = (string) $wpdb->db_version();
166 $db_type = 'MySQL';
167 }
168
169 // --- Multisite ---
170 $is_multisite = is_multisite();
171 $multisite = [
172 'is_multisite' => $is_multisite,
173 'is_main_site' => $is_multisite ? is_main_site() : true,
174 'site_count' => $is_multisite ? (int) get_blog_count() : 1,
175 'network_id' => $is_multisite ? (int) get_current_network_id() : 0,
176 ];
177
178 $message = sprintf(
179 'WordPress %s on PHP %s, %s %s. %s',
180 $wp_version,
181 PHP_VERSION,
182 $db_type !== '' ? $db_type : 'database',
183 $db_version,
184 $update_available
185 ? sprintf( 'Core update available: %s (%s).', $latest_version, $update_type )
186 : 'Core is up to date.'
187 );
188
189 return [
190 'wordpress' => [
191 'version' => $wp_version,
192 'latest_version' => $latest_version,
193 'update_available' => $update_available,
194 'update_type' => $update_type,
195 'automatic_updates_enabled' => (bool) $auto_updates_enabled,
196 'language' => (string) get_locale(),
197 ],
198 'php' => [
199 'version' => PHP_VERSION,
200 'recommended_minimum' => $php_recommended,
201 'is_supported_by_wp' => $php_supported,
202 'memory_limit' => (string) ini_get( 'memory_limit' ),
203 'max_execution_time' => (int) ini_get( 'max_execution_time' ),
204 ],
205 'database' => [
206 'server_version' => $db_version,
207 'server_type' => $db_type,
208 'charset' => isset( $wpdb->charset ) ? (string) $wpdb->charset : '',
209 'collate' => isset( $wpdb->collate ) ? (string) $wpdb->collate : '',
210 ],
211 'multisite' => $multisite,
212 'message' => $message,
213 ];
214 },
215 'permission_callback' => function() {
216 // Reading environment info is moderately sensitive (PHP version, server type can
217 // help an attacker fingerprint the host). Gate on update_core which is held by
218 // administrators only, matching WP's own dashboard widget visibility.
219 return current_user_can( 'update_core' );
220 },
221 'meta' => [
222 'mcp' => [ 'public' => true, 'type' => 'tool' ],
223 'annotations' => [
224 'readonly' => true,
225 'destructive' => false,
226 'idempotent' => true,
227 ],
228 ],
229 ] );
230
231 // ---- check-core-updates ----
232 wp_register_ability( 'atarim/check-core-updates', [
233 'label' => 'Check For Core Updates',
234 'description' => 'Forces a fresh check with WordPress.org for core updates, bypassing the cached result (which can be up to ~12 hours stale), and reports whether an update is available. Use this to get an authoritative, up-to-the-minute answer before deciding whether to run update-core.',
235 'category' => 'atarim',
236 'input_schema' => [
237 'type' => 'object',
238 'properties' => new \stdClass(),
239 'additionalProperties' => false,
240 ],
241 'output_schema' => [
242 'type' => 'object',
243 'properties' => [
244 'current_version' => [ 'type' => 'string' ],
245 'latest_version' => [ 'type' => 'string' ],
246 'update_available' => [ 'type' => 'boolean' ],
247 'update_type' => [ 'type' => 'string' ],
248 'checked_at' => [ 'type' => 'string' ],
249 'message' => [ 'type' => 'string' ],
250 ],
251 'required' => [ 'current_version', 'latest_version', 'update_available', 'message' ],
252 ],
253 'execute_callback' => function( $input = [] ) {
254 global $wp_version;
255
256 if ( ! function_exists( 'get_core_updates' ) ) {
257 require_once ABSPATH . 'wp-admin/includes/update.php';
258 }
259
260 // Force a fresh remote check (bypasses the update_core transient).
261 wp_version_check( [], true );
262
263 $updates = get_core_updates();
264 $current_version = $wp_version;
265 $latest_version = $wp_version;
266 $update_available = false;
267 $update_type = 'none';
268
269 if ( is_array( $updates ) && ! empty( $updates ) ) {
270 foreach ( $updates as $update ) {
271 if ( isset( $update->response ) && $update->response === 'upgrade' && isset( $update->current ) ) {
272 $latest_version = (string) $update->current;
273 $update_available = true;
274 $update_type = $this->avcf_core_update_type( $current_version, $latest_version );
275 break;
276 }
277 }
278 }
279
280 return [
281 'current_version' => $current_version,
282 'latest_version' => $latest_version,
283 'update_available' => $update_available,
284 'update_type' => $update_type,
285 'checked_at' => gmdate( 'c' ),
286 'message' => $update_available
287 ? sprintf( 'Core update available: %s -> %s (%s).', $current_version, $latest_version, $update_type )
288 : 'WordPress core is up to date.',
289 ];
290 },
291 'permission_callback' => function() {
292 return current_user_can( 'update_core' );
293 },
294 'meta' => [
295 'mcp' => [ 'public' => true, 'type' => 'tool' ],
296 'annotations' => [
297 'readonly' => false,
298 'destructive' => false,
299 'idempotent' => true,
300 ],
301 ],
302 ] );
303
304 // ---- update-core ----
305 wp_register_ability( 'atarim/update-core', [
306 'label' => 'Update WordPress Core',
307 'description' => 'Updates WordPress core to the latest available update, or to a specific version that WordPress.org is currently offering for this site. Only offered versions are accepted — arbitrary version strings and downgrades are rejected. Runs the database upgrade routine afterward so the site is not left needing a manual DB update. High impact: on constrained hosts the download/extract can exceed max_execution_time (the update may still complete server-side). Requires direct filesystem access (no FTP/SSH credential prompts are possible here).',
308 'category' => 'atarim',
309 'input_schema' => [
310 'type' => 'object',
311 'properties' => [
312 'version' => [
313 'type' => 'string',
314 'description' => 'Optional. Pin to a specific version WordPress is currently offering as an update (e.g. "6.5.2"). If omitted, updates to the latest offered update. Rejected if the version is not an offered update.',
315 ],
316 'locale' => [
317 'type' => 'string',
318 'description' => 'Optional WordPress locale for the update package (e.g. "en_US"). Defaults to the site locale.',
319 ],
320 ],
321 'additionalProperties' => false,
322 ],
323 'output_schema' => [
324 'type' => 'object',
325 'properties' => [
326 'success' => [ 'type' => 'boolean' ],
327 'updated' => [ 'type' => 'boolean' ],
328 'from_version' => [ 'type' => 'string' ],
329 'to_version' => [ 'type' => 'string' ],
330 'update_type' => [ 'type' => 'string' ],
331 'db_upgraded' => [ 'type' => 'boolean' ],
332 'message' => [ 'type' => 'string' ],
333 ],
334 'required' => [ 'success', 'updated', 'message' ],
335 ],
336 'execute_callback' => function( $input = [] ) {
337 global $wp_version;
338
339 $pre = $this->avcf_core_update_preflight();
340 if ( $pre !== null ) {
341 return [ 'success' => false, 'updated' => false, 'message' => $pre ];
342 }
343
344 $locale = ( isset( $input['locale'] ) && $input['locale'] !== '' ) ? (string) $input['locale'] : get_locale();
345 $from_version = $wp_version;
346
347 // Act on current data.
348 wp_version_check( [], true );
349 $updates = get_core_updates();
350 if ( ! is_array( $updates ) || empty( $updates ) ) {
351 return [
352 'success' => true,
353 'updated' => false,
354 'from_version' => $from_version,
355 'to_version' => $from_version,
356 'update_type' => 'none',
357 'db_upgraded' => false,
358 'message' => 'No core updates are available; WordPress is up to date.',
359 ];
360 }
361
362 $want_version = isset( $input['version'] ) ? trim( (string) $input['version'] ) : '';
363 $target = null;
364 foreach ( $updates as $update ) {
365 if ( ! isset( $update->response ) || $update->response !== 'upgrade' || ! isset( $update->current ) ) {
366 continue;
367 }
368 if ( $want_version !== '' ) {
369 if ( (string) $update->current === $want_version ) {
370 $target = $update;
371 break;
372 }
373 } else {
374 $target = $update;
375 break;
376 }
377 }
378
379 if ( $target === null ) {
380 if ( $want_version !== '' ) {
381 return [
382 'success' => false,
383 'updated' => false,
384 'from_version' => $from_version,
385 'to_version' => $from_version,
386 'message' => sprintf( 'Version "%s" is not currently offered as a core update for this site. Use check-core-updates to see what is available.', $want_version ),
387 ];
388 }
389 return [
390 'success' => true,
391 'updated' => false,
392 'from_version' => $from_version,
393 'to_version' => $from_version,
394 'update_type' => 'none',
395 'db_upgraded' => false,
396 'message' => 'No core upgrade offer found; WordPress is up to date.',
397 ];
398 }
399
400 $to_version = (string) $target->current;
401 $update_type = $this->avcf_core_update_type( $from_version, $to_version );
402
403 // Prefer the requested locale's package if a different one is available.
404 if ( $locale !== '' && isset( $target->locale ) && $target->locale !== $locale ) {
405 $located = find_core_update( $to_version, $locale );
406 if ( $located ) {
407 $target = $located;
408 }
409 }
410
411 $skin = new \WP_Ajax_Upgrader_Skin();
412 $upgrader = new \Core_Upgrader( $skin );
413 $result = $upgrader->upgrade( $target );
414
415 if ( is_wp_error( $result ) ) {
416 return [
417 'success' => false,
418 'updated' => false,
419 'from_version' => $from_version,
420 'to_version' => $to_version,
421 'update_type' => $update_type,
422 'db_upgraded' => false,
423 'message' => 'Core update failed: ' . $result->get_error_message(),
424 ];
425 }
426 if ( $result === false ) {
427 return [
428 'success' => false,
429 'updated' => false,
430 'from_version' => $from_version,
431 'to_version' => $to_version,
432 'update_type' => $update_type,
433 'db_upgraded' => false,
434 'message' => 'Core update did not complete (the upgrader returned no result).',
435 ];
436 }
437
438 // Run the DB upgrade routine so the site is not left "needs database update".
439 $db_upgraded = false;
440 if ( ! function_exists( 'wp_upgrade' ) ) {
441 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
442 }
443 if ( function_exists( 'wp_upgrade' ) ) {
444 wp_upgrade();
445 $db_upgraded = true;
446 }
447
448 return [
449 'success' => true,
450 'updated' => true,
451 'from_version' => $from_version,
452 'to_version' => $to_version,
453 'update_type' => $update_type,
454 'db_upgraded' => $db_upgraded,
455 'message' => sprintf( 'WordPress updated from %s to %s (%s).', $from_version, $to_version, $update_type ),
456 ];
457 },
458 'permission_callback' => function() {
459 return current_user_can( 'update_core' );
460 },
461 'meta' => [
462 'mcp' => [ 'public' => true, 'type' => 'tool' ],
463 'annotations' => [
464 'readonly' => false,
465 'destructive' => false,
466 'idempotent' => false,
467 ],
468 ],
469 ] );
470
471 // ---- reinstall-core ----
472 wp_register_ability( 'atarim/reinstall-core', [
473 'label' => 'Reinstall WordPress Core',
474 'description' => 'Re-installs the currently running WordPress version to repair corrupted or modified core files (equivalent to the dashboard "Re-install now" button). Does not change the version. Requires direct filesystem access.',
475 'category' => 'atarim',
476 'input_schema' => [
477 'type' => 'object',
478 'properties' => [
479 'locale' => [
480 'type' => 'string',
481 'description' => 'Optional WordPress locale for the package (e.g. "en_US"). Defaults to the site locale.',
482 ],
483 ],
484 'additionalProperties' => false,
485 ],
486 'output_schema' => [
487 'type' => 'object',
488 'properties' => [
489 'success' => [ 'type' => 'boolean' ],
490 'reinstalled' => [ 'type' => 'boolean' ],
491 'version' => [ 'type' => 'string' ],
492 'message' => [ 'type' => 'string' ],
493 ],
494 'required' => [ 'success', 'reinstalled', 'message' ],
495 ],
496 'execute_callback' => function( $input = [] ) {
497 global $wp_version;
498
499 $pre = $this->avcf_core_update_preflight();
500 if ( $pre !== null ) {
501 return [ 'success' => false, 'reinstalled' => false, 'version' => $wp_version, 'message' => $pre ];
502 }
503
504 $locale = ( isset( $input['locale'] ) && $input['locale'] !== '' ) ? (string) $input['locale'] : get_locale();
505
506 // Refresh, then locate the package for the CURRENT version (reinstall).
507 wp_version_check( [], true );
508 $update = find_core_update( $wp_version, $locale );
509 if ( ! $update ) {
510 return [
511 'success' => false,
512 'reinstalled' => false,
513 'version' => $wp_version,
514 'message' => sprintf( 'No reinstall package is available for the current version (%s, locale %s).', $wp_version, $locale ),
515 ];
516 }
517
518 $skin = new \WP_Ajax_Upgrader_Skin();
519 $upgrader = new \Core_Upgrader( $skin );
520 $result = $upgrader->upgrade( $update );
521
522 if ( is_wp_error( $result ) ) {
523 return [
524 'success' => false,
525 'reinstalled' => false,
526 'version' => $wp_version,
527 'message' => 'Core reinstall failed: ' . $result->get_error_message(),
528 ];
529 }
530 if ( $result === false ) {
531 return [
532 'success' => false,
533 'reinstalled' => false,
534 'version' => $wp_version,
535 'message' => 'Core reinstall did not complete (the upgrader returned no result).',
536 ];
537 }
538
539 return [
540 'success' => true,
541 'reinstalled' => true,
542 'version' => $wp_version,
543 'message' => sprintf( 'WordPress %s core files re-installed successfully.', $wp_version ),
544 ];
545 },
546 'permission_callback' => function() {
547 return current_user_can( 'update_core' );
548 },
549 'meta' => [
550 'mcp' => [ 'public' => true, 'type' => 'tool' ],
551 'annotations' => [
552 'readonly' => false,
553 'destructive' => false,
554 'idempotent' => true,
555 ],
556 ],
557 ] );
558 }
559
560 /**
561 * Shared safety preflight for core file operations (update / reinstall).
562 * Loads the upgrader includes and blocks when the environment cannot safely
563 * perform a core file operation. Returns null when OK, or an error string.
564 */
565 private function avcf_core_update_preflight() {
566 if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) {
567 return 'File modifications are disabled on this site (DISALLOW_FILE_MODS), so core file operations are unavailable.';
568 }
569
570 if ( ! function_exists( 'get_filesystem_method' ) ) {
571 require_once ABSPATH . 'wp-admin/includes/file.php';
572 }
573 if ( function_exists( 'get_filesystem_method' ) && get_filesystem_method() !== 'direct' ) {
574 return 'The filesystem is not directly writable (FTP/SSH credentials would be required, which cannot be provided here). Core file operations are unavailable on this host configuration.';
575 }
576
577 if ( ! function_exists( 'get_core_updates' ) ) {
578 require_once ABSPATH . 'wp-admin/includes/update.php';
579 }
580 if ( ! class_exists( '\Core_Upgrader' ) ) {
581 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
582 }
583
584 // Give the download/extract more headroom where the host permits it.
585 @set_time_limit( 300 );
586
587 return null;
588 }
589
590 /**
591 * Classify a version delta as major / minor / patch.
592 */
593 private function avcf_core_update_type( $from, $to ) {
594 $f = explode( '.', (string) $from );
595 $t = explode( '.', (string) $to );
596 $f0 = isset( $f[0] ) ? $f[0] : '';
597 $t0 = isset( $t[0] ) ? $t[0] : '';
598 $f1 = isset( $f[1] ) ? $f[1] : '';
599 $t1 = isset( $t[1] ) ? $t[1] : '';
600 if ( $f0 !== $t0 ) {
601 return 'major';
602 }
603 if ( $f1 !== $t1 ) {
604 return 'minor';
605 }
606 return 'patch';
607 }
608 }