2017-08-17 03:08:32 +00:00
< ? php
/**
* Webhook Data Store
*
2017-11-07 18:10:38 +00:00
* @ version 3.3 . 0
2020-08-05 16:36:24 +00:00
* @ package WooCommerce\Classes\Data_Store
2017-08-17 03:08:32 +00:00
*/
2017-08-17 14:14:41 +00:00
2017-08-17 03:08:32 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
/**
* Webhook data store class .
*/
2017-08-17 22:42:54 +00:00
class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
2017-08-17 03:08:32 +00:00
/**
* Create a new webhook in the database .
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2017-08-17 14:14:41 +00:00
* @ param WC_Webhook $webhook Webhook instance .
2017-08-17 03:08:32 +00:00
*/
public function create ( & $webhook ) {
global $wpdb ;
2017-08-17 21:43:08 +00:00
$changes = $webhook -> get_changes ();
if ( isset ( $changes [ 'date_created' ] ) ) {
2017-08-17 03:08:32 +00:00
$date_created = $webhook -> get_date_created () -> date ( 'Y-m-d H:i:s' );
$date_created_gmt = gmdate ( 'Y-m-d H:i:s' , $webhook -> get_date_created () -> getTimestamp () );
} else {
$date_created = current_time ( 'mysql' );
$date_created_gmt = current_time ( 'mysql' , 1 );
$webhook -> set_date_created ( $date_created );
}
2017-12-01 16:04:59 +00:00
// Pending delivery by default if not set while creating a new webhook.
if ( ! isset ( $changes [ 'pending_delivery' ] ) ) {
$webhook -> set_pending_delivery ( true );
}
2017-08-17 03:08:32 +00:00
$data = array (
2017-08-17 22:04:36 +00:00
'status' => $webhook -> get_status ( 'edit' ),
'name' => $webhook -> get_name ( 'edit' ),
'user_id' => $webhook -> get_user_id ( 'edit' ),
'delivery_url' => $webhook -> get_delivery_url ( 'edit' ),
'secret' => $webhook -> get_secret ( 'edit' ),
'topic' => $webhook -> get_topic ( 'edit' ),
'date_created' => $date_created ,
'date_created_gmt' => $date_created_gmt ,
'api_version' => $this -> get_api_version_number ( $webhook -> get_api_version ( 'edit' ) ),
'failure_count' => $webhook -> get_failure_count ( 'edit' ),
'pending_delivery' => $webhook -> get_pending_delivery ( 'edit' ),
2017-08-17 03:08:32 +00:00
);
2017-11-07 18:10:38 +00:00
$wpdb -> insert ( $wpdb -> prefix . 'wc_webhooks' , $data ); // WPCS: DB call ok.
2017-08-17 14:14:41 +00:00
2017-08-17 03:08:32 +00:00
$webhook_id = $wpdb -> insert_id ;
$webhook -> set_id ( $webhook_id );
$webhook -> apply_changes ();
2018-09-24 03:22:16 +00:00
$this -> delete_transients ( $webhook -> get_status ( 'edit' ) );
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'webhooks' );
2019-04-17 11:50:46 +00:00
do_action ( 'woocommerce_new_webhook' , $webhook_id , $webhook );
2017-08-17 03:08:32 +00:00
}
/**
* Read a webhook from the database .
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2017-08-17 03:08:32 +00:00
* @ param WC_Webhook $webhook Webhook instance .
2017-08-17 14:14:41 +00:00
* @ throws Exception When webhook is invalid .
2017-08-17 03:08:32 +00:00
*/
public function read ( & $webhook ) {
global $wpdb ;
2017-08-17 14:07:30 +00:00
$data = wp_cache_get ( $webhook -> get_id (), 'webhooks' );
if ( false === $data ) {
2017-11-07 18:10:38 +00:00
$data = $wpdb -> get_row ( $wpdb -> prepare ( " SELECT webhook_id, status, name, user_id, delivery_url, secret, topic, date_created, date_modified, api_version, failure_count, pending_delivery FROM { $wpdb -> prefix } wc_webhooks WHERE webhook_id = %d LIMIT 1; " , $webhook -> get_id () ), ARRAY_A ); // WPCS: cache ok, DB call ok.
2017-08-17 14:07:30 +00:00
wp_cache_add ( $webhook -> get_id (), $data , 'webhooks' );
}
if ( is_array ( $data ) ) {
2018-03-07 19:16:01 +00:00
$webhook -> set_props (
array (
'id' => $data [ 'webhook_id' ],
'status' => $data [ 'status' ],
'name' => $data [ 'name' ],
'user_id' => $data [ 'user_id' ],
'delivery_url' => $data [ 'delivery_url' ],
'secret' => $data [ 'secret' ],
'topic' => $data [ 'topic' ],
'date_created' => '0000-00-00 00:00:00' === $data [ 'date_created' ] ? null : $data [ 'date_created' ],
'date_modified' => '0000-00-00 00:00:00' === $data [ 'date_modified' ] ? null : $data [ 'date_modified' ],
'api_version' => $data [ 'api_version' ],
'failure_count' => $data [ 'failure_count' ],
'pending_delivery' => $data [ 'pending_delivery' ],
)
);
2017-08-17 03:08:32 +00:00
$webhook -> set_object_read ( true );
2017-08-17 14:07:30 +00:00
2017-08-17 03:08:32 +00:00
do_action ( 'woocommerce_webhook_loaded' , $webhook );
} else {
throw new Exception ( __ ( 'Invalid webhook.' , 'woocommerce' ) );
}
}
/**
* Update a webhook .
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2017-08-17 14:14:41 +00:00
* @ param WC_Webhook $webhook Webhook instance .
2017-08-17 03:08:32 +00:00
*/
public function update ( & $webhook ) {
global $wpdb ;
2017-08-17 21:43:08 +00:00
$changes = $webhook -> get_changes ();
2017-11-30 18:48:16 +00:00
$trigger = isset ( $changes [ 'delivery_url' ] );
2017-08-17 21:43:08 +00:00
if ( isset ( $changes [ 'date_modified' ] ) ) {
2017-08-17 03:08:32 +00:00
$date_modified = $webhook -> get_date_modified () -> date ( 'Y-m-d H:i:s' );
$date_modified_gmt = gmdate ( 'Y-m-d H:i:s' , $webhook -> get_date_modified () -> getTimestamp () );
} else {
$date_modified = current_time ( 'mysql' );
$date_modified_gmt = current_time ( 'mysql' , 1 );
$webhook -> set_date_modified ( $date_modified );
}
$data = array (
'status' => $webhook -> get_status ( 'edit' ),
'name' => $webhook -> get_name ( 'edit' ),
'user_id' => $webhook -> get_user_id ( 'edit' ),
'delivery_url' => $webhook -> get_delivery_url ( 'edit' ),
'secret' => $webhook -> get_secret ( 'edit' ),
'topic' => $webhook -> get_topic ( 'edit' ),
'date_modified' => $date_modified ,
'date_modified_gmt' => $date_modified_gmt ,
'api_version' => $this -> get_api_version_number ( $webhook -> get_api_version ( 'edit' ) ),
'failure_count' => $webhook -> get_failure_count ( 'edit' ),
'pending_delivery' => $webhook -> get_pending_delivery ( 'edit' ),
);
$wpdb -> update (
2017-08-18 16:41:02 +00:00
$wpdb -> prefix . 'wc_webhooks' ,
2017-08-17 03:08:32 +00:00
$data ,
array (
2019-01-10 19:10:13 +00:00
'webhook_id' => $webhook -> get_id (),
2017-08-17 03:08:32 +00:00
)
2017-11-07 18:10:38 +00:00
); // WPCS: DB call ok.
2017-08-17 03:08:32 +00:00
$webhook -> apply_changes ();
2019-01-17 05:55:54 +00:00
if ( isset ( $changes [ 'status' ] ) ) {
// We need to delete all transients, because we can't be sure of the old status.
$this -> delete_transients ( 'all' );
}
2017-08-17 14:07:30 +00:00
wp_cache_delete ( $webhook -> get_id (), 'webhooks' );
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'webhooks' );
2017-11-30 18:48:16 +00:00
2017-12-01 16:04:59 +00:00
if ( 'active' === $webhook -> get_status () && ( $trigger || $webhook -> get_pending_delivery () ) ) {
2017-11-30 18:48:16 +00:00
$webhook -> deliver_ping ();
}
2017-08-17 03:08:32 +00:00
do_action ( 'woocommerce_webhook_updated' , $webhook -> get_id () );
}
/**
* Remove a webhook from the database .
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2017-08-17 03:08:32 +00:00
* @ param WC_Webhook $webhook Webhook instance .
*/
2019-01-10 19:08:41 +00:00
public function delete ( & $webhook ) {
2017-08-17 03:08:32 +00:00
global $wpdb ;
$wpdb -> delete (
2017-08-18 16:41:02 +00:00
$wpdb -> prefix . 'wc_webhooks' ,
2017-08-17 03:08:32 +00:00
array (
'webhook_id' => $webhook -> get_id (),
),
array ( '%d' )
2017-11-07 18:10:38 +00:00
); // WPCS: cache ok, DB call ok.
2017-08-17 03:08:32 +00:00
2018-09-24 03:22:16 +00:00
$this -> delete_transients ( 'all' );
2019-12-04 04:04:35 +00:00
wp_cache_delete ( $webhook -> get_id (), 'webhooks' );
2019-11-28 13:03:57 +00:00
WC_Cache_Helper :: invalidate_cache_group ( 'webhooks' );
2017-08-17 03:08:32 +00:00
do_action ( 'woocommerce_webhook_deleted' , $webhook -> get_id (), $webhook );
}
/**
* Get API version number .
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2017-08-17 03:08:32 +00:00
* @ param string $api_version REST API version .
* @ return int
*/
public function get_api_version_number ( $api_version ) {
return 'legacy_v3' === $api_version ? - 1 : intval ( substr ( $api_version , - 1 ) );
}
2017-08-17 13:51:20 +00:00
/**
2019-02-18 13:07:04 +00:00
* Get webhooks IDs from the database .
2017-08-17 13:51:20 +00:00
*
2017-11-07 18:10:38 +00:00
* @ since 3.3 . 0
2018-09-24 03:22:16 +00:00
* @ throws InvalidArgumentException If a $status value is passed in that is not in the known wc_get_webhook_statuses () keys .
2019-02-18 13:34:13 +00:00
* @ param string $status Optional - status to filter results by . Must be a key in return value of @ see wc_get_webhook_statuses () . @ since 3.6 . 0.
2017-08-17 13:51:20 +00:00
* @ return int []
*/
2019-02-18 13:13:59 +00:00
public function get_webhooks_ids ( $status = '' ) {
2018-09-24 03:22:16 +00:00
if ( ! empty ( $status ) ) {
$this -> validate_status ( $status );
}
$ids = get_transient ( $this -> get_transient_key ( $status ) );
2017-08-17 13:51:20 +00:00
if ( false === $ids ) {
2018-09-24 04:22:01 +00:00
$ids = $this -> search_webhooks (
array (
'limit' => - 1 ,
'status' => $status ,
)
);
2019-02-18 13:13:59 +00:00
$ids = array_map ( 'absint' , $ids );
2018-09-24 03:22:16 +00:00
set_transient ( $this -> get_transient_key ( $status ), $ids );
2017-08-17 13:51:20 +00:00
}
return $ids ;
}
2017-11-07 18:10:38 +00:00
/**
* Search webhooks .
*
* @ param array $args Search arguments .
2019-02-18 14:58:32 +00:00
* @ return array | object
2017-11-07 18:10:38 +00:00
*/
public function search_webhooks ( $args ) {
global $wpdb ;
2018-03-07 19:16:01 +00:00
$args = wp_parse_args (
2018-09-24 05:31:57 +00:00
$args ,
array (
2019-02-18 14:58:32 +00:00
'limit' => 10 ,
'offset' => 0 ,
'order' => 'DESC' ,
'orderby' => 'id' ,
'paginate' => false ,
2018-03-07 19:16:01 +00:00
)
);
2017-11-07 18:10:38 +00:00
2017-11-28 19:39:42 +00:00
// Map post statuses.
$statuses = array (
'publish' => 'active' ,
'draft' => 'paused' ,
'pending' => 'disabled' ,
);
// Map orderby to support a few post keys.
$orderby_mapping = array (
'ID' => 'webhook_id' ,
'id' => 'webhook_id' ,
'name' => 'name' ,
'title' => 'name' ,
'post_title' => 'name' ,
'post_name' => 'name' ,
'date_created' => 'date_created_gmt' ,
'date' => 'date_created_gmt' ,
'post_date' => 'date_created_gmt' ,
'date_modified' => 'date_modified_gmt' ,
'modified' => 'date_modified_gmt' ,
'post_modified' => 'date_modified_gmt' ,
);
$orderby = isset ( $orderby_mapping [ $args [ 'orderby' ] ] ) ? $orderby_mapping [ $args [ 'orderby' ] ] : 'webhook_id' ;
2021-06-22 06:58:44 +00:00
$sort = 'ASC' === strtoupper ( $args [ 'order' ] ) ? 'ASC' : 'DESC' ;
$order = " ORDER BY { $orderby } { $sort } " ;
2019-02-18 13:34:13 +00:00
$limit = - 1 < $args [ 'limit' ] ? $wpdb -> prepare ( 'LIMIT %d' , $args [ 'limit' ] ) : '' ;
$offset = 0 < $args [ 'offset' ] ? $wpdb -> prepare ( 'OFFSET %d' , $args [ 'offset' ] ) : '' ;
$status = ! empty ( $args [ 'status' ] ) ? $wpdb -> prepare ( 'AND `status` = %s' , isset ( $statuses [ $args [ 'status' ] ] ) ? $statuses [ $args [ 'status' ] ] : $args [ 'status' ] ) : '' ;
$search = ! empty ( $args [ 'search' ] ) ? " AND `name` LIKE '% " . $wpdb -> esc_like ( sanitize_text_field ( $args [ 'search' ] ) ) . " %' " : '' ;
$include = '' ;
$exclude = '' ;
$date_created = '' ;
$date_modified = '' ;
2017-11-28 19:39:42 +00:00
if ( ! empty ( $args [ 'include' ] ) ) {
$args [ 'include' ] = implode ( ',' , wp_parse_id_list ( $args [ 'include' ] ) );
$include = 'AND webhook_id IN (' . $args [ 'include' ] . ')' ;
}
if ( ! empty ( $args [ 'exclude' ] ) ) {
$args [ 'exclude' ] = implode ( ',' , wp_parse_id_list ( $args [ 'exclude' ] ) );
$exclude = 'AND webhook_id NOT IN (' . $args [ 'exclude' ] . ')' ;
}
if ( ! empty ( $args [ 'after' ] ) || ! empty ( $args [ 'before' ] ) ) {
$args [ 'after' ] = empty ( $args [ 'after' ] ) ? '0000-00-00' : $args [ 'after' ];
$args [ 'before' ] = empty ( $args [ 'before' ] ) ? current_time ( 'mysql' , 1 ) : $args [ 'before' ];
2019-02-18 13:34:13 +00:00
$date_created = " AND `date_created_gmt` BETWEEN STR_TO_DATE(' " . esc_sql ( $args [ 'after' ] ) . " ', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE(' " . esc_sql ( $args [ 'before' ] ) . " ', '%Y-%m-%d %H:%i:%s') " ;
2017-11-28 19:39:42 +00:00
}
if ( ! empty ( $args [ 'modified_after' ] ) || ! empty ( $args [ 'modified_before' ] ) ) {
$args [ 'modified_after' ] = empty ( $args [ 'modified_after' ] ) ? '0000-00-00' : $args [ 'modified_after' ];
$args [ 'modified_before' ] = empty ( $args [ 'modified_before' ] ) ? current_time ( 'mysql' , 1 ) : $args [ 'modified_before' ];
2019-02-18 13:34:13 +00:00
$date_modified = " AND `date_modified_gmt` BETWEEN STR_TO_DATE(' " . esc_sql ( $args [ 'modified_after' ] ) . " ', '%Y-%m-%d %H:%i:%s') and STR_TO_DATE(' " . esc_sql ( $args [ 'modified_before' ] ) . " ', '%Y-%m-%d %H:%i:%s') " ;
2017-11-28 19:39:42 +00:00
}
// Check for cache.
2019-02-18 14:58:32 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'webhooks' ) . 'search_webhooks' . md5 ( implode ( ',' , $args ) );
$cache_value = wp_cache_get ( $cache_key , 'webhook_search_results' );
2017-11-07 18:10:38 +00:00
2019-02-18 14:58:32 +00:00
if ( $cache_value ) {
return $cache_value ;
2017-11-07 18:10:38 +00:00
}
2019-02-18 14:58:32 +00:00
if ( $args [ 'paginate' ] ) {
$query = trim (
" SELECT SQL_CALC_FOUND_ROWS webhook_id
FROM { $wpdb -> prefix } wc_webhooks
WHERE 1 = 1
{ $status }
{ $search }
{ $include }
{ $exclude }
{ $date_created }
{ $date_modified }
{ $order }
{ $limit }
{ $offset } "
);
$webhook_ids = wp_parse_id_list ( $wpdb -> get_col ( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$total = ( int ) $wpdb -> get_var ( 'SELECT FOUND_ROWS();' );
$return_value = ( object ) array (
'webhooks' => $webhook_ids ,
'total' => $total ,
'max_num_pages' => $args [ 'limit' ] > 1 ? ceil ( $total / $args [ 'limit' ] ) : 1 ,
);
} else {
$query = trim (
" SELECT webhook_id
FROM { $wpdb -> prefix } wc_webhooks
WHERE 1 = 1
{ $status }
{ $search }
{ $include }
{ $exclude }
{ $date_created }
{ $date_modified }
{ $order }
{ $limit }
{ $offset } "
);
2017-11-07 19:42:14 +00:00
2019-02-18 14:58:32 +00:00
$webhook_ids = wp_parse_id_list ( $wpdb -> get_col ( $query ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$return_value = $webhook_ids ;
}
2017-11-07 19:42:14 +00:00
2019-02-18 14:58:32 +00:00
wp_cache_set ( $cache_key , $return_value , 'webhook_search_results' );
2017-11-07 19:42:14 +00:00
2019-02-18 14:58:32 +00:00
return $return_value ;
2017-11-07 19:42:14 +00:00
}
2017-11-07 18:10:38 +00:00
2019-02-18 13:41:14 +00:00
/**
* Count webhooks .
*
* @ since 3.6 . 0
* @ param string $status Status to count .
* @ return int
*/
protected function get_webhook_count ( $status = 'active' ) {
global $wpdb ;
2019-05-15 08:47:50 +00:00
$cache_key = WC_Cache_Helper :: get_cache_prefix ( 'webhooks' ) . $status . '_count' ;
2019-12-20 17:21:08 +00:00
$count = wp_cache_get ( $cache_key , 'webhooks' );
2019-02-18 13:41:14 +00:00
if ( false === $count ) {
$count = absint ( $wpdb -> get_var ( $wpdb -> prepare ( " SELECT count( webhook_id ) FROM { $wpdb -> prefix } wc_webhooks WHERE `status` = %s; " , $status ) ) );
2019-05-15 08:47:50 +00:00
wp_cache_add ( $cache_key , $count , 'webhooks' );
2019-02-18 13:41:14 +00:00
}
return $count ;
}
2017-11-07 19:42:14 +00:00
/**
* Get total webhook counts by status .
*
* @ return array
*/
public function get_count_webhooks_by_status () {
$statuses = array_keys ( wc_get_webhook_statuses () );
$counts = array ();
foreach ( $statuses as $status ) {
2019-02-18 13:41:14 +00:00
$counts [ $status ] = $this -> get_webhook_count ( $status );
2017-11-07 19:42:14 +00:00
}
2017-11-07 18:10:38 +00:00
2017-11-07 19:42:14 +00:00
return $counts ;
2017-11-07 18:10:38 +00:00
}
2018-09-24 03:22:16 +00:00
/**
* Check if a given string is in known statuses , based on return value of @ see wc_get_webhook_statuses () .
*
2019-02-18 13:07:04 +00:00
* @ since 3.6 . 0
2018-09-24 03:22:16 +00:00
* @ throws InvalidArgumentException If $status is not empty and not in the known wc_get_webhook_statuses () keys .
* @ param string $status Status to check .
*/
private function validate_status ( $status ) {
if ( ! array_key_exists ( $status , wc_get_webhook_statuses () ) ) {
throw new InvalidArgumentException ( sprintf ( 'Invalid status given: %s. Status must be one of: %s.' , $status , implode ( ', ' , array_keys ( wc_get_webhook_statuses () ) ) ) );
}
}
/**
* Get the transient key used to cache a set of webhook IDs , optionally filtered by status .
*
2019-02-18 13:07:04 +00:00
* @ since 3.6 . 0
2018-09-24 03:22:16 +00:00
* @ param string $status Optional - status of cache key .
* @ return string
*/
private function get_transient_key ( $status = '' ) {
return empty ( $status ) ? 'woocommerce_webhook_ids' : sprintf ( 'woocommerce_webhook_ids_status_%s' , $status );
}
/**
* Delete the transients used to cache a set of webhook IDs , optionally filtered by status .
*
2019-02-18 13:07:04 +00:00
* @ since 3.6 . 0
2018-09-24 03:22:16 +00:00
* @ param string $status Optional - status of cache to delete , or 'all' to delete all caches .
*/
private function delete_transients ( $status = '' ) {
// Always delete the non-filtered cache.
delete_transient ( $this -> get_transient_key ( '' ) );
if ( ! empty ( $status ) ) {
if ( 'all' === $status ) {
foreach ( wc_get_webhook_statuses () as $status_key => $status_string ) {
delete_transient ( $this -> get_transient_key ( $status_key ) );
}
} else {
delete_transient ( $this -> get_transient_key ( $status ) );
}
}
}
2017-08-17 03:08:32 +00:00
}