Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,31 @@
require_once __DIR__ . '/vendor/autoload.php';
}

/*
* Load the consumer-supplied bootstrap file (if configured) before the command is registered.
* This mirrors the behaviour of the `object-cache.php` drop-in so that integrations have one
* consistent extension point across admin and WP-CLI paths.
*
* The constant is expected to hold an absolute filesystem path. When the constant is defined
* but the file is missing, a WP-CLI warning is emitted and execution continues.
*/
if ( defined( 'WP_PLUGIN_CHECK_BOOTSTRAP_FILE' ) ) {
$plugin_check_bootstrap_file = WP_PLUGIN_CHECK_BOOTSTRAP_FILE;
if ( is_string( $plugin_check_bootstrap_file ) && '' !== $plugin_check_bootstrap_file ) {
if ( is_file( $plugin_check_bootstrap_file ) ) {
require_once $plugin_check_bootstrap_file;
} else {
WP_CLI::warning(
sprintf(
'Plugin Check: WP_PLUGIN_CHECK_BOOTSTRAP_FILE points to "%s", but the file does not exist.',
$plugin_check_bootstrap_file
)
);
}
}
unset( $plugin_check_bootstrap_file );
}

if ( ! isset( $context ) ) {
$context = new Plugin_Context( __DIR__ . '/plugin.php' );
}
Expand Down
26 changes: 26 additions & 0 deletions drop-ins/object-cache.copy.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ function plugin_check_initialize_runner() {

require_once $plugin_dir . 'vendor/autoload.php';

/*
* Load the consumer-supplied bootstrap file (if configured) before the runner is initialized.
* The bootstrap file is the earliest point at which an integration can register listeners for
* plugin-check actions/filters, since this drop-in runs before mu-plugins.
*
* The constant is expected to hold an absolute filesystem path. When the constant is defined
* but the file is missing, a PHP warning is emitted so that misconfiguration is visible; PCP
* keeps running regardless.
*/
if ( defined( 'WP_PLUGIN_CHECK_BOOTSTRAP_FILE' ) ) {
$plugin_check_bootstrap_file = WP_PLUGIN_CHECK_BOOTSTRAP_FILE;
if ( is_string( $plugin_check_bootstrap_file ) && '' !== $plugin_check_bootstrap_file ) {
if ( is_file( $plugin_check_bootstrap_file ) ) {
require_once $plugin_check_bootstrap_file;
} else {
trigger_error(
sprintf(
'Plugin Check: WP_PLUGIN_CHECK_BOOTSTRAP_FILE points to "%s", but the file does not exist.',
$plugin_check_bootstrap_file
),
E_USER_WARNING
);
}
}
}

if ( class_exists( 'WordPress\Plugin_Check\Utilities\Plugin_Request_Utility' ) ) {
// Initialize the Check Runner class based on the request.
WordPress\Plugin_Check\Utilities\Plugin_Request_Utility::initialize_runner();
Expand Down
34 changes: 31 additions & 3 deletions includes/Checker/Check_Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ public function plugin() {
/**
* Adds an error or warning to the respective stack.
*
* @SuppressWarnings(PHPMD.NPathComplexity)
*
* @since 1.0.0
*
* @param bool $error Whether it is an error message.
Expand Down Expand Up @@ -130,9 +132,35 @@ public function add_message( $error, $message, $args = array() ) {
array_intersect_key( $args, $defaults )
);

$file = str_replace( $this->plugin()->path( '/' ), '', $data['file'] );
$line = $data['line'];
$column = $data['column'];
// Normalize the file path before the filter so consumers see the same value as the stored entry.
$data['file'] = str_replace( $this->plugin()->path(), '', $data['file'] );

/**
* Filters a single check result entry before it is recorded.
*
* Return `null` (or any non-array value) to suppress the entry entirely.
* Return a modified array to record the modified entry instead.
* The `$is_error` argument continues to drive whether the entry is stored
* as an error or a warning regardless of changes made to the filtered array —
* promotion / demotion is intentionally out of scope here.
*
* @since 2.0.0
*
* @param array|null $data Entry data with keys
* `message`, `code`, `file`, `line`, `column`, `link`, `docs`, `severity`.
* Return `null` to drop the entry.
* @param Check_Result $result The check result the entry will be added to.
* @param bool $is_error True if the entry is being recorded as an error, false if as a warning.
*/
$data = apply_filters( 'wp_plugin_check_check_result', $data, $this, $error );

if ( ! is_array( $data ) ) {
return;
}

$file = isset( $data['file'] ) ? (string) $data['file'] : '';
$line = isset( $data['line'] ) ? (int) $data['line'] : 0;
$column = isset( $data['column'] ) ? (int) $data['column'] : 0;
unset( $data['line'], $data['column'], $data['file'] );

if ( $error ) {
Expand Down
17 changes: 17 additions & 0 deletions includes/Checker/Checks/Abstract_PHP_CodeSniffer_Check.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ abstract protected function get_args( Check_Result $result );
* Amends the given result by running the check on the associated plugin.
*
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @since 1.0.0
*
Expand Down Expand Up @@ -92,6 +93,22 @@ final public function run( Check_Result $result ) {

$args = $this->get_args( $result );

/**
* Filters the PHPCS arguments for a check before it runs.
*
* Lets integrations override the PHPCS standard, runtime-set values,
* extensions, installed_paths, sniffs and other arguments returned by
* the check's `get_args()` implementation — without having to subclass
* and swap the check via `wp_plugin_check_checks`.
*
* @since 2.0.0
*
* @param array $args PHPCS arguments returned by `get_args()`.
* @param Abstract_PHP_CodeSniffer_Check $check The check instance.
* @param Check_Result $result The check result.
*/
$args = apply_filters( 'wp_plugin_check_phpcs_args', $args, $this, $result );

// Reset PHP_CodeSniffer config.
$this->reset_php_codesniffer_config();

Expand Down
214 changes: 136 additions & 78 deletions includes/Checker/Runtime_Environment_Setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,73 +24,103 @@ final class Runtime_Environment_Setup {
*
* @global wpdb $wpdb WordPress database abstraction object.
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function set_up() {
global $wpdb, $wp_filesystem;

require_once ABSPATH . '/wp-admin/includes/upgrade.php';

// Get the existing site URL.
$site_url = get_option( 'siteurl' );

// Get the existing active plugins.
$active_plugins = get_option( 'active_plugins' );

// Get the existing active theme.
$active_theme = get_option( 'stylesheet' );

// Get the existing permalink structure.
$permalink_structure = get_option( 'permalink_structure' );

// Set the new prefix.
$prefix_cleanup = $this->amend_db_base_prefix();

// Create and populate the test database tables if they do not exist.
if ( $wpdb->posts !== $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->posts ) ) ) {
/*
* Set the same permalink structure *before* install finishes,
* so that wp_install_maybe_enable_pretty_permalinks() does not flush rewrite rules.
*
* See https://github.com/WordPress/plugin-check/issues/330
*/
add_action(
'populate_options',
static function () use ( $permalink_structure ) {
/*
* If pretty permalinks are not used, temporarily enable them by setting a permalink structure, to
* avoid flushing rewrite rules in wp_install_maybe_enable_pretty_permalinks().
* Afterwards, on the 'wp_install' action, set the original (empty) permalink structure.
*/
if ( ! $permalink_structure ) {
add_action(
'wp_install',
static function () use ( $permalink_structure ) {
update_option( 'permalink_structure', $permalink_structure );
}
);
$permalink_structure = '/%postname%/';
/**
* Fires before the runtime environment is set up.
*
* @since 2.0.0
*
* @param array $context {
* Context for the hook.
*
* @type bool $early_exit Whether the method exited before completing all setup steps.
* }
*/
do_action( 'wp_plugin_check_before_runtime_setup', array( 'early_exit' => false ) );

try {
require_once ABSPATH . '/wp-admin/includes/upgrade.php';

// Get the existing site URL.
$site_url = get_option( 'siteurl' );

// Get the existing active plugins.
$active_plugins = get_option( 'active_plugins' );

// Get the existing active theme.
$active_theme = get_option( 'stylesheet' );

// Get the existing permalink structure.
$permalink_structure = get_option( 'permalink_structure' );

// Set the new prefix.
$prefix_cleanup = $this->amend_db_base_prefix();

// Create and populate the test database tables if they do not exist.
if ( $wpdb->posts !== $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->posts ) ) ) {
/*
* Set the same permalink structure *before* install finishes,
* so that wp_install_maybe_enable_pretty_permalinks() does not flush rewrite rules.
*
* See https://github.com/WordPress/plugin-check/issues/330
*/
add_action(
'populate_options',
static function () use ( $permalink_structure ) {
/*
* If pretty permalinks are not used, temporarily enable them by setting a permalink structure, to
* avoid flushing rewrite rules in wp_install_maybe_enable_pretty_permalinks().
* Afterwards, on the 'wp_install' action, set the original (empty) permalink structure.
*/
if ( ! $permalink_structure ) {
add_action(
'wp_install',
static function () use ( $permalink_structure ) {
update_option( 'permalink_structure', $permalink_structure );
}
);
$permalink_structure = '/%postname%/';
}
add_option( 'permalink_structure', $permalink_structure );
}
add_option( 'permalink_structure', $permalink_structure );
}
);
);

$this->install_wordpress( $site_url, $active_theme, $active_plugins );
}
$this->install_wordpress( $site_url, $active_theme, $active_plugins );
}

// Restore the old prefix.
$prefix_cleanup();
// Restore the old prefix.
$prefix_cleanup();

// Return early if the plugin check object cache already exists.
if ( defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) && WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) {
return;
}
// Return early if the plugin check object cache already exists.
if ( defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) && WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) {
return;
}

// Create the object-cache.php file.
if ( $wp_filesystem || WP_Filesystem() ) {
// Do not replace the object-cache.php file if it already exists.
if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
$wp_filesystem->copy( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'drop-ins/object-cache.copy.php', WP_CONTENT_DIR . '/object-cache.php' );
// Create the object-cache.php file.
if ( $wp_filesystem || WP_Filesystem() ) {
// Do not replace the object-cache.php file if it already exists.
if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
$wp_filesystem->copy( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'drop-ins/object-cache.copy.php', WP_CONTENT_DIR . '/object-cache.php' );
}
}
} finally {
/**
* Fires after the runtime environment is set up, including when it exits early.
*
* @since 2.0.0
*
* @param array $context {
* Context for the hook.
*
* @type bool $early_exit Whether the method exited before completing all setup steps.
* }
*/
do_action( 'wp_plugin_check_after_runtime_setup', array( 'early_exit' => false ) );
}
}

Expand All @@ -105,38 +135,66 @@ static function () use ( $permalink_structure ) {
public function clean_up() {
global $wpdb, $wp_filesystem;

require_once ABSPATH . '/wp-admin/includes/upgrade.php';
/**
* Fires before the runtime environment is cleaned up.
*
* @since 2.0.0
*
* @param array $context {
* Context for the hook.
*
* @type bool $early_exit Whether the method exited before completing all cleanup steps.
* }
*/
do_action( 'wp_plugin_check_before_runtime_cleanup', array( 'early_exit' => false ) );

$prefix_cleanup = $this->amend_db_base_prefix();
$tables = $wpdb->tables();
try {
require_once ABSPATH . '/wp-admin/includes/upgrade.php';

$tables = $this->ignore_custom_tables( $tables );
$prefix_cleanup = $this->amend_db_base_prefix();
$tables = $wpdb->tables();

foreach ( $tables as $table ) {
$wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
$tables = $this->ignore_custom_tables( $tables );

// Restore the old prefix.
$prefix_cleanup();
foreach ( $tables as $table ) {
$wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}

// Return early if the plugin check object cache does not exist.
if ( ! defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) || ! WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) {
return;
}
// Restore the old prefix.
$prefix_cleanup();

// Remove the object-cache.php file.
if ( $wp_filesystem || WP_Filesystem() ) {
if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
// Return early if the plugin check object cache does not exist.
if ( ! defined( 'WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION' ) || ! WP_PLUGIN_CHECK_OBJECT_CACHE_DROPIN_VERSION ) {
return;
}

// Check the drop-in file matches the copy.
$original_content = $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' );
$copy_content = $wp_filesystem->get_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'drop-ins/object-cache.copy.php' );
// Remove the object-cache.php file.
if ( $wp_filesystem || WP_Filesystem() ) {
if ( ! $wp_filesystem->exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
return;
}

// Check the drop-in file matches the copy.
$original_content = $wp_filesystem->get_contents( WP_CONTENT_DIR . '/object-cache.php' );
$copy_content = $wp_filesystem->get_contents( WP_PLUGIN_CHECK_PLUGIN_DIR_PATH . 'drop-ins/object-cache.copy.php' );

if ( $original_content && $original_content === $copy_content ) {
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
if ( $original_content && $original_content === $copy_content ) {
$wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
}
}
} finally {
/**
* Fires after the runtime environment is cleaned up, including when it exits early.
*
* @since 2.0.0
*
* @param array $context {
* Context for the hook.
*
* @type bool $early_exit Whether the method exited before completing all cleanup steps.
* }
*/
do_action( 'wp_plugin_check_after_runtime_cleanup', array( 'early_exit' => false ) );
}
}

Expand Down
Loading
Loading