/** * Constructor. * * @param object|array $instance Required at all times. * A parent object instance, which contains the parent's `$instance`, * or a new `$instance` array. * * @throws \exception If there is a missing and/or invalid `$instance`. */ public function __construct($instance) { if ($instance instanceof framework) { $plugin_root_ns = $instance->instance->plugin_root_ns; } else { if (is_array($instance) && !empty($instance['plugin_root_ns'])) { $plugin_root_ns = (string) $instance['plugin_root_ns']; } } if (empty($plugin_root_ns) || !isset($GLOBALS[$plugin_root_ns]) || !$GLOBALS[$plugin_root_ns] instanceof framework) { throw new \exception(sprintf(stub::__('Invalid `$instance` to constructor: `%1$s`'), print_r($instance, TRUE))); } $this->plugin = $GLOBALS[$plugin_root_ns]; }
/** * Core class constructor. * * @param framework|array $instance Required at all times. * A framework class object instance containing a parent's `$instance`. * Or, a new `$instance` array with the elements listed below. * * An array MUST contain the following elements: * • `(string)$instance['plugin_name']` — Name of current plugin. * • `(string)$instance['plugin_var_ns']` — Plugin variable namespace. * • `(string)$instance['plugin_cap']` — Capability required to manage the plugin. * • `(string)$instance['plugin_root_ns']` — Root namespace of current plugin. * • `(string)$instance['plugin_version']` — Version of current plugin. * • `(string)$instance['plugin_dir']` — Current plugin directory. * • `(string)$instance['plugin_site']` — Plugin site URL (http://). * * @throws \exception If there is a missing and/or invalid `$instance`. * @throws \exception If there are NOT 7 configuration elements in an `$instance` array. * * @throws \exception If the plugin's root namespace does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_plugin_root_ns} * * @throws \exception If the plugin's variable namespace does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_plugin_var_ns} * * @throws \exception If the plugin's capability does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_plugin_cap} * * @throws \exception If the plugin's version does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_plugin_version} * * @throws \exception If the plugin's directory is missing (e.g. the plugin's directory MUST actually exist). * In addition, the plugin's directory MUST contain a main `classes` directory with the name `classes`. * In addition, the plugin's directory MUST contain a main plugin file with the name `plugin.php`. * * @throws \exception If the plugin's site URL is NOT valid (MUST start with `http://.+`). * * @throws \exception If the namespace\class path does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_plugin_ns_class} * * @throws \exception If the core namespace does NOT match this regex validation pattern. * See: {@link stub::$regex_valid_core_ns_version} * * @public A magic/overload constructor MUST always remain public. * * @extenders If a class extender creates its own constructor, * it MUST collect an `$instance`, and it MUST call upon this core constructor using: * `parent::__construct($instance)`. * * @note This should NOT rely directly or indirectly on any other core class objects. * Any static properties/methods in the XDaRk Core stub will be fine to use though. * In addition — once the object if fully constructed; we can use anything :-) */ public function __construct($instance) { $this->hooks =& static::___hooks(); $this->static =& static::___static(); if ($instance instanceof framework) { $parent_instance = $instance->instance; } else { $parent_instance = null; } // No parent config. $ns_class = get_class($this); // Always NEED `$this` for cache entry. if ($parent_instance) { $cache_entry = $parent_instance->plugin_root_ns . $ns_class; if (isset(static::$___instance_cache[$cache_entry])) { $this->instance = static::$___instance_cache[$cache_entry]; return; // Using cache. Nothing more to do here. } $this->instance = clone $parent_instance; } else { if (is_array($instance) && count($instance) === 7 && !empty($instance['plugin_name']) && is_string($instance['plugin_name']) && !empty($instance['plugin_root_ns']) && is_string($instance['plugin_root_ns']) && preg_match(stub::$regex_valid_plugin_root_ns, $instance['plugin_root_ns']) && !empty($instance['plugin_var_ns']) && is_string($instance['plugin_var_ns']) && preg_match(stub::$regex_valid_plugin_var_ns, $instance['plugin_var_ns']) && !empty($instance['plugin_version']) && is_string($instance['plugin_version']) && preg_match(stub::$regex_valid_plugin_version, $instance['plugin_version']) && !empty($instance['plugin_cap']) && is_string($instance['plugin_cap']) && preg_match(stub::$regex_valid_plugin_cap, $instance['plugin_cap']) && !empty($instance['plugin_dir']) && is_string($instance['plugin_dir']) && is_dir($instance['plugin_dir'] = stub::n_dir_seps($instance['plugin_dir'])) && is_file($instance['plugin_dir'] . '/plugin.php') && is_dir($instance['plugin_dir'] . '/classes') && !empty($instance['plugin_site']) && is_string($instance['plugin_site']) && ($instance['plugin_site'] = rtrim($instance['plugin_site'], '/')) && preg_match('/^http\\:\\/\\/.+/i', $instance['plugin_site'])) { $cache_entry = $instance['plugin_root_ns'] . $ns_class; if (isset(static::$___instance_cache[$cache_entry])) { $this->instance = static::$___instance_cache[$cache_entry]; return; // Using cache (nothing more to do here). } $this->instance = new instance($instance); } else { throw new \exception(sprintf(stub::__('Invalid `$instance` to constructor: `%1$s`'), print_r($instance, true))); } } // Mostly from core stub. These properties will NOT change from one class instance to another. if (!$parent_instance) { // Core name & core site; begins with `http://`. $this->instance->core_name = stub::$core_name; $this->instance->core_site = stub::$core_site; // Core directories; mostly from stub. $this->instance->local_wp_dev_dir = stub::$local_wp_dev_dir; $this->instance->local_core_repo_dir = stub::$local_core_repo_dir; $this->instance->core_dir = stub::n_dir_seps_up(__FILE__, 3); $this->instance->core_classes_dir = $this->instance->core_dir . '/classes'; // Based on `stub::$core_prefix`. $this->instance->core_prefix = stub::$core_prefix; $this->instance->core_prefix_with_dashes = stub::$core_prefix_with_dashes; // Based on `stub::$core_ns`. $this->instance->core_ns = stub::$core_ns; $this->instance->core_ns_prefix = stub::$core_ns_prefix; $this->instance->core_ns_with_dashes = stub::$core_ns_with_dashes; // Based on `stub::$core_ns_stub`. $this->instance->{stub::$core_ns_stub} = stub::$core_ns_stub; $this->instance->core_ns_stub = stub::$core_ns_stub; $this->instance->core_ns_stub_with_dashes = stub::$core_ns_stub_with_dashes; // Based on `stub::$core_ns_stub_v`. $this->instance->core_ns_stub_v = stub::$core_ns_stub_v; $this->instance->core_ns_stub_v_with_dashes = stub::$core_ns_stub_v_with_dashes; // Based on `stub::$core_version`. $this->instance->core_version = stub::$core_version; $this->instance->core_version_with_underscores = stub::$core_version_with_underscores; $this->instance->core_version_with_dashes = stub::$core_version_with_dashes; // Check core `namespace` for validation issues. if (!preg_match(stub::$regex_valid_core_ns_version, $this->instance->core_ns)) { throw new \exception(sprintf(stub::__('Invalid core namespace: `%1$s`.'), $this->instance->core_ns)); } } // Check `namespace\sub_namespace\class` for validation issues. if (!preg_match(stub::$regex_valid_plugin_ns_class, $this->instance->ns_class = $ns_class)) { throw new \exception(sprintf(stub::__('Namespace\\class contains invalid chars: `%1$s`.'), $this->instance->ns_class)); } // The `namespace\sub_namespace` for `$this` class. $this->instance->ns = substr($this->instance->ns_class, 0, strrpos($this->instance->ns_class, '\\')); // The `sub_namespace\class` for `$this` class. $this->instance->sub_ns_class = ltrim(strstr($this->instance->ns_class, '\\'), '\\'); $this->instance->sub_ns_class_with_underscores = stub::with_underscores($this->instance->sub_ns_class); $this->instance->sub_ns_class_with_dashes = stub::with_dashes($this->instance->sub_ns_class); // The `namespace\sub_namespace\class` for `$this` class. $this->instance->ns_class_prefix = '\\' . $this->instance->ns_class; $this->instance->ns_class_with_underscores = stub::with_underscores($this->instance->ns_class); $this->instance->ns_class_with_dashes = stub::with_dashes($this->instance->ns_class); $this->instance->ns_class_basename = basename(str_replace('\\', '/', $this->instance->ns_class)); // Only if we're NOT in the same namespace as the `$parent_instance`. if (!$parent_instance || $parent_instance->ns !== $this->instance->ns) { // The `namespace\sub_namespace` for `$this` class. $this->instance->ns_prefix = '\\' . $this->instance->ns; $this->instance->ns_with_underscores = stub::with_underscores($this->instance->ns); $this->instance->ns_with_dashes = stub::with_dashes($this->instance->ns); // The `namespace` for `$this` class. $this->instance->root_ns = strstr($this->instance->ns_class, '\\', true); $this->instance->root_ns_prefix = '\\' . $this->instance->root_ns; $this->instance->root_ns_with_dashes = stub::with_dashes($this->instance->root_ns); } // Based entirely on current plugin. These properties will NOT change from one class instance to another. if (!$parent_instance) { // Plugin name & plugin site; begins with `http://`. $this->instance->plugin_name = $this->instance->plugin_name; $this->instance->plugin_site = $this->instance->plugin_site; // Based on `plugin_version`. $this->instance->plugin_version = $this->instance->plugin_version; $this->instance->plugin_version_with_underscores = stub::with_underscores($this->instance->plugin_version); $this->instance->plugin_version_with_dashes = stub::with_dashes($this->instance->plugin_version); // Based on `plugin_var_ns` (which serves a few different purposes). $this->instance->plugin_var_ns = $this->instance->plugin_var_ns; $this->instance->plugin_var_ns_with_dashes = stub::with_dashes($this->instance->plugin_var_ns); // Based on `plugin_var_ns` (which serves a few different purposes). $this->instance->plugin_prefix = $this->instance->plugin_var_ns . '_'; $this->instance->plugin_prefix_with_dashes = stub::with_dashes($this->instance->plugin_prefix); if ($this->instance->plugin_root_ns === $this->instance->core_ns) { $this->instance->plugin_prefix = $this->instance->core_prefix; $this->instance->plugin_prefix_with_dashes = $this->instance->core_prefix_with_dashes; } // Based on `plugin_cap` (used for a default set of access controls). $this->instance->plugin_cap = $this->instance->plugin_cap; $this->instance->plugin_cap_with_dashes = stub::with_dashes($this->instance->plugin_cap); // Based on plugin's root `namespace` (via `plugin_root_ns`). $this->instance->plugin_root_ns = $this->instance->plugin_root_ns; $this->instance->plugin_root_ns_prefix = '\\' . $this->instance->plugin_root_ns; $this->instance->plugin_root_ns_with_dashes = stub::with_dashes($this->instance->plugin_root_ns); // Based on plugin's root `namespace` (via `plugin_root_ns`). $this->instance->plugin_root_ns_stub = $this->instance->plugin_root_ns; $this->instance->plugin_root_ns_stub_with_dashes = $this->instance->plugin_root_ns_with_dashes; if ($this->instance->plugin_root_ns === $this->instance->core_ns) { $this->instance->plugin_root_ns_stub = $this->instance->core_ns_stub; $this->instance->plugin_root_ns_stub_with_dashes = $this->instance->core_ns_stub_with_dashes; } // Based on the plugin's directory (i.e. `plugin_dir`). $this->instance->plugin_dir = $this->instance->plugin_dir; $this->instance->plugin_dir_basename = basename($this->instance->plugin_dir); $this->instance->plugin_dir_file_basename = $this->instance->plugin_dir_basename . '/plugin.php'; // Based on the plugin's directory (i.e. `plugin_dir`). if ($this->instance->plugin_root_ns === $this->instance->core_ns) { $this->instance->plugin_data_dir = stub::get_temp_dir() . '/' . $this->instance->core_ns_stub_with_dashes . '-data'; } else { $this->instance->plugin_data_dir = stub::n_dir_seps(WP_CONTENT_DIR) . '/data/' . $this->instance->plugin_dir_basename; } // Based on the plugin's directory (i.e. `plugin_dir`). $this->instance->plugin_data_dir = apply_filters($this->instance->plugin_root_ns_stub . '__data_dir', $this->instance->plugin_data_dir); // Based on the plugin's directory (i.e. `plugin_dir`). $this->instance->plugin_file = $this->instance->plugin_dir . '/plugin.php'; $this->instance->plugin_classes_dir = $this->instance->plugin_dir . '/classes'; $this->instance->plugin_api_class_file = $this->instance->plugin_classes_dir . '/' . $this->instance->plugin_root_ns_with_dashes . '.php'; // Based on the current plugin; we establish properties for a pro add-on (optional). $this->instance->plugin_pro_var = $this->instance->plugin_root_ns . '_pro'; $this->instance->plugin_pro_dir = $this->instance->plugin_dir . '-pro'; if (stripos($this->instance->plugin_pro_dir, 'phar://') === 0) { // In case of core. $this->instance->plugin_pro_dir = substr($this->instance->plugin_pro_dir, 7); } $this->instance->plugin_pro_dir_basename = basename($this->instance->plugin_pro_dir); $this->instance->plugin_pro_dir_file_basename = $this->instance->plugin_pro_dir_basename . '/plugin.php'; $this->instance->plugin_pro_file = $this->instance->plugin_pro_dir . '/plugin.php'; $this->instance->plugin_pro_classes_dir = $this->instance->plugin_pro_dir . '/classes'; $this->instance->plugin_pro_class_file = $this->instance->plugin_pro_classes_dir . '/' . $this->instance->plugin_root_ns_with_dashes . '/pro.php'; } // Based on `plugin_root_ns_stub`. // Also on `namespace\sub_namespace\class` for `$this` class. // Here we swap out the real root namespace, in favor of the plugin's root namespace. // This is helpful when we need to build strings for hooks, filters, contextual slugs, and the like. $this->instance->plugin_stub_as_root_ns_class = $this->instance->plugin_root_ns_stub . substr(preg_replace('/_x$/', '', $this->instance->ns_class), $root_ns_length = strlen($this->instance->root_ns)); $this->instance->plugin_stub_as_root_ns_class_with_underscores = stub::with_underscores($this->instance->plugin_stub_as_root_ns_class); $this->instance->plugin_stub_as_root_ns_class_with_dashes = stub::with_dashes($this->instance->plugin_stub_as_root_ns_class); // Based on `plugin_root_ns_stub`. // Also on `namespace\sub_namespace` for `$this` class. // Here we swap out the real root namespace, in favor of the plugin's root namespace. // This is helpful when we need to build strings for hooks, filters, contextual slugs, and the like. $this->instance->plugin_stub_as_root_ns = $this->instance->plugin_root_ns_stub . substr($this->instance->ns, $root_ns_length); $this->instance->plugin_stub_as_root_ns_with_underscores = stub::with_underscores($this->instance->plugin_stub_as_root_ns); $this->instance->plugin_stub_as_root_ns_with_dashes = stub::with_dashes($this->instance->plugin_stub_as_root_ns); // Now let's cache `$this->instance` for easy re-use. static::$___instance_cache[$cache_entry] = $this->instance; // Check global reference & load plugin (if applicable). if (!isset($GLOBALS[$this->instance->plugin_root_ns]) || !$GLOBALS[$this->instance->plugin_root_ns] instanceof framework) { $GLOBALS[$this->instance->plugin_root_ns] = $this; if ($this->instance->plugin_root_ns !== stub::$core_ns) { $this->©plugin->load(); } // Not the core (only load plugins). } }
/** * Adds a new core class alias. * * @param string $ns_or_ns_class A class path (including namespace); or ONLY the namespace. * If this is ONLY the namespace; `$class_file` MUST be passed in also. * * @param string $class_file Optional. If passed, `$ns_or_ns_class` is assumed to be the namespace only. * * @throws \exception If invalid types are passed through arguments list. * @throws \exception If the parsed `$ns_class` is empty, or is NOT a valid core class name. * @throws \exception If the parsed `$ns_class` is NOT from this version of the core. * @throws \exception If the parsed `$ns_class` is NOT already defined. */ public static function add_core_ns_class_alias($ns_or_ns_class, $class_file = '') { if (!is_string($ns_or_ns_class) || !($ns_or_ns_class = trim($ns_or_ns_class, '\\')) || !is_string($class_file)) { throw new \exception(sprintf(stub::__('Invalid arguments: `%1$s`'), print_r(func_get_args(), TRUE))); } if ($class_file) { // Interpret `$ns_or_ns_class` as a namespace only. $ns_class = $ns_or_ns_class . '\\' . stub::with_underscores(basename($class_file, '.php')); } else { $ns_class = $ns_or_ns_class; } // Presume full class path. if (!preg_match(stub::$regex_valid_plugin_ns_class, $ns_class)) { throw new \exception(sprintf(stub::__('Namespace\\class contains invalid chars: `%1$s`.'), $ns_class)); } if (strpos($ns_class, stub::$core_ns . '\\') !== 0) { throw new \exception(sprintf(stub::__('Namespace\\class is NOT from this core: `%1$s`.'), $ns_class)); } if (!class_exists('\\' . $ns_class, FALSE) && !interface_exists('\\' . $ns_class, FALSE) && (!function_exists('trait_exists') || !trait_exists('\\' . $ns_class, FALSE))) { throw new \exception(sprintf(stub::__('Namespace\\class does NOT exist yet: `%1$s`.'), $ns_class)); } $alias = str_replace(array(stub::$core_ns . '\\', '\\'), array(stub::$core_ns_stub . '\\', '__'), $ns_class); if (!class_exists('\\' . $alias, FALSE) && !interface_exists('\\' . $alias, FALSE) && (!function_exists('trait_exists') || !trait_exists('\\' . $alias, FALSE))) { class_alias('\\' . $ns_class, $alias); } }
/** * Handles all PHP exceptions. * * @param exception|\exception $exception An exception. * * @note Exceptions handled successfully by this routine will NOT be logged by PHP as errors. * As the exception handler, we will need to log and/or display anything that needs to be recorded here. * The PHP interpreter simply terminates script execution whenever an exception occurs (nothing more). * * @note If an exception is thrown while handling an exception; PHP will revert to it's default exception handler. * This will result in a fatal error that may get logged by PHP itself (depending on `error_reporting` and `error_log`). * * @throws exception|\exception If we are unable to handle the exception (i.e. the XDaRk Core is not even available yet), * this handler will simply re-throw the exception (forcing a fatal error); as just described in the previous note. * * @note The display of exception messages is NOT dependent upon `display_errors`; nor do we consider that setting here. * However, we do tighten security within the `exception.php` template file; hiding most details by default; and displaying all details * only if the current user is a Super Administrator in WordPress; or if `WP_DEBUG_DISPLAY` mode has been enabled on this site. * * @note If there was another exception handler active on the site; and this exception is NOT for * a plugin under this version of the XDaRk Core; we simply hand the exception back to the previous handler. * In the case of multiple versions of the XDaRk Core across various plugins; this allows us to work up the chain * of previous handlers until we find the right version of the XDaRk Core; assuming each version * of the XDaRk Core handles things this way too (which is to be expected). * * @see http://php.net/manual/en/function.set-exception-handler.php */ public static function handle(\exception $exception) { try { static::$exception = $exception; // Reference. if (static::$exception instanceof exception) { static::$plugin = static::$exception->plugin; static::handle_plugin_exception(); return; // We're done here. } // Else this is some other type of exception. if (static::$previous_handler && is_callable(static::$previous_handler)) { call_user_func(static::$previous_handler, static::$exception); return; // We're done here. } // There is NO other handler available (deal w/ it here; if possible). if (is_callable('\\' . stub::$core_ns . '\\core')) { static::$plugin = core(); static::handle_plugin_exception(); return; // We're done here. } throw static::$exception; // Re-throw (forcing a fatal error). } catch (\exception $_exception) { throw new \exception(sprintf(stub::__('Failed to handle exception code: `%1$s` with message: `%2$s`.'), $exception->getCode(), $exception->getMessage()) . ' ' . sprintf(stub::__('Failure caused by exception code: `%1$s` with message: `%2$s`.'), $_exception->getCode(), $_exception->getMessage()), 20, $_exception); } }
/** * Constructor. * * @param object|array $instance Required at all times. * A parent object instance, which contains the parent's `$instance`, * or a new `$instance` array. * * @param string $code Optional error code (string, NO integers please). * * @param null|mixed $data Optional exception data (i.e. something to assist in reporting/logging). * This argument can be bypassed using a NULL value (that's fine). * * @param string $message Optional exception message (i.e. an error message). * * @param null|\exception $previous Optional previous exception (if re-thrown). * * @throws \exception If there is a missing and/or invalid `$instance`. * @throws \exception A standard exception class; if any additional issues occur during this type of exception. * This prevents endless exceptions, which may occur when/if we make use of a plugin instance. */ public function __construct($instance = NULL, $code = 'exception', $data = NULL, $message = '', \exception $previous = NULL) { try { if ($instance instanceof framework) { $plugin_root_ns = $instance->instance->plugin_root_ns; } else { if (is_array($instance) && !empty($instance['plugin_root_ns'])) { $plugin_root_ns = (string) $instance['plugin_root_ns']; } } if (empty($plugin_root_ns) || !isset($GLOBALS[$plugin_root_ns]) || !$GLOBALS[$plugin_root_ns] instanceof framework) { throw new \exception(sprintf(stub::__('Invalid `$instance` to constructor: `%1$s`'), print_r($instance, TRUE))); } $this->plugin = $GLOBALS[$plugin_root_ns]; $code = (string) $code ? (string) $code : 'exception'; $message = (string) $message ? (string) $message : sprintf($this->plugin->__('Exception code: `%1$s`.'), $code); parent::__construct($message, 0, $previous); // Call parent constructor. $this->code = $code; // Set code for this instance. We always use string exception codes (no exceptions :-). $this->data = $data; // Optional diagnostic data associated with this exception (possibly a NULL value). $this->wp_debug_log(); // Possible debug logging. $this->db_log(); // Possible database logging routine. } catch (\exception $_exception) { throw new \exception(sprintf(stub::__('Could NOT instantiate exception code: `%1$s` with message: `%2$s`.'), $code, $message) . ' ' . sprintf(stub::__('Failure caused by exception code: `%1$s` with message: `%2$s`.'), $_exception->getCode(), $_exception->getMessage()), 20, $_exception); } }