| 1 |
<?php |
| 2 |
// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- Shared onboarding loader keeps legacy cpo_* public APIs for compatibility. |
| 3 |
/** |
| 4 |
* Cool Plugins Onboarding Framework — versioned loader. |
| 5 |
* |
| 6 |
* This file is bundled IDENTICALLY inside every plugin that ships the |
| 7 |
* framework. WordPress.org has no shared-dependency mechanism, so several |
| 8 |
* active plugins may each carry their own copy of this framework at different |
| 9 |
* versions. Loading two copies would fatal with "Cannot redeclare class". |
| 10 |
* |
| 11 |
* The fix: every copy registers itself in a global list with its version and |
| 12 |
* path. On `after_setup_theme` (priority 1, before plugins build their UI) we |
| 13 |
* pick the HIGHEST version present and load only that one. Older plugins then |
| 14 |
* transparently run on the newest framework, so a single framework fix ships |
| 15 |
* to all of them as soon as any one plugin updates. |
| 16 |
* |
| 17 |
* Each plugin calls: |
| 18 |
* |
| 19 |
* require_once __DIR__ . '/cool-plugins-onboarding/loader.php'; |
| 20 |
* cpo_onboarding_register( '1.0.0', __DIR__ . '/cool-plugins-onboarding' ); |
| 21 |
* |
| 22 |
* ...then, once loaded, instantiates the framework with its own config (see |
| 23 |
* examples/cool-timeline.php). |
| 24 |
* |
| 25 |
* @package CoolPlugins\Onboarding |
| 26 |
*/ |
| 27 |
|
| 28 |
if ( ! defined( 'ABSPATH' ) ) { |
| 29 |
exit; |
| 30 |
} |
| 31 |
|
| 32 |
if ( ! function_exists( 'cpo_onboarding_register' ) ) { |
| 33 |
|
| 34 |
/** |
| 35 |
* Register a bundled framework copy as a load candidate. |
| 36 |
* |
| 37 |
* @param string $version Semantic version of THIS bundled copy. |
| 38 |
* @param string $base_dir Absolute path to the framework package directory |
| 39 |
* (the folder containing /framework). |
| 40 |
* @return void |
| 41 |
*/ |
| 42 |
function cpo_onboarding_register( $version, $base_dir ) { |
| 43 |
if ( ! isset( $GLOBALS['cpo_onboarding_candidates'] ) ) { |
| 44 |
$GLOBALS['cpo_onboarding_candidates'] = array(); |
| 45 |
} |
| 46 |
|
| 47 |
$GLOBALS['cpo_onboarding_candidates'][] = array( |
| 48 |
'version' => (string) $version, |
| 49 |
'bootstrap' => rtrim( $base_dir, '/\\' ) . '/framework/bootstrap.php', |
| 50 |
); |
| 51 |
|
| 52 |
// Register the resolver once. Priority 1 so the framework is available |
| 53 |
// before plugins register menus/assets on admin_menu (priority 9+). |
| 54 |
if ( ! has_action( 'after_setup_theme', 'cpo_onboarding_resolve' ) ) { |
| 55 |
add_action( 'after_setup_theme', 'cpo_onboarding_resolve', 1 ); |
| 56 |
} |
| 57 |
} |
| 58 |
|
| 59 |
/** |
| 60 |
* Pick the highest registered version and load it exactly once. |
| 61 |
* |
| 62 |
* @return void |
| 63 |
*/ |
| 64 |
function cpo_onboarding_resolve() { |
| 65 |
if ( defined( 'CPO_ONBOARDING_VERSION' ) ) { |
| 66 |
return; // Already resolved this request. |
| 67 |
} |
| 68 |
|
| 69 |
$candidates = isset( $GLOBALS['cpo_onboarding_candidates'] ) |
| 70 |
? $GLOBALS['cpo_onboarding_candidates'] |
| 71 |
: array(); |
| 72 |
|
| 73 |
if ( empty( $candidates ) ) { |
| 74 |
return; |
| 75 |
} |
| 76 |
|
| 77 |
usort( |
| 78 |
$candidates, |
| 79 |
static function ( $a, $b ) { |
| 80 |
return version_compare( $b['version'], $a['version'] ); |
| 81 |
} |
| 82 |
); |
| 83 |
|
| 84 |
foreach ( $candidates as $winner ) { |
| 85 |
if ( is_readable( $winner['bootstrap'] ) ) { |
| 86 |
define( 'CPO_ONBOARDING_VERSION', $winner['version'] ); |
| 87 |
require_once $winner['bootstrap']; |
| 88 |
|
| 89 |
/** |
| 90 |
* Fires once the framework is loaded and its classes are |
| 91 |
* available. Plugins hook here to instantiate the framework |
| 92 |
* with their own config. |
| 93 |
* |
| 94 |
* @param string $version The loaded framework version. |
| 95 |
*/ |
| 96 |
do_action( 'cpo_onboarding_loaded', $winner['version'] ); |
| 97 |
return; |
| 98 |
} |
| 99 |
} |
| 100 |
} |
| 101 |
} |
| 102 |
|