| 1 |
<?php |
| 2 |
/** |
| 3 |
* POSIMYTH Analytics SDK — version-aware loader. |
| 4 |
* |
| 5 |
* Every POSIMYTH plugin ships its own copy of the shared SDK classes, each wrapped in a |
| 6 |
* class_exists guard. That guard alone made load ORDER decide the winner: whichever plugin |
| 7 |
* WordPress happened to include first defined Posimyth_Tracker_Base for everyone, so one outdated |
| 8 |
* sibling could silently downgrade the whole suite — e.g. an older do_request() without the |
| 9 |
* $bypass_consent parameter swallowed the deactivation survey's third argument and its feedback |
| 10 |
* never sent, with no error anywhere. |
| 11 |
* |
| 12 |
* So no plugin requires the shared classes directly any more. Each one calls |
| 13 |
* posimyth_sdk_register() with its bundled copy's directory; at plugins_loaded priority 0 — before |
| 14 |
* every consumer, which runs at priority 10 — the loader requires the files from the NEWEST |
| 15 |
* registered copy, as declared by that copy's version.php. Per-product subclasses are not shared |
| 16 |
* and are loaded by their own plugin after the winner exists. |
| 17 |
* |
| 18 |
* The loader itself must stay tiny and boring: it is duplicated into every plugin too, and only |
| 19 |
* the first copy's functions define. Change it only in a backwards-compatible way. |
| 20 |
* |
| 21 |
* @package POSIMYTH\Analytics\SDK |
| 22 |
*/ |
| 23 |
|
| 24 |
if ( ! defined( 'ABSPATH' ) ) { |
| 25 |
exit; |
| 26 |
} |
| 27 |
|
| 28 |
if ( ! function_exists( 'posimyth_sdk_register' ) ) { |
| 29 |
|
| 30 |
/** |
| 31 |
* Registry of every bundled SDK copy seen this request. |
| 32 |
* |
| 33 |
* Static-in-function rather than a global so nothing outside these helpers can corrupt it. |
| 34 |
* |
| 35 |
* @param array|null $add Optional array{dir:string,version:string} to record. |
| 36 |
* @return array<string,string> Map of directory => version. |
| 37 |
*/ |
| 38 |
function posimyth_sdk_registry( ?array $add = null ): array { |
| 39 |
static $dirs = array(); |
| 40 |
if ( null !== $add ) { |
| 41 |
$dirs[ $add['dir'] ] = $add['version']; |
| 42 |
} |
| 43 |
return $dirs; |
| 44 |
} |
| 45 |
|
| 46 |
/** |
| 47 |
* The directory holding the highest-versioned copy. Ties keep the first registered — the |
| 48 |
* copies are byte-identical apart from their text domain, so a tie has no wrong answer. |
| 49 |
* |
| 50 |
* @param array<string,string> $dirs Map of directory => version. |
| 51 |
* @return string|null |
| 52 |
*/ |
| 53 |
function posimyth_sdk_pick_latest( array $dirs ): ?string { |
| 54 |
$best_dir = null; |
| 55 |
$best_version = '0'; |
| 56 |
foreach ( $dirs as $dir => $version ) { |
| 57 |
if ( null === $best_dir || version_compare( $version, $best_version, '>' ) ) { |
| 58 |
$best_dir = $dir; |
| 59 |
$best_version = $version; |
| 60 |
} |
| 61 |
} |
| 62 |
return $best_dir; |
| 63 |
} |
| 64 |
|
| 65 |
/** |
| 66 |
* Requires the shared SDK classes from the newest registered copy. Idempotent: once the base |
| 67 |
* class exists (from any copy), later calls do nothing. |
| 68 |
*/ |
| 69 |
function posimyth_sdk_load_latest(): void { |
| 70 |
if ( class_exists( 'Posimyth_Tracker_Base', false ) ) { |
| 71 |
return; |
| 72 |
} |
| 73 |
|
| 74 |
$dir = posimyth_sdk_pick_latest( posimyth_sdk_registry() ); |
| 75 |
if ( null === $dir ) { |
| 76 |
return; |
| 77 |
} |
| 78 |
|
| 79 |
$shared = array( |
| 80 |
'class-posimyth-tracker-base.php', |
| 81 |
'class-posimyth-consent-notice.php', |
| 82 |
'class-posimyth-deactivation-survey.php', |
| 83 |
); |
| 84 |
foreach ( $shared as $file ) { |
| 85 |
$path = $dir . '/' . $file; |
| 86 |
if ( is_readable( $path ) ) { |
| 87 |
require_once $path; |
| 88 |
} |
| 89 |
} |
| 90 |
} |
| 91 |
|
| 92 |
/** |
| 93 |
* Records a plugin's bundled SDK copy and arranges for the newest one to load. |
| 94 |
* |
| 95 |
* Normally that happens once at plugins_loaded priority 0. When a plugin is included AFTER |
| 96 |
* plugins_loaded has already fired — which is exactly what happens during its own activation |
| 97 |
* request — every already-active sibling has registered by then, so loading immediately is |
| 98 |
* both safe and required (the activation ping needs the classes in that same request). |
| 99 |
* |
| 100 |
* @param string $dir Absolute path of this plugin's posimyth-sdk directory (no trailing slash needed). |
| 101 |
*/ |
| 102 |
function posimyth_sdk_register( string $dir ): void { |
| 103 |
$dir = rtrim( $dir, '/\\' ); |
| 104 |
|
| 105 |
$version_file = $dir . '/version.php'; |
| 106 |
$version = is_readable( $version_file ) ? (string) include $version_file : '0.0.0'; |
| 107 |
|
| 108 |
posimyth_sdk_registry( |
| 109 |
array( |
| 110 |
'dir' => $dir, |
| 111 |
'version' => $version, |
| 112 |
) |
| 113 |
); |
| 114 |
|
| 115 |
if ( did_action( 'plugins_loaded' ) ) { |
| 116 |
posimyth_sdk_load_latest(); |
| 117 |
return; |
| 118 |
} |
| 119 |
|
| 120 |
if ( false === has_action( 'plugins_loaded', 'posimyth_sdk_load_latest' ) ) { |
| 121 |
add_action( 'plugins_loaded', 'posimyth_sdk_load_latest', 0 ); |
| 122 |
} |
| 123 |
} |
| 124 |
} |
| 125 |
|