Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | include_once( 'class.jetpack-admin-page.php' ); |
||
| 3 | |||
| 4 | // Builds the landing page and its menu |
||
| 5 | class Jetpack_React_Page extends Jetpack_Admin_Page { |
||
| 6 | |||
| 7 | protected $dont_show_if_not_active = false; |
||
| 8 | |||
| 9 | protected $is_redirecting = false; |
||
| 10 | |||
| 11 | function get_page_hook() { |
||
| 12 | $title = _x( 'Jetpack', 'The menu item label', 'jetpack' ); |
||
| 13 | |||
| 14 | // Add the main admin Jetpack menu |
||
| 15 | return add_menu_page( 'Jetpack', $title, 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div' ); |
||
| 16 | } |
||
| 17 | |||
| 18 | function add_page_actions( $hook ) { |
||
| 19 | /** This action is documented in class.jetpack.php */ |
||
| 20 | do_action( 'jetpack_admin_menu', $hook ); |
||
| 21 | |||
| 22 | // Place the Jetpack menu item on top and others in the order they appear |
||
| 23 | add_filter( 'custom_menu_order', '__return_true' ); |
||
| 24 | add_filter( 'menu_order', array( $this, 'jetpack_menu_order' ) ); |
||
| 25 | |||
| 26 | if ( ! isset( $_GET['page'] ) || 'jetpack' !== $_GET['page'] || ! empty( $_GET['configure'] ) ) { |
||
| 27 | return; // No need to handle the fallback redirection if we are not on the Jetpack page |
||
| 28 | } |
||
| 29 | |||
| 30 | // Adding a redirect meta tag for older WordPress versions or if the REST API is disabled |
||
| 31 | if ( $this->is_wp_version_too_old() || ! $this->is_rest_api_enabled() ) { |
||
| 32 | $this->is_redirecting = true; |
||
| 33 | add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) ); |
||
| 34 | } |
||
| 35 | |||
| 36 | // Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled |
||
| 37 | add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) ); |
||
| 38 | |||
| 39 | // Adding a redirect tag wrapped in browser conditional comments |
||
| 40 | add_action( 'admin_head', array( $this, 'add_legacy_browsers_head_script' ) ); |
||
| 41 | } |
||
| 42 | |||
| 43 | /** |
||
| 44 | * Add Jetpack Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active. |
||
| 45 | * Otherwise and only if user is allowed to see the Jetpack Admin, the Dashboard sub-link is added but pointed to Apps tab. |
||
| 46 | * |
||
| 47 | * Works in Dev Mode or when user is connected. |
||
| 48 | * |
||
| 49 | * @since 4.3.0 |
||
| 50 | */ |
||
| 51 | function jetpack_add_dashboard_sub_nav_item() { |
||
| 52 | if ( Jetpack::is_development_mode() || Jetpack::is_active() ) { |
||
| 53 | global $submenu; |
||
| 54 | if ( current_user_can( 'jetpack_manage_modules' ) || Jetpack::is_module_active( 'protect' ) || current_user_can( 'view_stats' ) ) { |
||
| 55 | $submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/dashboard' ) ); |
||
| 56 | } elseif ( current_user_can( 'jetpack_admin_page' ) ) { |
||
| 57 | $submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/apps' ) ); |
||
| 58 | } |
||
| 59 | } |
||
| 60 | } |
||
| 61 | |||
| 62 | /** |
||
| 63 | * If user is allowed to see the Jetpack Admin, add Settings sub-link. |
||
| 64 | * |
||
| 65 | * @since 4.3.0 |
||
| 66 | */ |
||
| 67 | function jetpack_add_settings_sub_nav_item() { |
||
| 68 | if ( ( Jetpack::is_development_mode() || Jetpack::is_active() ) && current_user_can( 'jetpack_admin_page' ) ) { |
||
| 69 | global $submenu; |
||
| 70 | $submenu['jetpack'][] = array( __( 'Settings', 'jetpack' ), 'jetpack_admin_page', Jetpack::admin_url( 'page=jetpack#/settings' ) ); |
||
| 71 | } |
||
| 72 | } |
||
| 73 | |||
| 74 | function add_fallback_head_meta() { |
||
| 75 | echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">'; |
||
| 76 | } |
||
| 77 | |||
| 78 | function add_noscript_head_meta() { |
||
| 79 | echo '<noscript>'; |
||
| 80 | $this->add_fallback_head_meta(); |
||
| 81 | echo '</noscript>'; |
||
| 82 | } |
||
| 83 | |||
| 84 | function add_legacy_browsers_head_script() { |
||
| 85 | echo |
||
| 86 | "<script type=\"text/javascript\">\n" |
||
| 87 | . "/*@cc_on\n" |
||
| 88 | . "if ( @_jscript_version <= 10) {\n" |
||
| 89 | . "window.location.href = '?page=jetpack_modules';\n" |
||
| 90 | . "}\n" |
||
| 91 | . "@*/\n" |
||
| 92 | . "</script>"; |
||
| 93 | } |
||
| 94 | |||
| 95 | View Code Duplication | function jetpack_menu_order( $menu_order ) { |
|
| 96 | $jp_menu_order = array(); |
||
| 97 | |||
| 98 | foreach ( $menu_order as $index => $item ) { |
||
| 99 | if ( $item != 'jetpack' ) |
||
| 100 | $jp_menu_order[] = $item; |
||
| 101 | |||
| 102 | if ( $index == 0 ) |
||
| 103 | $jp_menu_order[] = 'jetpack'; |
||
| 104 | } |
||
| 105 | |||
| 106 | return $jp_menu_order; |
||
| 107 | } |
||
| 108 | |||
| 109 | // Render the configuration page for the module if it exists and an error |
||
| 110 | // screen if the module is not configurable |
||
| 111 | // @todo remove when real settings are in place |
||
| 112 | function render_nojs_configurable( $module_name ) { |
||
| 113 | $module_name = preg_replace( '/[^\da-z\-]+/', '', $_GET['configure'] ); |
||
| 114 | |||
| 115 | include_once( JETPACK__PLUGIN_DIR . '_inc/header.php' ); |
||
| 116 | echo '<div class="wrap configure-module">'; |
||
| 117 | |||
| 118 | if ( Jetpack::is_module( $module_name ) && current_user_can( 'jetpack_configure_modules' ) ) { |
||
| 119 | Jetpack::admin_screen_configure_module( $module_name ); |
||
| 120 | } else { |
||
| 121 | echo '<h2>' . esc_html__( 'Error, bad module.', 'jetpack' ) . '</h2>'; |
||
| 122 | } |
||
| 123 | |||
| 124 | echo '</div><!-- /wrap -->'; |
||
| 125 | } |
||
| 126 | |||
| 127 | function page_render() { |
||
| 128 | // Handle redirects to configuration pages |
||
| 129 | if ( ! empty( $_GET['configure'] ) ) { |
||
| 130 | return $this->render_nojs_configurable( $_GET['configure'] ); |
||
| 131 | } |
||
| 132 | |||
| 133 | /** This action is already documented in views/admin/admin-page.php */ |
||
| 134 | do_action( 'jetpack_notices' ); |
||
| 135 | |||
| 136 | // Try fetching by patch |
||
| 137 | $static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' ); |
||
| 138 | |||
| 139 | if ( false === $static_html ) { |
||
| 140 | |||
| 141 | // If we still have nothing, display an error |
||
| 142 | esc_html_e( 'Error fetching static.html.', 'jetpack' ); |
||
| 143 | } else { |
||
| 144 | |||
| 145 | // We got the static.html so let's display it |
||
| 146 | echo $static_html; |
||
| 147 | } |
||
| 148 | } |
||
| 149 | |||
| 150 | function get_i18n_data() { |
||
| 151 | |||
| 152 | $i18n_json = JETPACK__PLUGIN_DIR . 'languages/json/jetpack-' . get_locale() . '.json'; |
||
| 153 | |||
| 154 | if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) { |
||
| 155 | $locale_data = @file_get_contents( $i18n_json ); |
||
| 156 | if ( $locale_data ) { |
||
| 157 | return $locale_data; |
||
| 158 | } |
||
| 159 | } |
||
| 160 | |||
| 161 | // Return empty if we have nothing to return so it doesn't fail when parsed in JS |
||
| 162 | return '{}'; |
||
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Gets array of any Jetpack notices that have been dismissed. |
||
| 167 | * |
||
| 168 | * @since 4.0.1 |
||
| 169 | * @return mixed|void |
||
| 170 | */ |
||
| 171 | function get_dismissed_jetpack_notices() { |
||
| 172 | $jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() ); |
||
| 173 | /** |
||
| 174 | * Array of notices that have been dismissed. |
||
| 175 | * |
||
| 176 | * @since 4.0.1 |
||
| 177 | * |
||
| 178 | * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices. |
||
| 179 | */ |
||
| 180 | $dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices ); |
||
| 181 | return $dismissed_notices; |
||
| 182 | } |
||
| 183 | |||
| 184 | function additional_styles() { |
||
| 185 | $rtl = is_rtl() ? '.rtl' : ''; |
||
| 186 | |||
| 187 | wp_enqueue_style( 'dops-css', plugins_url( "_inc/build/admin.dops-style$rtl.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION ); |
||
| 188 | wp_enqueue_style( 'components-css', plugins_url( "_inc/build/style.min$rtl.css", JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION ); |
||
| 189 | } |
||
| 190 | |||
| 191 | function page_admin_scripts() { |
||
| 192 | if ( $this->is_redirecting ) { |
||
| 193 | return; // No need for scripts on a fallback page |
||
| 194 | } |
||
| 195 | |||
| 196 | $is_dev_mode = Jetpack::is_development_mode(); |
||
| 197 | |||
| 198 | // Enqueue jp.js and localize it |
||
| 199 | wp_enqueue_script( 'react-plugin', plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true ); |
||
| 200 | |||
| 201 | if ( ! $is_dev_mode ) { |
||
| 202 | // Required for Analytics |
||
| 203 | wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true ); |
||
| 204 | } |
||
| 205 | |||
| 206 | $localeSlug = explode( '_', get_locale() ); |
||
| 207 | $localeSlug = $localeSlug[0]; |
||
| 208 | |||
| 209 | // Collecting roles that can view site stats |
||
| 210 | $stats_roles = array(); |
||
| 211 | $enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' ); |
||
| 212 | foreach( get_editable_roles() as $slug => $role ) { |
||
| 213 | $stats_roles[ $slug ] = array( |
||
| 214 | 'name' => translate_user_role( $role['name'] ), |
||
| 215 | 'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false, |
||
| 216 | ); |
||
| 217 | } |
||
| 218 | |||
| 219 | $response = rest_do_request( new WP_REST_Request( 'GET', '/jetpack/v4/module/all' ) ); |
||
| 220 | $modules = $response->get_data(); |
||
| 221 | |||
| 222 | // Preparing translated fields for JSON encoding by transforming all HTML entities to |
||
| 223 | // respective characters. |
||
| 224 | foreach( $modules as $slug => $data ) { |
||
| 225 | $modules[ $slug ]['name'] = html_entity_decode( $data['name'] ); |
||
| 226 | $modules[ $slug ]['description'] = html_entity_decode( $data['description'] ); |
||
| 227 | $modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] ); |
||
| 228 | $modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] ); |
||
| 229 | } |
||
| 230 | |||
| 231 | // Get last post, to build the link to Customizer in the Related Posts module. |
||
| 232 | $last_post = get_posts( array( 'posts_per_page' => 1 ) ); |
||
| 233 | if ( $last_post[0] instanceof WP_Post ) { |
||
|
0 ignored issues
–
show
|
|||
| 234 | $last_post = get_permalink( $last_post[0]->ID ); |
||
| 235 | } |
||
| 236 | |||
| 237 | // Add objects to be passed to the initial state of the app |
||
| 238 | wp_localize_script( 'react-plugin', 'Initial_State', array( |
||
| 239 | 'WP_API_root' => esc_url_raw( rest_url() ), |
||
| 240 | 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ), |
||
| 241 | 'pluginBaseUrl' => plugins_url( '', JETPACK__PLUGIN_FILE ), |
||
| 242 | 'connectionStatus' => array( |
||
| 243 | 'isActive' => Jetpack::is_active(), |
||
| 244 | 'isStaging' => Jetpack::is_staging_site(), |
||
| 245 | 'devMode' => array( |
||
| 246 | 'isActive' => $is_dev_mode, |
||
| 247 | 'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG, |
||
| 248 | 'url' => site_url() && false === strpos( site_url(), '.' ), |
||
| 249 | 'filter' => apply_filters( 'jetpack_development_mode', false ), |
||
| 250 | ), |
||
| 251 | 'isPublic' => '1' == get_option( 'blog_public' ), |
||
| 252 | 'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(), |
||
| 253 | ), |
||
| 254 | 'dismissedNotices' => $this->get_dismissed_jetpack_notices(), |
||
| 255 | 'isDevVersion' => Jetpack::is_development_version(), |
||
| 256 | 'currentVersion' => JETPACK__VERSION, |
||
| 257 | 'happinessGravIds' => jetpack_get_happiness_gravatar_ids(), |
||
| 258 | 'getModules' => $modules, |
||
| 259 | 'showJumpstart' => jetpack_show_jumpstart(), |
||
| 260 | 'rawUrl' => Jetpack::build_raw_urls( get_home_url() ), |
||
| 261 | 'adminUrl' => esc_url( admin_url() ), |
||
| 262 | 'stats' => array( |
||
| 263 | // data is populated asynchronously on page load |
||
| 264 | 'data' => array( |
||
| 265 | 'general' => false, |
||
| 266 | 'day' => false, |
||
| 267 | 'week' => false, |
||
| 268 | 'month' => false, |
||
| 269 | ), |
||
| 270 | 'roles' => $stats_roles, |
||
| 271 | ), |
||
| 272 | 'settingNames' => array( |
||
| 273 | 'jetpack_holiday_snow_enabled' => function_exists( 'jetpack_holiday_snow_option_name' ) ? jetpack_holiday_snow_option_name() : false, |
||
| 274 | ), |
||
| 275 | 'userData' => array( |
||
| 276 | // 'othersLinked' => Jetpack::get_other_linked_admins(), |
||
| 277 | 'currentUser' => jetpack_current_user_data(), |
||
| 278 | ), |
||
| 279 | 'locale' => $this->get_i18n_data(), |
||
| 280 | 'localeSlug' => $localeSlug, |
||
| 281 | 'jetpackStateNotices' => array( |
||
| 282 | 'messageCode' => Jetpack::state( 'message' ), |
||
| 283 | 'errorCode' => Jetpack::state( 'error' ), |
||
| 284 | 'errorDescription' => Jetpack::state( 'error_description' ), |
||
| 285 | ), |
||
| 286 | 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(), |
||
| 287 | 'currentIp' => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false, |
||
| 288 | 'lastPostUrl' => esc_url( $last_post ), |
||
| 289 | ) ); |
||
| 290 | } |
||
| 291 | } |
||
| 292 | |||
| 293 | /* |
||
| 294 | * List of happiness Gravatar IDs |
||
| 295 | * |
||
| 296 | * @todo move to functions.global.php when available |
||
| 297 | * @since 4.1.0 |
||
| 298 | * @return array |
||
| 299 | */ |
||
| 300 | function jetpack_get_happiness_gravatar_ids() { |
||
| 301 | return array( |
||
| 302 | '623f42e878dbd146ddb30ebfafa1375b', |
||
| 303 | '561be467af56cefa58e02782b7ac7510', |
||
| 304 | 'd8ad409290a6ae7b60f128a0b9a0c1c5', |
||
| 305 | '790618302648bd80fa8a55497dfd8ac8', |
||
| 306 | '6e238edcb0664c975ccb9e8e80abb307', |
||
| 307 | '4e6c84eeab0a1338838a9a1e84629c1a', |
||
| 308 | '9d4b77080c699629e846d3637b3a661c', |
||
| 309 | '4626de7797aada973c1fb22dfe0e5109', |
||
| 310 | '190cf13c9cd358521085af13615382d5', |
||
| 311 | 'f7006d10e9f7dd7bea89a001a2a2fd59', |
||
| 312 | '16acbc88e7aa65104ed289d736cb9698', |
||
| 313 | '4d5ad4219c6f676ea1e7d40d2e8860e8', |
||
| 314 | 'e301f7d01b09e7578fdfc1b1ec1bc08d', |
||
| 315 | '42f4c73f5337486e199f6e3b3910f168', |
||
| 316 | 'e7b26de48e76498cff880abca1eed8da', |
||
| 317 | '764fb02aaae2ff64c0625c763d82b74e', |
||
| 318 | '4988305772319fb9bc8fce0a7acb3aa1', |
||
| 319 | '5d8695c4b81592f1255721d2644627ca', |
||
| 320 | '0e2249a7de3404bc6d5207a45e911187', |
||
| 321 | ); |
||
| 322 | } |
||
| 323 | |||
| 324 | /* |
||
| 325 | * Only show Jump Start on first activation. |
||
| 326 | * Any option 'jumpstart' other than 'new connection' will hide it. |
||
| 327 | * |
||
| 328 | * The option can be of 4 things, and will be stored as such: |
||
| 329 | * new_connection : Brand new connection - Show |
||
| 330 | * jumpstart_activated : Jump Start has been activated - dismiss |
||
| 331 | * jetpack_action_taken: Manual activation of a module already happened - dismiss |
||
| 332 | * jumpstart_dismissed : Manual dismissal of Jump Start - dismiss |
||
| 333 | * |
||
| 334 | * @todo move to functions.global.php when available |
||
| 335 | * @since 3.6 |
||
| 336 | * @return bool | show or hide |
||
| 337 | */ |
||
| 338 | function jetpack_show_jumpstart() { |
||
| 339 | if ( ! Jetpack::is_active() ) { |
||
| 340 | return false; |
||
| 341 | } |
||
| 342 | $jumpstart_option = Jetpack_Options::get_option( 'jumpstart' ); |
||
| 343 | |||
| 344 | $hide_options = array( |
||
| 345 | 'jumpstart_activated', |
||
| 346 | 'jetpack_action_taken', |
||
| 347 | 'jumpstart_dismissed' |
||
| 348 | ); |
||
| 349 | |||
| 350 | if ( ! $jumpstart_option || in_array( $jumpstart_option, $hide_options ) ) { |
||
| 351 | return false; |
||
| 352 | } |
||
| 353 | |||
| 354 | return true; |
||
| 355 | } |
||
| 356 | |||
| 357 | /* |
||
| 358 | * Gather data about the master user. |
||
| 359 | * |
||
| 360 | * @since 4.1.0 |
||
| 361 | * |
||
| 362 | * @return array |
||
| 363 | */ |
||
| 364 | View Code Duplication | function jetpack_master_user_data() { |
|
| 365 | $masterID = Jetpack_Options::get_option( 'master_user' ); |
||
| 366 | if ( ! get_user_by( 'id', $masterID ) ) { |
||
| 367 | return false; |
||
| 368 | } |
||
| 369 | |||
| 370 | $jetpack_user = get_userdata( $masterID ); |
||
| 371 | $wpcom_user = Jetpack::get_connected_user_data( $jetpack_user->ID ); |
||
| 372 | $gravatar = get_avatar( $jetpack_user->ID, 40 ); |
||
| 373 | |||
| 374 | $master_user_data = array( |
||
| 375 | 'jetpackUser' => $jetpack_user, |
||
| 376 | 'wpcomUser' => $wpcom_user, |
||
| 377 | 'gravatar' => $gravatar, |
||
| 378 | ); |
||
| 379 | |||
| 380 | return $master_user_data; |
||
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * Gather data about the current user. |
||
| 385 | * |
||
| 386 | * @since 4.1.0 |
||
| 387 | * |
||
| 388 | * @return array |
||
| 389 | */ |
||
| 390 | function jetpack_current_user_data() { |
||
| 391 | global $current_user; |
||
| 392 | $is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' ); |
||
| 393 | $dotcom_data = Jetpack::get_connected_user_data(); |
||
| 394 | // Add connected user gravatar to the returned dotcom_data |
||
| 395 | $avatar_data = Jetpack::get_avatar_url( $dotcom_data[ 'email' ] ); |
||
| 396 | $dotcom_data[ 'avatar'] = $avatar_data[ 0 ]; |
||
| 397 | |||
| 398 | $current_user_data = array( |
||
| 399 | 'isConnected' => Jetpack::is_user_connected( $current_user->ID ), |
||
| 400 | 'isMaster' => $is_master_user, |
||
| 401 | 'username' => $current_user->user_login, |
||
| 402 | 'wpcomUser' => $dotcom_data, |
||
| 403 | 'gravatar' => get_avatar( $current_user->ID, 40 ), |
||
| 404 | 'permissions' => array( |
||
| 405 | 'admin_page' => current_user_can( 'jetpack_admin_page' ), |
||
| 406 | 'connect' => current_user_can( 'jetpack_connect' ), |
||
| 407 | 'disconnect' => current_user_can( 'jetpack_disconnect' ), |
||
| 408 | 'manage_modules' => current_user_can( 'jetpack_manage_modules' ), |
||
| 409 | 'network_admin' => current_user_can( 'jetpack_network_admin_page' ), |
||
| 410 | 'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ), |
||
| 411 | 'edit_posts' => current_user_can( 'edit_posts' ), |
||
| 412 | 'manage_options' => current_user_can( 'manage_options' ), |
||
| 413 | 'view_stats' => current_user_can( 'view_stats' ), |
||
| 414 | 'manage_plugins' => current_user_can( 'install_plugins' ) |
||
| 415 | && current_user_can( 'activate_plugins' ) |
||
| 416 | && current_user_can( 'update_plugins' ) |
||
| 417 | && current_user_can( 'delete_plugins' ), |
||
| 418 | ), |
||
| 419 | ); |
||
| 420 | |||
| 421 | return $current_user_data; |
||
| 422 | } |
||
| 423 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.jsonfile (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.jsonto be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
requireorrequire-devsection?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceofchecks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.