wizard
6 days ago
class-addons-template-util.php
6 days ago
class-allowed-template-html-util.php
6 days ago
class-base-exception.php
6 days ago
class-cache-constants.php
6 days ago
class-cache-exception.php
6 days ago
class-engagement.php
6 days ago
class-key-exception.php
6 days ago
class-keytypes.php
6 days ago
class-option-exception.php
6 days ago
class-quiet-skin.php
6 days ago
class-request-exception.php
6 days ago
class-script-translations.php
6 days ago
class-settings-exception.php
6 days ago
class-theme-installer-exception.php
6 days ago
class-theme-installer.php
6 days ago
class-third-party-compatibility-util.php
6 days ago
class-third-party-compatibility-util.php
109 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SuperbAddons\Data\Utils; |
| 4 | |
| 5 | use SuperbAddons\Data\Controllers\LogController; |
| 6 | |
| 7 | defined('ABSPATH') || exit(); |
| 8 | |
| 9 | /** |
| 10 | * Compatibility helpers for coexisting with third-party plugins inside our own |
| 11 | * synthetic rendering contexts (e.g. the wizard template preview canvas), where |
| 12 | * the usual front-end page-load state is not fully set up. |
| 13 | * |
| 14 | * Two layers: |
| 15 | * - guard_preview_render(): registers shims for specific, known conflicts that |
| 16 | * we can fully fix so the preview renders completely. |
| 17 | * - run_guarded(): a generic safety net so that an uncaught error from ANY |
| 18 | * third-party hook degrades gracefully instead of white-screening the |
| 19 | * preview. |
| 20 | * |
| 21 | * Everything here only affects the short-lived, admin-only preview request, so |
| 22 | * normal site pages are never touched. |
| 23 | */ |
| 24 | class ThirdPartyCompatibilityUtil |
| 25 | { |
| 26 | /** |
| 27 | * Register shims for known, fully-fixable third-party conflicts. |
| 28 | * |
| 29 | * Call this immediately before wp_head() in a synthetic render context. |
| 30 | */ |
| 31 | public static function guard_preview_render() |
| 32 | { |
| 33 | self::guard_gutenverse_conditional_handles(); |
| 34 | } |
| 35 | |
| 36 | /** |
| 37 | * Run a synthetic-render callback (e.g. wp_head / wp_body_open / wp_footer) |
| 38 | * with a safety net around any uncaught error a third-party hook may throw. |
| 39 | * |
| 40 | * In our preview we call wp_head()/wp_footer() ourselves, so a TypeError or |
| 41 | * other Error thrown by some plugin's enqueue/print callback propagates back |
| 42 | * up to our call site. Without this it would abort the whole preview (a |
| 43 | * "critical error" white screen). Catching it lets the rest of the canvas |
| 44 | * render and records the offender so a targeted shim can be added later. |
| 45 | * |
| 46 | * Errors originating from our own plugin are re-thrown: this is strictly a |
| 47 | * third-party safety net, not a blanket error suppressor for our own bugs. |
| 48 | * |
| 49 | * @param callable $callback Output-producing callback to execute. |
| 50 | */ |
| 51 | public static function run_guarded($callback) |
| 52 | { |
| 53 | try { |
| 54 | call_user_func($callback); |
| 55 | } catch (\Throwable $e) { |
| 56 | // PHP 7+. On PHP 5.6 this class of failure is a warning, not a |
| 57 | // throwable, so there is nothing to catch and \Throwable never matches. |
| 58 | self::handle_render_throwable($e); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * Decide what to do with an error caught during a synthetic render. |
| 64 | * |
| 65 | * @param \Throwable $e |
| 66 | */ |
| 67 | private static function handle_render_throwable($e) |
| 68 | { |
| 69 | // Never hide our own bugs: if the failure originates inside our plugin, re-throw. |
| 70 | if (defined('SUPERBADDONS_PLUGIN_DIR')) { |
| 71 | $file = wp_normalize_path($e->getFile()); |
| 72 | $plugin_dir = wp_normalize_path(SUPERBADDONS_PLUGIN_DIR); |
| 73 | if ($plugin_dir !== '' && strpos($file, $plugin_dir) === 0) { |
| 74 | throw $e; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | // Third-party fatal during our preview: record it and let the render continue. |
| 79 | LogController::HandleException($e); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Gutenverse compatibility. |
| 84 | * |
| 85 | * Frontend_Generator::load_conditional_scripts() and ::load_conditional_styles() |
| 86 | * run array_unique() on the result of the gutenverse_conditional_script_handles / |
| 87 | * gutenverse_conditional_style_handles filters. Gutenverse's own Frontend_Cache |
| 88 | * hooks those filters (at priority 0) and, in its default 'file' render mechanism, |
| 89 | * returns json_decode() of cached content, which is null when there is no cache for |
| 90 | * our synthetic preview. array_unique(null) throws a TypeError on PHP 8 and aborts |
| 91 | * the whole preview. |
| 92 | * |
| 93 | * We hook the same filters at PHP_INT_MAX (the highest priority, so our callback |
| 94 | * runs LAST, after Gutenverse's own cache callback) and coerce any non-array value |
| 95 | * to an empty array. On a healthy render the value is already an array and passes |
| 96 | * through unchanged, so this only ever intervenes in the broken case. |
| 97 | */ |
| 98 | private static function guard_gutenverse_conditional_handles() |
| 99 | { |
| 100 | add_filter('gutenverse_conditional_script_handles', array(__CLASS__, '_ensure_array'), PHP_INT_MAX); |
| 101 | add_filter('gutenverse_conditional_style_handles', array(__CLASS__, '_ensure_array'), PHP_INT_MAX); |
| 102 | } |
| 103 | |
| 104 | public static function _ensure_array($value) |
| 105 | { |
| 106 | return is_array($value) ? $value : array(); |
| 107 | } |
| 108 | } |
| 109 |