File "ActionScheduler_DBStore.php"

Full path: /home/kosmetik/public_html/wp-content/plugins/woocommerce/packages/action-scheduler/classes/data-stores/ActionScheduler_DBStore.php
File size: 18.96 B
MIME-type: text/x-php
Charset: utf-8

Download   Open   Edit   Advanced Editor   Back

<?php

class ActionScheduler_DBStore extends ActionScheduler_Store
{
    private $claim_before_date = null;
    protected static $max_args_length = 8000;
    protected static $max_index_length = 191;
    public function init()
    {
        $table_maker = new ActionScheduler_StoreSchema();
        $table_maker->init();
        $table_maker->register_tables();
    }
    public function save_action(ActionScheduler_Action $action, \DateTime $date = null)
    {
        try {
            $this->validate_action($action);
            global $wpdb;
            $data = ['hook' => $action->get_hook(), 'status' => $action->is_finished() ? self::STATUS_COMPLETE : self::STATUS_PENDING, 'scheduled_date_gmt' => $this->get_scheduled_date_string($action, $date), 'scheduled_date_local' => $this->get_scheduled_date_string_local($action, $date), 'schedule' => serialize($action->get_schedule()), 'group_id' => $this->get_group_id($action->get_group())];
            $args = wp_json_encode($action->get_args());
            if (strlen($args) <= static::$max_index_length) {
                $data['args'] = $args;
            } else {
                $data['args'] = $this->hash_args($args);
                $data['extended_args'] = $args;
            }
            $table_name = !empty($wpdb->actionscheduler_actions) ? $wpdb->actionscheduler_actions : $wpdb->prefix . 'actionscheduler_actions';
            $wpdb->insert($table_name, $data);
            $action_id = $wpdb->insert_id;
            if (is_wp_error($action_id)) {
                throw new RuntimeException($action_id->get_error_message());
            } elseif (empty($action_id)) {
                throw new RuntimeException($wpdb->last_error ? $wpdb->last_error : __('Database error.', 'woocommerce'));
            }
            do_action('action_scheduler_stored_action', $action_id);
            return $action_id;
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf(__('Error saving action: %s', 'woocommerce'), $e->getMessage()), 0);
        }
    }
    protected function hash_args($args)
    {
        return md5($args);
    }
    protected function get_args_for_query($args)
    {
        $encoded = wp_json_encode($args);
        if (strlen($encoded) <= static::$max_index_length) {
            return $encoded;
        }
        return $this->hash_args($encoded);
    }
    protected function get_group_id($slug, $create_if_not_exists = true)
    {
        if (empty($slug)) {
            return 0;
        }
        global $wpdb;
        $group_id = (int) $wpdb->get_var($wpdb->prepare("SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug));
        if (empty($group_id) && $create_if_not_exists) {
            $group_id = $this->create_group($slug);
        }
        return $group_id;
    }
    protected function create_group($slug)
    {
        global $wpdb;
        $wpdb->insert($wpdb->actionscheduler_groups, ['slug' => $slug]);
        return (int) $wpdb->insert_id;
    }
    public function fetch_action($action_id)
    {
        global $wpdb;
        $data = $wpdb->get_row($wpdb->prepare("SELECT a.*, g.slug AS `group` FROM {$wpdb->actionscheduler_actions} a LEFT JOIN {$wpdb->actionscheduler_groups} g ON a.group_id=g.group_id WHERE a.action_id=%d", $action_id));
        if (empty($data)) {
            return $this->get_null_action();
        }
        if (!empty($data->extended_args)) {
            $data->args = $data->extended_args;
            unset($data->extended_args);
        }
        $date_fields = ['scheduled_date_gmt', 'scheduled_date_local', 'last_attempt_gmt', 'last_attempt_gmt'];
        foreach ($date_fields as $date_field) {
            if (is_null($data->{$date_field})) {
                $data->{$date_field} = ActionScheduler_StoreSchema::DEFAULT_DATE;
            }
        }
        try {
            $action = $this->make_action_from_db_record($data);
        } catch (ActionScheduler_InvalidActionException $exception) {
            do_action('action_scheduler_failed_fetch_action', $action_id, $exception);
            return $this->get_null_action();
        }
        return $action;
    }
    protected function get_null_action()
    {
        return new ActionScheduler_NullAction();
    }
    protected function make_action_from_db_record($data)
    {
        $hook = $data->hook;
        $args = json_decode($data->args, true);
        $schedule = unserialize($data->schedule);
        $this->validate_args($args, $data->action_id);
        $this->validate_schedule($schedule, $data->action_id);
        if (empty($schedule)) {
            $schedule = new ActionScheduler_NullSchedule();
        }
        $group = $data->group ? $data->group : '';
        return ActionScheduler::factory()->get_stored_action($data->status, $data->hook, $args, $schedule, $group);
    }
    protected function get_query_actions_sql(array $query, $select_or_count = 'select')
    {
        if (!in_array($select_or_count, array('select', 'count'))) {
            throw new InvalidArgumentException(__('Invalid value for select or count parameter. Cannot query actions.', 'woocommerce'));
        }
        $query = wp_parse_args($query, ['hook' => '', 'args' => null, 'date' => null, 'date_compare' => '<=', 'modified' => null, 'modified_compare' => '<=', 'group' => '', 'status' => '', 'claimed' => null, 'per_page' => 5, 'offset' => 0, 'orderby' => 'date', 'order' => 'ASC']);
        global $wpdb;
        $sql = 'count' === $select_or_count ? 'SELECT count(a.action_id)' : 'SELECT a.action_id';
        $sql .= " FROM {$wpdb->actionscheduler_actions} a";
        $sql_params = [];
        if (!empty($query['group']) || 'group' === $query['orderby']) {
            $sql .= " LEFT JOIN {$wpdb->actionscheduler_groups} g ON g.group_id=a.group_id";
        }
        $sql .= " WHERE 1=1";
        if (!empty($query['group'])) {
            $sql .= " AND g.slug=%s";
            $sql_params[] = $query['group'];
        }
        if ($query['hook']) {
            $sql .= " AND a.hook=%s";
            $sql_params[] = $query['hook'];
        }
        if (!is_null($query['args'])) {
            $sql .= " AND a.args=%s";
            $sql_params[] = $this->get_args_for_query($query['args']);
        }
        if ($query['status']) {
            $statuses = (array) $query['status'];
            $placeholders = array_fill(0, count($statuses), '%s');
            $sql .= ' AND a.status IN (' . join(', ', $placeholders) . ')';
            $sql_params = array_merge($sql_params, array_values($statuses));
        }
        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 a.scheduled_date_gmt {$comparator} %s";
            $sql_params[] = $date_string;
        }
        if ($query['modified'] instanceof \DateTime) {
            $modified = clone $query['modified'];
            $modified->setTimezone(new \DateTimeZone('UTC'));
            $date_string = $modified->format('Y-m-d H:i:s');
            $comparator = $this->validate_sql_comparator($query['modified_compare']);
            $sql .= " AND a.last_attempt_gmt {$comparator} %s";
            $sql_params[] = $date_string;
        }
        if ($query['claimed'] === true) {
            $sql .= " AND a.claim_id != 0";
        } elseif ($query['claimed'] === false) {
            $sql .= " AND a.claim_id = 0";
        } elseif (!is_null($query['claimed'])) {
            $sql .= " AND a.claim_id = %d";
            $sql_params[] = $query['claimed'];
        }
        if (!empty($query['search'])) {
            $sql .= " AND (a.hook LIKE %s OR (a.extended_args IS NULL AND a.args LIKE %s) OR a.extended_args LIKE %s";
            for ($i = 0; $i < 3; $i++) {
                $sql_params[] = sprintf('%%%s%%', $query['search']);
            }
            $search_claim_id = (int) $query['search'];
            if ($search_claim_id) {
                $sql .= ' OR a.claim_id = %d';
                $sql_params[] = $search_claim_id;
            }
            $sql .= ')';
        }
        if ('select' === $select_or_count) {
            if ('ASC' === strtoupper($query['order'])) {
                $order = 'ASC';
            } else {
                $order = 'DESC';
            }
            switch ($query['orderby']) {
                case 'hook':
                    $sql .= " ORDER BY a.hook {$order}";
                    break;
                case 'group':
                    $sql .= " ORDER BY g.slug {$order}";
                    break;
                case 'modified':
                    $sql .= " ORDER BY a.last_attempt_gmt {$order}";
                    break;
                case 'none':
                    break;
                case 'action_id':
                    $sql .= " ORDER BY a.action_id {$order}";
                    break;
                case 'date':
                default:
                    $sql .= " ORDER BY a.scheduled_date_gmt {$order}";
                    break;
            }
            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);
        }
        return $sql;
    }
    public function query_actions($query = array(), $query_type = 'select')
    {
        global $wpdb;
        $sql = $this->get_query_actions_sql($query, $query_type);
        return 'count' === $query_type ? $wpdb->get_var($sql) : $wpdb->get_col($sql);
    }
    public function action_counts()
    {
        global $wpdb;
        $sql = "SELECT a.status, count(a.status) as 'count'";
        $sql .= " FROM {$wpdb->actionscheduler_actions} a";
        $sql .= " GROUP BY a.status";
        $actions_count_by_status = array();
        $action_stati_and_labels = $this->get_status_labels();
        foreach ($wpdb->get_results($sql) as $action_data) {
            if (array_key_exists($action_data->status, $action_stati_and_labels)) {
                $actions_count_by_status[$action_data->status] = $action_data->count;
            }
        }
        return $actions_count_by_status;
    }
    public function cancel_action($action_id)
    {
        global $wpdb;
        $updated = $wpdb->update($wpdb->actionscheduler_actions, ['status' => self::STATUS_CANCELED], ['action_id' => $action_id], ['%s'], ['%d']);
        if (empty($updated)) {
            throw new \InvalidArgumentException(sprintf(__('Unidentified action %s', 'woocommerce'), $action_id));
        }
        do_action('action_scheduler_canceled_action', $action_id);
    }
    public function cancel_actions_by_hook($hook)
    {
        $this->bulk_cancel_actions(['hook' => $hook]);
    }
    public function cancel_actions_by_group($group)
    {
        $this->bulk_cancel_actions(['group' => $group]);
    }
    protected function bulk_cancel_actions($query_args)
    {
        global $wpdb;
        if (!is_array($query_args)) {
            return;
        }
        if (isset($query_args['status']) && $query_args['status'] == self::STATUS_CANCELED) {
            return;
        }
        $action_ids = true;
        $query_args = wp_parse_args($query_args, ['per_page' => 1000, 'status' => self::STATUS_PENDING, 'orderby' => 'action_id']);
        while ($action_ids) {
            $action_ids = $this->query_actions($query_args);
            if (empty($action_ids)) {
                break;
            }
            $format = array_fill(0, count($action_ids), '%d');
            $query_in = '(' . implode(',', $format) . ')';
            $parameters = $action_ids;
            array_unshift($parameters, self::STATUS_CANCELED);
            $wpdb->query($wpdb->prepare("UPDATE {$wpdb->actionscheduler_actions} SET status = %s WHERE action_id IN {$query_in}", $parameters));
            do_action('action_scheduler_bulk_cancel_actions', $action_ids);
        }
    }
    public function delete_action($action_id)
    {
        global $wpdb;
        $deleted = $wpdb->delete($wpdb->actionscheduler_actions, ['action_id' => $action_id], ['%d']);
        if (empty($deleted)) {
            throw new \InvalidArgumentException(sprintf(__('Unidentified action %s', 'woocommerce'), $action_id));
        }
        do_action('action_scheduler_deleted_action', $action_id);
    }
    public function get_date($action_id)
    {
        $date = $this->get_date_gmt($action_id);
        ActionScheduler_TimezoneHelper::set_local_timezone($date);
        return $date;
    }
    protected function get_date_gmt($action_id)
    {
        global $wpdb;
        $record = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d", $action_id));
        if (empty($record)) {
            throw new \InvalidArgumentException(sprintf(__('Unidentified action %s', 'woocommerce'), $action_id));
        }
        if ($record->status == self::STATUS_PENDING) {
            return as_get_datetime_object($record->scheduled_date_gmt);
        } else {
            return as_get_datetime_object($record->last_attempt_gmt);
        }
    }
    public function stake_claim($max_actions = 10, \DateTime $before_date = null, $hooks = array(), $group = '')
    {
        $claim_id = $this->generate_claim_id();
        $this->claim_before_date = $before_date;
        $this->claim_actions($claim_id, $max_actions, $before_date, $hooks, $group);
        $action_ids = $this->find_actions_by_claim_id($claim_id);
        $this->claim_before_date = null;
        return new ActionScheduler_ActionClaim($claim_id, $action_ids);
    }
    protected function generate_claim_id()
    {
        global $wpdb;
        $now = as_get_datetime_object();
        $wpdb->insert($wpdb->actionscheduler_claims, ['date_created_gmt' => $now->format('Y-m-d H:i:s')]);
        return $wpdb->insert_id;
    }
    protected function claim_actions($claim_id, $limit, \DateTime $before_date = null, $hooks = array(), $group = '')
    {
        global $wpdb;
        $now = as_get_datetime_object();
        $date = is_null($before_date) ? $now : clone $before_date;
        $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
        $params = array($claim_id, $now->format('Y-m-d H:i:s'), current_time('mysql'));
        $where = "WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s";
        $params[] = $date->format('Y-m-d H:i:s');
        $params[] = self::STATUS_PENDING;
        if (!empty($hooks)) {
            $placeholders = array_fill(0, count($hooks), '%s');
            $where .= ' AND hook IN (' . join(', ', $placeholders) . ')';
            $params = array_merge($params, array_values($hooks));
        }
        if (!empty($group)) {
            $group_id = $this->get_group_id($group, false);
            if (empty($group_id)) {
                throw new InvalidArgumentException(sprintf(__('The group "%s" does not exist.', 'woocommerce'), $group));
            }
            $where .= ' AND group_id = %d';
            $params[] = $group_id;
        }
        $order = "ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC LIMIT %d";
        $params[] = $limit;
        $sql = $wpdb->prepare("{$update} {$where} {$order}", $params);
        $rows_affected = $wpdb->query($sql);
        if ($rows_affected === false) {
            throw new \RuntimeException(__('Unable to claim actions. Database error.', 'woocommerce'));
        }
        return (int) $rows_affected;
    }
    public function get_claim_count()
    {
        global $wpdb;
        $sql = "SELECT COUNT(DISTINCT claim_id) FROM {$wpdb->actionscheduler_actions} WHERE claim_id != 0 AND status IN ( %s, %s)";
        $sql = $wpdb->prepare($sql, [self::STATUS_PENDING, self::STATUS_RUNNING]);
        return (int) $wpdb->get_var($sql);
    }
    public function get_claim_id($action_id)
    {
        global $wpdb;
        $sql = "SELECT claim_id FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d";
        $sql = $wpdb->prepare($sql, $action_id);
        return (int) $wpdb->get_var($sql);
    }
    public function find_actions_by_claim_id($claim_id)
    {
        global $wpdb;
        $action_ids = array();
        $before_date = isset($this->claim_before_date) ? $this->claim_before_date : as_get_datetime_object();
        $cut_off = $before_date->format('Y-m-d H:i:s');
        $sql = $wpdb->prepare("SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d", $claim_id);
        foreach ($wpdb->get_results($sql) as $claimed_action) {
            if ($claimed_action->scheduled_date_gmt <= $cut_off) {
                $action_ids[] = absint($claimed_action->action_id);
            }
        }
        return $action_ids;
    }
    public function release_claim(ActionScheduler_ActionClaim $claim)
    {
        global $wpdb;
        $wpdb->update($wpdb->actionscheduler_actions, ['claim_id' => 0], ['claim_id' => $claim->get_id()], ['%d'], ['%d']);
        $wpdb->delete($wpdb->actionscheduler_claims, ['claim_id' => $claim->get_id()], ['%d']);
    }
    public function unclaim_action($action_id)
    {
        global $wpdb;
        $wpdb->update($wpdb->actionscheduler_actions, ['claim_id' => 0], ['action_id' => $action_id], ['%s'], ['%d']);
    }
    public function mark_failure($action_id)
    {
        global $wpdb;
        $updated = $wpdb->update($wpdb->actionscheduler_actions, ['status' => self::STATUS_FAILED], ['action_id' => $action_id], ['%s'], ['%d']);
        if (empty($updated)) {
            throw new \InvalidArgumentException(sprintf(__('Unidentified action %s', 'woocommerce'), $action_id));
        }
    }
    public function log_execution($action_id)
    {
        global $wpdb;
        $sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d";
        $sql = $wpdb->prepare($sql, self::STATUS_RUNNING, current_time('mysql', true), current_time('mysql'), $action_id);
        $wpdb->query($sql);
    }
    public function mark_complete($action_id)
    {
        global $wpdb;
        $updated = $wpdb->update($wpdb->actionscheduler_actions, ['status' => self::STATUS_COMPLETE, 'last_attempt_gmt' => current_time('mysql', true), 'last_attempt_local' => current_time('mysql')], ['action_id' => $action_id], ['%s'], ['%d']);
        if (empty($updated)) {
            throw new \InvalidArgumentException(sprintf(__('Unidentified action %s', 'woocommerce'), $action_id));
        }
    }
    public function get_status($action_id)
    {
        global $wpdb;
        $sql = "SELECT status FROM {$wpdb->actionscheduler_actions} WHERE action_id=%d";
        $sql = $wpdb->prepare($sql, $action_id);
        $status = $wpdb->get_var($sql);
        if ($status === null) {
            throw new \InvalidArgumentException(__('Invalid action ID. No status found.', 'woocommerce'));
        } elseif (empty($status)) {
            throw new \RuntimeException(__('Unknown status found for action.', 'woocommerce'));
        } else {
            return $status;
        }
    }
}