PluginProbe
FunnelKit – Funnel Builder for WooCommerce Checkout / trunk
FunnelKit – Funnel Builder for WooCommerce Checkout vtrunk
3.16.0.5 3.16.0.4 3.16.0.3 3.16.0.2 3.16.0 3.16.0.1 3.15.0.9 3.15.0.8 3.0.0.1 3.0.1.1 3.0.2.1 3.1.0.1 3.1.1.1 3.1.2.1 3.10.0.1 3.10.1.1 3.10.2.1 3.11.0.0.1 3.11.0.1.1 3.11.0.2.1 3.11.1.1 3.12.0.0.1 3.12.0.1.1 3.13.0.0.1 3.13.1.0.1 All 219 releases
funnel-builder / start.php

start.php in FunnelKit – Funnel Builder for WooCommerce Checkout trunk, at start.php

287 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly
4 }
5
6 /**
7 * This file is to initiate WooFunnel core and to run some common methods and decide which WooFunnels core should run
8 */
9 if ( ! class_exists( 'WooFunnel_Loader' ) ) {
10 #[AllowDynamicProperties]
11
12 class WooFunnel_Loader {
13
14 public static $plugins = array();
15 public static $loaded = false;
16 public static $ultimate_path = '';
17 public static $version = null;
18
19
20 public static function include_core() {
21
22 $get_configuration = self::get_the_latest();
23 if ( false === self::$loaded && $get_configuration && is_array( $get_configuration ) && isset( $get_configuration['class'] ) ) {
24
25 if ( is_callable( array( $get_configuration['class'], 'load_files' ) ) ) {
26 self::$version = $get_configuration['version'];
27 self::$ultimate_path = $get_configuration['plugin_path'] . '/woofunnels/';
28 self::$loaded = true;
29 call_user_func( array( $get_configuration['class'], 'load_files' ) );
30
31 }
32 }
33 }
34
35 public static function register( $configuration ) {
36 array_push( self::$plugins, $configuration );
37 }
38
39 public static function get_the_latest() {
40 $get_all = self::$plugins;
41 uasort(
42 $get_all,
43 function ( $a, $b ) {
44 if ( version_compare( $a['version'], $b['version'], '=' ) ) {
45 return 0;
46 } else {
47 return ( version_compare( $a['version'], $b['version'], '<' ) ) ? - 1 : 1;
48 }
49 }
50 );
51
52 $get_most_recent_configuration = end( $get_all );
53
54 return $get_most_recent_configuration;
55 }
56 }
57 }
58
59
60 #[AllowDynamicProperties]
61
62 class WooFunnel_WFFN {
63
64 public static $version = WFFN_BWF_VERSION;
65
66 /**
67 * Newest full (UN-dismantled) WooFunnels core that counts as abandoned.
68 *
69 * Some plugins bundled a WooFunnels core once and then stopped shipping
70 * updates for it. CartHopper (woofunnels-carthopper-skip-cart) is the known
71 * case: its core is frozen at exactly 1.9.90 while every maintained core is on
72 * the 1.10.x line. Yielding to a core that old would downgrade the whole site,
73 * so any full core at or below this version is dropped from the loader pool
74 * and our dismantled core runs instead.
75 *
76 * Compared with '<=', not '<' -- 1.9.90 is CartHopper's own version, the one we
77 * are excluding, not the first version we accept.
78 *
79 * @var string
80 */
81 const MAX_ABANDONED_FULL_CORE_VERSION = '1.10.0';
82
83 public static function register() {
84
85 $configuration = array(
86 'basename' => plugin_basename( WFFN_PLUGIN_FILE ),
87 'version' => self::$version,
88 'plugin_path' => dirname( WFFN_PLUGIN_FILE ),
89 'class' => __CLASS__,
90 /**
91 * Marks this as a dismantled core (the FunnelKit common-core split:
92 * contact / compatibilities / usage-tracking / includes + fb/). Cores
93 * shipped by un-dismantled plugins do not carry this flag; that is how
94 * maybe_yield_to_full_core() tells them apart during the migration window.
95 */
96 'dismantled' => true,
97 );
98 WooFunnel_Loader::register( $configuration );
99 }
100
101 /**
102 * Transition arbitration.
103 *
104 * While any other plugin still ships an UN-dismantled (full) WooFunnels core,
105 * our minimal core must not win: it no longer ships every class those plugins
106 * hardcode-require (e.g. WooFunnels_Notifications, WooFunnels_Deactivate). So if
107 * any registered core lacks the 'dismantled' marker, withdraw our own
108 * registration and let the full core win include_core() -- we ride it (our fb/
109 * classes still load directly on woofunnels_loaded). Once every core is
110 * dismantled (all carry the marker) nobody withdraws and normal newest-wins
111 * arbitration resumes.
112 *
113 * The yield is version-gated: only full cores newer than
114 * MAX_ABANDONED_FULL_CORE_VERSION are worth riding. Anything at or below it is
115 * an abandoned leftover (see the constant) and is evicted from the pool so it
116 * cannot load at all. So there are three outcomes:
117 * - no full core at all -> stay registered, newest-wins as usual
118 * - maintained full core -> withdraw, it loads, we ride it
119 * - abandoned full core -> evict it, stay registered, our core loads
120 *
121 * Hooked on plugins_loaded at PHP_INT_MIN, ahead of every include_core() call in
122 * the request (ours at -1, and siblings that call it themselves -- CartHopper
123 * does, from WFCH_Common::plugins_loaded, also -1). Lives here (our registrant),
124 * never in the shared WooFunnel_Loader class.
125 */
126 public static function maybe_yield_to_full_core() {
127 if ( ! class_exists( 'WooFunnel_Loader' ) || ! is_array( WooFunnel_Loader::$plugins ) || WooFunnel_Loader::$loaded ) {
128 return;
129 }
130
131 /**
132 * Full cores shipped by plugins that register LATE are not in the pool yet.
133 * FunnelKit Automations, for example, requires its start.php on
134 * plugins_loaded:1 -- after this hook and after include_core (-1). If we
135 * only looked at the pool we would not see it, win with our minimal core, and
136 * it would fail (its connector/ framework, WFCO_Common, only exists in its own
137 * core). So force those late cores to register NOW by requiring their start.php,
138 * so include_core (-1) can pick the full core and load it before our own
139 * load_classes (also plugins_loaded:1) needs it. Requiring them also puts their
140 * version on the table, which is what the eviction below needs.
141 */
142 if ( false === self::full_core_in_pool() ) {
143 foreach ( self::undismantled_core_start_files() as $start_file ) {
144 require_once $start_file; // runs the sibling's WooFunnel_*::register()
145 }
146 }
147
148 self::drop_abandoned_full_cores();
149
150 if ( false === self::full_core_in_pool() ) {
151 return; // Nothing left to yield to -- compete on version as usual.
152 }
153
154 foreach ( WooFunnel_Loader::$plugins as $key => $config ) {
155 if ( isset( $config['class'] ) && __CLASS__ === $config['class'] ) {
156 unset( WooFunnel_Loader::$plugins[ $key ] );
157 }
158 }
159 WooFunnel_Loader::$plugins = array_values( WooFunnel_Loader::$plugins );
160 }
161
162 /**
163 * @return bool Whether any already-registered core lacks the 'dismantled' marker.
164 */
165 private static function full_core_in_pool() {
166 foreach ( WooFunnel_Loader::$plugins as $config ) {
167 if ( empty( $config['dismantled'] ) ) {
168 return true;
169 }
170 }
171
172 return false;
173 }
174
175 /**
176 * Remove every registered full (UN-dismantled) core at or below
177 * MAX_ABANDONED_FULL_CORE_VERSION from the loader pool.
178 *
179 * Dropping the configuration is what stops it: include_core() only ever calls
180 * load_files() on the winner it picks out of WooFunnel_Loader::$plugins, so a
181 * core that is not in the pool never boots. Timing works because this runs on
182 * plugins_loaded at PHP_INT_MIN, ahead of every include_core() call in the
183 * request -- ours at -1 and the siblings' own (CartHopper calls it directly from
184 * WFCH_Common::plugins_loaded, also -1). And if the evicted plugin registers
185 * again afterwards, that registration is inert: include_core has already run and
186 * flipped WooFunnel_Loader::$loaded.
187 *
188 * Configurations with no version string are left alone: we cannot tell how old
189 * they are, and wrongly evicting a live full core is the more expensive mistake.
190 */
191 private static function drop_abandoned_full_cores() {
192 $changed = false;
193 foreach ( WooFunnel_Loader::$plugins as $key => $config ) {
194 if ( ! empty( $config['dismantled'] ) || empty( $config['version'] ) ) {
195 continue;
196 }
197 if ( version_compare( $config['version'], self::MAX_ABANDONED_FULL_CORE_VERSION, '<=' ) ) {
198 unset( WooFunnel_Loader::$plugins[ $key ] );
199 $changed = true;
200 }
201 }
202
203 if ( true === $changed ) {
204 WooFunnel_Loader::$plugins = array_values( WooFunnel_Loader::$plugins );
205 }
206 }
207
208 /**
209 * Folders an UN-dismantled WooFunnels core ships that our dismantled core does NOT.
210 *
211 * The dismantle stripped these from FB's woofunnels/: the connector framework
212 * (connector/, home of WFCO_Common), the custom Action Scheduler store
213 * (as-data-store/), the pre-kernel bootstrap loader (bootstrap/), and the licence
214 * UI (legacy/). A plugin whose woofunnels/ ships ANY of them is running a full,
215 * un-dismantled core -- and MUST win arbitration so those classes exist. We probe
216 * on the whole set (not a single folder) because different un-dismantled core
217 * versions carry different subsets: e.g. FunnelKit Automations' current core has
218 * connector/ + as-data-store/ but NO bootstrap/ (kernel already folded elsewhere),
219 * so a bootstrap-only probe misses it and our minimal core wrongly wins, fataling
220 * WFCO_Common.
221 */
222 private static function full_core_folder_markers() {
223 return array( 'connector', 'as-data-store', 'bootstrap', 'legacy' );
224 }
225
226 /**
227 * Active plugins (other than us) that ship an UN-dismantled WooFunnels core which
228 * has not registered yet (e.g. FunnelKit Automations registers late, on
229 * plugins_loaded:1). Detected by the full-core folder markers above; their start.php
230 * sits at the plugin root.
231 *
232 * @return string[] Absolute paths to those start.php files.
233 */
234 private static function undismantled_core_start_files() {
235 $files = array();
236
237 $plugins = array();
238 if ( function_exists( 'wp_get_active_and_valid_plugins' ) ) {
239 $plugins = (array) wp_get_active_and_valid_plugins();
240 }
241 if ( function_exists( 'wp_get_active_network_plugins' ) ) {
242 $plugins = array_merge( $plugins, (array) wp_get_active_network_plugins() );
243 }
244
245 $our_dir = dirname( WFFN_PLUGIN_FILE );
246 $markers = self::full_core_folder_markers();
247 foreach ( $plugins as $plugin_file ) {
248 $dir = dirname( $plugin_file );
249 if ( $dir === $our_dir || ! is_readable( $dir . '/start.php' ) ) {
250 continue;
251 }
252 foreach ( $markers as $marker ) {
253 if ( is_dir( $dir . '/woofunnels/' . $marker ) ) {
254 $files[] = $dir . '/start.php';
255 break;
256 }
257 }
258 }
259
260 return $files;
261 }
262
263
264 public static function load_files() {
265
266 $get_global_path = __DIR__ . '/woofunnels/';
267
268 if ( false === @file_exists( $get_global_path . 'includes/class-woofunnels-api.php' ) ) {
269 _doing_it_wrong( __FUNCTION__, esc_html__( 'WooFunnels Core should be present in folder \'woofunnels\' in order to run this properly. ', 'funnel-builder' ), self::$version );
270 die( 0 );
271 }
272
273 /**
274 * Loading Core XL Files
275 */
276 require_once dirname( WFFN_PLUGIN_FILE ) . '/woofunnels/' . 'includes/class-woofunnels-dashboard-loader.php';
277
278 if ( self::$version === BWF_VERSION ) {
279 do_action( 'woofunnels_loaded', $get_global_path );
280 } elseif ( ( defined( 'WFFN_IS_DEV' ) && true === WFFN_IS_DEV ) || ( defined( 'BWF_DEV' ) && true === BWF_DEV ) ) {
281 _doing_it_wrong( __FUNCTION__, esc_html__( 'WooFunnels Core should be at the same version as declared in your start.php', 'funnel-builder' ), self::$version );
282 die( 0 );
283 }
284 }
285 }
286
287 WooFunnel_WFFN::register();