oont-contents/plugins/tabby-checkout/includes/class-wc-tabby-feed-sharing.php
2025-02-08 15:10:23 +01:00

253 lines
9.2 KiB
PHP

<?php
class WC_Tabby_Feed_Sharing {
public const CRON_JOB_NAME = 'tabby_feed_sharing_service';
private static $updates = [
'availability' => [],
'updated' => []
];
public static function init() {
if (!WC_Tabby_Api_Feed::canOperate()) return;
if (!WC_Tabby_Config::getShareFeed()) {
if (WC_Tabby_Api_Feed::isRegistered()) {
self::unregister();
}
return;
}
if (!WC_Tabby_Api_Feed::isRegistered()) {
// register feed only
add_action( 'shutdown', array( __CLASS__, 'register' ), 15);
return;
}
add_action(self::CRON_JOB_NAME, array(__CLASS__, 'cron_service'));
add_action( 'shutdown', array( __CLASS__, 'shutdown' ), 20);
add_action( 'woocommerce_new_product', array(WC_Tabby_Feed_Sharing::class, 'update_product'), 10, 1 );
add_action( 'woocommerce_update_product', array(WC_Tabby_Feed_Sharing::class, 'update_product'), 10, 1 );
add_action( 'woocommerce_before_delete_product', array(WC_Tabby_Feed_Sharing::class, 'delete_product'), 10, 1 );
add_action( 'woocommerce_trash_product', array(WC_Tabby_Feed_Sharing::class, 'delete_product'), 10, 1 );
add_action( 'woocommerce_before_delete_product_variation', array(WC_Tabby_Feed_Sharing::class, 'delete_product'), 10, 1 );
add_action( 'woocommerce_trash_product_variation', array(WC_Tabby_Feed_Sharing::class, 'delete_product'), 10, 1 );
add_action( 'transition_post_status', array( __CLASS__, 'transition_post_status' ), 10, 3 );
}
public static function add_every_five_minutes( $schedules ) {
$schedules['every_five_minutes'] = array(
'interval' => 60 * 5,
'display' => __( 'Every 5 Minutes', 'textdomain' )
);
return $schedules;
}
public static function registerCronJob() {
if (!wp_next_scheduled(self::CRON_JOB_NAME)) {
wp_schedule_event(time(), 'every_five_minutes', self::CRON_JOB_NAME);
}
}
public static function unregisterCronJob() {
if (($timestamp = wp_next_scheduled(self::CRON_JOB_NAME)) !== false) {
wp_unschedule_event($timestamp, self::CRON_JOB_NAME);
}
}
public static function update_product($product_id) {
self::$updates['updated'][] = $product_id;
}
public static function transition_post_status($new_status, $old_status, $post) {
if (in_array( $post->post_type, array( 'product', 'product_variation' ), true )) {
$product = wc_get_product($post->ID);
self::$updates['availability'][(string)$post->ID] = WC_Tabby_Feed_Product::getTabbyisAvailable($product);
}
}
public static function delete_product($post_id) {
self::$updates['availability'][(string)$post_id] = false;
$product = wc_get_product($post_id);
if ($product->is_type('variable')) {
foreach ($product->get_children() as $id) {
self::$updates['availability'][(string)$id] = false;
}
}
}
public static function api_change_availability($data) {
$api = new WC_Tabby_Api_Feed();
$res = $api->request('availability', 'POST', ['availabilityInfo' => $data]);
if (!is_object($res) || property_exists($res, 'errors')) {
return false;
}
return true;
}
public static function share_product_feed() {
$args = [
'posts_per_page' => -1,
'return' => 'ids'
];
$products = wc_get_products($args);
self::saveFailedToDatabase('update', $products);
}
public static function update_products($ids) {
$ids = array_unique($ids);
$args = [
'posts_per_page' => -1,
'include' => $ids
];
$failed = [];
$ignored = 0;
$data = [];
$products = wc_get_products($args);
foreach ($products as $product) {
if ($product->is_type('variable')) {
foreach ($product->get_available_variations() as $p) {
try {
$data[] = WC_Tabby_Feed_Product::getFeedRecord(wc_get_product($p['variation_id']));
} catch (\WC_Tabby_Feed_Product_Exception $e) {
$ignored++;
}
}
} else {
try {
$data[] = WC_Tabby_Feed_Product::getFeedRecord($product);
} catch (\WC_Tabby_Feed_Product_Exception $e) {
$ignored++;
}
}
}
if (!empty($data)) {
$chunked = array_chunk($data, 100);
foreach ($chunked as $chunk) {
if (!self::api_update_products($chunk)) {
$failed = array_merge($failed, array_map(function ($item) {return (int)$item["id"];}, $chunk));
};
}
}
return $failed;
}
public static function api_update_products($data) {
$api = new WC_Tabby_Api_Feed();
$res = $api->request('products', 'POST', ['products' => $data]);
if (!is_object($res) || property_exists($res, 'errors')) {
return false;
}
return true;
}
public static function register() {
if (!WC_Tabby_Config::getShareFeed()) return false;
$api = new WC_Tabby_Api_Feed();
if ($api->register()) {
self::share_product_feed();
self::registerCronJob();
return true;
}
return false;
}
public static function unregister() {
$api = new WC_Tabby_Api_Feed();
$api->uninstall();
self::unregisterCronJob();
}
public static function shutdown() {
// run cron service on every 100 request
if (rand(1, 1000) <= 10) {
self::cron_service();
}
$processed = [];
// build product updates queries
$ids = self::$updates['updated'];
if (!empty($ids)) {
$failed = self::update_products($ids);
if (!empty($failed)) {
self::saveFailedToDatabase('update', $failed);
}
$processed = array_diff($ids, $failed);
}
if (!empty(self::$updates['availability'])) {
$data = [];
foreach (self::$updates['availability'] as $id => $status) {
if (in_array((int)$id, $processed)) continue;
$data[] = [
'id' => (string)$id,
'isAvailable' => $status
];
}
if (!empty($data)) {
if (!self::api_change_availability($data)) {
self::saveFailedToDatabase('availability', array_map(function ($item) {return (int)$item->id;}, $data));
};
}
}
}
public static function cron_service() {
$option_name = 'tabby_feed_sharing_update';
$lock = new WC_Tabby_Lock();
$processed = [];
if ($lock->lock($option_name)) {
$ids = get_option($option_name, []);
$failed = [];
if (!empty($ids)) {
$failed = self::update_products($ids);
update_option($option_name, $failed);
}
$processed = array_diff($ids, $failed);
$lock->unlock($option_name);
}
$option_name = 'tabby_feed_sharing_availability';
if ($lock->lock($option_name)) {
$ids = get_option($option_name, []);
if (!empty($ids)) {
$args = [
'posts_per_page' => -1,
'include' => $ids
];
$products = wc_get_products($args);
$data = [];
foreach ($products as $product) {
$data[] = [
'id' => (string)$product->get_id(),
'isAvailable' => WC_Tabby_Feed_Product::getTabbyisAvailable($product)
];
if ($product->is_type('variable')) {
foreach ($product->get_children() as $variation_id) {
$variation = wc_get_product($variation_id);
$data[] = [
'id' => (string)$variation->get_id(),
'isAvailable' => WC_Tabby_Feed_Product::getTabbyisAvailable($variation)
];
}
}
}
if (!empty($data)) {
if (self::api_change_availability($data)) {
$ids = [];
}
}
}
update_option($option_name, $ids);
$lock->unlock($option_name);
}
}
public static function saveFailedToDatabase($type, $ids) {
$lock = new WC_Tabby_Lock();
$option_name = 'tabby_feed_sharing_' . $type;
if ($lock->lock($option_name)) {
$value = get_option($option_name, []);
$new_value = array_unique(array_merge($value, $ids));
update_option($option_name, $new_value);
$lock->unlock($option_name);
}
}
}