529 lines
11 KiB
PHP
529 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* Development Toolkit
|
|
*
|
|
* @package WP_Ultimo
|
|
* @subpackage Development
|
|
* @since 2.0.11
|
|
*/
|
|
|
|
namespace WP_Ultimo\Development;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Toolkit API Helpers
|
|
*
|
|
* @since 2.0.11
|
|
*/
|
|
class Toolkit {
|
|
|
|
use \WP_Ultimo\Traits\Singleton;
|
|
|
|
/**
|
|
* The parameter used to activate sandbox.
|
|
*/
|
|
const LISTENER_PARAM = 'development';
|
|
|
|
/**
|
|
* List of registered WordPress hooks.
|
|
*
|
|
* @since 2.0.11
|
|
* @var array
|
|
*/
|
|
protected $wp_hooks = array();
|
|
|
|
/**
|
|
* Listeners registered.
|
|
*
|
|
* @since 2.0.11
|
|
* @var array
|
|
*/
|
|
protected $listeners = array();
|
|
|
|
/**
|
|
* Keeps track of number of listeners run.
|
|
*
|
|
* @since 2.0.11
|
|
* @var integer
|
|
*/
|
|
protected $run = 0;
|
|
|
|
/**
|
|
* If we should die or not.
|
|
*
|
|
* @since 2.0.11
|
|
* @var boolean
|
|
*/
|
|
protected $should_die = false;
|
|
|
|
/**
|
|
* Keeps track if we already displayed the footer.
|
|
*
|
|
* @since 2.0.11
|
|
* @var boolean
|
|
*/
|
|
protected $displayed_footer = false;
|
|
|
|
/**
|
|
* Initialize the development hooks and the sandbox.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
public function init() {
|
|
|
|
$this->register_default_listeners();
|
|
|
|
$this->load_sandbox();
|
|
|
|
add_filter('qm/collectors', array($this, 'register_collector_overview'), 1, 2);
|
|
|
|
add_filter('qm/outputter/html', array($this, 'add_overview_panel'), 50, 2);
|
|
|
|
} // end init;
|
|
|
|
/**
|
|
* Registers the default listeners.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
protected function register_default_listeners() {
|
|
|
|
/**
|
|
* In the case of an empty listener name (?development)
|
|
* adds the listener link.
|
|
*/
|
|
$this->listen('index', array($this, 'render_listeners_menu'), 'init');
|
|
|
|
/**
|
|
* Saves the rest endpoints to the mpb data folder
|
|
* on the listener save-rest-arguments.
|
|
*/
|
|
$this->listen('save-rest-arguments', '__return_false', 'wu_rest_get_endpoint_schema_use_cache');
|
|
|
|
$this->listen('save-rest-arguments', array($this, 'save_route_arguments'), 'wu_rest_register_routes_general');
|
|
|
|
$this->listen('save-rest-arguments', array($this, 'save_route_arguments'), 'wu_rest_register_routes_with_id');
|
|
|
|
} // end register_default_listeners;
|
|
|
|
/**
|
|
* Save route arguments to files to deal with opcache.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param array $routes Routes passed to WordPress.
|
|
* @param string $path The rest api route path.
|
|
* @param string $context The context. Can be either 'create' or 'update'.
|
|
* @param \WP_Ultimo\Traits\Rest_Api $manager The model manager instance.
|
|
* @return void
|
|
*/
|
|
public function save_route_arguments($routes, $path, $context, $manager) {
|
|
|
|
$class_name = str_replace('_', '-', strtolower($path));
|
|
|
|
$args = $manager->get_arguments_schema($context === 'update');
|
|
|
|
file_put_contents(wu_path("/mpb/data/endpoint/.endpoint-$class_name-$context"), json_encode($args)); // phpcs:ignore
|
|
|
|
} // end save_route_arguments;
|
|
|
|
/**
|
|
* Adds a listener for development purposes.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string $hook The name of the listener hook.
|
|
* @param callable $callback The callback to be run.
|
|
* @param string $wp_hook The WordPress hook to add this listener to.
|
|
* @param integer $order The order to be run.
|
|
* @return mixed
|
|
*/
|
|
public function listen($hook, $callback, $wp_hook = 'wp_ultimo_load', $order = 1) {
|
|
|
|
$this->listeners[$hook] = ($this->listeners[$hook] ?? 0) + 1;
|
|
|
|
$this->wp_hooks[$wp_hook] = 1;
|
|
|
|
$action = $this->get_action($hook, $wp_hook);
|
|
|
|
$order = $this->get_order($hook, $order);
|
|
|
|
add_action($action, function(...$arguments) use ($callback, $action, $order) {
|
|
|
|
$timing_id = sprintf('%s_%s_%s', $action, $this->run + 1, $order);
|
|
|
|
// phpcs:ignore
|
|
do_action('qm/start', $timing_id);
|
|
|
|
$result = call_user_func_array($callback, $arguments);
|
|
|
|
// phpcs:ignore
|
|
do_action('qm/stop', $timing_id);
|
|
|
|
$this->run++;
|
|
|
|
return $result;
|
|
|
|
}, $order, 100);
|
|
|
|
return $this;
|
|
|
|
} // end listen;
|
|
|
|
/**
|
|
* Sets flags for the development environment.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param array $configs The config constants.
|
|
* @return void
|
|
*/
|
|
public function config($configs = array()) {
|
|
|
|
$allowed_configs = array(
|
|
'QM_DARK_MODE',
|
|
'QM_DB_EXPENSIVE',
|
|
'QM_DISABLED',
|
|
'QM_DISABLE_ERROR_HANDLER',
|
|
'QM_ENABLE_CAPS_PANEL',
|
|
'QM_HIDE_CORE_ACTIONS',
|
|
'QM_HIDE_SELF',
|
|
'QM_NO_JQUERY',
|
|
'QM_SHOW_ALL_HOOKS',
|
|
);
|
|
|
|
foreach ($configs as $constant_name => $constant_value) {
|
|
|
|
if (in_array($constant_name, $allowed_configs, true)) {
|
|
|
|
// phpcs:ignore
|
|
defined($constant_name) === false && define($constant_name, $constant_value);
|
|
|
|
} // end if;
|
|
|
|
} // end foreach;
|
|
|
|
} // end config;
|
|
|
|
/**
|
|
* Marks the development environment to finish execution after all listeners are run.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string|bool $should_die False to prevent it from dying, or a WordPress hook to wait before dying.
|
|
* @return void
|
|
*/
|
|
public function die($should_die = true) {
|
|
|
|
if ($should_die === true) {
|
|
|
|
$should_die = is_admin() ? 'admin_enqueue_scripts' : 'wp_enqueue_scripts';
|
|
|
|
} // end if;
|
|
|
|
$this->should_die = $should_die;
|
|
|
|
} // end die;
|
|
|
|
/**
|
|
* Run a registered listener.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string $wp_hook The WordPress hook to run.
|
|
* @param array $arguments The arguments passed to the WordPress hook.
|
|
* @return mixed
|
|
*/
|
|
public function run_listener($wp_hook, $arguments = array()) {
|
|
|
|
$listener = wu_request(self::LISTENER_PARAM, 'no-dev-param');
|
|
|
|
if ($listener === 'no-dev-param') {
|
|
|
|
return current($arguments);
|
|
|
|
} elseif ($listener === '') {
|
|
|
|
$listener = 'index';
|
|
|
|
} // end if;
|
|
|
|
$action = $this->get_action($listener, $wp_hook);
|
|
|
|
return do_action_ref_array($action, $arguments); // phpcs:ignore
|
|
|
|
} // end run_listener;
|
|
|
|
/**
|
|
* Loads the sandbox environment.
|
|
*
|
|
* Checks for the existence of a development.php file
|
|
* inside the root folder and loads it if it does.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
protected function load_sandbox() {
|
|
|
|
$dev_file = wu_path_join(dirname((string) WP_ULTIMO_SUNRISE_FILE, 2), 'development.php');
|
|
|
|
if (file_exists($dev_file)) {
|
|
|
|
/**
|
|
* Make the $toolkit variable available
|
|
* to the development.php file.
|
|
*/
|
|
$toolkit = $this;
|
|
|
|
include $dev_file;
|
|
|
|
} // end if;
|
|
|
|
$wp_hooks = array_keys($this->wp_hooks);
|
|
|
|
foreach ($wp_hooks as $wp_hook) {
|
|
|
|
add_action($wp_hook, fn(...$arguments) => $this->run_listener($wp_hook, $arguments), 0, 100);
|
|
|
|
} // end foreach;
|
|
|
|
add_action('shutdown', array($this, 'setup_query_monitor'));
|
|
|
|
if ($this->should_die) {
|
|
|
|
$this->dump_and_die(end($wp_hooks));
|
|
|
|
} // end if;
|
|
|
|
} // end load_sandbox;
|
|
|
|
/**
|
|
* Setups the query monitor integration.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
public function setup_query_monitor() {
|
|
|
|
if (class_exists('\QM_Dispatchers')) {
|
|
|
|
// phpcs:ignore
|
|
do_action('qm/debug', sprintf('Actions with listeners: %s', $this->get_wp_hooks_list()));
|
|
|
|
// phpcs:ignore
|
|
do_action('qm/debug', sprintf('Listeners: %s', $this->get_listeners_list()));
|
|
|
|
// phpcs:ignore
|
|
$dispatcher = \QM_Dispatchers::get('html');
|
|
|
|
if (!$dispatcher) {
|
|
|
|
return;
|
|
|
|
} // end if;
|
|
|
|
$dispatcher->did_footer = true;
|
|
|
|
if ($this->should_die && $this->run) {
|
|
|
|
$this->enqueue_scripts($dispatcher);
|
|
|
|
} // end if;
|
|
|
|
} // end if;
|
|
|
|
} // end setup_query_monitor;
|
|
|
|
/**
|
|
* Registers the collector overview.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param array $collectors The collectors array.
|
|
* @param \QueryMonitor $qm The Query Monitor instance.
|
|
* @return array
|
|
*/
|
|
public function register_collector_overview(array $collectors, \QueryMonitor $qm) {
|
|
|
|
$collectors['wp-ultimo'] = new Query_Monitor\Collectors\Collector_Overview();
|
|
|
|
return $collectors;
|
|
|
|
} // end register_collector_overview;
|
|
|
|
/**
|
|
* Adds the overview panel.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param array $output Array containing all registered output-generators.
|
|
* @return array
|
|
*/
|
|
public function add_overview_panel($output) {
|
|
|
|
$collector = \QM_Collectors::get('wp-ultimo');
|
|
|
|
$output['wp-ultimo'] = new Query_Monitor\Panel\Overview($collector);
|
|
|
|
return $output;
|
|
|
|
} // end add_overview_panel;
|
|
|
|
/**
|
|
* Manually enqueues query monitor and WP Multisite WaaS styles.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param \QM_Dispatcher $dispatcher The Query Monitor dispatcher object.
|
|
* @return void
|
|
*/
|
|
protected function enqueue_scripts($dispatcher) {
|
|
|
|
echo sprintf('<link rel="stylesheet" id="toolkit" href="%s" type="text/css" media="all">', wu_url('inc/development/assets/development.css'));
|
|
|
|
wp_print_styles(array(
|
|
'wu-admin',
|
|
));
|
|
|
|
$dispatcher->manually_print_assets(); // phpcs:ignore
|
|
|
|
} // end enqueue_scripts;
|
|
|
|
/**
|
|
* Returns a comma-separated list of listeners.
|
|
*
|
|
* @since 2.0.11
|
|
*/
|
|
protected function get_listeners_list(): string {
|
|
|
|
$listener_names = array_keys($this->listeners);
|
|
|
|
return implode(', ', $listener_names);
|
|
|
|
} // end get_listeners_list;
|
|
|
|
/**
|
|
* Returns a comma-separated list of WordPress hooks.
|
|
*
|
|
* @since 2.0.11
|
|
*/
|
|
protected function get_wp_hooks_list(): string {
|
|
|
|
$wp_hook_names = array_keys($this->wp_hooks);
|
|
|
|
return implode(', ', $wp_hook_names);
|
|
|
|
} // end get_wp_hooks_list;
|
|
|
|
/**
|
|
* Dumps the development content and kill the execution.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string $hook Hook to die on.
|
|
* @return void
|
|
*/
|
|
protected function dump_and_die($hook) {
|
|
|
|
add_action($hook, function() use ($hook) {
|
|
|
|
if (did_action($this->should_die) && $this->run) {
|
|
|
|
$this->render_listeners_menu();
|
|
|
|
do_action('shutdown'); // phpcs:ignore
|
|
|
|
$message = sprintf('Execution killed on %s.', $hook);
|
|
|
|
do_action('qm/info', $message); // phpcs:ignore
|
|
|
|
die();
|
|
|
|
} else {
|
|
|
|
return $this->dump_and_die($this->should_die);
|
|
|
|
} // end if;
|
|
|
|
}, 110);
|
|
|
|
} // end dump_and_die;
|
|
|
|
/**
|
|
* Get the order of a newly added listener.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string $hook The listener action name.
|
|
* @param integer $order The order of execution.
|
|
* @return integer
|
|
*/
|
|
protected function get_order($hook, $order = 1) {
|
|
|
|
return 10 + (absint($this->listeners[$hook]) * $order * 10) + 5;
|
|
|
|
} // end get_order;
|
|
|
|
/**
|
|
* Get the action name based on the listener hook and WP action.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param string $hook The listener action name.
|
|
* @param string $wp_hook The WordPress hook.
|
|
*/
|
|
protected function get_action($hook, $wp_hook): string {
|
|
|
|
$hook = str_replace('-', '_', $hook);
|
|
|
|
return sprintf('wu_sandbox_run_%s_%s', $hook, $wp_hook);
|
|
|
|
} // end get_action;
|
|
|
|
/**
|
|
* Render the list of listeners with links.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
public function render_listeners_menu() {
|
|
|
|
/**
|
|
* Make sure we display it only once.
|
|
*/
|
|
if ($this->displayed_footer) {
|
|
|
|
return;
|
|
|
|
} // end if;
|
|
|
|
// phpcs:disable
|
|
echo '
|
|
<div class="wu-styling">
|
|
<strong class="wu-block wu-mb-2 wu-mt-10">Listeners</strong>
|
|
<ul id="listeners">';
|
|
|
|
foreach (array_keys($this->listeners) as $listener) {
|
|
|
|
echo sprintf(
|
|
'<li><a href="%s">→ Listener "%s"</a></li>',
|
|
add_query_arg(self::LISTENER_PARAM, $listener),
|
|
$listener
|
|
);
|
|
|
|
} // end foreach;
|
|
|
|
echo '
|
|
</ul>
|
|
</div>'; // phpcs: enable
|
|
|
|
$this->displayed_footer = true;
|
|
|
|
} // end render_listeners_menu;
|
|
|
|
} // end class Toolkit;
|