AlkantarClanX12
| Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/wp-cerber/includes/ |
| Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/wp-cerber/includes/Revalt.php |
<?php
/**
* Class Revalt
*
* A return-value object that carries the complete outcome of an operation:
* the result payload, the error state, and an optional diagnostic chain
* across architectural layers.
*
* Revalt always represents a COMPLETED operation. It is returned by value
* and never passed down the call stack for lower-level code to populate.
*
* USAGE
* -----
* - Instantiate inside the function or method that owns the operation.
* - Return the Revalt instance as the completed operation outcome.
* - Manage successful results with put_results(), merge(), get_results().
* - Handle errors with add_error(), get_error_messages(), get_error_data().
* - Check error presence using has_errors() before reading results.
* - When propagating errors across layers, see PROPAGATION PATTERNS below.
*
* AT A GLANCE
* -----------
* - Unified return type: callers always receive a Revalt, never a union of payload-or-error.
* - Multiple errors per instance, each with a code, one or more messages, and arbitrary structured data.
* - Execution stack: a Revalt may reference the Revalt returned by a lower layer, forming a single-linked chain similar to Exception::$previous.
* - Root cause traversal: get_root_cause() returns the deepest error in the chain; get_stack() returns the full trace for logging.
* - Result helpers for common payload shapes: get_element(), get_slice(), get_results_list(), count_results().
*
* CALLER CONTRACT
* ---------------
* Every caller MUST check has_errors() before using get_results().
* A Revalt may technically hold both a payload and errors. Treat
* has_errors() === true as authoritative: do not rely on get_results()
* when errors are present unless the specific producer documents
* partial-success semantics.
*
* PRODUCING A REVALT
* ------------------
* Success:
* return new Revalt( $results );
*
* Success, assembled incrementally:
* $result = new Revalt();
* $result->put_results( $partial );
* $result->merge( $more );
* return $result;
*
* Error:
* return new Revalt( null, 'unique_error_code', 'Human readable message' );
*
* Error with structured data:
* return new Revalt( null, 'invalid_input', 'Field foo is missing', array( 'field' => 'foo' ) );
*
*
* PROPAGATION PATTERNS
* --------------------
* When the current layer receives an erroring Revalt from below:
*
* Pattern A. Pass through unchanged. Use when this layer adds no context:
*
* $lower = lower_layer();
* if ( $lower->has_errors() ) {
* return $lower;
* }
*
* Pattern B. Wrap with this layer's context. Use when the caller needs
* an error code at this layer's abstraction, while diagnostics still
* need to reach the original root cause:
*
* $lower = lower_layer();
* if ( $lower->has_errors() ) {
* return new Revalt( null, 'upper_failed', 'Could not complete X', null, $lower );
* }
*
* Pattern C. Mutate via add_error(). ONLY on a Revalt this layer created. Never on a Revalt returned from a lower layer.
*
* ANTI-PATTERNS
* -------------
* Do NOT mutate a Revalt received from a lower layer:
*
* // WRONG
* $lower = lower_layer();
* $lower->add_error( 'upper_failed', '...' );
* return $lower;
*
* Do NOT call get_results() without checking has_errors() first.
*
* ERROR LOGGING (OPTIONAL)
* ------------------------
* Revalt can send errors to an external logger configured globally via
* Revalt::set_error_logger(). Two modes:
*
* - Revalt::LOG_BUFFERED (default): errors accumulate in the Revalt instance and
* are sent to the logger only when log_errors() is explicitly called.
*
* - Revalt::LOG_INSTANT: errors are sent to the logger the moment add_error() is called.
*
* @version 8.6
*/
class Revalt {
/**
* Stores the results of an operation, which may be a scalar value, object, or collection.
*
* @var mixed
*
*/
protected $operation_results = null;
/**
* Stores the list of errors.
*
* @var array<string, string[]>
*/
protected array $errors = array();
/**
* Stores optional error data for each error code.
*
* @var array<string, array>
*/
protected array $error_data = array();
/**
* Reference to the previous Revalt instance in the execution stack.
*
* This property establishes a single directional chain of result objects.
* Each Revalt represents a logical layer and may reference the layer below it.
* The chain forms a simple linked structure similar to PHP Exception::$previous,
* but used here for propagating structured execution results instead of exceptions.
*
* It enables propagating low-level diagnostic information up the execution stack.
*
* Null if no execution stack was involved.
*
* @var Revalt|null
*
* @since 7.0
*/
protected ?Revalt $previous_result = null;
const MAX_STACK_DEPTH = 100;
/**
* Errors accumulate in the instance and are sent to the logger only when log_errors() is explicitly called. Default mode.
*/
const LOG_BUFFERED = 0;
/**
* Errors are sent to the logger the moment add_error() is called.
*/
const LOG_INSTANT = 1;
/** @var callable|null */
private static $error_logger = null;
/** @var int */
private static int $log_mode = self::LOG_BUFFERED;
/**
* Initializes the results of operations with optional error message and error data.
*
* @param mixed $results Results of operations.
* @param string $error_code Optional. Error code.
* @param string $error_message Optional. Error message.
* @param mixed $error_data Optional. Error data.
* @param Revalt|null $previous_result Optional. Reference to the previous Revalt instance in the execution stack. It enables context and error propagation in multi-level architectures.
*
*
*/
public function __construct( $results = null, string $error_code = '', string $error_message = '', $error_data = null, ?Revalt $previous_result = null ) {
$this->operation_results = $results;
$this->set_previous_result( $previous_result );
if ( empty( $error_code ) ) {
return;
}
$this->add_error( $error_code, $error_message, $error_data );
}
/**
* Put results of operations. Replaces existing ones.
*
* @param mixed $results Results of operations
* @param Revalt|null $previous_result Optional. Reference to the previous Revalt instance in the execution stack. It enables context and error propagation in multi-level architectures.
*
* @return void
*/
public function put_results( $results, ?Revalt $previous_result = null ) {
$this->operation_results = $results;
$this->set_previous_result( $previous_result );
}
/**
* Sets the previous operation result used as lower-level diagnostic context.
*
* @param Revalt|null $previous_result Reference to the previous Revalt instance in the execution stack.
*
* @return void
*
* @since 8.1
*/
private function set_previous_result( ?Revalt $previous_result = null ): void {
if ( ! ( $previous_result instanceof Revalt )
|| $previous_result === $this ) {
return;
}
$this->previous_result = $previous_result;
}
/**
* Merges the elements of results and the given array together.
* Note: existing non-array (scalar) results will be destroyed.
*
* @param array $results Results of operations
* @param Revalt|null $previous_result Optional. Reference to the previous Revalt instance in the execution stack. It enables context and error propagation in multi-level architectures.
*
* @return void
*/
public function merge( array $results, ?Revalt $previous_result = null ) {
if ( ! is_array( $this->operation_results ) ) {
$this->operation_results = array();
}
$this->operation_results = array_merge( $this->operation_results, $results );
$this->set_previous_result( $previous_result );
}
/**
* A handler for a fully successful operation.
*
* Note: It cleans all existing/accumulated errors and replaces the operation results with new results.
* Use this method if operations completely went well. For a partial success result, use Revalt::put_results() or new Revalt().
*
* @param mixed $results Results of operations
* @param Revalt|null $previous_result Optional. Reference to the previous Revalt instance in the execution stack. It enables context propagation in multi-level architectures.
*
* @return void
*/
public function success( $results, ?Revalt $previous_result = null ) {
$this->put_results( $results, $previous_result );
// Explicitly clean the error state, making the successful outcome self-contained.
$this->errors = array();
$this->error_data = array();
}
/**
* Returns the results of operations.
*
* @param mixed $default Default value returned when no result payload exists.
*
* @return mixed The result payload previously set by the constructor, Revalt::put_results(), Revalt::success(), or Revalt::merge().
*/
public function get_results( $default = null ) {
if ( $this->operation_results === null ) {
return $default;
}
return $this->operation_results;
}
/**
* Retrieve the preceding Revalt instance in the execution stack.
*
* This method allows traversing the result chain to get underlying results
* or to inspect the underlying diagnostic information (errors) of the current operation.
*
* @return Revalt|null Preceding Revalt instance. Null if this is the root of the stack OR no execution stack was involved.
*
* @since 7.0
*/
public function get_previous_result(): ?Revalt {
return $this->previous_result;
}
/**
* Returns the results of operations as an array, ensuring the return type is always array.
*
* @param array $default Default value if no results.
*
* @return array Results of operations as an array.
*/
public function get_results_list( array $default = array() ): array {
$val = $this->get_results( $default );
if ( ! is_array( $val ) ) {
$val = array( $val );
}
return $val;
}
/**
* Returns a slice of the results
*
* @param int $offset
* @param int|null $length
* @param bool $preserve_keys
*
* @return array
*
* @see array_slice()
*
*/
public function get_slice( int $offset, ?int $length, bool $preserve_keys = false ): array {
if ( ! is_array( $this->operation_results ) ) {
return array();
}
return array_slice( $this->operation_results, $offset, $length, $preserve_keys );
}
/**
* Returns an element from the results
*
* @param string|int $key The array key to return the value for
* @param mixed $default Default value if no element can be returned
*
* @return mixed Value from the results
*
*/
public function get_element( $key, $default = null ) {
if ( ! is_array( $this->operation_results ) ) {
return $default;
}
return $this->operation_results[ $key ] ?? $default;
}
/**
* Returns the number of elements in the results.
*
* Note: this method returns 1 if the stored result is a non-array value (e.g., string, int, or object)
*
* @return int
*/
public function count_results(): int {
if ( $this->operation_results === null ) {
return 0;
}
if ( is_array( $this->operation_results ) ) {
return count( $this->operation_results );
}
return 1;
}
/**
* Retrieves all error codes.
*
*
* @return array List of error codes, if available.
*/
public function get_all_error_codes(): array {
if ( ! $this->has_errors() ) {
return array();
}
return array_keys( $this->errors );
}
/**
* Retrieves the first error code available.
*
*
* @return string Empty string, if no error codes.
*/
public function get_error_code(): string {
$codes = $this->get_all_error_codes();
if ( empty( $codes ) ) {
return '';
}
return (string) $codes[0];
}
/**
* Retrieves all error messages, or the error messages for the given error code.
*
*
* @param string $code Optional. Error code to retrieve the messages for.
* Default empty string.
*
* @return string[] Error strings on success, or empty array if there are none.
*
*/
public function get_error_messages( string $code = '' ): array {
// Return all messages if no code specified.
if ( empty( $code ) ) {
$all_messages = array();
foreach ( $this->errors as $code => $messages ) {
$all_messages = array_merge( $all_messages, $messages );
}
return $all_messages;
}
return $this->errors[ $code ] ?? array();
}
/**
* Gets a single error message.
*
* This will get the first message available for the code. If no code is
* given then the first code available will be used.
*
* @param string $code Optional. Error code to retrieve the message for.
* Default empty string.
*
* @return string The error message.
*
*/
public function get_error_message( string $code = '' ): string {
if ( empty( $code ) ) {
$code = $this->get_error_code();
}
$messages = $this->get_error_messages( $code );
if ( empty( $messages ) ) {
return '';
}
return $messages[0];
}
/**
* Retrieves the most recently added error data for an error code.
*
* @param string $code Optional. Error code. Default empty string.
*
* @return mixed Error data, if it exists, otherwise NULL.
*
*/
public function get_error_data( string $code = '' ) {
if ( empty( $code ) ) {
$code = $this->get_error_code();
}
if ( ! empty( $this->error_data[ $code ] ) ) {
$count = count( $this->error_data[ $code ] );
return $this->error_data[ $code ][ $count - 1 ];
}
return null;
}
/**
* Retrieves all error data for an error code in the order in which the data was added.
*
* @param string $code Error code.
*
* @return array Array of error data, if it exists.
*
* @since 5.6.0
*/
public function get_all_error_data( string $code = '' ): array {
if ( empty( $code ) ) {
$code = $this->get_error_code();
}
return $this->error_data[ $code ] ?? array();
}
/**
* Verifies if the instance contains errors.
*
* @return bool If the instance contains errors.
*
* @since 5.1.0
*/
public function has_errors(): bool {
if ( ! empty( $this->errors ) ) {
return true;
}
return false;
}
/**
* Adds an error or appends an additional message to an existing error.
*
* @param string $code Error code.
* @param string $message Error message.
* @param mixed $data Optional. Error data. Stored whenever it is not null.
* @param Revalt|null $previous_result Deprecated. Still honored for backward compatibility.
*
* @return void
*
*/
public function add_error( string $code, string $message, $data = null, ?Revalt $previous_result = null ) {
$this->errors[ $code ][] = $message;
if ( $data !== null ) {
$this->add_error_data( $data, $code );
}
$this->set_previous_result( $previous_result );
if ( self::$log_mode === self::LOG_INSTANT && self::$error_logger !== null ) {
( self::$error_logger )( $code, $message, $data );
}
}
/**
* Adds data to an error with the given code.
*
* @param mixed $data Error data.
* @param string $code Optional. Error code. Default empty string.
* When empty, the first existing error code is used (see get_error_code()).
*
* @return void
*
*/
public function add_error_data( $data, string $code = '' ) {
if ( empty( $code ) ) {
$code = $this->get_error_code();
}
$this->error_data[ $code ][] = $data;
}
/**
* Get the entire execution stack as an array e.g. for debugging.
*
* Each entry describes one layer of the chain. The `data` key holds that layer's
* result payload (get_results()), not its error data.
*
* @return array<int, array{level: int, has_errors: bool, codes: array, messages: string[], data: mixed}>
* List of all layers in the chain, starting from the current one (level 0).
*
* @since 7.0
*/
public function get_stack(): array {
$trace = array();
$current = $this;
$level = 0;
while ( $current instanceof Revalt
&& $level < $this::MAX_STACK_DEPTH ) {
$trace[] = array(
'level' => $level ++,
'has_errors' => $current->has_errors(),
'codes' => $current->get_all_error_codes(),
'messages' => $current->get_error_messages(),
'data' => $current->get_results(),
);
$current = $current->get_previous_result();
}
return $trace;
}
/**
* Retrieves details of the deepest error in the execution stack (Root Cause).
*
* Traverses the stack downwards and finds the last Revalt instance that contains errors.
* Useful when the top-level error is generic (e.g., "Action failed") and you need
* the specific low-level reason (e.g., "DB Connection Refused").
*
* @return array{level: int, code: string, message: string, data: mixed}|array{} Empty array if no errors found.
*
* @since 7.0
*/
public function get_root_cause(): array {
$root_cause = array();
$current = $this;
$level = 0;
while ( $current instanceof Revalt
&& $level < $this::MAX_STACK_DEPTH ) {
if ( $current->has_errors() ) {
$root_cause = array(
'level' => $level,
'code' => $current->get_error_code(),
'message' => $current->get_error_message(),
'data' => $current->get_error_data(),
);
}
$level ++;
$current = $current->get_previous_result();
}
return $root_cause;
}
/**
* Configure error logging behavior for all Revalt instances.
*
* Note: this affects all instances.
*
* @param callable|null $logger Receives (string $code, string $message, mixed $data).
* Pass null to disable logging entirely.
* @param int $mode LOG_BUFFERED (default) or LOG_INSTANT.
* See the constants for behaviour details.
*
* @since 8.5
*/
public static function set_error_logger( ?callable $logger, int $mode = self::LOG_BUFFERED ): void {
self::$error_logger = $logger;
self::$log_mode = $mode;
}
/**
* Send all accumulated errors to the configured logger.
*
* Note: set_error_logger() must be called once, globally, beforehand to configure the logger.
*
* Walks the previous_result chain; each entry is prefixed with [L{n}],
* where n is the chain depth (L0 = current layer, higher = deeper).
*
* Note: if Revalt::LOG_INSTANT is enabled, this method may duplicate log entries.
*
* Does not clear the error state. No-op if no logger is configured.
*
* @since 8.5
*/
public function log_errors(): void {
if ( self::$error_logger === null ) {
return;
}
$current = $this;
$level = 0;
while ( $current instanceof Revalt
&& $level < self::MAX_STACK_DEPTH ) {
$prefix = '[L' . $level . '] ';
foreach ( $current->errors as $code => $messages ) {
// 1. Every message for the code. Nothing is dropped.
foreach ( $messages as $message ) {
( self::$error_logger )( $code, $prefix . $message, null );
}
// 2. Every accumulated data item, attached to the last message.
$all_data = $current->get_all_error_data( $code );
if ( ! empty( $all_data ) ) {
$last_message = $messages[ array_key_last( $messages ) ];
foreach ( $all_data as $data ) {
( self::$error_logger )( $code, $prefix . $last_message, $data );
}
}
}
$level++;
$current = $current->get_previous_result();
}
}
}