File manager - Edit - /home/palg2351/public_html/klanaobsesiindonesia.com/wp-includes/Text/Diff/Engine/elementor-pro.zip
Back
PK j��\^��� � core/container/config.phpnu �[��� <?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor Dependency Injection Configuration. * * This file registers definitions for PHP-DI used by Elementor. * * @since 3.25.0 */ return []; PK j��\S�?� core/container/container.phpnu �[��� <?php namespace ElementorPro\Core\Container; use ElementorProDeps\DI\ContainerBuilder; use ElementorProDeps\DI\Container as DIContainer; use Exception; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Elementor Container. * * Elementor container handler class is responsible for the containerization * of manager classes and their dependencies. * * @since 3.25.0 */ class Container { private static $instance; private function __construct() {} private function __clone() {} /** * @throws Exception */ private static function initialize(): void { $builder = new ContainerBuilder(); self::register_configuration( $builder ); self::$instance = $builder->build(); } private static function register_configuration( ContainerBuilder $builder ): void { $builder->addDefinitions( __DIR__ . '/config.php' ); } /** * @throws Exception */ public static function get_instance(): DIContainer { if ( is_null( static::$instance ) ) { self::initialize(); } return static::$instance; } } PK j��\!��� ) core/integrations/actions/action-base.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Actions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Action_Base { /** * Validate a payload. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * * @return mixed */ abstract public function validate( $payload ); /** * Apply the action. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ abstract public function apply( $payload ); /** * Run the action. * * @param mixed $payload - Payload object instance. * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ public function run( $payload ) { $this->validate( $payload ); $this->apply( $payload ); } } PK j��\����V V 1 core/integrations/actions/email/email-address.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Actions\Email; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email_Address { /** * Recipient email address. * * @var array */ public $address; /** * Recipient name. * * @var string */ public $name; /** * Email_Address constructor. * * @param string $address * @param string $name * * @return void */ public function __construct( $address, $name ) { $this->address = (string) $address; $this->name = (string) $name; } /** * Format an email to be ready for header (e.g. `Recipient Name <user@email.com>` or `user@email.com`) * * @return string */ public function format() { if ( ! empty( $this->name ) ) { return sprintf( '%s <%s>', $this->name, $this->address ); } return sprintf( '%s', $this->address ); } } PK j��\Y�U8 1 core/integrations/actions/email/email-message.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Actions\Email; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email_Message { /** * Email sender. * * @var Email_Address */ public $from; /** * Email recipient. * * @var Email_Address */ public $to; /** * Email reply to address. * * @var Email_Address[] */ public $reply_to = []; /** * Email CC recipient. * * @var Email_Address[] */ public $cc = []; /** * Email BCC recipient. * * @var Email_Address[] */ public $bcc = []; /** * Email subject. * * @var string */ public $subject; /** * Email content type. * * @var string */ public $content_type; /** * Email body. * * @var string */ public $body; /** * Email attachments. * * @var array */ public $attachments = []; /** * Email_Message constructor. * * @return void */ public function __construct() { // Set defaults. $this->from( get_bloginfo( 'admin_email' ), get_bloginfo( 'name' ) ); } /** * Set the email sender. * * @param string $email * @param string|null $name * * @return $this */ public function from( $email, $name = null ) { $this->from = new Email_Address( $email, $name ); return $this; } /** * Set the email recipient. * * @param string $email * @param string|null $name * * @return $this */ public function to( $email, $name = null ) { $this->to = new Email_Address( $email, $name ); return $this; } /** * Add a reply to. * * @param string $email * @param string|null $name * * @return $this */ public function reply_to( $email, $name = null ) { $this->reply_to[] = new Email_Address( $email, $name ); return $this; } /** * Add a CC. * * @param string $email * @param string|null $name * * @return $this */ public function cc( $email, $name = null ) { $this->cc[] = new Email_Address( $email, $name ); return $this; } /** * Add a BCC. * * @param string $email * @param string|null $name * * @return $this */ public function bcc( $email, $name = null ) { $this->bcc[] = new Email_Address( $email, $name ); return $this; } /** * Set the email subject. * * @param string $subject * * @return $this */ public function subject( $subject ) { $this->subject = (string) $subject; return $this; } /** * Set the email content type. * * @param string $content_type * * @return $this */ public function content_type( $content_type ) { $this->content_type = (string) $content_type; return $this; } /** * Set the email body using plain text. * * @param string $body * @param string $content_type * * @return $this */ public function body( $body, $content_type = 'text/html' ) { $this->body = (string) $body; return $this->content_type( $content_type ); } /** * Set the email body using a view. * * @param string $path - View path, * @param array $data - Data that will be passes to the view. * * @return $this * @throws \Exception */ public function view( $path, $data = [] ) { if ( ! is_file( $path ) ) { throw new \Exception( "`{$path}` is not a valid view." ); } ob_start(); // Inspired from Laravel's view mechanism: // [1] https://github.dev/illuminate/filesystem/blob/b179f9ea3b3195d1f4b5ae2aee67e42eac6ceb5e/Filesystem.php#L98 // [2] https://github.dev/illuminate/view/blob/6dd315634a44450c5e443fa8735d4a526833fad3/Engines/PhpEngine.php#L48 call_user_func( function( $__view_path, $__view_data ) { extract( $__view_data, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract unset( $__view_data ); // `$__view_data` keys are available in the file as variables. require $__view_path; }, $path, $data ); $this->body = ob_get_clean(); return $this->content_type( 'text/html' ); } /** * Add an attachment. * * @param string $path - Attachment path on the server. * * @return $this */ public function attach( $path ) { $this->attachments[] = (string) $path; return $this; } } PK j��\���� � ) core/integrations/actions/email/email.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Actions\Email; use ElementorPro\Core\Integrations\Actions\Action_Base; use ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception; use ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Email extends Action_Base { /** * @param Email_Message $payload * * @return void * @throws \Exception */ public function apply( $payload ) { // Set default headers. $headers = [ sprintf( 'Content-Type: %s; charset=UTF-8', $payload->content_type ), sprintf( 'From: %s', $payload->from->format() ), ]; foreach ( $payload->reply_to as $recipient ) { $headers[] = sprintf( 'Reply-To: %s', $recipient->format() ); } // Set CC headers. $cc_headers = []; foreach ( $payload->cc as $recipient ) { $cc_headers[] = sprintf( 'Cc: %s', $recipient->format() ); } // Send email. $this->send_mail( $payload->to->format(), $payload->subject, $payload->body, implode( PHP_EOL, array_merge( $headers, $cc_headers ) ), $payload->attachments ); // Send BCC emails. foreach ( $payload->bcc as $bcc ) { $this->send_mail( $bcc->format(), $payload->subject, $payload->body, implode( PHP_EOL, $headers ), $payload->attachments ); } } /** * @alias `$this->run()` * * @param Email_Message $payload * * @return void *@throws \Exception * */ public function send( Email_Message $payload ) { $this->run( $payload ); } /** * Validate the email message DTO. * * @param Email_Message $payload * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception * * @return void */ public function validate( $payload ) { $required_fields = [ 'from', 'to', 'subject', 'body', 'content_type', ]; foreach ( $required_fields as $field ) { if ( empty( $payload->{$field} ) ) { throw new Action_Validation_Failed_Exception( static::class, "`Email_Message::\${$field}` is required." ); } } } /** * Calls `wp_mail()`. Used for testing. * * @param mixed ...$args * * @return void */ protected function send_mail( ...$args ) { add_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); wp_mail( ...$args ); remove_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] ); } /** * Throw exception on `wp_mail()` error. * * @param \WP_Error $error * * @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception * * @return void */ public function on_wp_mail_error( \WP_Error $error ) { throw new Action_Failed_Exception( static::class, '`wp_mail()` cannot send email', $error ); } } PK j��\�mt�� � * core/integrations/integrations-manager.phpnu �[��� <?php namespace ElementorPro\Core\Integrations; use ElementorPro\Core\Integrations\Actions\Action_Base; use ElementorPro\Core\Integrations\Actions\Email\Email; use ElementorPro\Core\Integrations\Actions\Email\Email_Message; use ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception; use ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception; use ElementorPro\Core\Utils\Registrar; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Integrations_Manager { /** * Registered action types. * * @var Registrar */ protected $actions_registrar; /** * Integrations_Manager constructor. * * @return void */ public function __construct() { $this->actions_registrar = new Registrar(); } /** * Get an action instance. * * @shortcut `Registrar->get()`. * * @return \ElementorPro\Core\Integrations\Actions\Action_Base|null */ public function get_action( $id ) { if ( ! $this->is_initialized() ) { $this->init_actions(); } return $this->actions_registrar->get( $id ); } /** * Run an action for a selected payload. * * @param array|mixed $payloads - Payloads instances to run the actions on. * @param null|string $id - If `$payloads` is not an array, a custom action ID can be provided. * * @return void */ public function run( $payloads, $id = null ) { if ( ! is_array( $payloads ) ) { $payloads = $id ? [ $id => $payloads ] : [ $payloads ]; } foreach ( $payloads as $key => $payload ) { // Get the action ID for the provided payload type. $action_id = is_numeric( $key ) ? get_class( $payload ) : $key; /** * @type Action_Base $action */ $action = $this->get_action( $action_id ); if ( ! $action ) { throw new \Exception( "{$action_id} doesn't have an associated `Action`." ); } if ( ! ( $action instanceof Action_Base ) ) { $action_class = get_class( $action ); throw new \Exception( "{$action_class} is not a valid `Action_Base`." ); } try { $action->run( $payload ); } catch ( Action_Validation_Failed_Exception $e ) { $e->log(); } catch ( Action_Failed_Exception $e ) { $e->log(); } } } /** * Initialize the manager actions. * * @return void */ protected function init_actions() { add_action( 'elementor_pro/core/integrations/actions/register', function ( Registrar $actions_registrar ) { $actions_registrar->register( new Email(), Email_Message::class ); } ); do_action( 'elementor_pro/core/integrations/actions/register', $this->actions_registrar ); } /** * Determine if the manager is initialized. * * @return boolean */ protected function is_initialized() { return ! ! did_action( 'elementor_pro/core/integrations/actions/register' ); } } PK j��\i��Z Z C core/integrations/exceptions/action-validation-failed-exception.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Exceptions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Action_Validation_Failed_Exception extends Exception_Base { protected function format_message( $message ) { return sprintf( 'Action `%s` failed validation: %s', $this->action, $message ); } } PK j��\��yK K 8 core/integrations/exceptions/action-failed-exception.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Exceptions; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Action_Failed_Exception extends Exception_Base { protected function format_message( $message ) { return sprintf( 'Action `%s` failed to run: %s', $this->action, $message ); } } PK j��\hl��9 9 / core/integrations/exceptions/exception-base.phpnu �[��� <?php namespace ElementorPro\Core\Integrations\Exceptions; use ElementorPro\Plugin; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } abstract class Exception_Base extends \Exception { /** * @var string */ protected $action; /** * @var array */ protected $meta = []; /** * Get a formatted message specific to the current exception type. * * @param string $message * * @return string */ abstract protected function format_message( $message ); /** * Exception_Base constructor. * * @param string $action - Action name that failed (ideally the class name, e.g. Email::class). * @param string $message - Message to show. * @param array $meta - Exception meta data. Used for logging. * */ public function __construct( $action, $message = '', $meta = [] ) { $this->action = $action; $this->meta = $meta; $message = $this->format_message( $message ); parent::__construct( $message ); } /** * Log the exception to Elementor's log. * * @return void */ public function log() { Plugin::elementor()->logger->get_logger()->error( $this->getMessage(), [ 'meta' => $this->meta ] ); } /** * Get the error format. * * @return string */ public function __toString() { return sprintf( '%s: %s', __CLASS__, $this->getMessage() ); } } PK j��\>_eh� � % core/database/model-query-builder.phpnu �[��� <?php namespace ElementorPro\Core\Database; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } class Model_Query_Builder extends Query_Builder { /** * The Query Builder associated model. * * @var string */ public $model; /** * Whether the returned value should be hydrated into a model. * * @var bool */ public $return_as_model = true; /** * Model_Query_Builder constructor. * * @param string $model_classname - Model to use inside the builder. * @param \wpdb|null $connection - MySQL connection. */ public function __construct( $model_classname, \wpdb $connection = null ) { $this->set_model( $model_classname ); parent::__construct( $connection ); } /** * Set the model the generated from the query builder. * * @param $model_classname * * @return $this */ public function set_model( $model_classname ) { $this->model = $model_classname; return $this; } /** * Disable model hydration. * * @return $this */ public function disable_model_initiation() { $this->return_as_model = false; return $this; } /** * Disable hydration before calling the original count. * * @param string $column * * @return int */ public function count( $column = '*' ) { $this->disable_model_initiation(); return parent::count( $column ); } /** * Disable hydration before calling the original pluck. * * @inheritDoc */ public function pluck( $column = null ) { $this->disable_model_initiation(); return parent::pluck( $column ); } /** * Override the parent `get()` and make Models from the results. * * @return \ElementorPro\Core\Utils\Collection */ public function get() { $items = parent::get(); if ( ! $this->return_as_model ) { return $items; } // Convert the SQL results to Model instances. return $items->map( function ( $comment ) { return new $this->model( $comment ); } ); } } PK j��\�#�� � core/database/base-migration.phpnu �[��� <?php namespace ElementorPro\Core\Database; use Elementor\Core\Utils\Collection; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Base_Migration { /* * @see https://github.com/WordPress/WordPress/blob/d2694aa46647af48d1bcaff48a4f6cac7f5cf470/wp-admin/includes/schema.php#L49 */ const MAX_INDEX_LENGTH = 191; /** * @var \wpdb */ protected $wpdb; /** * @param \wpdb|null $wpdb_instance */ public function __construct( \wpdb $wpdb_instance = null ) { if ( ! $wpdb_instance ) { global $wpdb; $this->wpdb = $wpdb; } else { $this->wpdb = $wpdb_instance; } } /** * Runs when upgrading the database * * @return void */ abstract public function up(); /** * Runs when downgrading the database. * * @return void */ abstract public function down(); /** * A util to run SQL for creating tables. * * @param $table_name * @param array $columns */ protected function create_table( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $columns_sql = ( new Collection( $columns ) ) ->map( function( $definition, $col_name ) { return "`{$col_name}` {$definition}"; } ) ->implode( ', ' ); $query = "CREATE TABLE `{$table_name}` ({$columns_sql}) {$this->wpdb->get_charset_collate()};"; $this->run_db_delta( $query ); } /** * Add columns. * * @param $table_name * @param array $columns */ protected function add_columns( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $add_columns_sql = ( new Collection( $columns ) ) ->map( function ( $definition, $column_name ) { return "ADD COLUMN `{$column_name}` {$definition}"; } ) ->implode( ', ' ); $this->wpdb->query( "ALTER TABLE `{$table_name}` {$add_columns_sql};" ); // phpcs:ignore } /** * Drop columns * * @param $table_name * @param array $columns */ protected function drop_columns( $table_name, array $columns ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $drop_columns_sql = ( new Collection( $columns ) ) ->map( function ( $column_name ) { return "DROP COLUMN `{$column_name}`"; } ) ->implode( ', ' ); $this->wpdb->query( "ALTER TABLE `{$table_name}` {$drop_columns_sql};" ); // phpcs:ignore } /** * A util to run SQL for dropping tables. * * @param $table_name */ protected function drop_table( $table_name ) { $table_name = "{$this->wpdb->prefix}{$table_name}"; $query = "DROP TABLE IF EXISTS `{$table_name}`;"; // Safe query that shouldn't be escaped. $this->wpdb->query( $query ); // phpcs:ignore } /** * A util to run SQL for creating indexes. * * @param $table_name * @param array $column_names */ protected function create_indexes( $table_name, array $column_names ) { $max_index_length = static::MAX_INDEX_LENGTH; $table_name = "{$this->wpdb->prefix}{$table_name}"; // Safe query that shouldn't be escaped. $column_definition = $this->get_column_definition( $table_name ); if ( ! $column_definition ) { return; } $should_set_max_length_index = ( new Collection( $column_definition ) ) ->filter( function ( $value ) { preg_match( '/\((\d+)\)/', $value['Type'], $match ); return ( isset( $match[1] ) && intval( $match[1] ) > Base_Migration::MAX_INDEX_LENGTH ) || in_array( strtolower( $value['Type'] ), [ 'text', 'longtext' ], true ); } ) ->pluck( 'Field' ) ->values(); $indexes_sql = ( new Collection( $column_names ) ) ->map( function( $col_name ) use ( $should_set_max_length_index, $max_index_length ) { $max_index_length_sql = ''; if ( in_array( $col_name, $should_set_max_length_index, true ) ) { $max_index_length_sql = " ({$max_index_length})"; } return "ADD INDEX `{$col_name}_index` (`{$col_name}`{$max_index_length_sql})"; } ) ->implode( ', ' ); // Safe query that shouldn't be escaped. $this->wpdb->query( "ALTER TABLE `{$table_name}` {$indexes_sql};" ); // phpcs:ignore } /** * @param $table_name * * @return array */ protected function get_column_definition( $table_name ) { return $this->wpdb->get_results( "SHOW COLUMNS FROM `{$table_name}`;", ARRAY_A ); // phpcs:ignore } /** * Runs global dbDelta function (wrapped into method to allowing mock for testing). * * @param $query * * @return array */ protected function run_db_delta( $query ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; return dbDelta( $query ); } } PK j��\��Ͼ"x "x core/database/query-builder.phpnu �[��� <?php namespace ElementorPro\Core\Database; use ElementorPro\Core\Utils\Collection; use InvalidArgumentException; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } class Query_Builder { // Relation types. const RELATION_AND = 'AND'; const RELATION_OR = 'OR'; // Column types. const COLUMN_BASIC = 'basic'; // Regular column - will be automatically escaped. const COLUMN_RAW = 'raw'; // Raw column - SHOULD BE ESCAPED BY THE DEVELOPER. const COLUMN_SUB_SELECT = 'sub-select'; // Sub select - will be automatically bind & escaped. const COLUMN_COUNT = 'count'; // Count - wrap the column with a COUNT function. // WHERE types. const WHERE_BASIC = 'basic'; const WHERE_NULL = 'null'; const WHERE_COLUMN = 'column'; const WHERE_IN = 'in'; const WHERE_NOT_IN = 'not-in'; const WHERE_SUB = 'sub'; const WHERE_NESTED = 'nested'; const WHERE_EXISTS = 'exists'; const WHERE_NOT_EXISTS = 'not-exists'; // HAVING types. const HAVING_RAW = 'raw'; /** * MySQL connection. * * @var \wpdb */ protected $connection; /** * Current query value binding. * * @var array[] */ protected $bindings = [ 'select' => [], 'join' => [], 'where' => [], ]; /** * Current query columns to return. * * @var array */ protected $columns = [ [ 'type' => self::COLUMN_RAW, 'column' => '*', 'as' => null, ], ]; /** * Table to select from. * * @var array */ protected $from = []; /** * Current query joins. * * @var array */ protected $joins = []; /** * The where constraints for the query. * * @var array */ protected $wheres = []; /** * The having constraints for the query. * * @var array */ protected $havings = []; /** * The groupings for the query. * * @var array */ protected $groups = []; /** * The orderings for the query. * * @var array */ protected $orders = []; /** * The maximum number of records to return. * * @var int */ protected $limit; /** * The number of records to skip. * * @var int */ protected $offset; /** * Aggregations. * * @var array */ protected $with = []; /** * Query_Builder constructor. * * @param \wpdb|null $connection - The Mysql connection instance to use. */ public function __construct( \wpdb $connection = null ) { if ( $connection ) { $this->connection = $connection; return; } global $wpdb; $this->connection = $wpdb; } /** * Add columns to the SELECT clause. * * @param string[] $columns - Array of column names. * @param string $type - Select type. * * @return $this */ public function select( $columns = [ '*' ], $type = self::COLUMN_BASIC ) { $this->columns = []; $this->bindings['select'] = []; foreach ( $columns as $as => $column ) { $this->columns[ $as ] = [ 'type' => $type, 'as' => is_string( $as ) ? $as : null, 'column' => $column, ]; } return $this; } /** * @shortcut `$this->select()`. */ public function select_raw( $raw_columns = [ '*' ] ) { return $this->select( $raw_columns, self::COLUMN_RAW ); } /** * Add a `(SELECT ...) AS alias` statement to the SELECT clause. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $as - Alias for the sub select. * * @return $this */ public function add_sub_select( callable $callback, $as ) { call_user_func( $callback, $query = $this->new_query() ); $this->add_binding( $query->get_bindings(), 'select' ); $this->columns[] = [ 'type' => self::COLUMN_SUB_SELECT, 'column' => $query->to_sql(), 'as' => $as, ]; return $this; } /** * Add a `COUNT({col}) AS {alias}` statement to the SELECT clause. * * @param $column_name * @param $as * * @return $this */ public function add_count_select( $column_name, $as = null ) { $this->columns[] = [ 'type' => self::COLUMN_COUNT, 'column' => $column_name, 'as' => $as, ]; return $this; } /** * Set the table to select from. * * @param string $table - Table name. * @param string|null $as - Table alias. * * @return $this */ public function from( $table, $as = null ) { // Default the alias to the table name without prefix. $as = $as ? $as : $table; // Get the prefixed table name from the connection. $table = $this->connection->$table; $this->from = [ 'table' => $table, 'as' => $as, ]; return $this; } /** * @shortcut $this->from() * * Used for readability with UPDATE / INSERT / DELETE statements. */ public function table( $table, $as = null ) { return $this->from( $table, $as ); } /** * Execute a query operation only on specific condition. * For example: * * $query->when( 1 === $a, function( Query_Builder $builder ) { * // Runs if $a = 1. * $builder->where( ... ); * }, function( Query_Builder $builder ) { * // Runs if $a != 1. * $builder->where( ... ); * } ) * * @param mixed $condition - Condition to check. * @param callable $true_callback - Callback if the condition is truthy. * @param callable|null $false_callback - Callback if the condition is falsy. Optional. * * @return $this */ public function when( $condition, callable $true_callback, callable $false_callback = null ) { if ( $condition ) { call_user_func( $true_callback, $this, $condition ); } elseif ( $false_callback instanceof \Closure ) { call_user_func( $false_callback, $this, $condition ); } return $this; } /** * Add a `WHERE` statement. * * @param string|callable $column - Column name to check. * @param string $operator - Statement operator. * @param string|callable $value - Value as string or callback. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where( $column, $operator = null, $value = null, $and_or = self::RELATION_AND ) { // `$column` is a function, create a nested where. if ( $column instanceof \Closure ) { return $this->where_nested( $column, $and_or ); } // `$value` is a function, create a sub select. if ( $value instanceof \Closure ) { return $this->where_sub( $column, $operator, $value, $and_or ); } // Validate relation. if ( ! in_array( strtoupper( $and_or ), [ self::RELATION_AND, self::RELATION_OR ], true ) ) { throw new InvalidArgumentException( 'Relation must be "and" or "or".' ); } // If it's a `LIKE` statement, escape it using WP's `esc_like`. if ( 'like' === strtolower( $operator ) ) { $value = $this->escape_like( $value ); } // Create an `IS NULL` statement if the `$value` is null. if ( null === $value ) { $type = self::WHERE_NULL; } else { $this->add_binding( $value, 'where' ); $type = self::WHERE_BASIC; } $this->wheres[] = [ 'type' => $type, 'column' => $column, 'operator' => $operator, 'value' => $value, 'and_or' => $and_or, ]; return $this; } /** * Add an `OR WHERE` statement. * * @shortcut $this->where(). */ public function or_where( $column, $operator = null, $value = null ) { return $this->where( $column, $operator, $value, self::RELATION_OR ); } /** * @shortcut `$this->where()`. */ public function where_null( $column, $and_or = self::RELATION_AND ) { return $this->where( $column, '=', null ); } /** * @shortcut `$this->where_null()`. */ public function or_where_null( $column ) { return $this->where_null( $column, self::RELATION_OR ); } /** * Add a `WHERE col1 = col2` statement. * * @param string $first - First column name to check. * @param string $operator - Statement operator. * @param string $second - Second column name to check. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_column( $first, $operator, $second, $and_or = self::RELATION_AND ) { // Validate relation. if ( ! in_array( strtoupper( $and_or ), [ self::RELATION_AND, self::RELATION_OR ], true ) ) { throw new InvalidArgumentException( 'Relation must be "and" or "or".' ); } $this->wheres[] = [ 'type' => self::WHERE_COLUMN, 'first' => $first, 'second' => $second, 'operator' => $operator, 'and_or' => $and_or, ]; return $this; } /** * Add an `OR WHERE col1 = col2` statement. * * @shortcut $this->where_column(). */ public function or_where_column( $first, $operator, $second ) { return $this->where_column( $first, $operator, $second, self::RELATION_OR ); } /** * Add a `WHERE IN()` statement. * * @param string $column - Column name to check. * @param string[]|callable $values - Array of values. * @param string $and_or - Boolean relation, one of `and` / `or`. * @param boolean $in - Whether it's `IN` or `NOT IN`. * * @return $this */ public function where_in( $column, $values, $and_or = self::RELATION_AND, $in = true ) { $type = $in ? self::WHERE_IN : self::WHERE_NOT_IN; // Support `WHERE IN ( SELECT ... FROM )`. if ( $values instanceof \Closure ) { $operator = $in ? 'IN' : 'NOT IN'; return $this->where( $column, $operator, $values ); } $this->wheres[] = [ 'type' => $type, 'column' => $column, 'value' => $values, 'and_or' => $and_or, ]; $this->add_binding( $values, 'where' ); return $this; } /** * Add an `OR WHERE IN()` statement. * * @shortcut $this->where_in(). */ public function or_where_in( $column, $values ) { return $this->where_in( $column, $values, self::RELATION_OR ); } /** * Add a `WHERE NOT IN()` statement. * * @shortcut $this->where_in(). */ public function where_not_in( $column, $values, $and_or = self::RELATION_AND ) { return $this->where_in( $column, $values, $and_or, false ); } /** * Add an `OR WHERE NOT IN()` statement. * * @shortcut $this->where_in(). */ public function or_where_not_in( $column, $values ) { return $this->where_not_in( $column, $values, self::RELATION_OR ); } /** * Add a `WHERE EXISTS()` statement. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * @param bool $exists - Whether to use `EXISTS` or `NOT EXISTS` statement. * * @return $this */ public function where_exists( callable $callback, $and_or = self::RELATION_AND, $exists = true ) { call_user_func( $callback, $query = $this->new_query() ); $type = $exists ? self::WHERE_EXISTS : self::WHERE_NOT_EXISTS; $this->wheres[] = [ 'type' => $type, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings(), 'where' ); return $this; } /** * Add an `OR WHERE EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function or_where_exists( callable $callback, $exists = true ) { return $this->where_exists( $callback, self::RELATION_OR, $exists ); } /** * Add a `WHERE NOT EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function where_not_exists( callable $callback, $and_or = self::RELATION_AND ) { return $this->where_exists( $callback, $and_or, false ); } /** * Add an `OR WHERE NOT EXISTS()` statement. * * @shortcut $this->where_exists(). */ public function or_where_not_exists( callable $callback ) { return $this->or_where_exists( $callback, false ); } /** * Add a sub query. * * @param string $column - Column name to check. * @param string $operator - Statement operator. * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_sub( $column, $operator, callable $callback, $and_or = self::RELATION_AND ) { call_user_func( $callback, $query = $this->new_query() ); $this->wheres[] = [ 'type' => self::WHERE_SUB, 'column' => $column, 'operator' => $operator, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings(), 'where' ); return $this; } /** * Add a nested `WHERE` query. * * @param callable $callback - Callback that gets a `Query_Builder` and modifies it. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function where_nested( callable $callback, $and_or = self::RELATION_AND ) { call_user_func( $callback, $query = $this->new_query() ); $this->wheres[] = [ 'type' => self::WHERE_NESTED, 'query' => $query, 'and_or' => $and_or, ]; $this->add_binding( $query->get_bindings( 'where' ), 'where' ); return $this; } /** * Add `HAVING` statement. * * @param string $sql - RAW SQL having clause. * @param string $and_or - Boolean relation, one of `and` / `or`. * * @return $this */ public function having_raw( $sql, $and_or = self::RELATION_AND ) { $this->havings[] = [ 'type' => self::HAVING_RAW, 'and_or' => $and_or, 'sql' => $sql, ]; return $this; } /** * Add `OR HAVING` statement. * * @param string $sql - RAW SQL having clause. * * @return $this */ public function or_having_raw( $sql ) { return $this->having_raw( $sql, self::RELATION_OR ); } /** * Add a `JOIN ... ON` statement. * * @param callable $callback - Closure that builds the JOIN clause. * @param string $type - JOIN type. * * @return $this */ public function join( callable $callback, $type = Join_Clause::TYPE_INNER ) { // Validate type. if ( ! in_array( strtolower( $type ), [ Join_Clause::TYPE_INNER, Join_Clause::TYPE_LEFT, Join_Clause::TYPE_RIGHT ], true ) ) { throw new InvalidArgumentException( 'Join type must be "inner", "left" or "right".' ); } call_user_func( $callback, $join = $this->new_join_clause( $type ) ); $this->add_binding( $join->get_bindings(), 'join' ); $this->joins[] = $join; return $this; } /** * @shortcut `$this->join()` */ public function left_join( callable $callback ) { return $this->join( $callback, Join_Clause::TYPE_LEFT ); } /** * @shortcut `$this->join()` */ public function right_join( callable $callback ) { return $this->join( $callback, Join_Clause::TYPE_RIGHT ); } /** * Creates a new Query Builder instance using the same connection as the initiator. * * @return self */ public function new_query() { // Make sure this is `new self` and not `new static`. // When extending the Query Builder, sometimes it comes with default table or queries. // For that reason it should be avoided passing those defaults to `nested` or `sub-queries`. return new self( $this->connection ); } /** * Creates a new Join Clause instance using the same connection as the initiator. * * @param string $type - JOIN type. * * @return Join_Clause */ public function new_join_clause( $type ) { return new Join_Clause( $type, $this->connection ); } /** * Limit the returned results. * Adds a `LIMIT` statement. * * @param int $limit - Max count of results to return. * * @return $this */ public function limit( $limit ) { $this->limit = (int) $limit; return $this; } /** * Add and `OFFSET` statement. * * @param int $offset - Count of results to skip. * * @return $this */ public function offset( $offset ) { $this->offset = (int) $offset; return $this; } /** * Adds an `ORDER BY` statement. * NOTE: `$column` IS NOT ESCAPED & SHOULD BE WHITELISTED! * * @param string $column - Column to order by. * @param string $direction - Direction (`asc` / `desc`). * * @return $this */ public function order_by( $column, $direction = 'asc' ) { if ( ! in_array( strtolower( $direction ), [ 'asc', 'desc' ], true ) ) { throw new InvalidArgumentException( 'Order direction must be "asc" or "desc".' ); } $this->orders[] = [ 'column' => $column, 'direction' => $direction, ]; return $this; } /** * Adds a `GROUP BY` statement. * NOTE: `$column` IS NOT ESCAPED & SHOULD BE WHITELISTED! * * @param string $column - Column to group by. * * @return $this */ public function group_by( $column ) { $this->groups[] = [ 'column' => $column, ]; return $this; } /** * Get the raw bindings array. * * @return array[] */ public function get_raw_bindings() { return $this->bindings; } /** * Get the columns to use inside the SELECT statement. * Defaults to `*` if non are selected. * * @return string */ public function compile_columns() { if ( 0 === count( $this->columns ) ) { return '*'; }; $columns = []; foreach ( $this->columns as $column ) { switch ( $column['type'] ) { case self::COLUMN_BASIC: $column_name = $this->parse_column( $column['column'] ); $as = $this->parse_as( $column['as'] ); $columns[] = "{$column_name}{$as}"; break; case self::COLUMN_SUB_SELECT: $as = $this->parse_as( $column['as'] ); $columns[] = "( {$column['column']} ){$as}"; break; case self::COLUMN_RAW: $columns[] = $column['column']; break; case self::COLUMN_COUNT: $column_name = $this->parse_column( $column['column'] ); $as = $this->parse_as( $column['as'] ); $columns[] = "COUNT({$column_name}){$as}"; break; } } return $this->concatenate( $columns, ', ' ); } /** * Get the raw columns array. * * @return string[] */ public function get_raw_columns() { return $this->columns; } /** * Compile the `columns` & `from` attributes into an actual `SELECT` statement. * * @return string */ public function compile_select() { return $this->concatenate( [ 'SELECT', $this->compile_columns(), 'FROM', $this->compile_from(), ] ); } /** * Compile the table name and alias. * * @return string */ public function compile_from() { $table = $this->wrap_with_backticks( $this->from['table'] ); $as = $this->parse_as( $this->from['as'] ); return "{$table}{$as}"; } /** * Compile the `joins` array into an actual `JOIN` statement. * * @return string */ public function compile_joins() { $joins = []; foreach ( $this->joins as $join ) { /** * @var Join_Clause $join */ $table = $join->compile_from(); $ons = $join->compile_wheres(); switch ( $join->type ) { case Join_Clause::TYPE_INNER: $joins[] = "INNER JOIN {$table} ON {$ons}"; break; case Join_Clause::TYPE_LEFT: $joins[] = "LEFT JOIN {$table} ON {$ons}"; break; case Join_Clause::TYPE_RIGHT: $joins[] = "RIGHT JOIN {$table} ON {$ons}"; break; } } return $this->concatenate( $joins ); } /** * Compile the `wheres` array into an actual `WHERE` statement. * * @return string */ public function compile_wheres() { $wheres = [ '1 = 1', // A default statement for easier `WHERE` concatenation. ]; foreach ( $this->wheres as $where ) { switch ( $where['type'] ) { case self::WHERE_BASIC: $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} {$where['operator']} {$binding}"; break; case self::WHERE_NULL: $column = $this->parse_column( $where['column'] ); $wheres[] = "{$where['and_or']} {$column} IS NULL"; break; case self::WHERE_COLUMN: $first = $this->parse_column( $where['first'] ); $second = $this->parse_column( $where['second'] ); $wheres[] = "{$where['and_or']} {$first} {$where['operator']} {$second}"; break; case self::WHERE_IN: // Handle invalid `WHERE IN` - Force the SQL to fail. if ( empty( $where['value'] ) ) { $wheres[] = "{$where['and_or']} 0 = 1"; break; } $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} IN( {$binding} )"; break; case self::WHERE_NOT_IN: // Handle invalid `WHERE IN` - Force the SQL to fail. if ( empty( $where['value'] ) ) { $wheres[] = "{$where['and_or']} 1 = 1"; break; } $column = $this->parse_column( $where['column'] ); $binding = $this->get_binding_type( $where['value'] ); $wheres[] = "{$where['and_or']} {$column} NOT IN( {$binding} )"; break; case self::WHERE_SUB: $column = $this->parse_column( $where['column'] ); $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} {$column} {$where['operator']} ( {$sub_query} )"; break; case self::WHERE_NESTED: $nested_query = $where['query']->compile_wheres(); $wheres[] = "{$where['and_or']} ( {$nested_query} )"; break; case self::WHERE_EXISTS: $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} EXISTS ( {$sub_query} )"; break; case self::WHERE_NOT_EXISTS: $sub_query = $where['query']->to_sql(); $wheres[] = "{$where['and_or']} NOT EXISTS ( {$sub_query} )"; break; } } return $this->concatenate( $wheres ); } /** * Compile the `havings` array into an actual `HAVING` statement. * TODO: Add more types. * * @return string */ public function compile_having() { if ( 0 === count( $this->havings ) ) { return ''; } $havings = [ 'HAVING', '1 = 1', // A default statement for easier `HAVING` concatenation. ]; foreach ( $this->havings as $having ) { switch ( $having['type'] ) { case self::HAVING_RAW: $havings[] = "{$having['and_or']} {$having['sql']}"; break; } } return $this->concatenate( $havings ); } /** * Compile the `groups` array into an actual `GROUP BY` statement. * * @return string */ public function compile_group_by() { if ( 0 === count( $this->groups ) ) { return ''; } $groups = []; foreach ( $this->groups as $group ) { $groups[] = $this->parse_column( $group['column'] ); } return $this->concatenate( [ 'GROUP BY', $this->concatenate( $groups, ', ' ), ] ); } /** * Compile the `orders` array into an actual `ORDER BY` statement. * * @return string */ public function compile_order_by() { if ( 0 === count( $this->orders ) ) { return ''; } $orders = []; foreach ( $this->orders as $order ) { $column = $this->parse_column( $order['column'] ); $orders[] = "{$column} {$order['direction']}"; } return $this->concatenate( [ 'ORDER BY', $this->concatenate( $orders, ', ' ), ] ); } /** * Compile the `limit` attribute into an actual `LIMIT` statement. * * @return string */ public function compile_limit() { return $this->limit ? "LIMIT {$this->limit}" : ''; } /** * Compile the `offset` attribute into an actual `OFFSET` statement. * * @return string */ public function compile_offset() { return $this->offset ? "OFFSET {$this->offset}" : ''; } /** * Get the final SQL of the query, with bindings placeholders. * * @return string */ public function to_sql() { $select = $this->compile_select(); $join = $this->compile_joins(); $where = $this->compile_wheres(); $group_by = $this->compile_group_by(); $having = $this->compile_having(); $order_by = $this->compile_order_by(); $limit = $this->compile_limit(); $offset = $this->compile_offset(); return $this->concatenate( [ $select, $join, 'WHERE', $where, $group_by, $having, $order_by, $limit, $offset, ] ); } /** * Find & get by id. * * @param int $id - ID to search for. * @param string $field - Field name. Defaults to `id`. * * @return array|null */ public function find( $id, $field = 'id' ) { return $this->where( $field, '=', $id )->first(); } /** * Return the first matching row or null otherwise. * * @return array|null */ public function first() { return $this->limit( 1 )->get()->first(); } /** * Pluck a specific column from the query results. * * @param string $column - The column to pluck. * * @return Collection */ public function pluck( $column ) { return $this ->select( [ $column ] ) ->get() ->pluck( $column ); } /** * Return the count of rows based on the query. * * @param string $column * * @return int */ public function count( $column = '*' ) { return (int) ( new Collection( $this->select( [] ) ->add_count_select( $column ) ->first() ) )->first( 0 ); } /** * Get the query result. * * @return Collection */ public function get() { $sql = $this->to_sql(); $bindings = $this->get_bindings(); if ( 0 !== count( $bindings ) ) { $sql = $this->connection->prepare( $sql, $bindings ); } $result = $this->connection->get_results( $sql, ARRAY_A ); $result = new Collection( $result ); // Add aggregations. foreach ( $this->with as $resolver ) { $result = $resolver( $result ); } return $result; } /** * Insert data to a table. * * @param array $values - Array of [ `column` => `value` ] pairs. Non-escaped. * * @return int * @throws \Exception */ public function insert( array $values ) { // Take the raw table name since `wpdb` wraps it with backticks. $table = $this->from['table']; // Data should be escaped since `wpdb` escapes it. // https://developer.wordpress.org/reference/classes/wpdb/insert/ $succeed = $this->connection->insert( $table, $values ); if ( ! $succeed ) { throw new \Exception( $this->connection->last_error ); } return $this->connection->insert_id; } /** * Update data in the table. * * @param array $values - Array of [ `column` => `value` ] pairs. Non-escaped. * * @return bool|int */ public function update( array $values ) { $this->add_binding( array_values( $values ), 'select' ); $columns = []; foreach ( $values as $column => $value ) { $binding_type = $this->get_binding_type( $value ); $column = $this->wrap_with_backticks( $column ); $columns[] = "{$column} = {$binding_type}"; } $table = $this->compile_from(); $columns = $this->concatenate( $columns, ', ' ); $where = $this->compile_wheres(); $sql = $this->concatenate( [ 'UPDATE', $table, 'SET', $columns, 'WHERE', $where, ] ); $prepared = $this->connection->prepare( $sql, $this->get_bindings() ); return $this->connection->query( $prepared ); } /** * Delete data from the table. * * @return bool|int */ public function delete() { $where = $this->compile_wheres(); $table = $this->wrap_with_backticks( $this->from['table'] ); $sql = $this->concatenate( [ 'DELETE FROM', $table, 'WHERE', $where, ] ); $prepared = $this->connection->prepare( $sql, $this->get_bindings() ); return $this->connection->query( $prepared ); } /** * Add an eager loaded relation. * * @param string $key - Array key to store the resolver in. * @param callable $resolver - Resolve function that gets the results and adds the eager loaded relation. * * @return $this */ protected function add_with( $key, callable $resolver ) { $this->with[ $key ] = $resolver; return $this; } /** * Escape a value for `LIKE` statement. * * @param string $value - Value to escape. * * @return string */ protected function escape_like( $value ) { $value = explode( '%', $value ); $value = array_map( function ( $str ) { return $this->connection->esc_like( $str ); }, $value ); return implode( '%', $value ); } /** * Get a flat array of the current bindings. * * @param null|string $type - The binding type to get. * * @return array */ protected function get_bindings( $type = null ) { if ( $type && isset( $this->bindings[ $type ] ) ) { return $this->bindings[ $type ]; } return ( new Collection( $this->bindings ) )->flatten_recursive(); } /** * Add a binding to the bindings array by a sector. * * @param string|array $value - Raw value that needs to be bind. * @param string $type - Bind type (the sector in the SQL query). * * @return $this */ protected function add_binding( $value, $type ) { if ( is_array( $value ) ) { $this->bindings[ $type ] = array_values( array_merge( $this->bindings[ $type ], $value ) ); } else { $this->bindings[ $type ][] = $value; } return $this; } /** * Get the type of the binding type for SQL `prepare` function. * * @param array|string|numeric $value - The value to get the binding for. * * @return string - One of `%d` / `%f` / `%s`. */ protected function get_binding_type( $value ) { if ( is_array( $value ) ) { $bindings = array_map( function( $value ) { return $this->get_binding_type( $value ); }, array_values( $value ) ); return $this->concatenate( $bindings, ', ' ); } return is_float( $value ) ? '%f' : ( is_int( $value ) ? '%d' : '%s' ); } /** * Wrap a value with backticks. * * @param numeric|string|string[] $value - Value to wrap. * * @return string|string[] */ protected function wrap_with_backticks( $value ) { if ( is_array( $value ) ) { return array_map( [ $this, 'wrap_with_backticks' ], $value ); } // It should not wrap '*' with backticks. if ( '*' === $value ) { return $value; } $sanitized_value = is_scalar( $value ) ? preg_replace( '/[^a-zA-Z0-9_\-]/', '', $value ) : ''; return "`{$sanitized_value}`"; } /** * Concatenate an array of segments, removing empties. * * @param array $segments - Segments to concatenate. * @param array $separator - Separator string. Defaults to empty space. * * @return string */ protected function concatenate( array $segments, $separator = ' ' ) { return implode( $separator, array_filter( $segments, function ( $value ) { return '' !== (string) $value; } ) ); } /** * Parse a column by splitting it to table & column names, and wrapping it with backticks. * * @param $column - Column to parse. * * @return string */ protected function parse_column( $column ) { $parsed = explode( '.', $column ); $parsed = $this->wrap_with_backticks( $parsed ); return $this->concatenate( $parsed, '.' ); } protected function parse_as( $as ) { if ( ! $as ) { return ''; } $as = $this->wrap_with_backticks( $as ); return " AS {$as}"; } /** * Determine if a column is already selected. * * @param string $name - Column name to check. * * @return mixed|null */ protected function is_column_selected( $name ) { return ( new Collection( $this->columns ) ) ->find( function ( $column ) use ( $name ) { // Check for aliases. if ( ! empty( $column['as'] ) ) { return $name === $column['as']; } return $name === $column['column']; } ); } } PK j��\��`M� � core/database/model-base.phpnu �[��� <?php namespace ElementorPro\Core\Database; use ElementorPro\Core\Utils\Collection; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } abstract class Model_Base implements \JsonSerializable { // Casting types. const TYPE_BOOLEAN = 'boolean'; const TYPE_COLLECTION = 'collection'; const TYPE_INTEGER = 'integer'; const TYPE_STRING = 'string'; const TYPE_JSON = 'json'; const TYPE_DATETIME = 'datetime'; const TYPE_DATETIME_GMT = 'datetime_gmt'; /** * Casts array. * Used to automatically cast values from DB to the appropriate property type. * * @var array */ protected static $casts = []; /** * Model_Base constructor. * * @param array $fields - Fields from the DB to fill. * * @return void */ public function __construct( array $fields ) { foreach ( $fields as $key => $value ) { if ( ! property_exists( $this, $key ) ) { continue; } $this->{$key} = ( empty( static::$casts[ $key ] ) ) ? $value : static::cast( $value, static::$casts[ $key ] ); } } /** * Get the model's table name. * Throws an exception by default in order to require implementation, * since abstract static functions are not allowed. * * @return string */ public static function get_table() { throw new \Exception( 'You must implement `get_table()` inside ' . static::class ); } /** * Create a Query Builder for the model's table. * * @param \wpdb|null $connection - MySQL connection to use. * * @return Query_Builder */ public static function query( \wpdb $connection = null ) { $builder = new Model_Query_Builder( static::class, $connection ); return $builder->from( static::get_table() ); } /** * Cast value into specific type. * * @param $value - Value to cast. * @param $type - Type to cast into. * * @return mixed */ protected static function cast( $value, $type ) { if ( null === $value ) { return null; } switch ( $type ) { case self::TYPE_BOOLEAN: return boolval( $value ); case self::TYPE_COLLECTION: return new Collection( $value ); case self::TYPE_INTEGER: return intval( $value ); case self::TYPE_STRING: return strval( $value ); case self::TYPE_JSON: return json_decode( $value, true ); case self::TYPE_DATETIME: return new \DateTime( $value ); case self::TYPE_DATETIME_GMT: return new \DateTime( $value, new \DateTimeZone( 'GMT' ) ); } return $value; } /** * Cast a model property value into a JSON compatible data type. * * @param $value - Value to cast. * @param $type - Type to cast into. * @param $property_name - The model property name. * * @return mixed */ protected static function json_serialize_property( $value, $type, $property_name ) { switch ( $type ) { case self::TYPE_DATETIME: case self::TYPE_DATETIME_GMT: /** @var \DateTime $value */ return $value->format( 'c' ); } /** @var mixed $value */ return $value; } /** * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize() { return ( new Collection( (array) $this ) ) ->map( function ( $_, $key ) { $value = $this->{$key}; $type = array_key_exists( $key, static::$casts ) ? static::$casts[ $key ] : null; if ( null === $value ) { return $value; } // Can be overridden by child model. $value = static::json_serialize_property( $value, $type, $key ); if ( $value instanceof \JsonSerializable ) { return $value->jsonSerialize(); } return $value; } ) ->all(); } } PK j��\��\�� � '