| 1 |
<?php |
| 2 |
|
| 3 |
namespace Better_Payment\Lite\Classes; |
| 4 |
|
| 5 |
if ( ! defined( 'ABSPATH' ) ) { |
| 6 |
exit; |
| 7 |
} |
| 8 |
|
| 9 |
/** |
| 10 |
* The Lite ↔ Pro compatibility contract. |
| 11 |
* |
| 12 |
* Pro-feature code is being moved out of Lite in stages — expand, migrate, |
| 13 |
* contract: a Lite release adds the hook, a Pro release takes the code over and |
| 14 |
* announces it, and only a later Lite release deletes Lite's copy. Two things |
| 15 |
* make that safe, and both live here. |
| 16 |
* |
| 17 |
* - provides(): Pro lists what it now ships itself on `better_payment/pro_provides`, |
| 18 |
* and Lite skips its own copy of that feature. Without it a transition release |
| 19 |
* registers the same Elementor control, stylesheet or schema twice — and a |
| 20 |
* duplicate Elementor control id is an editor error, not a cosmetic one. |
| 21 |
* - The outdated-Pro notice: neither plugin used to check the other's version, |
| 22 |
* so a site could run a new Lite beside a Pro that still expects code Lite has |
| 23 |
* removed. MIN_PRO_VERSION is raised in the Lite release that removes such code. |
| 24 |
* |
| 25 |
* Nothing an installed Pro calls directly (classes, methods, constants, partials) |
| 26 |
* may be removed from Lite until MIN_PRO_VERSION is past the Pro release that |
| 27 |
* stopped calling it. tests/Unit/ProCouplingContractTest.php lists those symbols |
| 28 |
* and fails if one disappears. |
| 29 |
* |
| 30 |
* @since 2.4.0 |
| 31 |
*/ |
| 32 |
final class ProCompatibility { |
| 33 |
|
| 34 |
/** |
| 35 |
* The oldest Better Payment Pro this Lite is known to work with. |
| 36 |
*/ |
| 37 |
const MIN_PRO_VERSION = '1.4.3'; |
| 38 |
|
| 39 |
/** |
| 40 |
* Wire the admin notice. |
| 41 |
* |
| 42 |
* Hooked on both notice actions: Admin::hide_admin_notices() strips |
| 43 |
* `admin_notices` on Better Payment screens and replays only |
| 44 |
* `better_payment_admin_notices` there, and that action never fires anywhere |
| 45 |
* else — so the notice prints exactly once on every screen. |
| 46 |
* |
| 47 |
* @return void |
| 48 |
*/ |
| 49 |
public static function register(): void { |
| 50 |
add_action( 'admin_notices', array( __CLASS__, 'render_outdated_pro_notice' ) ); |
| 51 |
add_action( 'better_payment_admin_notices', array( __CLASS__, 'render_outdated_pro_notice' ) ); |
| 52 |
} |
| 53 |
|
| 54 |
/** |
| 55 |
* Whether the active Pro ships its own copy of a feature. |
| 56 |
* |
| 57 |
* Entitlement is read live on every call, like every other Pro gate, so a |
| 58 |
* deactivated Pro immediately hands the feature back to Lite. |
| 59 |
* |
| 60 |
* @param string $feature Feature slug, e.g. 'campaign_theme_styles'. |
| 61 |
* @return bool |
| 62 |
*/ |
| 63 |
public static function provides( string $feature ): bool { |
| 64 |
$features = apply_filters( 'better_payment/pro_provides', array() ); |
| 65 |
|
| 66 |
return is_array( $features ) && in_array( $feature, $features, true ); |
| 67 |
} |
| 68 |
|
| 69 |
/** |
| 70 |
* Whether Pro is active on this request. |
| 71 |
* |
| 72 |
* @return bool |
| 73 |
*/ |
| 74 |
public static function pro_enabled(): bool { |
| 75 |
return (bool) apply_filters( 'better_payment/pro_enabled', false ); |
| 76 |
} |
| 77 |
|
| 78 |
/** |
| 79 |
* Whether Lite's transitional copy of a Pro feature should run. |
| 80 |
* |
| 81 |
* True only for an active Pro that does not ship the feature itself — an |
| 82 |
* older Pro during a hand-off. A free install never runs a Lite copy of Pro |
| 83 |
* code: without Pro there is nothing to serve, and with a current Pro, Pro |
| 84 |
* serves it. Every Lite copy of Pro code gates on this, never on provides() |
| 85 |
* alone (`! provides()` is true on every free install). |
| 86 |
* |
| 87 |
* @param string $feature Feature slug. |
| 88 |
* @return bool |
| 89 |
*/ |
| 90 |
public static function lite_copy_applies( string $feature ): bool { |
| 91 |
return self::pro_enabled() && ! self::provides( $feature ); |
| 92 |
} |
| 93 |
|
| 94 |
/** |
| 95 |
* The installed Pro version, or '' when Pro is not loaded. |
| 96 |
* |
| 97 |
* @return string |
| 98 |
*/ |
| 99 |
public static function pro_version(): string { |
| 100 |
return defined( 'BETTER_PAYMENT_PRO_VERSION' ) ? (string) BETTER_PAYMENT_PRO_VERSION : ''; |
| 101 |
} |
| 102 |
|
| 103 |
/** |
| 104 |
* Whether a Pro version is older than this Lite supports. |
| 105 |
* |
| 106 |
* An empty version means Pro is not installed, which is not "outdated". |
| 107 |
* |
| 108 |
* @param string $pro_version Installed Pro version. |
| 109 |
* @param string $minimum Oldest supported version. |
| 110 |
* @return bool |
| 111 |
*/ |
| 112 |
public static function is_pro_outdated( string $pro_version, string $minimum = self::MIN_PRO_VERSION ): bool { |
| 113 |
if ( '' === $pro_version ) { |
| 114 |
return false; |
| 115 |
} |
| 116 |
|
| 117 |
return version_compare( $pro_version, $minimum, '<' ); |
| 118 |
} |
| 119 |
|
| 120 |
/** |
| 121 |
* Print the "update Better Payment Pro" notice when it applies. |
| 122 |
* |
| 123 |
* @return void |
| 124 |
*/ |
| 125 |
public static function render_outdated_pro_notice(): void { |
| 126 |
$pro_version = self::pro_version(); |
| 127 |
|
| 128 |
if ( ! self::is_pro_outdated( $pro_version ) || ! current_user_can( 'update_plugins' ) ) { |
| 129 |
return; |
| 130 |
} |
| 131 |
|
| 132 |
printf( |
| 133 |
'<div class="notice notice-warning better-payment-pro-outdated-notice"><p>%s</p></div>', |
| 134 |
esc_html( |
| 135 |
sprintf( |
| 136 |
/* translators: 1: installed Better Payment Pro version, 2: minimum supported version. */ |
| 137 |
__( 'Better Payment Pro %1$s is older than this version of Better Payment supports. Please update Better Payment Pro to %2$s or newer so every Pro feature keeps working.', 'better-payment' ), |
| 138 |
$pro_version, |
| 139 |
self::MIN_PRO_VERSION |
| 140 |
) |
| 141 |
) |
| 142 |
); |
| 143 |
} |
| 144 |
} |
| 145 |
|