Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,713 @@
<?php
if ( ! class_exists( 'BWF_AS_Action_Store' ) ) {
#[AllowDynamicProperties]
class BWF_AS_Action_Store extends ActionScheduler_Store {
public $bwf_action_data = [];
public $action_table = '';
public $claim_table = '';
public $p_key = '';
public function init() {
global $wpdb;
$this->action_table = BWF_AS_Actions_Crud::_table();
$this->claim_table = $wpdb->bwf_action_claim;
$this->p_key = BWF_AS_Actions_Crud::$primary_key;
}
public function save_action( ActionScheduler_Action $action, ?DateTime $date = null ) {
/** Not scheduling any new action while processing our requests */
}
public function fetch_action( $action_id ) {
global $wpdb;
if ( empty( $action_id ) ) {
return $this->get_null_action();
}
$this->log( 'fetch running action id: ' . $action_id );
if ( class_exists( 'BWFAN_Merge_Tag_Loader' ) ) {
BWFAN_Merge_Tag_Loader::reset_data();
}
/**
* Below code will run between these 2 hooks action_scheduler_begin_execute and action_scheduler_after_execute
*/
if ( true === BWF_AS::is_execution_started() ) {
/** Changing status to running i.e. 1 on action */
$wpdb->update( $this->action_table, [ 'status' => 1 ], [ $this->p_key => $action_id ], [ '%s' ], [ '%d' ] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}
$data = $this->get_action_data( $action_id );
if ( empty( $data ) || ! ( $data instanceof StdClass ) ) {
return $this->get_null_action();
}
if ( true === BWF_AS::is_execution_started() ) {
/** Scheduling recurring action if possible */
$this->schedule_recurring_action( $action_id );
}
/** Fetching action data again as status may be altered */
$data = $this->get_action_data( $action_id );
/** If hook not present, return null action */
if ( empty( $data->hook ) || ! ( $data instanceof StdClass ) ) {
return $this->get_null_action();
}
return $this->make_action_from_db_record( $data );
}
protected function get_null_action() {
return new ActionScheduler_NullAction();
}
public function log( $msg ) {
BWF_Logger::get_instance()->log( WooFunnels_AS_DS::$unique . ' - ' . $msg, 'woofunnel-as' );
}
protected function get_action_data( $action_id ) {
$cache_key = 'bwf_fetch_action_' . $action_id;
if ( isset( $this->bwf_action_data[ $action_id ] ) ) {
return $this->bwf_action_data[ $action_id ];
}
$data = wp_cache_get( $cache_key, __CLASS__ );
if ( false === $data ) {
$data = BWF_AS_Actions_Crud::get_single_action( $action_id );
/** Saving data to local scope and cache */
if ( is_object( $data ) ) {
$this->bwf_action_data[ $action_id ] = $data;
wp_cache_set( $cache_key, $data, __CLASS__, ( 60 ) );
}
}
return $data;
}
/**
* Helper method to schedule recurring action before execution itself.
* This make sure recurring action should be scheduled.
*
* @param $action_id
*/
protected function schedule_recurring_action( $action_id ) {
global $wpdb;
$data = $this->get_action_data( $action_id );
/** Checking if recurring action */
if ( ! ( $data instanceof StdClass ) || false === $this->action_is_recurring( $data ) ) {
return;
}
$args = ( is_array( $data->args ) && count( $data->args ) > 0 ) ? $data->args : array();
$group = ( ! empty( $data->group_slug ) ) ? $data->group_slug : '';
/** Checking if already running then change the status to failed and schedule new action */
$count = bwf_scheduled_action_count( $data->hook, $args, $group, '1', 'recurring' );
if ( false === $count || 1 < $count ) {
$this->log( __FUNCTION__ . ' id ' . $action_id . ', recurring action already running count: ' . $count );
/** Changing status to failed i.e. 2 on action */
$wpdb->update( $this->action_table, [ 'status' => 2 ], [ $this->p_key => $action_id ], [ '%s' ], [ '%d' ] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
/** Modify action object cache data status */
if ( isset( $this->bwf_action_data[ $action_id ] ) ) {
$this->bwf_action_data[ $action_id ]->status = '2';
}
}
/** Validate non-running actions */
$count = bwf_scheduled_action_count( $data->hook, $args, $group, '0', 'recurring' );
if ( 1 === $count ) {
/** if one current and one more action non-running found then no need to schedule */
return;
}
if ( 1 < $count ) {
/** delete all non running actions i.e. 0 except current action */
bwf_delete_scheduled_recurring_action( $data->hook, 0, $action_id );
}
/** Scheduling new action */
$curr_time = current_time( 'mysql', 1 );
$exec_time = time() + (int) $data->recurring_interval;
$exec_time = apply_filters( 'bwf_recurring_action_' . $data->hook . '_execution_time', $exec_time, $data );
$new_data = array(
'c_date' => $curr_time,
'e_time' => $exec_time,
'hook' => $data->hook,
'recurring_interval' => (int) $data->recurring_interval,
'claim_id' => 0
);
if ( is_array( $data->args ) && count( $data->args ) > 0 ) {
$new_data['args'] = wp_json_encode( $data->args );
}
if ( ! empty( $data->group_slug ) ) {
$new_data['group_slug'] = $data->group_slug;
}
BWF_AS_Actions_Crud::insert( $new_data );
}
/**
* Helper method that checks if an action is recurring.
*
* @param $data
*
* @return bool
*/
protected function action_is_recurring( $data ) {
if ( ! is_object( $data ) ) {
return false;
}
if ( (int) $data->recurring_interval < 1 ) {
return false;
}
return true;
}
/**
* Initiate action class object with needful data
*
* @param $data
*
* @return ActionScheduler_Action|ActionScheduler_FinishedAction
*/
protected function make_action_from_db_record( $data ) {
$hook = $data->hook;
$args = ( is_array( $data->args ) && count( $data->args ) > 0 ) ? $data->args : [];
/** creating fresh schedule */
$schedule = new ActionScheduler_NullSchedule();
$group = ( ! empty( $data->group_slug ) ) ? $data->group_slug : '';
if ( $this->verify_status( $data->status ) ) {
$action = new ActionScheduler_Action( $hook, $args, $schedule, $group );
} else {
/** status not 0 - finishing AS action (status won't occur as we are fetching 0 status actions only) */
$action = new ActionScheduler_FinishedAction( $hook, $args, $schedule, $group );
}
return $action;
}
/**
* Helper method: If pending action then bool true otherwise false
*
* @param $status
*
* @return bool
*/
protected function verify_status( $status ) {
return ( 0 == $status || 1 == $status ) ? true : false;
}
/**
* @param string $hook
* @param array $params
*
* @return string
*/
public function find_action( $hook, $params = [] ) {
/** This is invoked during unscheduled or next schedule, we are not doing anything, so blank */
return '';
}
/**
* @param array $query
* @param string $query_type
*
* @return array|string ids array or count
*/
public function query_actions( $query = [], $query_type = 'select' ) {
global $wpdb;
/** cleanup call handling */
if ( ! isset( $query['status'] ) || in_array( $query['status'], array( 'complete', 'canceled', 'failed' ), true ) || is_array( $query['status'] ) ) {
return array();
}
if ( 'pending' === $query['status'] ) {
$query['status'] = '0';
} elseif ( 'in-progress' === $query['status'] ) {
$query['status'] = '1';
}
/** Code will through in case of pending status i.e. 0 */
$sql = $this->get_query_actions_sql( $query, $query_type );
$value = ( 'count' === $query_type ) ? $wpdb->get_var( $sql ) : $wpdb->get_col( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
if ( empty( $value ) ) {
return array();
}
$this->log( __FUNCTION__ . ': status ' . $query['status'] . ' - (' . $this->get_as_defined_status_val( $query['status'] ) . ')' . ' query result: ' . implode( ',', $value ) );
return $value;
}
/**
* Returns the SQL statement to query (or count) actions.
*
* @param array $query Filtering options
* @param string $select_or_count Whether the SQL should select and return the IDs or just the row count
*
* @return string SQL statement. The returned SQL is already properly escaped.
* 'status' => ActionScheduler_Store::STATUS_PENDING,
* 'modified' => $cutoff,
* 'modified_compare' => '<=',
* 'claimed' => true,
* 'per_page' => $this->get_batch_size(),
*/
protected function get_query_actions_sql( array $query, $select_or_count = 'select' ) {
if ( ! in_array( $select_or_count, array( 'select', 'count' ), true ) ) {
throw new InvalidArgumentException( esc_html__( 'Invalid value for select or count parameter. Cannot query actions.', 'action-scheduler' ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
}
$query = wp_parse_args( $query, [
'hook' => '',
'args' => null,
'date' => null,
'date_compare' => '<=',
'modified' => null,
'modified_compare' => '<=',
'group' => '',
'status' => '0',
'claimed' => null,
'per_page' => 5,
'offset' => 0,
'orderby' => 'date',
'order' => 'ASC',
] );
global $wpdb;
$sql = 'SELECT ';
$sql .= ( 'count' === $select_or_count ) ? "count({$this->p_key})" : "{$this->p_key} ";
$sql .= "FROM {$this->action_table}";
$sql_params = [];
$sql .= ' WHERE 1=1';
if ( ! empty( $query['group'] ) ) {
$sql .= ' AND group_slug=%s';
$sql_params[] = $query['group'];
}
if ( $query['hook'] ) {
$sql .= ' AND hook=%s';
$sql_params[] = $query['hook'];
}
if ( ! is_null( $query['args'] ) ) {
$sql .= ' AND args=%s';
$sql_params[] = wp_json_encode( $query['args'] );
}
/** 0 or 1 in our case */
if ( '' !== $query['status'] ) {
$sql .= ' AND status=%s';
$sql_params[] = $query['status'];
}
if ( $query['date'] instanceof DateTime ) {
$date = clone $query['date'];
$date->setTimezone( new DateTimeZone( 'UTC' ) );
$date_string = $date->format( 'Y-m-d H:i:s' );
$comparator = $this->validate_sql_comparator( $query['date_compare'] );
$sql .= " AND e_time $comparator %s";
$sql_params[] = $date_string;
}
if ( $query['claimed'] === true ) {
$sql .= ' AND claim_id != 0';
} elseif ( $query['claimed'] === false ) {
$sql .= ' AND claim_id = 0';
} elseif ( ! is_null( $query['claimed'] ) ) {
$sql .= ' AND claim_id = %d';
$sql_params[] = $query['claimed'];
}
if ( $query['modified'] instanceof DateTime ) {
$modified = clone $query['modified'];
$modified->setTimezone( new DateTimeZone( 'UTC' ) );
$date_string = $modified->getTimestamp();
$comparator = $this->validate_sql_comparator( $query['modified_compare'] );
$sql .= " AND e_time $comparator %s";
$sql_params[] = $date_string;
}
if ( 'select' === $select_or_count ) {
switch ( $query['orderby'] ) {
case 'date':
default:
$orderby = 'e_time';
break;
}
$order = ( strtoupper( $query['order'] ) === 'ASC' ) ? 'ASC' : 'DESC';
$sql .= " ORDER BY $orderby $order";
if ( $query['per_page'] > 0 ) {
$sql .= ' LIMIT %d, %d';
$sql_params[] = $query['offset'];
$sql_params[] = $query['per_page'];
}
}
if ( ! empty( $sql_params ) ) {
$sql = $wpdb->prepare( $sql, $sql_params ); //phpcs:ignore WordPress.DB.PreparedSQL
}
return $sql;
}
protected function get_as_defined_status_val( $status ) {
switch ( $status ) {
case '0':
return 'pending';
case '1':
return 'in-progress';
case '2':
return 'canceled';
}
return $status;
}
/**
* Get a count of all actions in the store, grouped by status; used in native actions listing. Not used in our scope.
*/
public function action_counts() {
return [];
}
/**
* @param string $action_id
*
* @return void
* @throws InvalidArgumentException
*/
public function cancel_action( $action_id ) {
$this->log( __FUNCTION__ . ' id ' . $action_id );
return;
}
/**
* @param string $action_id
*/
public function delete_action( $action_id ) {
$this->log( __FUNCTION__ . ' id ' . $action_id );
return;
}
/**
* don't know where using
*
* @param string $action_id
*
* @return DateTime The local date the action is scheduled to run, or the date that it ran.
* @throws InvalidArgumentException
*/
public function get_date( $action_id ) {
$date = $this->get_date_gmt( $action_id );
ActionScheduler_TimezoneHelper::set_local_timezone( $date );
return $date;
}
/**
* @param string $action_id
*
* @return DateTime The GMT date the action is scheduled to run, or the date that it ran.
* @throws InvalidArgumentException
*/
protected function get_date_gmt( $action_id ) {
$record = BWF_AS_Actions_Crud::get_single_action( $action_id );
if ( empty( $record ) ) {
throw new InvalidArgumentException( esc_html( sprintf( __( 'Unidentified action %s', 'action-scheduler' ), $action_id ) ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch, WordPress.WP.I18n.MissingTranslatorsComment
}
if ( $this->verify_status( $record->status ) ) {
return as_get_datetime_object( $record->e_time );
}
}
/**
* @param int $max_actions
* @param DateTime $before_date Jobs must be schedule before this date. Defaults to now.
*
* @return ActionScheduler_ActionClaim
*/
public function stake_claim( $max_actions = 10, ?DateTime $before_date = null, $hooks = array(), $group = '' ) {
$claim_id = $this->generate_claim_id();
$this->log( __FUNCTION__ . ' claim id: ' . $claim_id );
$this->claim_actions( $claim_id, $max_actions, $before_date, $hooks, $group );
$action_ids = $this->find_actions_by_claim_id( $claim_id );
return new ActionScheduler_ActionClaim( $claim_id, $action_ids );
}
/**
* Generate claim id of current date time
*
* @return int
*/
protected function generate_claim_id() {
global $wpdb;
$now = as_get_datetime_object();
//phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->insert( $this->claim_table, [
'date' => $now->format( 'Y-m-d H:i:s' ),
] );
return $wpdb->insert_id;
}
/**
* Claim actions which are executable based on given inputs
*
* @param string $claim_id
* @param int $limit
* @param DateTime $before_date Should use UTC timezone.
*
* @return int The number of actions that were claimed
* @throws RuntimeException
*
*/
protected function claim_actions( $claim_id, $limit, ?DateTime $before_date = null, $hooks = array(), $group = '' ) {
global $wpdb;
/** can't use $wpdb->update() because of the <= condition */
$update = "SELECT {$this->p_key} FROM {$this->action_table}";
$params = [];
$where = 'WHERE `claim_id` = 0 AND `e_time` <= %s AND `status` = 0';
$params[] = time();
if ( ! empty( $group ) ) {
$where .= ' AND `group` = %s';
$params[] = $group;
}
$order = 'ORDER BY `e_time` ASC LIMIT %d';
$params[] = $limit;
$sql = $wpdb->prepare( "{$update} {$where} {$order}", $params ); //phpcs:ignore WordPress.DB.PreparedSQL
$action_ids = $wpdb->get_results( $sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
if ( ! is_array( $action_ids ) || count( $action_ids ) == 0 ) {
return 0;
}
$action_ids = array_column( $action_ids, $this->p_key );
/** Update call */
$type = array_fill( 0, count( $action_ids ), '%d' );
$format = implode( ', ', $type );
$query = "UPDATE {$this->action_table} SET `claim_id` = %d WHERE {$this->p_key} IN ({$format})";
$params = array( $claim_id );
$params = array_merge( $params, $action_ids );
$sql = $wpdb->prepare( $query, $params ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
$rows_affected = $wpdb->query( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
if ( $rows_affected === false ) {
throw new RuntimeException( esc_html__( 'Unable to claim actions. Database error.', 'action-scheduler' ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
}
return (int) $rows_affected;
}
/**
* Get Actions against a claim_id
*
* @param string $claim_id
*
* @return $array
*/
public function find_actions_by_claim_id( $claim_id ) {
global $wpdb;
$cache_key = 'bwf_action_ids_for_claim_id_' . $claim_id;
$cache_available = wp_cache_get( $cache_key, __CLASS__ );
if ( false !== $cache_available ) {
return $cache_available;
}
$sql = "SELECT `{$this->p_key}` FROM {$this->action_table} WHERE claim_id=%d ORDER BY e_time ASC";
$sql = $wpdb->prepare( $sql, $claim_id ); //phpcs:ignore WordPress.DB.PreparedSQL
$action_ids = $wpdb->get_col( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
$return = array_map( 'intval', $action_ids );
wp_cache_set( $cache_key, $return, __CLASS__, ( HOUR_IN_SECONDS / 4 ) );
$this->log( 'Found ids: ' . implode( ', ', $return ) );
return $return;
}
/**
* Return unique claim id counts
*
* @return int
*/
public function get_claim_count() {
global $wpdb;
/** status 0 have actions which are executable */
$sql = "SELECT COUNT(DISTINCT claim_id) FROM {$this->action_table} WHERE claim_id != 0 AND status = 0";
return (int) $wpdb->get_var( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
}
/**
* Return an action's claim ID, as stored in the claim_id column
*
* @param string $action_id
*
* @return mixed
*/
public function get_claim_id( $action_id ) {
$this->log( __FUNCTION__ . ' ' . $action_id );
$claim_id = BWF_AS_Actions_Crud::get_single_action( $action_id, 'claim_id' );
return (int) $claim_id;
}
/**
* Releasing the claim
*
* @param ActionScheduler_ActionClaim $claim
*/
public function release_claim( ActionScheduler_ActionClaim $claim ) {
$this->log( __FUNCTION__ . ' id ' . $claim->get_id() );
global $wpdb;
$wpdb->update( $this->action_table, [ 'claim_id' => 0 ], [ 'claim_id' => $claim->get_id() ], [ '%d' ], [ '%d' ] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->delete( $this->claim_table, [ 'id' => $claim->get_id() ], [ '%d' ] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* Unclaim pending actions that have not been run within a given time limit.
* Default 300
* Called inside reset_timeouts method
*
* @param string $action_id
*
* @return void
*/
public function unclaim_action( $action_id ) {
$this->log( __FUNCTION__ . ' id ' . $action_id );
global $wpdb;
$wpdb->update( $this->action_table, [ 'claim_id' => 0 ], [ $this->p_key => $action_id ], [ '%s' ], [ '%d' ] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}
/**
* @param string $action_id
* @param null $e
*/
public function mark_failure( $action_id, $e = null ) {
$this->log( __FUNCTION__ . ' for action id ' . $action_id );
/** Log failure data */
$this->log_failure_data( $action_id );
/** Deleting existing action */
BWF_AS_Actions_Crud::delete( $action_id );
}
/**
* Helper method
* Log failure action data in bwf logs
*
* @param $action_id
*/
protected function log_failure_data( $action_id ) {
$data = $this->get_action_data( $action_id );
if ( $data instanceof StdClass ) {
$log_arr = array(
'action_id' => $action_id,
'creation_date' => $data->c_date,
'execution_time' => $data->e_time,
'hook' => $data->hook,
'arguments' => $data->args,
'group' => $data->group_slug,
'recurring' => $data->recurring_interval,
'error' => error_get_last(),
);
/** updating logs force */
add_filter( 'bwf_logs_allowed', array( $this, 'overriding_bwf_logging' ), 99999, 2 );
BWF_Logger::get_instance()->log( print_r( $log_arr, true ), 'woofunnel-failed-actions' );
remove_filter( 'bwf_logs_allowed', array( $this, 'overriding_bwf_logging' ), 99999, 2 );
}
}
/**
* @param string $action_id
*
* @return void
*/
public function log_execution( $action_id ) {
/** no need to log as we are managing logs differently, even attempts */
}
/**
* @param string $action_id
*/
public function mark_complete( $action_id ) {
$this->log( __FUNCTION__ . ' for action id ' . $action_id );
/** Deleting existing action */
BWF_AS_Actions_Crud::delete( $action_id );
}
public function get_status( $action_id ) {
$this->log( __FUNCTION__ . ' of action id ' . $action_id );
$status = BWF_AS_Actions_Crud::get_action_status( $action_id );
if ( null === $status ) {
throw new InvalidArgumentException( esc_html__( 'Invalid action ID. No status found.', 'action-scheduler' ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
} else {
return $this->get_as_defined_status_val( $status );
}
}
public function overriding_bwf_logging( $value, $filename ) {
return true;
}
/**
* Cancel pending actions by hook.
*
* @param string $hook
*
* @since 3.0.0 Action Scheduler and 1.9.15 Core
*/
public function cancel_actions_by_hook( $hook ) {
return;
}
/**
* Cancel pending actions by group.
*
* @param string $group
*
* @since 3.0.0 Action Scheduler and 1.9.15 Core
*/
public function cancel_actions_by_group( $group ) {
return;
}
}
}

View File

@@ -0,0 +1,107 @@
<?php
use WP_CLI\ExitException;
use function WP_CLI\Utils\get_flag_value;
if ( ! class_exists( 'BWF_AS_CLI' ) ) {
/**
* Commands for the Custom Action Scheduler
*/
#[AllowDynamicProperties]
class BWF_AS_CLI extends WP_CLI_Command {
/**
* Run the Autonami tasks
*
* ## OPTIONS
*
* [--size=<size>]
* : The maximum number of tasks to run. Defaults to 50.
*
* @param array $args Positional arguments.
* @param array $assoc_args Keyed arguments.
*
* @throws ExitException When an error occurs.
*/
public function run( $args, $assoc_args ) {
// Handle passed arguments.
$size = absint( get_flag_value( $assoc_args, 'size', 50 ) );
$tasks_completed = 0;
try {
if ( ! class_exists( 'ActionScheduler_QueueRunner' ) ) {
$this->print_custom_error( '1' );
}
$global_settings = BWFAN_Common::get_global_settings();
if ( 1 == $global_settings['bwfan_sandbox_mode'] || ( defined( 'BWFAN_SANDBOX_MODE' ) && true === BWFAN_SANDBOX_MODE ) ) {
$this->print_custom_error( '2' );
}
/** Custom queue cleaner instance */
$cleaner = new ActionScheduler_QueueCleaner( null, $size );
/** Queue runner instance */
$runner = new ActionScheduler_WPCLI_QueueRunner( null, null, $cleaner );
/** Run Action Scheduler worker */
// Determine how many tasks will be run in the first batch.
$total = $runner->setup( $size );
WP_CLI::log( "Current batch size is: " . $size );
$this->print_total_tasks( $total );
$tasks_completed = $runner->run();
} catch ( Exception $e ) {
$this->print_error( $e );
}
$this->print_success( $tasks_completed );
}
protected function print_custom_error( $type ) {
switch ( $type ) {
case '1':
$msg = 'ActionScheduler_QueueRunner class not found.';
break;
case '2':
$msg = 'FunnelKit Automations Sandbox mode is ON.';
break;
default:
$msg = 'Some error occurred';
}
WP_CLI::error( sprintf( /* translators: %s refers to the exception error message. */ $msg ) );
}
/**
* Print WP CLI message about how many tasks are about to be processed.
*
* @param int $total
*/
protected function print_total_tasks( $total ) {
WP_CLI::log( sprintf( /* translators: %d refers to how many scheduled tasks were found to run */ _n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ), number_format_i18n( $total ) ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch, WordPress.WP.I18n.MissingTranslatorsComment
}
/**
* Convert an exception into a WP CLI error.
*
* @param Exception $e The error object.
*
* @throws ExitException
*/
protected function print_error( Exception $e ) {
WP_CLI::error( sprintf( /* translators: %s refers to the exception error message. */ __( 'There was an error running the action scheduler: %s', 'action-scheduler' ), $e->getMessage() ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch, WordPress.WP.I18n.MissingTranslatorsComment
}
/**
* Print a success message with the number of completed tasks.
*
* @param int $tasks_completed
*/
protected function print_success( $tasks_completed ) {
WP_CLI::success( sprintf( /* translators: %d refers to the total number of tasks completed */ _n( '%d scheduled task completed.', '%d scheduled tasks completed.', $tasks_completed, 'action-scheduler' ), number_format_i18n( $tasks_completed ) ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch, WordPress.WP.I18n.MissingTranslatorsComment
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
if ( ! class_exists( 'BWF_AS_Log_Store' ) ) {
/**
* Not saving any log as no do_action left in action data store
*
* Class BWFAN_AS_CT_Log_Store
*/
#[AllowDynamicProperties]
class BWF_AS_Log_Store extends ActionScheduler_Logger {
public function log( $action_id, $message, ?DateTime $date = null ) {
return;
}
public function get_entry( $entry_id ) {
return new ActionScheduler_NullLogEntry();
}
public function get_logs( $action_id ) {
return array();
}
public function init() {
}
public function clear_deleted_action_logs( $action_id ) {
}
}
}

View File

@@ -0,0 +1,192 @@
<?php
if ( ! class_exists( 'BWF_AS' ) ) {
#[AllowDynamicProperties]
class BWF_AS {
private static $instance;
protected $start_time = 0;
public static $execution_started = 0;
public function __construct() {
global $wpdb;
$wpdb->bwf_actions = $wpdb->prefix . 'bwf_actions';
$wpdb->bwf_action_claim = $wpdb->prefix . 'bwf_action_claim';
$this->start_time = time();
}
public static function instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Change the data store
*/
public function change_data_store() {
/** Hook late so that functionalities can be hooked in */
add_action( 'plugins_loaded', [ $this, 'bwf_data_store_set' ], 1000 );
/** Removing all action data store change filter and then assign ours */
remove_all_filters( 'action_scheduler_store_class' );
add_filter( 'action_scheduler_store_class', [ $this, 'set_store_class' ], 1000000, 1 );
/** Removing all log data store change filter and then assign ours */
remove_all_filters( 'action_scheduler_logger_class' );
add_filter( 'action_scheduler_logger_class', [ $this, 'set_logger_class' ], 1000000, 1 );
/** Removing all AS memory exceeds filter */
remove_all_filters( 'action_scheduler_memory_exceeded' );
add_filter( 'action_scheduler_memory_exceeded', [ $this, 'check_memory_exceeded' ], 1000000, 1 );
add_action( 'action_scheduler_begin_execute', [ $this, 'attach_flag' ] );
add_action( 'action_scheduler_after_execute', [ $this, 'detach_flag' ] );
add_action( 'action_scheduler_failed_execution', [ $this, 'detach_flag' ] );
add_action( 'action_scheduler_failed_validation', [ $this, 'detach_flag' ] );
}
public function bwf_data_store_set() {
do_action( 'bwf_as_data_store_set' );
}
/**
* Override the action store with our own
*
* @param string $class
*
* @return string
*/
public function set_store_class( $class ) {
return BWF_AS_Action_Store::class;
}
/**
* Override the logger with our own
*
* @param string $class
*
* @return string
*/
public function set_logger_class( $class ) {
return BWF_AS_Log_Store::class;
}
/**
* Override memory exceeded filter value
*
* @param $memory_exceeded
*
* @return bool
*/
public function check_memory_exceeded( $memory_exceeded ) {
if ( true === $memory_exceeded ) {
return $memory_exceeded;
}
return $this->validate_time_breach();
}
/**
* Validate if call input time reached
*
* @return bool
*/
public function validate_time_breach() {
$per_call_time = apply_filters( 'bwfan_as_per_call_time', 30 );
if ( ( time() - $this->start_time ) >= $per_call_time || $this->memory_exceeded() ) {
return true;
}
return false;
}
/**
* Check server memory limit.
* Using 75% max
*
* @return bool
*/
public function memory_exceeded() {
$memory_limit = $this->get_memory_limit() * 0.9;
$current_memory = memory_get_usage( true );
return ( $current_memory >= $memory_limit );
}
/**
* Get Server memory limit value
*
* @return int|mixed
*/
public function get_memory_limit() {
if ( function_exists( 'ini_get' ) ) {
$memory_limit = ini_get( 'memory_limit' );
} else {
$memory_limit = '128M'; // Sensible default, and minimum required by WooCommerce
}
if ( ! $memory_limit || - 1 === $memory_limit || '-1' === $memory_limit ) {
// Unlimited, set to 32GB.
$memory_limit = '32G';
}
return $this->convert_hr_to_bytes( $memory_limit );
}
/**
* Convert memory to bytes
*
* @param $value
*
* @return int|mixed
*/
public function convert_hr_to_bytes( $value ) {
if ( function_exists( 'wp_convert_hr_to_bytes' ) ) {
return wp_convert_hr_to_bytes( $value );
}
$value = strtolower( trim( $value ) );
$bytes = (int) $value;
if ( false !== strpos( $value, 'g' ) ) {
$bytes *= GB_IN_BYTES;
} elseif ( false !== strpos( $value, 'm' ) ) {
$bytes *= MB_IN_BYTES;
} elseif ( false !== strpos( $value, 'k' ) ) {
$bytes *= KB_IN_BYTES;
}
// Deal with large (float) values which run into the maximum integer size.
return min( $bytes, PHP_INT_MAX );
}
/**
* Attach flag before starting execution of the action
*
* @return void
*/
public function attach_flag() {
self::$execution_started = 1;
}
/**
* Detach flag after finishing the execution of the action
*
* @return void
*/
public function detach_flag() {
self::$execution_started = 0;
}
/**
* Check if action execution started
*
* @return bool
*/
public static function is_execution_started() {
return ( self::$execution_started === 1 );
}
}
}

View File

@@ -0,0 +1,2 @@
<?php
// Silence is golden.

View File

@@ -0,0 +1,685 @@
<?php
/**
* @todo things to do
* AS data store cli
*/
if ( ! class_exists( 'WooFunnels_AS_DS' ) ) {
#[AllowDynamicProperties]
final class WooFunnels_AS_DS {
public static $unique = '';
private static $ins = null;
public $dir = __DIR__;
/**
* WooFunnels_Actions constructor.
*/
public function __construct() {
$enable_as_ds = apply_filters( 'enable_woofunnels_as_ds', false );
if ( true !== $enable_as_ds && ! class_exists( 'BWFAN_Core' ) ) {
return;
}
add_action( 'action_scheduler_pre_init', array( $this, 'load_files' ) );
/** Rest API endpoint */
add_action( 'rest_api_init', array( $this, 'register_endpoints' ) );
/** BWF Action Scheduler custom table worker callback */
add_action( 'bwf_as_run_queue', array( $this, 'run_as_ct_worker' ) );
add_action( 'action_scheduler_pre_init', array( $this, 'as_pre_init_cb' ) );
/** Needs to code */
add_action( 'action_scheduler_pre_init', array( $this, 'as_pre_init_cli_cb' ) );
/** Creating tables */
add_action( 'bwf_after_action_scheduler_load', [ $this, 'bwf_after_action_scheduler_load' ] );
}
/**
* @return WooFunnels_AS_DS instance
*/
public static function get_instance() {
if ( null === self::$ins ) {
self::$ins = new self();
}
return self::$ins;
}
/**
* Load files
*/
public function load_files() {
foreach ( glob( $this->dir . '/db/class-*.php' ) as $file_name ) {
require_once( $file_name );
}
foreach ( glob( $this->dir . '/asct/class-*.php' ) as $file_name ) {
if ( false !== strpos( $file_name, '-cli.php' ) ) {
/** Will load CLI when need to run */
continue;
}
require_once( $file_name );
}
/** Loading WooFunnels Actions CLI */
if ( version_compare( PHP_VERSION, '5.3', '>' ) ) {
$this->load_cli();
}
do_action( 'bwf_after_action_scheduler_load' );
}
/**
* Load CLI file
*/
public function load_cli() {
/** Not including files if Action Scheduler doesn't exist */
if ( ! class_exists( 'ActionScheduler' ) ) {
return;
}
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once $this->dir . '/asct/class-bwf-as-cli.php';
WP_CLI::add_command( 'woofunnels-actions', 'BWF_AS_CLI' );
}
}
/**
* Load Hooks after Action Scheduler is loaded
*/
public function bwf_after_action_scheduler_load() {
/** Create action scheduler custom tables */
add_filter( 'bwf_add_db_table_schema', [ $this, 'create_db_tables' ], 10, 2 );
/** Un-schedule older WP cron event */
add_action( 'admin_init', [ $this, 'maybe_set_bwf_ct_worker' ], 9 );
/** Registering custom schedule */
add_filter( 'cron_schedules', [ $this, 'add_cron_schedule' ] );
}
/**
* Create DB tables
* Actions and Action_Claim
*/
public function create_db_tables( $args, $tables ) {
$max_index_length = 191;
if ( $tables['version'] !== BWF_DB_VERSION || ! in_array( 'bwf_actions', $tables['tables'], true ) ) {
$args[] = [
'name' => 'bwf_actions',
'schema' => "CREATE TABLE `{table_prefix}bwf_actions` (
id bigint(20) unsigned NOT NULL auto_increment,
c_date datetime NOT NULL default '0000-00-00 00:00:00',
e_time int(12) NOT NULL default 0,
hook varchar(255) not null,
args longtext null,
status int(1) not null default 0 COMMENT '0 - Pending | 1 - Running',
recurring_interval int(10) not null default 0,
group_slug varchar(255) not null default 'woofunnels',
claim_id bigint(20) unsigned NOT NULL default 0,
PRIMARY KEY (id),
KEY id (id),
KEY e_time (e_time),
KEY hook (hook($max_index_length)),
KEY status (status),
KEY group_slug (group_slug($max_index_length)),
KEY claim_id (claim_id)
) {table_collate};",
];
}
if ( $tables['version'] !== BWF_DB_VERSION || ! in_array( 'bwf_action_claim', $tables['tables'], true ) ) {
$args[] = [
'name' => 'bwf_action_claim',
'schema' => "CREATE TABLE `{table_prefix}bwf_action_claim` (
id bigint(20) unsigned NOT NULL auto_increment,
date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (id),
KEY date (date)
) {table_collate};",
];
}
return $args;
}
public function maybe_set_bwf_ct_worker() {
$hook = 'bwf_as_ct_1min_worker';
if ( wp_next_scheduled( $hook ) ) {
$timestamp = wp_next_scheduled( $hook );
wp_unschedule_event( $timestamp, $hook );
}
}
public function add_cron_schedule( $schedules ) {
$schedules['bwf_every_minute'] = apply_filters( 'bwf_every_minute_cron', array(
'interval' => MINUTE_IN_SECONDS,
'display' => __( 'Every minute', 'woofunnels' ), // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
) );
return $schedules;
}
/**
* 1 min worker callback
*/
public function run_as_ct_worker() {
$url = rest_url( '/woofunnels/v1/worker' ) . '?' . time();
$args = bwf_get_remote_rest_args( [], 'GET' );
wp_remote_post( $url, $args );
}
/**
* Register WooFunnels Core WP endpoints
*/
public function register_endpoints() {
register_rest_route( 'woofunnels/v1', '/worker', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'rest_worker_callback' ),
'permission_callback' => '__return_true',
) );
}
/**
* action_scheduler_pre_init action hook
*/
public function as_pre_init_cb() {
$is_worker_request = false;
if ( isset( $_GET['rest_route'] ) && false !== strpos( bwf_clean( $_GET['rest_route'] ), '/woofunnels/v1/worker' ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_worker_request = true;
} else if ( isset( $_SERVER['REQUEST_URI'] ) && false !== strpos( bwf_clean( $_SERVER['REQUEST_URI'] ), '/woofunnels/v1/worker' ) ) {
$is_worker_request = true;
}
if ( false === $is_worker_request ) {
return;
}
if ( ! class_exists( 'BWF_AS' ) ) {
return;
}
/** BWF_AS instance */
$as_ct_ins = BWF_AS::instance();
/** Set new AS CT data store */
$as_ct_ins->change_data_store();
/** Set unique key */
self::$unique = time();
}
/**
* action_scheduler_pre_init action hook for autonami cli
*/
public function as_pre_init_cli_cb() {
global $argv;
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
return;
}
/**
* $argv holds arguments passed to script
* https://www.php.net/manual/en/reserved.variables.argv.php
*/
if ( ! is_array( $argv ) || 0 === count( $argv ) ) {
WP_CLI::log( 'WooFunnels WP CLI arguments not found.' );
return;
}
if ( ! isset( $argv[1] ) || 'woofunnels-actions' !== $argv[1] ) {
return;
}
if ( ! isset( $argv[2] ) || 'run' !== $argv[2] ) {
return;
}
if ( ! class_exists( 'BWF_AS' ) ) {
WP_CLI::log( "BWF_AS class not found." );
}
/** BWF_AS instance */
$as_ct_ins = BWF_AS::instance();
/** Set new AS CT data store */
$as_ct_ins->change_data_store();
}
/**
* Callback function for running WooFunnels actions
*
* @param WP_REST_Request $request
*/
public function rest_worker_callback( WP_REST_Request $request ) {
/** Update execution time */
if ( function_exists( 'bwf_options_update' ) ) {
bwf_options_update( 'fk_core_worker_let', time() );
}
$this->worker_as_run();
$resp['msg'] = 'success';
$resp['time'] = date_i18n( 'Y-m-d H:i:s' );
$resp['datastore'] = get_class( ActionScheduler_Store::instance() );
$path = filter_input( INPUT_GET, 'path' );
if ( ! empty( $path ) && 1 === intval( $path ) ) {
$obj = new ReflectionClass( 'ActionScheduler_QueueRunner' );
$resp['as-path'] = $obj->getFileName();
$obj = new ReflectionClass( 'BWF_Logger' );
$resp['core-path'] = $obj->getFileName();
}
/** Logs */
if ( ( defined( 'BWF_CHECK_CRON_SCHEDULE' ) && true === BWF_CHECK_CRON_SCHEDULE ) || true === apply_filters( 'bwf_check_cron_schedule_logging', false ) ) {
add_filter( 'bwf_logs_allowed', '__return_true', PHP_INT_MAX );
$logger_obj = BWF_Logger::get_instance();
$logger_obj->log( $resp['time'], 'fka-cron-check', 'autonami' );
}
wp_send_json( $resp );
}
/**
* Helper method to run action scheduler
*/
public function worker_as_run() {
if ( ! class_exists( 'ActionScheduler_QueueRunner' ) ) {
return;
}
/** Modify Action Scheduler filters */
$this->modify_as_filters();;
$as_ins = ActionScheduler_QueueRunner::instance();
/** Run Action Scheduler worker */
$as_ins->run();
}
public function modify_as_filters() {
/** Remove all existing filters */
remove_all_filters( 'action_scheduler_queue_runner_time_limit' );
remove_all_filters( 'action_scheduler_queue_runner_batch_size' );
remove_all_filters( 'action_scheduler_queue_runner_concurrent_batches' );
remove_all_filters( 'action_scheduler_timeout_period' );
remove_all_filters( 'action_scheduler_cleanup_batch_size' );
remove_all_filters( 'action_scheduler_maximum_execution_time_likely_to_be_exceeded' );
remove_all_filters( 'action_scheduler_failure_period' );
/** Adding all filters for Autonami Action Scheduler only */
add_filter( 'action_scheduler_queue_runner_time_limit', function () {
return 20;
}, 998 );
add_filter( 'action_scheduler_queue_runner_batch_size', function () {
return 20;
}, 998 );
add_filter( 'action_scheduler_queue_runner_concurrent_batches', function () {
return 5;
}, 998 );
add_filter( 'action_scheduler_timeout_period', function () {
return 300;
}, 998 );
add_filter( 'action_scheduler_cleanup_batch_size', function () {
return 20;
}, 998 );
add_filter( 'action_scheduler_maximum_execution_time_likely_to_be_exceeded', function ( $val, $ins, $processed_actions, $execution_time, $max_execution_time ) {
return ( $execution_time > $max_execution_time );
}, 998, 5 );
add_filter( 'action_scheduler_failure_period', function () {
return 180;
}, 999 );
}
/**
* Deprecated
* Since v1.9.82
*/
public function fallback_execution_on_heartbeat() {
/**
* Added the filter so that we can keep this heartbeat off by default and any plugin from our family can hook into it
*/
if ( ( true === apply_filters( 'bwf_as_ds_should_register_heartbeat', false ) ) || class_exists( 'BWFAN_Core' ) ) {
add_action( 'heartbeat_tick', [ $this, 'heartbeat_callback' ] );
}
}
public function heartbeat_callback() {
$save_time = get_option( 'bwf_heartbeat_run', time() );
if ( time() < $save_time ) {
return;
}
$url = rest_url( '/woofunnels/v1/worker' ) . '?' . time();
$args = bwf_get_remote_rest_args( [], 'GET' );
wp_remote_post( $url, $args );
update_option( 'bwf_heartbeat_run', ( time() + 60 ) );
}
}
WooFunnels_AS_DS::get_instance();
}
/**
* Schedule single action
*
* @param $timestamp
* @param $hook
* @param array $args
* @param string $group
*
* @return bool|int
*/
function bwf_schedule_single_action( $timestamp, $hook, $args = array(), $group = '' ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( ! class_exists( 'ActionScheduler' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Action Scheduler class not found.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$data = array(
'c_date' => current_time( 'mysql', 1 ),
'e_time' => (int) $timestamp,
'hook' => $hook,
'claim_id' => 0
);
if ( is_array( $args ) && count( $args ) > 0 ) {
$data['args'] = wp_json_encode( $args );
}
if ( ! empty( $group ) ) {
$data['group_slug'] = $group;
}
BWF_AS_Actions_Crud::insert( $data );
$inserted_id = BWF_AS_Actions_Crud::insert_id();
return $inserted_id;
}
/**
* Schedule recurring action
*
* @param $timestamp
* @param int $interval_in_seconds - should be min 1 otherwise not recurring
* @param $hook
* @param array $args
* @param string $group
*
* @return bool|int
*/
function bwf_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '' ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( ! class_exists( 'ActionScheduler' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Action Scheduler class not found.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$recurring_interval = ( (int) $interval_in_seconds > 0 ) ? (int) $interval_in_seconds : 0;
$data = array(
'c_date' => current_time( 'mysql', 1 ),
'e_time' => (int) $timestamp,
'hook' => $hook,
'recurring_interval' => $recurring_interval,
'claim_id' => 0
);
if ( is_array( $args ) && count( $args ) > 0 ) {
$data['args'] = wp_json_encode( $args );
}
if ( ! empty( $group ) ) {
$data['group_slug'] = $group;
}
BWF_AS_Actions_Crud::insert( $data );
$inserted_id = BWF_AS_Actions_Crud::insert_id();
return $inserted_id;
}
/**
* Unschedule actions based on given hook or args or group
*
* @param $hook
* @param array $args
* @param string $group
*
* @return bool
*/
function bwf_unschedule_actions( $hook, $args = array(), $group = '' ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$arr = array(
'hook' => $hook,
);
if ( is_array( $args ) && count( $args ) > 0 ) {
$arr['args'] = $args;
}
if ( ! empty( $group ) ) {
$arr['group_slug'] = $group;
}
$action_ids = BWF_AS_Actions_Crud::find_actions( $arr );
if ( false === $action_ids ) {
_doing_it_wrong( __FUNCTION__, __( 'No actions found for data: ', 'woofunnels' ) . print_r( $arr, true ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
}
BWF_AS_Actions_Crud::delete_actions( $action_ids );
return true;
}
/**
* Check if action is already scheduled based on given hook or args or group
*
* @param $hook
* @param array $args
* @param string $group
*
* @return bool
*/
function bwf_has_action_scheduled( $hook, $args = array(), $group = '' ) {
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
global $wpdb;
$table = $wpdb->prefix . 'bwf_actions';
$sql = "SELECT `id` FROM $table WHERE `hook` LIKE %s";
$sql_params = [];
$sql_params[] = trim( $hook );
if ( is_array( $args ) && count( $args ) > 0 ) {
$sql .= ' AND `args` LIKE %s';
$sql_params[] = wp_json_encode( $args );
}
if ( ! empty( trim( $group ) ) ) {
$sql .= ' AND `group_slug` LIKE %s';
$sql_params[] = trim( $group );
}
$sql .= ' LIMIT 0,1';
$sql = $wpdb->prepare( $sql, $sql_params ); //phpcs:ignore WordPress.DB.PreparedSQL
$action_ids = $wpdb->get_var( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
return ( intval( $action_ids ) > 0 );
}
/**
* Check if action is running based on given hook or args or group
*
* @param $hook
* @param array $args
* @param string $group
*
* @return bool
*/
function bwf_is_action_running( $hook, $args = array(), $group = '' ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$arr = array(
'hook' => $hook,
);
if ( is_array( $args ) && count( $args ) > 0 ) {
$arr['args'] = $args;
}
if ( ! empty( $group ) ) {
$arr['group_slug'] = $group;
}
$arr['status'] = 1;
$action_ids = BWF_AS_Actions_Crud::find_actions( $arr );
if ( false === $action_ids ) {
return false;
}
return $action_ids;
}
/**
* Delete action by action id
*
* @param array $action_ids
*
* @return bool|int
*/
function bwf_delete_action( $action_ids = [] ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $action_ids ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Action ID is required.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$delete_count = BWF_AS_Actions_Crud::delete_actions( $action_ids );
if ( false === $action_ids ) {
return false;
}
return $delete_count;
}
/**
* Get scheduled actions count based on given hook or args or group
*
* @param $hook
* @param array $args
* @param string $group
* @param string $status
* @param string $recurring
*
* @return bool|int
*/
function bwf_scheduled_action_count( $hook, $args = array(), $group = '', $status = '0', $recurring = 'all' ) {
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Method is called before plugins_loaded hook.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
if ( empty( $hook ) ) {
_doing_it_wrong( __FUNCTION__, __( 'Hook is a required entity.', 'woofunnels' ), BWF_VERSION ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.WP.I18n.TextDomainMismatch
return false;
}
$arr = array(
'hook' => $hook,
);
if ( is_array( $args ) && count( $args ) > 0 ) {
$arr['args'] = $args;
}
if ( ! empty( $group ) ) {
$arr['group_slug'] = $group;
}
if ( '' !== $status ) {
$arr['status'] = $status;
}
if ( 'recurring' === $recurring ) {
$arr['recurring_interval'] = '0';
}
$action_ids = BWF_AS_Actions_Crud::find_actions( $arr );
if ( false === $action_ids ) {
return false;
}
return count( $action_ids );
}
/**
* Delete recurring action
*
* @param $hook
* @param $status
* @param $exclude_id
*
* @return void
*/
function bwf_delete_scheduled_recurring_action( $hook, $status, $exclude_id = '' ) {
if ( empty( $hook ) ) {
return;
}
global $wpdb;
$query = "DELETE FROM {$wpdb->prefix}bwf_actions WHERE `hook`=%s AND `status` = %d";
$args = [ $hook, $status ];
if ( ! empty( $exclude_id ) ) {
$query .= " AND `id` != %d";
$args[] = $exclude_id;
}
$query = $wpdb->prepare( $query, $args );
$wpdb->query( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}

View File

@@ -0,0 +1,183 @@
<?php
if ( ! class_exists( 'BWF_AS_Actions_Crud' ) ) {
#[AllowDynamicProperties]
class BWF_AS_Actions_Crud {
public static $primary_key = 'id';
/**
* Return single action data
*
* @param $action_id
* @param string $return_vars
*
* @return stdClass
*/
public static function get_single_action( $action_id, $return_vars = '*' ) {
global $wpdb;
$table = self::_table();
$p_key = self::$primary_key;
$sql = "SELECT {$return_vars} FROM {$table} WHERE {$p_key}=%d";
$sql = $wpdb->prepare( $sql, $action_id ); //phpcs:ignore WordPress.DB.PreparedSQL
$status = $wpdb->get_results( $sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
$return = new stdClass();
if ( is_array( $status ) && count( $status ) > 0 && isset( $status[0] ) && is_array( $status[0] ) && count( $status[0] ) > 0 ) {
foreach ( $status[0] as $key => $value ) {
$value = maybe_unserialize( $value );
if ( true === self::is_json( $value ) ) {
$value = json_decode( $value, ARRAY_A );
}
$return->$key = $value;
}
}
return $return;
}
public static function _table() {
global $wpdb;
$table_name = strtolower( 'BWF_Actions' );
return $wpdb->prefix . $table_name;
}
public static function is_json( $string ) {
if ( empty( $string ) ) {
return false;
}
json_decode( $string );
return ( json_last_error() == JSON_ERROR_NONE );
}
public static function get_executable_actions( $status, $group ) {
}
/**
* Find actions based on column inputs
*
* @param $args
*
* @return array|bool
*/
public static function find_actions( $args ) {
global $wpdb;
$table = self::_table();
$p_key = self::$primary_key;
$sql = "SELECT `{$p_key}` FROM $table WHERE 1=1";
$sql_params = [];
if ( isset( $args['hook'] ) && ! empty( trim( $args['hook'] ) ) ) {
$sql .= ' AND `hook` LIKE %s';
$sql_params[] = trim( $args['hook'] );
}
if ( isset( $args['args'] ) && is_array( $args['args'] ) && count( $args['args'] ) > 0 ) {
$sql .= ' AND `args` LIKE %s';
$sql_params[] = wp_json_encode( $args['args'] );
}
if ( isset( $args['group_slug'] ) && ! empty( trim( $args['group_slug'] ) ) ) {
$sql .= ' AND `group_slug` LIKE %s';
$sql_params[] = trim( $args['group_slug'] );
}
if ( isset( $args['status'] ) && '' !== $args['status'] ) {
$sql .= ' AND `status` LIKE %d';
$sql_params[] = $args['status'];
}
/** Always != hard */
if ( isset( $args['recurring_interval'] ) && '' !== $args['recurring_interval'] ) {
$sql .= ' AND `recurring_interval` != %d';
$sql_params[] = $args['recurring_interval'];
}
$sql = $wpdb->prepare( $sql, $sql_params ); //phpcs:ignore WordPress.DB.PreparedSQL
$action_ids = $wpdb->get_results( $sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
if ( is_array( $action_ids ) && count( $action_ids ) > 0 ) {
$action_ids = array_column( $action_ids, 'id' );
return $action_ids;
}
return false;
}
public static function get_action_status( $action_id ) {
global $wpdb;
$table = self::_table();
$p_key = self::$primary_key;
$sql = "SELECT `status` FROM {$table} WHERE {$p_key}=%d";
$sql = $wpdb->prepare( $sql, $action_id ); //phpcs:ignore WordPress.DB.PreparedSQL
$status = $wpdb->get_var( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
return $status;
}
public static function insert( $data ) {
global $wpdb;
$wpdb->insert( self::_table(), $data ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
}
public static function delete( $value ) {
global $wpdb;
if ( empty( $value ) ) {
return;
}
$resp = $wpdb->delete( self::_table(), array( static::$primary_key => $value ), array( '%d' ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
return $resp;
}
public static function delete_actions( $action_ids ) {
global $wpdb;
$table = self::_table();
$p_key = self::$primary_key;
if ( ! is_array( $action_ids ) || 0 === count( $action_ids ) ) {
BWFAN_Core()->logger->log( 'no action ids to delete, blank array passed', 'sync' );
return false;
}
$type = array_fill( 0, count( $action_ids ), '%d' );
$format = implode( ', ', $type );
$query = "DELETE FROM {$table} WHERE {$p_key} IN ({$format})";
$sql = $wpdb->prepare( $query, $action_ids ); //phpcs:ignore WordPress.DB.PreparedSQL
$rows_affected = $wpdb->query( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
return $rows_affected;
}
public static function query( $query ) {
global $wpdb;
$query = str_replace( '{table_name}', self::_table(), $query );
$results = $wpdb->query( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
return $results;
}
public static function insert_id() {
global $wpdb;
return $wpdb->insert_id;
}
public static function get_results( $query ) {
global $wpdb;
$query = str_replace( '{table_name}', self::_table(), $query );
$results = $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
return $results;
}
}
}

View File

@@ -0,0 +1,2 @@
<?php
// Silence is golden.