<?php

namespace HelpGent\WP_CLI;

use HelpGent\WP_CLI;
use HelpGent\WP_CLI\Dispatcher;
use HelpGent\WP_CLI\Dispatcher\CompositeCommand;
use HelpGent\WP_CLI\Dispatcher\Subcommand;
use HelpGent\WP_CLI\Fetchers;
use HelpGent\WP_CLI\Iterators\Exception;
use HelpGent\WP_CLI\Loggers;
use HelpGent\WP_CLI\Utils;
use WP_Error;
/**
 * Performs the execution of a command.
 *
 * @property-read string         $global_config_path
 * @property-read string         $project_config_path
 * @property-read array          $config
 * @property-read array          $extra_config
 * @property-read ContextManager $context_manager
 * @property-read string         $alias
 * @property-read array          $aliases
 * @property-read array          $arguments
 * @property-read array          $assoc_args
 * @property-read array          $runtime_config
 * @property-read bool           $colorize
 * @property-read array          $early_invoke
 * @property-read string         $global_config_path_debug
 * @property-read string         $project_config_path_debug
 * @property-read array          $required_files
 *
 * @package WP_CLI
 */
class Runner
{
    /**
     * List of byte-order marks (BOMs) to detect.
     *
     * @var array<string, string>
     */
    const BYTE_ORDER_MARKS = ['UTF-8' => "﻿", 'UTF-16 (BE)' => "\xfe\xff", 'UTF-16 (LE)' => "\xff\xfe"];
    private $global_config_path;
    private $project_config_path;
    private $config;
    private $extra_config;
    private $context_manager;
    private $alias;
    private $aliases;
    private $arguments;
    private $assoc_args;
    private $runtime_config;
    private $colorize = \false;
    private $early_invoke = [];
    private $global_config_path_debug;
    private $project_config_path_debug;
    private $required_files;
    public function __get($key)
    {
        if ('_' === $key[0]) {
            return null;
        }
        return $this->{$key};
    }
    public function register_context_manager(ContextManager $context_manager)
    {
        $this->context_manager = $context_manager;
    }
    /**
     * Register a command for early invocation, generally before WordPress loads.
     *
     * @param string $when Named execution hook
     * @param Subcommand $command
     */
    public function register_early_invoke($when, $command)
    {
        $cmd_path = \array_slice(Dispatcher\get_path($command), 1);
        $command_name = \implode(' ', $cmd_path);
        WP_CLI::debug("Attaching command '{$command_name}' to hook {$when}", 'bootstrap');
        $this->early_invoke[$when][] = $cmd_path;
        if ($command->get_alias()) {
            \array_pop($cmd_path);
            $cmd_path[] = $command->get_alias();
            $alias_name = \implode(' ', $cmd_path);
            WP_CLI::debug("Attaching command alias '{$alias_name}' to hook {$when}", 'bootstrap');
            $this->early_invoke[$when][] = $cmd_path;
        }
    }
    /**
     * Perform the early invocation of a command.
     *
     * @param string $when Named execution hook
     */
    private function do_early_invoke($when)
    {
        WP_CLI::debug("Executing hook: {$when}", 'hooks');
        if (!isset($this->early_invoke[$when])) {
            return;
        }
        // Search the value of @when from the command method.
        $real_when = '';
        $r = $this->find_command_to_run($this->arguments);
        if (\is_array($r)) {
            list($command, $final_args, $cmd_path) = $r;
            foreach ($this->early_invoke as $_when => $_path) {
                foreach ($_path as $cmd) {
                    if ($cmd === $cmd_path) {
                        $real_when = $_when;
                    }
                }
            }
        }
        foreach ($this->early_invoke[$when] as $path) {
            if ($this->cmd_starts_with($path)) {
                if (empty($real_when) || $real_when && $real_when === $when) {
                    $this->run_command_and_exit();
                }
            }
        }
    }
    /**
     * Get the path to the global configuration YAML file.
     *
     * @param bool $create_config_file Optional. If a config file doesn't exist,
     *                                 should it be created? Defaults to false.
     *
     * @return string|false
     */
    public function get_global_config_path($create_config_file = \false)
    {
        if (\getenv('WP_CLI_CONFIG_PATH')) {
            $config_path = \getenv('WP_CLI_CONFIG_PATH');
            $this->global_config_path_debug = 'Using global config from WP_CLI_CONFIG_PATH env var: ' . $config_path;
        } else {
            $config_path = Utils\get_home_dir() . '/.wp-cli/config.yml';
            $this->global_config_path_debug = 'Using default global config: ' . $config_path;
        }
        // If global config doesn't exist create one.
        if (\true === $create_config_file && !\file_exists($config_path)) {
            $this->global_config_path_debug = "Default global config doesn't exist, creating one in {$config_path}";
            Process::create(Utils\esc_cmd('touch %s', $config_path))->run();
        }
        if (\is_readable($config_path)) {
            return $config_path;
        }
        $this->global_config_path_debug = 'No readable global config found';
        return \false;
    }
    /**
     * Get the path to the project-specific configuration
     * YAML file.
     * wp-cli.local.yml takes priority over wp-cli.yml.
     *
     * @return string|false
     */
    public function get_project_config_path()
    {
        $config_files = ['wp-cli.local.yml', 'wp-cli.yml'];
        // Stop looking upward when we find we have emerged from a subdirectory
        // installation into a parent installation
        $project_config_path = Utils\find_file_upward($config_files, \getcwd(), static function ($dir) {
            static $wp_load_count = 0;
            $wp_load_path = $dir . \DIRECTORY_SEPARATOR . 'wp-load.php';
            if (\file_exists($wp_load_path)) {
                ++$wp_load_count;
            }
            return $wp_load_count > 1;
        });
        $this->project_config_path_debug = 'No project config found';
        if (!empty($project_config_path)) {
            $this->project_config_path_debug = 'Using project config: ' . $project_config_path;
        }
        return $project_config_path;
    }
    /**
     * Get the path to the packages directory
     *
     * @return string
     */
    public function get_packages_dir_path()
    {
        if (\getenv('WP_CLI_PACKAGES_DIR')) {
            $packages_dir = Utils\trailingslashit(\getenv('WP_CLI_PACKAGES_DIR'));
        } else {
            $packages_dir = Utils\get_home_dir() . '/.wp-cli/packages/';
        }
        return $packages_dir;
    }
    /**
     * Attempts to find the path to the WP installation inside index.php
     *
     * @param string $index_path
     * @return string|false
     */
    private static function extract_subdir_path($index_path)
    {
        $index_code = \file_get_contents($index_path);
        if (!\preg_match('|^\\s*require\\s*\\(?\\s*(.+?)/wp-blog-header\\.php([\'"])|m', $index_code, $matches)) {
            return \false;
        }
        $wp_path_src = $matches[1] . $matches[2];
        $wp_path_src = Utils\replace_path_consts($wp_path_src, $index_path);
        $wp_path = eval("return {$wp_path_src};");
        // phpcs:ignore Squiz.PHP.Eval.Discouraged
        if (!Utils\is_path_absolute($wp_path)) {
            $wp_path = \dirname($index_path) . "/{$wp_path}";
        }
        return $wp_path;
    }
    /**
     * Find the directory that contains the WordPress files.
     * Defaults to the current working dir.
     *
     * @return string An absolute path.
     */
    public function find_wp_root()
    {
        if (isset($this->config['path']) && (\is_bool($this->config['path']) || empty($this->config['path']))) {
            WP_CLI::error('The --path parameter cannot be empty when provided.');
        }
        if (!empty($this->config['path'])) {
            $path = $this->config['path'];
            if (!Utils\is_path_absolute($path)) {
                $path = \getcwd() . '/' . $path;
            }
            return $path;
        }
        if ($this->cmd_starts_with(['core', 'download'])) {
            return \getcwd();
        }
        $dir = \getcwd();
        while (\is_readable($dir)) {
            if (\file_exists("{$dir}/wp-load.php")) {
                return $dir;
            }
            if (\file_exists("{$dir}/index.php")) {
                $path = self::extract_subdir_path("{$dir}/index.php");
                if (!empty($path)) {
                    return $path;
                }
            }
            $parent_dir = \dirname($dir);
            if (empty($parent_dir) || $parent_dir === $dir) {
                break;
            }
            $dir = $parent_dir;
        }
        return \getcwd();
    }
    /**
     * Set WordPress root as a given path.
     *
     * @param string $path
     */
    private static function set_wp_root($path)
    {
        if (!\defined('ABSPATH')) {
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Declaring a WP native constant.
            \define('ABSPATH', Utils\normalize_path(Utils\trailingslashit($path)));
        } elseif (!\is_null($path)) {
            WP_CLI::error_multi_line(['The --path parameter cannot be used when ABSPATH is already defined elsewhere', 'ABSPATH is defined as: "' . ABSPATH . '"']);
        }
        WP_CLI::debug('ABSPATH defined: ' . ABSPATH, 'bootstrap');
        $_SERVER['DOCUMENT_ROOT'] = \realpath($path);
    }
    /**
     * Guess which URL context WP-CLI has been invoked under.
     *
     * @param array $assoc_args
     * @return string|false
     */
    private static function guess_url($assoc_args)
    {
        if (isset($assoc_args['blog'])) {
            $assoc_args['url'] = $assoc_args['blog'];
        }
        if (isset($assoc_args['url'])) {
            $url = $assoc_args['url'];
            if (\true === $url) {
                WP_CLI::warning('The --url parameter expects a value.');
            }
            return $url;
        }
        return \false;
    }
    private function cmd_starts_with($prefix)
    {
        return \array_slice($this->arguments, 0, \count($prefix)) === $prefix;
    }
    /**
     * Given positional arguments, find the command to execute.
     *
     * @param array $args
     * @return array|string Command, args, and path on success; error message on failure
     */
    public function find_command_to_run($args)
    {
        $command = WP_CLI::get_root_command();
        WP_CLI::do_hook('find_command_to_run_pre');
        $cmd_path = [];
        while (!empty($args) && $command->can_have_subcommands()) {
            $cmd_path[] = $args[0];
            $full_name = \implode(' ', $cmd_path);
            $subcommand = $command->find_subcommand($args);
            if (!$subcommand) {
                if (\count($cmd_path) > 1) {
                    $child = \array_pop($cmd_path);
                    $parent_name = \implode(' ', $cmd_path);
                    $suggestion = $this->get_subcommand_suggestion($child, $command);
                    if ('network' === $parent_name && 'option' === $child) {
                        $suggestion = 'meta';
                    }
                    return \sprintf("'%s' is not a registered subcommand of '%s'. See 'wp help %s' for available subcommands.%s", $child, $parent_name, $parent_name, !empty($suggestion) ? \PHP_EOL . "Did you mean '{$suggestion}'?" : '');
                }
                $suggestion = $this->get_subcommand_suggestion($full_name, $command);
                return \sprintf("'%s' is not a registered wp command. See 'wp help' for available commands.%s", $full_name, !empty($suggestion) ? \PHP_EOL . "Did you mean '{$suggestion}'?" : '');
            }
            if ($this->is_command_disabled($subcommand)) {
                return \sprintf("The '%s' command has been disabled from the config file.", $full_name);
            }
            $command = $subcommand;
        }
        return [$command, $args, $cmd_path];
    }
    /**
     * Find the WP-CLI command to run given arguments, and invoke it.
     *
     * @param array $args        Positional arguments including command name
     * @param array $assoc_args  Associative arguments for the command.
     * @param array $options     Configuration options for the function.
     */
    public function run_command($args, $assoc_args = [], $options = [])
    {
        WP_CLI::do_hook('before_run_command', $args, $assoc_args, $options);
        if (!empty($options['back_compat_conversions'])) {
            list($args, $assoc_args) = self::back_compat_conversions($args, $assoc_args);
        }
        $r = $this->find_command_to_run($args);
        if (\is_string($r)) {
            WP_CLI::error($r);
        }
        list($command, $final_args, $cmd_path) = $r;
        $name = \implode(' ', $cmd_path);
        $extra_args = [];
        if (isset($this->extra_config[$name])) {
            $extra_args = $this->extra_config[$name];
        }
        WP_CLI::debug('Running command: ' . $name, 'bootstrap');
        try {
            $command->invoke($final_args, $assoc_args, $extra_args);
        } catch (Exception $e) {
            WP_CLI::error($e->getMessage());
        }
    }
    /**
     * Show synopsis if the called command is a composite command
     */
    public function show_synopsis_if_composite_command()
    {
        $r = $this->find_command_to_run($this->arguments);
        if (\is_array($r)) {
            list($command) = $r;
            if ($command->can_have_subcommands()) {
                $command->show_usage();
                exit;
            }
        }
    }
    private function run_command_and_exit($help_exit_warning = '')
    {
        $this->show_synopsis_if_composite_command();
        $this->run_command($this->arguments, $this->assoc_args);
        if ($this->cmd_starts_with(['help'])) {
            // Help couldn't find the command so exit with suggestion.
            $suggestion_or_disabled = $this->find_command_to_run(\array_slice($this->arguments, 1));
            if (\is_string($suggestion_or_disabled)) {
                if ($help_exit_warning) {
                    WP_CLI::warning($help_exit_warning);
                }
                WP_CLI::error($suggestion_or_disabled);
            }
            // Should never get here.
        }
        exit;
    }
    /**
     * Perform a command against a remote server over SSH (or a container using
     * scheme of "docker", "docker-compose", or "docker-compose-run").
     *
     * @param string $connection_string Passed connection string.
     * @return void
     */
    private function run_ssh_command($connection_string)
    {
        WP_CLI::do_hook('before_ssh');
        $bits = Utils\parse_ssh_url($connection_string);
        $pre_cmd = \getenv('WP_CLI_SSH_PRE_CMD');
        if ($pre_cmd) {
            $message = WP_CLI::warning("WP_CLI_SSH_PRE_CMD found, executing the following command(s) on the remote machine:\n {$pre_cmd}");
            WP_CLI::log($message);
            $pre_cmd = \rtrim($pre_cmd, ';') . '; ';
        }
        if (!empty($bits['path'])) {
            $pre_cmd .= 'cd ' . \escapeshellarg($bits['path']) . '; ';
        }
        $env_vars = '';
        if (\getenv('WP_CLI_STRICT_ARGS_MODE')) {
            $env_vars .= 'WP_CLI_STRICT_ARGS_MODE=1 ';
        }
        $wp_binary = 'wp';
        $wp_args = \array_slice($GLOBALS['argv'], 1);
        if ($this->alias && !empty($wp_args[0]) && $this->alias === $wp_args[0]) {
            \array_shift($wp_args);
            $runtime_alias = [];
            foreach ($this->aliases[$this->alias] as $key => $value) {
                if ('ssh' === $key) {
                    continue;
                }
                $runtime_alias[$key] = $value;
            }
            if (!empty($runtime_alias)) {
                $encoded_alias = \json_encode([$this->alias => $runtime_alias]);
                $wp_binary = "WP_CLI_RUNTIME_ALIAS='{$encoded_alias}' {$wp_binary} {$this->alias}";
            }
        }
        foreach ($wp_args as $k => $v) {
            if (\preg_match('#--ssh=#', $v)) {
                unset($wp_args[$k]);
            }
        }
        $wp_command = $pre_cmd . $env_vars . $wp_binary . ' ' . \implode(' ', \array_map('escapeshellarg', $wp_args));
        if (isset($bits['scheme']) && 'docker-compose-run' === $bits['scheme']) {
            $wp_command = \implode(' ', $wp_args);
        }
        $escaped_command = $this->generate_ssh_command($bits, $wp_command);
        \passthru($escaped_command, $exit_code);
        if (255 === $exit_code) {
            WP_CLI::error('Cannot connect over SSH using provided configuration.', 255);
        } else {
            exit($exit_code);
        }
    }
    /**
     * Generate a shell command from the parsed connection string.
     *
     * @param array  $bits       Parsed connection string.
     * @param string $wp_command WP-CLI command to run.
     * @return string
     */
    private function generate_ssh_command($bits, $wp_command)
    {
        $escaped_command = '';
        // Set default values.
        foreach (['scheme', 'user', 'host', 'port', 'path', 'key', 'proxyjump'] as $bit) {
            if (!isset($bits[$bit])) {
                $bits[$bit] = null;
            }
            WP_CLI::debug('SSH ' . $bit . ': ' . $bits[$bit], 'bootstrap');
        }
        $is_tty = \function_exists('posix_isatty') && \posix_isatty(\STDOUT);
        $docker_compose_v2_version_cmd = Utils\esc_cmd(Utils\force_env_on_nix_systems('docker') . ' compose %s', 'version');
        $docker_compose_cmd = !empty(Process::create($docker_compose_v2_version_cmd)->run()->stdout) ? 'docker compose' : 'docker-compose';
        if ('docker' === $bits['scheme']) {
            $command = 'docker exec %s%s%s sh -c %s';
            $escaped_command = \sprintf($command, $bits['user'] ? '--user ' . \escapeshellarg($bits['user']) . ' ' : '', $is_tty ? '-t ' : '', \escapeshellarg($bits['host']), \escapeshellarg($wp_command));
        }
        if ('docker-compose' === $bits['scheme']) {
            $command = '%s exec %s%s%s sh -c %s';
            $escaped_command = \sprintf($command, $docker_compose_cmd, $bits['user'] ? '--user ' . \escapeshellarg($bits['user']) . ' ' : '', $is_tty ? '' : '-T ', \escapeshellarg($bits['host']), \escapeshellarg($wp_command));
        }
        if ('docker-compose-run' === $bits['scheme']) {
            $command = '%s run %s%s%s %s';
            $escaped_command = \sprintf($command, $docker_compose_cmd, $bits['user'] ? '--user ' . \escapeshellarg($bits['user']) . ' ' : '', $is_tty ? '' : '-T ', \escapeshellarg($bits['host']), $wp_command);
        }
        // Vagrant ssh-config.
        if ('vagrant' === $bits['scheme']) {
            $cache = WP_CLI::get_cache();
            $cache_key = 'vagrant:' . $this->project_config_path;
            if ($cache->has($cache_key)) {
                $cached = $cache->read($cache_key);
                $values = \json_decode($cached, \true);
            } else {
                $ssh_config = \shell_exec('vagrant ssh-config 2>/dev/null');
                if (\preg_match_all('#\\s*(?<NAME>[a-zA-Z]+)\\s(?<VALUE>.+)\\s*#', $ssh_config, $matches)) {
                    $values = \array_combine($matches['NAME'], $matches['VALUE']);
                    $cache->write($cache_key, \json_encode($values));
                }
            }
            if (empty($bits['host']) || isset($values['Host']) && $bits['host'] === $values['Host']) {
                $bits['scheme'] = 'ssh';
                $bits['host'] = $values['HostName'];
                $bits['port'] = $values['Port'];
                $bits['user'] = $values['User'];
                $bits['key'] = $values['IdentityFile'];
            }
            // If we could not resolve the bits still, fallback to just `vagrant ssh`
            if ('vagrant' === $bits['scheme']) {
                $command = 'vagrant ssh -c %s %s';
                $escaped_command = \sprintf($command, \escapeshellarg($wp_command), \escapeshellarg($bits['host']));
            }
        }
        // Default scheme is SSH.
        if ('ssh' === $bits['scheme'] || null === $bits['scheme']) {
            $command = 'ssh %s %s %s';
            if ($bits['user']) {
                $bits['host'] = $bits['user'] . '@' . $bits['host'];
            }
            if (!empty($this->alias)) {
                $alias_config = isset($this->aliases[$this->alias]) ? $this->aliases[$this->alias] : \false;
                if (\is_array($alias_config)) {
                    $bits['proxyjump'] = isset($alias_config['proxyjump']) ? $alias_config['proxyjump'] : '';
                    $bits['key'] = isset($alias_config['key']) ? $alias_config['key'] : '';
                }
            }
            $command_args = [$bits['proxyjump'] ? \sprintf('-J %s', \escapeshellarg($bits['proxyjump'])) : '', $bits['port'] ? \sprintf('-p %d', (int) $bits['port']) : '', $bits['key'] ? \sprintf('-i %s', \escapeshellarg($bits['key'])) : '', $is_tty ? '-t' : '-T', WP_CLI::get_config('debug') ? '-vvv' : '-q'];
            $escaped_command = \sprintf($command, \implode(' ', \array_filter($command_args)), \escapeshellarg($bits['host']), \escapeshellarg($wp_command));
        }
        WP_CLI::debug('Running SSH command: ' . $escaped_command, 'bootstrap');
        return $escaped_command;
    }
    /**
     * Check whether a given command is disabled by the config.
     *
     * @return bool
     */
    public function is_command_disabled($command)
    {
        $path = \implode(' ', \array_slice(Dispatcher\get_path($command), 1));
        return \in_array($path, $this->config['disabled_commands'], \true);
    }
    /**
     * Returns wp-config.php code, skipping the loading of wp-settings.php.
     *
     * @param string $wp_config_path Optional. Config file path. If left empty, it tries to
     *                               locate the wp-config.php file automatically.
     *
     * @return string
     */
    public function get_wp_config_code($wp_config_path = '')
    {
        if (empty($wp_config_path)) {
            $wp_config_path = Utils\locate_wp_config();
        }
        $wp_config_code = \explode("\n", \file_get_contents($wp_config_path));
        // Detect and strip byte-order marks (BOMs).
        // This code assumes they can only be found on the first line.
        foreach (self::BYTE_ORDER_MARKS as $bom_name => $bom_sequence) {
            WP_CLI::debug("Looking for {$bom_name} BOM", 'bootstrap');
            $length = \strlen($bom_sequence);
            while (\substr($wp_config_code[0], 0, $length) === $bom_sequence) {
                WP_CLI::warning("{$bom_name} byte-order mark (BOM) detected in wp-config.php file, stripping it for parsing.");
                $wp_config_code[0] = \substr($wp_config_code[0], $length);
            }
        }
        $found_wp_settings = \false;
        $lines_to_run = [];
        foreach ($wp_config_code as $line) {
            if (\preg_match('/^\\s*require.+wp-settings\\.php/', $line)) {
                $found_wp_settings = \true;
                continue;
            }
            $lines_to_run[] = $line;
        }
        if (!$found_wp_settings) {
            WP_CLI::error('Strange wp-config.php file: wp-settings.php is not loaded directly.');
        }
        $source = \implode("\n", $lines_to_run);
        $source = Utils\replace_path_consts($source, $wp_config_path);
        return \preg_replace('|^\\s*\\<\\?php\\s*|', '', $source);
    }
    /**
     * Transparently convert deprecated syntaxes
     *
     * @param array $args
     * @param array $assoc_args
     * @return array
     */
    private static function back_compat_conversions($args, $assoc_args)
    {
        $top_level_aliases = ['sql' => 'db', 'blog' => 'site'];
        if (\count($args) > 0) {
            foreach ($top_level_aliases as $old => $new) {
                if ($old === $args[0]) {
                    $args[0] = $new;
                    break;
                }
            }
        }
        // *-meta  ->  * meta
        if (!empty($args) && \preg_match('/(post|comment|user|network)-meta/', $args[0], $matches)) {
            \array_shift($args);
            \array_unshift($args, 'meta');
            \array_unshift($args, $matches[1]);
        }
        // cli aliases  ->  cli alias list
        if (['cli', 'aliases'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1], $args[2]) = ['cli', 'alias', 'list'];
        }
        // core (multsite-)install --admin_name=  ->  --admin_user=
        if (\count($args) > 0 && 'core' === $args[0] && isset($assoc_args['admin_name'])) {
            $assoc_args['admin_user'] = $assoc_args['admin_name'];
            unset($assoc_args['admin_name']);
        }
        // core config  ->  config create
        if (['core', 'config'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1]) = ['config', 'create'];
        }
        // core language  ->  language core
        if (['core', 'language'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1]) = ['language', 'core'];
        }
        // checksum core  ->  core verify-checksums
        if (['checksum', 'core'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1]) = ['core', 'verify-checksums'];
        }
        // checksum plugin  ->  plugin verify-checksums
        if (['checksum', 'plugin'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1]) = ['plugin', 'verify-checksums'];
        }
        // site create --site_id=  ->  site create --network_id=
        if (\count($args) >= 2 && 'site' === $args[0] && 'create' === $args[1] && isset($assoc_args['site_id'])) {
            $assoc_args['network_id'] = $assoc_args['site_id'];
            unset($assoc_args['site_id']);
        }
        // {plugin|theme} update-all  ->  {plugin|theme} update --all
        if (\count($args) > 1 && \in_array($args[0], ['plugin', 'theme'], \true) && 'update-all' === $args[1]) {
            $args[1] = 'update';
            $assoc_args['all'] = \true;
        }
        // transient delete-expired  ->  transient delete --expired
        if (\count($args) > 1 && 'transient' === $args[0] && 'delete-expired' === $args[1]) {
            $args[1] = 'delete';
            $assoc_args['expired'] = \true;
        }
        // transient delete-all  ->  transient delete --all
        if (\count($args) > 1 && 'transient' === $args[0] && 'delete-all' === $args[1]) {
            $args[1] = 'delete';
            $assoc_args['all'] = \true;
        }
        // plugin scaffold  ->  scaffold plugin
        if (['plugin', 'scaffold'] === \array_slice($args, 0, 2)) {
            list($args[0], $args[1]) = [$args[1], $args[0]];
        }
        // foo --help  ->  help foo
        if (isset($assoc_args['help'])) {
            \array_unshift($args, 'help');
            unset($assoc_args['help']);
        }
        // {post|user} list --ids  ->  {post|user} list --format=ids
        if (\count($args) > 1 && \in_array($args[0], ['post', 'user'], \true) && 'list' === $args[1] && isset($assoc_args['ids'])) {
            $assoc_args['format'] = 'ids';
            unset($assoc_args['ids']);
        }
        // --json  ->  --format=json
        if (isset($assoc_args['json'])) {
            $assoc_args['format'] = 'json';
            unset($assoc_args['json']);
        }
        // --{version|info}  ->  cli {version|info}
        if (empty($args)) {
            $special_flags = ['version', 'info'];
            foreach ($special_flags as $key) {
                if (isset($assoc_args[$key])) {
                    $args = ['cli', $key];
                    unset($assoc_args[$key]);
                    break;
                }
            }
        }
        // (post|comment|site|term) url  --> (post|comment|site|term) list --*__in --field=url
        if (\count($args) >= 2 && \in_array($args[0], ['post', 'comment', 'site', 'term'], \true) && 'url' === $args[1]) {
            switch ($args[0]) {
                case 'post':
                    $post_ids = \array_slice($args, 2);
                    $args = ['post', 'list'];
                    $assoc_args['post__in'] = \implode(',', $post_ids);
                    $assoc_args['post_type'] = 'any';
                    $assoc_args['orderby'] = 'post__in';
                    $assoc_args['field'] = 'url';
                    break;
                case 'comment':
                    $comment_ids = \array_slice($args, 2);
                    $args = ['comment', 'list'];
                    $assoc_args['comment__in'] = \implode(',', $comment_ids);
                    $assoc_args['orderby'] = 'comment__in';
                    $assoc_args['field'] = 'url';
                    break;
                case 'site':
                    $site_ids = \array_slice($args, 2);
                    $args = ['site', 'list'];
                    $assoc_args['site__in'] = \implode(',', $site_ids);
                    $assoc_args['field'] = 'url';
                    break;
                case 'term':
                    $taxonomy = '';
                    if (isset($args[2])) {
                        $taxonomy = $args[2];
                    }
                    $term_ids = \array_slice($args, 3);
                    $args = ['term', 'list', $taxonomy];
                    $assoc_args['include'] = \implode(',', $term_ids);
                    $assoc_args['orderby'] = 'include';
                    $assoc_args['field'] = 'url';
                    break;
            }
        }
        // config get --[global|constant]=<global|constant> --> config get <name> --type=constant|variable
        // config get --> config list
        if (\count($args) === 2 && 'config' === $args[0] && 'get' === $args[1]) {
            if (isset($assoc_args['global'])) {
                $name = $assoc_args['global'];
                $type = 'variable';
                unset($assoc_args['global']);
            } elseif (isset($assoc_args['constant'])) {
                $name = $assoc_args['constant'];
                $type = 'constant';
                unset($assoc_args['constant']);
            }
            if (!empty($name) && !empty($type)) {
                $args[] = $name;
                $assoc_args['type'] = $type;
            } else {
                // We had a 'config get' without a '<name>', so assume 'list' was wanted.
                $args[1] = 'list';
            }
        }
        return [$args, $assoc_args];
    }
    /**
     * Whether or not the output should be rendered in color
     *
     * @return bool
     */
    public function in_color()
    {
        return $this->colorize;
    }
    public function init_colorization()
    {
        if ('auto' === $this->config['color']) {
            $this->colorize = !Utils\isPiped() && !Utils\is_windows();
        } else {
            $this->colorize = $this->config['color'];
        }
    }
    public function init_logger()
    {
        if ($this->config['quiet']) {
            $logger = new Loggers\Quiet($this->in_color());
        } else {
            $logger = new Loggers\Regular($this->in_color());
        }
        WP_CLI::set_logger($logger);
    }
    public function get_required_files()
    {
        return $this->required_files;
    }
    /**
     * Do WordPress core files exist?
     *
     * @return bool
     */
    private function wp_exists()
    {
        return \file_exists(ABSPATH . 'wp-includes/version.php');
    }
    /**
     * Are WordPress core files readable?
     *
     * @return bool
     */
    private function wp_is_readable()
    {
        return \is_readable(ABSPATH . 'wp-includes/version.php');
    }
    private function check_wp_version()
    {
        $wp_exists = $this->wp_exists();
        $wp_is_readable = $this->wp_is_readable();
        if (!$wp_exists || !$wp_is_readable) {
            $this->show_synopsis_if_composite_command();
            // If the command doesn't exist use as error.
            $args = $this->cmd_starts_with(['help']) ? \array_slice($this->arguments, 1) : $this->arguments;
            $suggestion_or_disabled = $this->find_command_to_run($args);
            if (\is_string($suggestion_or_disabled)) {
                if (!\preg_match('/disabled from the config file.$/', $suggestion_or_disabled)) {
                    WP_CLI::warning("No WordPress installation found. If the command '" . \implode(' ', $args) . "' is in a plugin or theme, pass --path=`path/to/wordpress`.");
                }
                WP_CLI::error($suggestion_or_disabled);
            }
            if ($wp_exists && !$wp_is_readable) {
                WP_CLI::error('It seems, the WordPress core files do not have the proper file permissions.');
            }
            WP_CLI::error("This does not seem to be a WordPress installation.\n" . 'The used path is: ' . ABSPATH . "\n" . 'Pass --path=`path/to/wordpress` or run `wp core download`.');
        }
        global $wp_version;
        include ABSPATH . 'wp-includes/version.php';
        $minimum_version = '3.7';
        if (\version_compare($wp_version, $minimum_version, '<')) {
            WP_CLI::error("WP-CLI needs WordPress {$minimum_version} or later to work properly. " . "The version currently installed is {$wp_version}.\n" . 'Try running `wp core download --force`.');
        }
    }
    public function init_config()
    {
        $configurator = WP_CLI::get_configurator();
        $argv = \array_slice($GLOBALS['argv'], 1);
        $this->alias = null;
        if (!empty($argv[0]) && \preg_match('#' . Configurator::ALIAS_REGEX . '#', $argv[0], $matches)) {
            $this->alias = \array_shift($argv);
        }
        // File config
        $this->global_config_path = $this->get_global_config_path();
        $this->project_config_path = $this->get_project_config_path();
        $configurator->merge_yml($this->global_config_path, $this->alias);
        $config = $configurator->to_array();
        $this->required_files['global'] = $config[0]['require'];
        $configurator->merge_yml($this->project_config_path, $this->alias);
        $config = $configurator->to_array();
        $this->required_files['project'] = $config[0]['require'];
        // Runtime config and args
        list($args, $assoc_args, $this->runtime_config) = $configurator->parse_args($argv);
        list($this->arguments, $this->assoc_args) = self::back_compat_conversions($args, $assoc_args);
        $configurator->merge_array($this->runtime_config);
        list($this->config, $this->extra_config) = $configurator->to_array();
        $this->aliases = $configurator->get_aliases();
        if (\count($this->aliases) && !isset($this->aliases['@all'])) {
            $this->aliases = \array_reverse($this->aliases);
            $this->aliases['@all'] = 'Run command against every registered alias.';
            $this->aliases = \array_reverse($this->aliases);
        }
        $this->required_files['runtime'] = $this->config['require'];
    }
    private function check_root()
    {
        if ($this->config['allow-root'] || \getenv('WP_CLI_ALLOW_ROOT')) {
            return;
            # they're aware of the risks!
        }
        if (\count($this->arguments) >= 2 && 'cli' === $this->arguments[0] && \in_array($this->arguments[1], ['update', 'info'], \true)) {
            return;
            # make it easier to update root-owned copies
        }
        if (!\function_exists('posix_geteuid')) {
            return;
            # posix functions not available
        }
        if (\posix_geteuid() !== 0) {
            return;
            # not root
        }
        WP_CLI::error("YIKES! It looks like you're running this as root. You probably meant to " . "run this as the user that your WordPress installation exists under.\n" . "\n" . "If you REALLY mean to run this as root, we won't stop you, but just " . 'bear in mind that any code on this site will then have full control of ' . "your server, making it quite DANGEROUS.\n" . "\n" . "If you'd like to continue as root, please run this again, adding this " . "flag:  --allow-root\n" . "\n" . "If you'd like to run it as the user that this site is under, you can " . "run the following to become the respective user:\n" . "\n" . "    sudo -u USER -i -- wp <command>\n" . "\n");
    }
    private function run_alias_group($aliases)
    {
        Utils\check_proc_available('group alias');
        $php_bin = \escapeshellarg(Utils\get_php_binary());
        $script_path = $GLOBALS['argv'][0];
        if (\getenv('WP_CLI_CONFIG_PATH')) {
            $config_path = \getenv('WP_CLI_CONFIG_PATH');
        } else {
            $config_path = Utils\get_home_dir() . '/.wp-cli/config.yml';
        }
        $config_path = \escapeshellarg($config_path);
        foreach ($aliases as $alias) {
            WP_CLI::log($alias);
            $args = \implode(' ', \array_map('escapeshellarg', $this->arguments));
            $assoc_args = Utils\assoc_args_to_str($this->assoc_args);
            $runtime_config = Utils\assoc_args_to_str($this->runtime_config);
            $full_command = "WP_CLI_CONFIG_PATH={$config_path} {$php_bin} {$script_path} {$alias} {$args}{$assoc_args}{$runtime_config}";
            $pipes = [];
            $proc = Utils\proc_open_compat($full_command, [\STDIN, \STDOUT, \STDERR], $pipes);
            \proc_close($proc);
        }
    }
    private function set_alias($alias)
    {
        $orig_config = $this->config;
        $alias_config = $this->aliases[$alias];
        $this->config = \array_merge($orig_config, $alias_config);
        foreach ($alias_config as $key => $_) {
            if (isset($orig_config[$key]) && !\is_null($orig_config[$key])) {
                $this->assoc_args[$key] = $orig_config[$key];
            }
        }
    }
    public function start()
    {
        // Enable PHP error reporting to stderr if testing. Will need to be re-enabled after WP loads.
        if (\getenv('BEHAT_RUN')) {
            $this->enable_error_reporting();
        }
        WP_CLI::debug($this->global_config_path_debug, 'bootstrap');
        WP_CLI::debug($this->project_config_path_debug, 'bootstrap');
        WP_CLI::debug('argv: ' . \implode(' ', $GLOBALS['argv']), 'bootstrap');
        $this->check_root();
        if ($this->alias) {
            if ('@all' === $this->alias && !isset($this->aliases['@all'])) {
                WP_CLI::error("Cannot use '@all' when no aliases are registered.");
            }
            if ('@all' === $this->alias && \is_string($this->aliases['@all'])) {
                $aliases = \array_keys($this->aliases);
                $k = \array_search('@all', $aliases, \true);
                unset($aliases[$k]);
                $this->run_alias_group($aliases);
                exit;
            }
            if (!\array_key_exists($this->alias, $this->aliases)) {
                $error_msg = "Alias '{$this->alias}' not found.";
                $suggestion = Utils\get_suggestion($this->alias, \array_keys($this->aliases), $threshold = 2);
                if ($suggestion) {
                    $error_msg .= \PHP_EOL . "Did you mean '{$suggestion}'?";
                }
                WP_CLI::error($error_msg);
            }
            // Numerically indexed means a group of aliases
            if (isset($this->aliases[$this->alias][0])) {
                $group_aliases = $this->aliases[$this->alias];
                $all_aliases = \array_keys($this->aliases);
                $diff = \array_diff($group_aliases, $all_aliases);
                if (!empty($diff)) {
                    WP_CLI::error("Group '{$this->alias}' contains one or more invalid aliases: " . \implode(', ', $diff));
                }
                $this->run_alias_group($group_aliases);
                exit;
            }
            $this->set_alias($this->alias);
        }
        if (empty($this->arguments)) {
            $this->arguments[] = 'help';
        }
        // Protect 'cli info' from most of the runtime,
        // except when the command will be run over SSH
        if ('cli' === $this->arguments[0] && !empty($this->arguments[1]) && 'info' === $this->arguments[1] && !$this->config['ssh']) {
            $this->run_command_and_exit();
        }
        if (isset($this->config['http']) && !\class_exists('HelpGent\\WP_REST_CLI\\Runner')) {
            WP_CLI::error("RESTful WP-CLI needs to be installed. Try 'wp package install wp-cli/restful'.");
        }
        if ($this->config['ssh']) {
            $this->run_ssh_command($this->config['ssh']);
            return;
        }
        // Handle --path parameter
        self::set_wp_root($this->find_wp_root());
        // First try at showing man page - if help command and either haven't found 'version.php' or 'wp-config.php' (so won't be loading WP & adding commands) or help on subcommand.
        if ($this->cmd_starts_with(['help']) && (!$this->wp_exists() || !Utils\locate_wp_config() || \count($this->arguments) > 2)) {
            $this->auto_check_update();
            $this->run_command($this->arguments, $this->assoc_args);
            // Help didn't exit so failed to find the command at this stage.
        }
        // Handle --url parameter
        $url = self::guess_url($this->config);
        if ($url) {
            WP_CLI::set_url($url);
        }
        $this->do_early_invoke('before_wp_load');
        $this->check_wp_version();
        if ($this->cmd_starts_with(['config', 'create'])) {
            $this->run_command_and_exit();
        }
        if (!Utils\locate_wp_config()) {
            WP_CLI::error("'wp-config.php' not found.\n" . 'Either create one manually or use `wp config create`.');
        }
        /*
         * Set the MySQLi error reporting off because WordPress handles its own.
         * This is due to the default value change from `MYSQLI_REPORT_OFF`
         * to `MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT` in PHP 8.1.
         */
        if (\function_exists('mysqli_report')) {
            \mysqli_report(0);
            // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_report
        }
        // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Declaring WP native constants.
        if ($this->cmd_starts_with(['core', 'is-installed']) || $this->cmd_starts_with(['core', 'update-db'])) {
            \define('WP_INSTALLING', \true);
        }
        if (\count($this->arguments) >= 2 && 'core' === $this->arguments[0] && \in_array($this->arguments[1], ['install', 'multisite-install'], \true)) {
            \define('WP_INSTALLING', \true);
            // We really need a URL here
            if (!isset($_SERVER['HTTP_HOST'])) {
                $url = 'https://example.com';
                WP_CLI::set_url($url);
            }
            if ('multisite-install' === $this->arguments[1]) {
                // need to fake some globals to skip the checks in wp-includes/ms-settings.php
                $url_parts = Utils\parse_url($url);
                self::fake_current_site_blog($url_parts);
                if (!\defined('COOKIEHASH')) {
                    \define('COOKIEHASH', \md5($url_parts['host']));
                }
            }
        }
        if ($this->cmd_starts_with(['import'])) {
            \define('WP_LOAD_IMPORTERS', \true);
            \define('WP_IMPORTING', \true);
        }
        if ($this->cmd_starts_with(['cron', 'event', 'run'])) {
            \define('DOING_CRON', \true);
        }
        // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
        $this->load_wordpress();
        $this->run_command_and_exit();
    }
    /**
     * Load WordPress, if it hasn't already been loaded
     */
    public function load_wordpress()
    {
        static $wp_cli_is_loaded;
        // Globals not explicitly globalized in WordPress
        global $site_id, $wpdb, $public, $current_site, $current_blog, $path, $shortcode_tags;
        if (!empty($wp_cli_is_loaded)) {
            return;
        }
        $wp_cli_is_loaded = \true;
        // Handle --context flag.
        $this->context_manager->switch_context($this->config);
        WP_CLI::debug('Begin WordPress load', 'bootstrap');
        WP_CLI::do_hook('before_wp_load');
        $this->check_wp_version();
        $wp_config_path = Utils\locate_wp_config();
        if (!$wp_config_path) {
            WP_CLI::error("'wp-config.php' not found.\n" . 'Either create one manually or use `wp config create`.');
        }
        WP_CLI::debug('wp-config.php path: ' . $wp_config_path, 'bootstrap');
        WP_CLI::do_hook('before_wp_config_load');
        // Load wp-config.php code, in the global scope
        $wp_cli_original_defined_vars = \get_defined_vars();
        eval($this->get_wp_config_code());
        // phpcs:ignore Squiz.PHP.Eval.Discouraged
        foreach (\get_defined_vars() as $key => $var) {
            if (\array_key_exists($key, $wp_cli_original_defined_vars) || 'wp_cli_original_defined_vars' === $key) {
                continue;
            }
            // phpcs:ignore PHPCompatibility.Variables.ForbiddenGlobalVariableVariable.NonBareVariableFound
            global ${$key};
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound
            ${$key} = $var;
        }
        $this->maybe_update_url_from_domain_constant();
        WP_CLI::do_hook('after_wp_config_load');
        $this->do_early_invoke('after_wp_config_load');
        // Prevent error notice from wp_guess_url() when core isn't installed
        if ($this->cmd_starts_with(['core', 'is-installed']) && !\defined('COOKIEHASH')) {
            \define('COOKIEHASH', \md5('wp-cli'));
        }
        // Load WP-CLI utilities
        require WP_CLI_ROOT . '/php/utils-wp.php';
        // Set up WordPress bootstrap actions and filters
        $this->setup_bootstrap_hooks();
        // Load Core, mu-plugins, plugins, themes etc.
        if (Utils\wp_version_compare('4.6-alpha-37575', '>=')) {
            if ($this->cmd_starts_with(['help'])) {
                // Hack: define `WP_DEBUG` and `WP_DEBUG_DISPLAY` to get `wpdb::bail()` to `wp_die()`.
                if (!\defined('WP_DEBUG')) {
                    \define('WP_DEBUG', \true);
                }
                if (!\defined('WP_DEBUG_DISPLAY')) {
                    \define('WP_DEBUG_DISPLAY', \true);
                }
            }
            require ABSPATH . 'wp-settings.php';
        } else {
            require WP_CLI_ROOT . '/php/wp-settings-cli.php';
        }
        // Fix memory limit. See https://core.trac.wordpress.org/ticket/14889
        // phpcs:ignore WordPress.PHP.IniSet.memory_limit_Disallowed -- This is perfectly fine for CLI usage.
        \ini_set('memory_limit', -1);
        // Load all the admin APIs, for convenience
        require ABSPATH . 'wp-admin/includes/admin.php';
        add_filter('filesystem_method', static function () {
            return 'direct';
        }, 99);
        // Re-enable PHP error reporting to stderr if testing.
        if (\getenv('BEHAT_RUN')) {
            $this->enable_error_reporting();
        }
        WP_CLI::debug('Loaded WordPress', 'bootstrap');
        WP_CLI::do_hook('after_wp_load');
    }
    private static function fake_current_site_blog($url_parts)
    {
        global $current_site, $current_blog;
        if (!isset($url_parts['path'])) {
            $url_parts['path'] = '/';
        }
        // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Intentional override.
        $current_site = (object) ['id' => 1, 'blog_id' => 1, 'domain' => $url_parts['host'], 'path' => $url_parts['path'], 'cookie_domain' => $url_parts['host'], 'site_name' => 'WordPress'];
        // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Intentional override.
        $current_blog = (object) ['blog_id' => 1, 'site_id' => 1, 'domain' => $url_parts['host'], 'path' => $url_parts['path'], 'public' => '1', 'archived' => '0', 'mature' => '0', 'spam' => '0', 'deleted' => '0', 'lang_id' => '0'];
    }
    /**
     * Called after wp-config.php is eval'd, to potentially reset `--url`
     */
    private function maybe_update_url_from_domain_constant()
    {
        if (!empty($this->config['url']) || !empty($this->config['blog'])) {
            return;
        }
        if (\defined('DOMAIN_CURRENT_SITE')) {
            $url = DOMAIN_CURRENT_SITE;
            if (\defined('PATH_CURRENT_SITE')) {
                $url .= PATH_CURRENT_SITE;
            }
            WP_CLI::set_url($url);
        }
    }
    /**
     * Set up hooks meant to run during the WordPress bootstrap process
     */
    private function setup_bootstrap_hooks()
    {
        if ($this->config['skip-plugins']) {
            $this->setup_skip_plugins_filters();
        }
        if ($this->config['skip-themes']) {
            WP_CLI::add_wp_hook('setup_theme', [$this, 'action_setup_theme_wp_cli_skip_themes'], 999);
        }
        if ($this->cmd_starts_with(['help'])) {
            // Try to trap errors on help.
            $help_handler = [$this, 'help_wp_die_handler'];
            // Avoid any cross PHP version issues by not using $this in anon function.
            WP_CLI::add_wp_hook('wp_die_handler', function () use($help_handler) {
                return $help_handler;
            });
        } else {
            WP_CLI::add_wp_hook('wp_die_handler', static function () {
                return 'HelpGent\\WP_CLI\\Utils\\wp_die_handler';
            });
        }
        // Prevent code from performing a redirect
        WP_CLI::add_wp_hook('wp_redirect', 'HelpGent\\WP_CLI\\Utils\\wp_redirect_handler');
        WP_CLI::add_wp_hook('nocache_headers', static function ($headers) {
            // WordPress might be calling nocache_headers() because of a dead db
            global $wpdb;
            if (!empty($wpdb->error)) {
                Utils\wp_die_handler($wpdb->error);
            }
            // Otherwise, WP might be calling nocache_headers() because WP isn't installed
            Utils\wp_not_installed();
            return $headers;
        });
        WP_CLI::add_wp_hook('setup_theme', static function () {
            // Polyfill is_customize_preview(), as it is needed by TwentyTwenty to
            // check for starter content.
            if (!\function_exists('HelpGent\\is_customize_preview')) {
                function is_customize_preview()
                {
                    return \false;
                }
            }
        }, 0);
        // ALTERNATE_WP_CRON might trigger a redirect, which we can't handle
        if (\defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON) {
            WP_CLI::add_wp_hook('muplugins_loaded', static function () {
                remove_action('init', 'wp_cron');
            });
        }
        // Get rid of warnings when converting single site to multisite
        if (\defined('WP_INSTALLING') && $this->is_multisite()) {
            $values = ['ms_files_rewriting' => null, 'active_sitewide_plugins' => [], '_site_transient_update_core' => null, '_site_transient_update_themes' => null, '_site_transient_update_plugins' => null, 'WPLANG' => ''];
            foreach ($values as $key => $value) {
                WP_CLI::add_wp_hook("pre_site_option_{$key}", static function () use($values, $key) {
                    return $values[$key];
                });
            }
        }
        // Always permit operations against sites, regardless of status
        WP_CLI::add_wp_hook('ms_site_check', '__return_true');
        // Always permit operations against WordPress, regardless of maintenance mode
        WP_CLI::add_wp_hook('enable_maintenance_mode', static function () {
            return \false;
        });
        // Use our own debug mode handling instead of WP core
        WP_CLI::add_wp_hook('enable_wp_debug_mode_checks', static function ($ret) {
            // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- WP core hook.
            Utils\wp_debug_mode();
            return \false;
        });
        // Never load advanced-cache.php drop-in when WP-CLI is operating
        WP_CLI::add_wp_hook('enable_loading_advanced_cache_dropin', static function () {
            return \false;
        });
        // In a multisite installation, die if unable to find site given in --url parameter
        if ($this->is_multisite()) {
            $run_on_site_not_found = \false;
            if ($this->cmd_starts_with(['cache', 'flush'])) {
                $run_on_site_not_found = 'cache flush';
            }
            if ($this->cmd_starts_with(['search-replace'])) {
                // Table-specified
                // Bits: search-replace <search> <replace> [<table>...]
                // Or not against a specific blog
                if (\count($this->arguments) > 3 || !empty($this->assoc_args['network']) || !empty($this->assoc_args['all-tables']) || !empty($this->assoc_args['all-tables-with-prefix'])) {
                    $run_on_site_not_found = 'search-replace';
                }
            }
            if ($run_on_site_not_found && Utils\wp_version_compare('4.0', '>=')) {
                WP_CLI::add_wp_hook('ms_site_not_found', static function () use($run_on_site_not_found) {
                    // esc_sql() isn't yet loaded, but needed.
                    if ('search-replace' === $run_on_site_not_found) {
                        require_once ABSPATH . WPINC . '/formatting.php';
                    }
                    // PHP 5.3 compatible implementation of run_command_and_exit().
                    $runner = WP_CLI::get_runner();
                    $runner->run_command($runner->arguments, $runner->assoc_args);
                    exit;
                }, 1);
            }
            WP_CLI::add_wp_hook('ms_site_not_found', static function ($current_site, $domain, $path) {
                $url = $domain . $path;
                $message = $url ? "Site '{$url}' not found." : 'Site not found.';
                $has_param = isset(WP_CLI::get_runner()->config['url']);
                $has_const = \defined('DOMAIN_CURRENT_SITE');
                $explanation = '';
                if ($has_param) {
                    $explanation = 'Verify `--url=<url>` matches an existing site.';
                } else {
                    $explanation = "Define DOMAIN_CURRENT_SITE in 'wp-config.php' or use `--url=<url>` to override.";
                    if ($has_const) {
                        $explanation = 'Verify DOMAIN_CURRENT_SITE matches an existing site or use `--url=<url>` to override.';
                    }
                }
                if ($explanation) {
                    $message .= ' ' . $explanation;
                }
                WP_CLI::error($message);
            }, 10, 3);
        }
        // The APC cache is not available on the command-line, so bail, to prevent cache poisoning
        WP_CLI::add_wp_hook('muplugins_loaded', static function () {
            if ($GLOBALS['_wp_using_ext_object_cache'] && \class_exists('HelpGent\\APC_Object_Cache')) {
                WP_CLI::warning('Running WP-CLI while the APC object cache is activated can result in cache corruption.');
                WP_CLI::confirm('Given the consequences, do you wish to continue?');
            }
        }, 0);
        // Handle --user parameter
        if (!\defined('WP_INSTALLING')) {
            $config = $this->config;
            WP_CLI::add_wp_hook('init', static function () use($config) {
                if (isset($config['user'])) {
                    $fetcher = new Fetchers\User();
                    $user = $fetcher->get_check($config['user']);
                    wp_set_current_user($user->ID);
                } else {
                    add_action('init', 'kses_remove_filters', 11);
                }
            }, 0);
        }
        // Avoid uncaught exception when using wp_mail() without defined $_SERVER['SERVER_NAME']
        WP_CLI::add_wp_hook('wp_mail_from', static function ($from_email) {
            if ('wordpress@' === $from_email) {
                $sitename = \strtolower(Utils\parse_url(site_url(), \PHP_URL_HOST));
                if (\substr($sitename, 0, 4) === 'www.') {
                    $sitename = \substr($sitename, 4);
                }
                $from_email = 'wordpress@' . $sitename;
            }
            return $from_email;
        });
        // Don't apply set_url_scheme in get_home_url() or get_site_url().
        WP_CLI::add_wp_hook('home_url', static function ($url, $path, $scheme, $blog_id) {
            if (empty($blog_id) || !is_multisite()) {
                $url = get_option('home');
            } else {
                switch_to_blog($blog_id);
                $url = get_option('home');
                restore_current_blog();
            }
            if ($path && \is_string($path)) {
                $url .= '/' . \ltrim($path, '/');
            }
            return $url;
        }, 0, 4);
        WP_CLI::add_wp_hook('site_url', static function ($url, $path, $scheme, $blog_id) {
            if (empty($blog_id) || !is_multisite()) {
                $url = get_option('siteurl');
            } else {
                switch_to_blog($blog_id);
                $url = get_option('siteurl');
                restore_current_blog();
            }
            if ($path && \is_string($path)) {
                $url .= '/' . \ltrim($path, '/');
            }
            return $url;
        }, 0, 4);
        // Set up hook for plugins and themes to conditionally add WP-CLI commands.
        WP_CLI::add_wp_hook('init', static function () {
            do_action('cli_init');
        });
    }
    /**
     * Set up the filters to skip the loaded plugins
     */
    private function setup_skip_plugins_filters()
    {
        $wp_cli_filter_active_plugins = static function ($plugins) {
            $skipped_plugins = WP_CLI::get_runner()->config['skip-plugins'];
            if (\true === $skipped_plugins) {
                return [];
            }
            if (!\is_array($plugins)) {
                return $plugins;
            }
            foreach ($plugins as $a => $b) {
                // active_sitewide_plugins stores plugin name as the key.
                if (\false !== \strpos(current_filter(), 'active_sitewide_plugins') && Utils\is_plugin_skipped($a)) {
                    unset($plugins[$a]);
                    // active_plugins stores plugin name as the value.
                } elseif (\false !== \strpos(current_filter(), 'active_plugins') && Utils\is_plugin_skipped($b)) {
                    unset($plugins[$a]);
                }
            }
            // Reindex because active_plugins expects a numeric index.
            if (\false !== \strpos(current_filter(), 'active_plugins')) {
                $plugins = \array_values($plugins);
            }
            return $plugins;
        };
        $hooks = ['pre_site_option_active_sitewide_plugins', 'site_option_active_sitewide_plugins', 'pre_option_active_plugins', 'option_active_plugins'];
        foreach ($hooks as $hook) {
            WP_CLI::add_wp_hook($hook, $wp_cli_filter_active_plugins, 999);
        }
        WP_CLI::add_wp_hook('plugins_loaded', static function () use($hooks, $wp_cli_filter_active_plugins) {
            foreach ($hooks as $hook) {
                remove_filter($hook, $wp_cli_filter_active_plugins, 999);
            }
        }, 0);
    }
    /**
     * Set up the filters to skip the loaded theme
     */
    public function action_setup_theme_wp_cli_skip_themes()
    {
        $wp_cli_filter_active_theme = static function ($value) {
            $skipped_themes = WP_CLI::get_runner()->config['skip-themes'];
            if (\true === $skipped_themes) {
                return '';
            }
            if (!\is_array($skipped_themes)) {
                $skipped_themes = \explode(',', $skipped_themes);
            }
            $checked_value = $value;
            // Always check against the stylesheet value
            // This ensures a child theme can be skipped when template differs
            if (\false !== \stripos(current_filter(), 'option_template')) {
                $checked_value = get_option('stylesheet');
            }
            if ('' === $checked_value || \in_array($checked_value, $skipped_themes, \true)) {
                return '';
            }
            return $value;
        };
        $hooks = ['pre_option_template', 'option_template', 'pre_option_stylesheet', 'option_stylesheet'];
        foreach ($hooks as $hook) {
            add_filter($hook, $wp_cli_filter_active_theme, 999);
        }
        // Noop memoization added in WP 6.4.
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress core global.
        $GLOBALS['wp_stylesheet_path'] = null;
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress core global.
        $GLOBALS['wp_template_path'] = null;
        // Remove theme-related actions not directly tied into the theme lifecycle.
        if (WP_CLI::get_runner()->config['skip-themes']) {
            $theme_related_actions = [
                ['init', '_register_theme_block_patterns'],
                // Block patterns registration in WP Core.
                ['init', 'gutenberg_register_theme_block_patterns'],
            ];
            foreach ($theme_related_actions as $action) {
                list($hook, $callback) = $action;
                remove_action($hook, $callback);
            }
        }
        // Clean up after the TEMPLATEPATH and STYLESHEETPATH constants are defined
        WP_CLI::add_wp_hook('after_setup_theme', static function () use($hooks, $wp_cli_filter_active_theme) {
            foreach ($hooks as $hook) {
                remove_filter($hook, $wp_cli_filter_active_theme, 999);
            }
            // Noop memoization added in WP 6.4 again.
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress core global.
            $GLOBALS['wp_stylesheet_path'] = null;
            // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- WordPress core global.
            $GLOBALS['wp_template_path'] = null;
        }, 0);
    }
    /**
     * Whether or not this WordPress installation is multisite.
     *
     * For use after wp-config.php has loaded, but before the rest of WordPress
     * is loaded.
     */
    private function is_multisite()
    {
        if (\defined('MULTISITE')) {
            return MULTISITE;
        }
        if (\defined('SUBDOMAIN_INSTALL') || \defined('VHOST') || \defined('SUNRISE')) {
            return \true;
        }
        return \false;
    }
    /**
     * Error handler for `wp_die()` when the command is help to try to trap errors (db connection failure in particular) during WordPress load.
     */
    public function help_wp_die_handler($message)
    {
        $help_exit_warning = 'Error during WordPress load.';
        if ($message instanceof WP_Error) {
            $help_exit_warning = Utils\wp_clean_error_message($message->get_error_message());
        } elseif (\is_string($message)) {
            $help_exit_warning = Utils\wp_clean_error_message($message);
        }
        $this->run_command_and_exit($help_exit_warning);
    }
    /**
     * Check whether there's a WP-CLI update available, and suggest update if so.
     */
    private function auto_check_update()
    {
        // `wp cli update` only works with Phars at this time.
        if (!Utils\inside_phar()) {
            return;
        }
        $existing_phar = \realpath($_SERVER['argv'][0]);
        // Phar needs to be writable to be easily updateable.
        if (!\is_writable($existing_phar) || !\is_writable(\dirname($existing_phar))) {
            return;
        }
        // Only check for update when a human is operating.
        if (!\function_exists('posix_isatty') || !\posix_isatty(\STDOUT)) {
            return;
        }
        // Allow hosts and other providers to disable automatic check update.
        if (\getenv('WP_CLI_DISABLE_AUTO_CHECK_UPDATE')) {
            return;
        }
        // Permit configuration of number of days between checks.
        $days_between_checks = \getenv('WP_CLI_AUTO_CHECK_UPDATE_DAYS');
        if (\false === $days_between_checks) {
            $days_between_checks = 1;
        }
        $cache = WP_CLI::get_cache();
        $cache_key = 'wp-cli-update-check';
        // Bail early on the first check, so we don't always check on an unwritable cache.
        if (!$cache->has($cache_key)) {
            $cache->write($cache_key, \time());
            return;
        }
        // Bail if last check is still within our update check time period.
        $last_check = (int) $cache->read($cache_key);
        if (\time() - 24 * 60 * 60 * $days_between_checks < $last_check) {
            return;
        }
        // In case the operation fails, ensure the timestamp has been updated.
        $cache->write($cache_key, \time());
        // Check whether any updates are available.
        \ob_start();
        WP_CLI::run_command(['cli', 'check-update'], ['format' => 'count']);
        $count = \ob_get_clean();
        if (!$count) {
            return;
        }
        // Looks like an update is available, so let's prompt to update.
        WP_CLI::run_command(['cli', 'update']);
        // If the Phar was replaced, we can't proceed with the original process.
        exit;
    }
    /**
     * Get a suggestion on similar (sub)commands when the user entered an
     * unknown (sub)command.
     *
     * @param string                $entry        User entry that didn't match an
     *                                            existing command.
     * @param CompositeCommand|null $root_command Root command to start search for
     *                                            suggestions at.
     *
     * @return string Suggestion that fits the user entry, or an empty string.
     */
    private function get_subcommand_suggestion($entry, $root_command = null)
    {
        $commands = [];
        if ($root_command instanceof CompositeCommand === \false) {
            $root_command = WP_CLI::get_root_command();
        }
        $this->enumerate_commands($root_command, $commands);
        return Utils\get_suggestion($entry, $commands, $threshold = 2);
    }
    /**
     * Recursive method to enumerate all known commands.
     *
     * @param CompositeCommand $command Composite command to recurse over.
     * @param array            $list    Reference to list accumulating results.
     * @param string           $parent  Parent command to use as prefix.
     */
    private function enumerate_commands(CompositeCommand $command, array &$list, $parent = '')
    {
        foreach ($command->get_subcommands() as $subcommand) {
            /** @var CompositeCommand $subcommand */
            $command_string = empty($parent) ? $subcommand->get_name() : "{$parent} {$subcommand->get_name()}";
            $list[] = $command_string;
            $this->enumerate_commands($subcommand, $list, $command_string);
        }
    }
    /**
     * Enables (almost) full PHP error reporting to stderr.
     */
    private function enable_error_reporting()
    {
        if (\E_ALL !== \error_reporting()) {
            // Don't enable E_DEPRECATED as old versions of WP use PHP 4 style constructors and the mysql extension.
            \error_reporting(\E_ALL & ~\E_DEPRECATED);
        }
        \ini_set('display_errors', 'stderr');
        // phpcs:ignore WordPress.PHP.IniSet.display_errors_Disallowed
    }
}
