'wordpress_default', 'via' => null ) : array( 'route' => 'plugin', 'via' => $via ); } /** * The plugin folder that defines a function, when a plugin does (a * pluggable `wp_mail()` replaced before WordPress defined its own). */ private static function plugin_folder_of_function( string $name ): ?string { if ( ! function_exists( $name ) ) { return null; } try { $file = ( new \ReflectionFunction( $name ) )->getFileName(); } catch ( \ReflectionException $e ) { return null; } return is_string( $file ) ? self::plugin_folder( $file ) : null; } /** The first plugin (not WPPilot) with a callback on a hook. */ private static function plugin_folder_hooked_on( string $hook ): ?string { global $wp_filter; if ( ! isset( $wp_filter[ $hook ] ) || ! is_object( $wp_filter[ $hook ] ) || ! isset( $wp_filter[ $hook ]->callbacks ) ) { return null; } foreach ( (array) $wp_filter[ $hook ]->callbacks as $callbacks ) { foreach ( (array) $callbacks as $entry ) { $file = self::callback_file( $entry['function'] ?? null ); if ( null === $file ) { continue; } $folder = self::plugin_folder( $file ); if ( null !== $folder ) { return $folder; } } } return null; } /** The file a callback is defined in, when reflection can say. */ private static function callback_file( mixed $callback ): ?string { try { if ( $callback instanceof \Closure || ( is_string( $callback ) && ! str_contains( $callback, '::' ) && function_exists( $callback ) ) ) { $file = ( new \ReflectionFunction( $callback ) )->getFileName(); } elseif ( is_array( $callback ) && 2 === count( $callback ) && is_string( $callback[1] ) ) { $file = ( new \ReflectionMethod( $callback[0], $callback[1] ) )->getFileName(); } elseif ( is_string( $callback ) && str_contains( $callback, '::' ) ) { $file = ( new \ReflectionMethod( $callback ) )->getFileName(); } elseif ( is_object( $callback ) && method_exists( $callback, '__invoke' ) ) { $file = ( new \ReflectionMethod( $callback, '__invoke' ) )->getFileName(); } else { return null; } } catch ( \ReflectionException $e ) { return null; } return is_string( $file ) ? $file : null; } /** * The plugin folder (or must-use plugin file) a path is in, or null for * WordPress itself, a theme, and WPPilot. */ public static function plugin_folder( string $file ): ?string { $file = wp_normalize_path( $file ); $roots = array(); if ( defined( 'WP_PLUGIN_DIR' ) ) { $roots[] = wp_normalize_path( WP_PLUGIN_DIR ); } if ( defined( 'WPMU_PLUGIN_DIR' ) ) { $roots[] = wp_normalize_path( WPMU_PLUGIN_DIR ); } foreach ( $roots as $root ) { $root = rtrim( $root, '/' ) . '/'; if ( ! str_starts_with( $file, $root ) ) { continue; } $folder = strtok( substr( $file, strlen( $root ) ), '/' ); if ( ! is_string( $folder ) || '' === $folder ) { return null; } $own = defined( 'WPPILOT_DIR' ) ? basename( rtrim( wp_normalize_path( WPPILOT_DIR ), '/' ) ) : 'wingpilot'; return $folder === $own ? null : sanitize_key( preg_replace( '/\.php$/', '', $folder ) ); } return null; } }