ACF.php
1 year ago
AdvancedMathCaptcha.php
1 year ago
AeliaCurrencySwitcher.php
11 months ago
BeaverBuilder.php
1 year ago
CF_Helper.php
1 year ago
CURCY_MultiCurrency.php
1 year ago
Cloudflare.php
1 year ago
CommonHelpers.php
1 year ago
CookieNotice.php
1 year ago
DownloadManager.php
1 year ago
Elementor.php
1 year ago
Ezoic.php
1 year ago
FusionBuilder.php
1 year ago
GeoTargetingWP.php
1 year ago
GravityForms.php
1 year ago
JetPackNP.php
1 year ago
MPG.php
11 months ago
NginxHelper.php
1 year ago
RC.php
11 months ago
RankMathNP.php
1 year ago
ShortPixel.php
1 year ago
SquirrlySEO.php
1 year ago
TheEventsCalendar.php
1 year ago
ThriveTheme.php
1 year ago
WCML.php
1 year ago
WPBakeryNP.php
1 year ago
WPCacheHelper.php
1 year ago
WPForms.php
1 year ago
WPML.php
1 year ago
WPRocket.php
1 year ago
WooCommerce.php
11 months ago
WoocommerceCacheHandler.php
1 year ago
YoastSEO.php
1 year ago
WooCommerce.php
283 lines
| 1 | <?php |
| 2 | |
| 3 | namespace NitroPack\Integration\Plugin; |
| 4 | |
| 5 | class WooCommerce { |
| 6 | const STAGE = "late"; |
| 7 | |
| 8 | public static function isActive() { |
| 9 | if ( class_exists( 'WooCommerce' ) ) { |
| 10 | return true; |
| 11 | } |
| 12 | return false; |
| 13 | } |
| 14 | |
| 15 | public function init( $stage ) { |
| 16 | if ( ! self::isActive() ) |
| 17 | return; |
| 18 | |
| 19 | add_action( 'init', [ $this, 'cache_sale_products' ] ); |
| 20 | add_action( 'updated_post_meta', [ $this, 'update_cached_sale_products' ], 10, 4 ); |
| 21 | add_action( 'added_post_meta', [ $this, 'update_cached_sale_products' ], 10, 4 ); |
| 22 | add_action( 'deleted_post_meta', [ $this, 'update_cached_sale_products' ], 10, 4 ); |
| 23 | //update transient on post status change |
| 24 | add_action( 'transition_post_status', [ $this, 'update_product_from_transient' ], 10, 3 ); |
| 25 | //delete transient on post delete |
| 26 | add_action( 'delete_post', [ $this, 'remove_deleted_product_from_transient' ] ); |
| 27 | if ( nitropack_is_optimizer_request() ) { |
| 28 | add_action( 'template_redirect', [ $this, 'purge_site_cache_on_sale_start_and_end' ] ); |
| 29 | } |
| 30 | add_filter( 'wc_product_post_type_link_product_cat', [ $this, 'uppercase_product_cat_links' ] ); |
| 31 | |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Convert %product_cat% links to uppercase due to https://github.com/woocommerce/woocommerce/pull/51637 |
| 36 | * |
| 37 | * This function checks if the category slug starts with a '%' character. |
| 38 | * If it does, the slug is converted to uppercase. |
| 39 | * |
| 40 | * @param object $cat The product category object. |
| 41 | * @return object The modified product category object. |
| 42 | */ |
| 43 | public function uppercase_product_cat_links( $cat ) { |
| 44 | if ( $cat->slug && strpos( $cat->slug, '%' ) === 0 ) { |
| 45 | $cat->slug = strtoupper( $cat->slug ); |
| 46 | } |
| 47 | return $cat; |
| 48 | } |
| 49 | /** |
| 50 | * Retrieves the sale dates for a given WooCommerce product. |
| 51 | * |
| 52 | * @param \WC_Product $product The WooCommerce product object. |
| 53 | * @return array An associative array containing the start and end dates of the sale. |
| 54 | */ |
| 55 | private function get_sale_dates( $product ) { |
| 56 | $sale_start = $product->get_date_on_sale_from(); |
| 57 | $sale_end = $product->get_date_on_sale_to(); |
| 58 | $result = []; |
| 59 | |
| 60 | if ( $sale_start ) { |
| 61 | $sale_start = strtotime( date( 'Y-m-d', $sale_start->getTimestamp() ) ); |
| 62 | $result['from'] = $sale_start; |
| 63 | } |
| 64 | if ( $sale_end ) { |
| 65 | $sale_end = strtotime( date( 'Y-m-d', $sale_end->getTimestamp() ) ); |
| 66 | $result['to'] = $sale_end; |
| 67 | } |
| 68 | |
| 69 | return $result; |
| 70 | } |
| 71 | /** |
| 72 | * This method sets the expiration date for the cache. |
| 73 | * |
| 74 | * @param int $date The expiration date as a timestamp. |
| 75 | * @return void |
| 76 | */ |
| 77 | private function add_cache_expiration( $date ) { |
| 78 | global $np_customExpirationTimes; |
| 79 | |
| 80 | $np_customExpirationTimes[] = $date; |
| 81 | nitropack_set_custom_expiration(); |
| 82 | } |
| 83 | /** |
| 84 | * Update cached products when post meta is updated, deleted, or added. |
| 85 | * |
| 86 | * This function updates the cached sale product dates when the post meta |
| 87 | * keys '_sale_price_dates_from' or '_sale_price_dates_to' are modified. |
| 88 | * It ensures that the cached products are updated accordingly and removes |
| 89 | * the product from the cache if both dates are empty. |
| 90 | * |
| 91 | * @param int $meta_id The ID of the meta entry being updated. |
| 92 | * @param int $post_id The ID of the post being updated. |
| 93 | * @param string $meta_key The meta key being updated. |
| 94 | * @param string $meta_value The new value of the meta key. |
| 95 | * @return void |
| 96 | */ |
| 97 | public function update_cached_sale_products( $meta_id, $post_id, $meta_key, $meta_value ) { |
| 98 | //bail if we dont update future sale dates |
| 99 | if ( $meta_key != '_sale_price_dates_from' && $meta_key != '_sale_price_dates_to' ) |
| 100 | return; |
| 101 | |
| 102 | $cached_products = get_transient( 'nitropack_sale_product_dates' ); |
| 103 | // If $cached_products is empty, initialize it as an array |
| 104 | if ( empty( $cached_products ) ) { |
| 105 | $cached_products = []; |
| 106 | } |
| 107 | |
| 108 | // Ensure that the $post_id key exists in the $cached_products array |
| 109 | if ( ! isset( $cached_products[ $post_id ] ) ) { |
| 110 | $cached_products[ $post_id ] = []; |
| 111 | } |
| 112 | //update |
| 113 | if ( $meta_key === '_sale_price_dates_from' ) { |
| 114 | $cached_products[ $post_id ]['from'] = $meta_value; |
| 115 | } |
| 116 | if ( $meta_key === '_sale_price_dates_to' ) { |
| 117 | $cached_products[ $post_id ]['to'] = $meta_value; |
| 118 | } |
| 119 | //delete product |
| 120 | if ( empty( $cached_products[ $post_id ]['from'] ) && empty( $cached_products[ $post_id ]['to'] ) ) { |
| 121 | unset( $cached_products[ $post_id ] ); |
| 122 | } |
| 123 | set_transient( 'nitropack_sale_product_dates', $cached_products ); |
| 124 | } |
| 125 | /** |
| 126 | * Cache all products with sale dates. |
| 127 | * |
| 128 | * This method identifies all products that have sale dates and caches them |
| 129 | * to improve performance and reduce load times for users viewing sale items. |
| 130 | * |
| 131 | * @return void |
| 132 | */ |
| 133 | public function cache_sale_products() { |
| 134 | $cached_products = get_transient( 'nitropack_sale_product_dates' ); |
| 135 | if ( $cached_products !== false ) |
| 136 | return; |
| 137 | |
| 138 | $scheduled_sale_products = $this->get_products_with_sale(); |
| 139 | $sale_dates = array(); |
| 140 | if ( ! empty( $scheduled_sale_products ) ) { |
| 141 | |
| 142 | foreach ( $scheduled_sale_products as $scheduled_sale_product_id ) { |
| 143 | $current_product_sale_dates = $this->get_sale_dates( wc_get_product( $scheduled_sale_product_id ) ); |
| 144 | $sale_dates[ $scheduled_sale_product_id ] = $current_product_sale_dates; |
| 145 | } |
| 146 | } |
| 147 | /* |
| 148 | * If there are no products with sale dates, set the transient to false |
| 149 | * to avoid unnecessary queries in the future. |
| 150 | */ |
| 151 | set_transient( 'nitropack_sale_product_dates', $sale_dates ); |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Purges the site cache when a sale starts or ends. |
| 156 | * |
| 157 | * This function sets the X-Nitro-Expire header to the earliest future date |
| 158 | * based on the sale start or end date. It ensures that the cache is |
| 159 | * appropriately purged to reflect the changes in sale status. |
| 160 | */ |
| 161 | public function purge_site_cache_on_sale_start_and_end() { |
| 162 | $sale_dates = get_transient( 'nitropack_sale_product_dates' ); |
| 163 | if ( $sale_dates === false ) |
| 164 | return; |
| 165 | |
| 166 | $current_time = time(); |
| 167 | $valid_timestamps = []; |
| 168 | foreach ( $sale_dates as $product_id => $dates ) { |
| 169 | $timestamps = []; |
| 170 | |
| 171 | if ( isset( $dates['from'] ) && $dates['from'] >= $current_time ) { |
| 172 | $timestamps[] = $dates['from']; |
| 173 | } |
| 174 | |
| 175 | if ( isset( $dates['to'] ) && $dates['to'] >= $current_time ) { |
| 176 | $timestamps[] = $dates['to']; |
| 177 | } |
| 178 | |
| 179 | if ( ! empty( $timestamps ) ) { |
| 180 | // Use the earliest timestamp that is greater than or equal to the current time |
| 181 | $valid_timestamps[ $product_id ] = min( $timestamps ); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | // Find the earliest date from the valid timestamps |
| 186 | if ( ! empty( $valid_timestamps ) ) { |
| 187 | $earliest_key = array_search( min( $valid_timestamps ), $valid_timestamps ); |
| 188 | $earliest_date = $valid_timestamps[ $earliest_key ]; |
| 189 | |
| 190 | |
| 191 | $this->add_cache_expiration( $earliest_date ); |
| 192 | } |
| 193 | } |
| 194 | /** |
| 195 | * Retrieves all products that have sale dates. |
| 196 | * |
| 197 | * @return array An array of products that are currently on sale. |
| 198 | */ |
| 199 | public function get_products_with_sale() { |
| 200 | |
| 201 | $product_ids = []; |
| 202 | $args = array( |
| 203 | 'post_type' => array( 'product', 'product_variation' ), |
| 204 | 'post_status' => 'publish', |
| 205 | 'posts_per_page' => -1, |
| 206 | 'meta_query' => array( |
| 207 | 'relation' => 'OR', |
| 208 | array( |
| 209 | 'key' => '_sale_price_dates_from', |
| 210 | 'value' => time(), |
| 211 | 'compare' => '>=', |
| 212 | 'type' => 'NUMERIC', |
| 213 | ), |
| 214 | array( |
| 215 | 'key' => '_sale_price_dates_to', |
| 216 | 'value' => time(), |
| 217 | 'compare' => '>=', |
| 218 | 'type' => 'NUMERIC', |
| 219 | ), |
| 220 | ), |
| 221 | ); |
| 222 | |
| 223 | $query = new \WP_Query( $args ); |
| 224 | |
| 225 | if ( $query->have_posts() ) { |
| 226 | while ( $query->have_posts() ) { |
| 227 | global $post; |
| 228 | $query->the_post(); |
| 229 | $product_ids[] = $post->ID; |
| 230 | } |
| 231 | } |
| 232 | wp_reset_postdata(); |
| 233 | |
| 234 | return $product_ids; |
| 235 | } |
| 236 | /** |
| 237 | * Updates the product in the transient cache when its status changes. |
| 238 | * |
| 239 | * This function updates the cached sale product dates when the product's |
| 240 | * status changes. It ensures that the cached products are updated accordingly |
| 241 | * and removes the product from the cache if it is moved to trash. |
| 242 | * |
| 243 | * @param string $new The new status of the post. |
| 244 | * @param string $old The old status of the post. |
| 245 | * @param \WP_Post $post The post object being updated. |
| 246 | * @return void |
| 247 | */ |
| 248 | public function update_product_from_transient( $new, $old, $post ) { |
| 249 | if ( $new === "auto-draft" || ( $new === "draft" && $old === "auto-draft" ) || ( $new === "draft" && $old != "publish" ) || $new === "inherit" ) { // Creating a new post or draft, don't do anything for now. |
| 250 | return; |
| 251 | } |
| 252 | $post_id = $post->ID; |
| 253 | $cached_products = get_transient( 'nitropack_sale_product_dates' ); |
| 254 | |
| 255 | if ( $new === "trash" && ! empty( $cached_products[ $post_id ] ) ) { |
| 256 | unset( $cached_products[ $post_id ] ); |
| 257 | set_transient( 'nitropack_sale_product_dates', $cached_products ); |
| 258 | } |
| 259 | if ( $new === "publish" ) { |
| 260 | $meta = get_post_meta( $post_id ); |
| 261 | if ( ! empty( $meta['_sale_price_dates_from'] ) ) { |
| 262 | $cached_products[ $post_id ]['from'] = $meta['_sale_price_dates_from'][0]; |
| 263 | } |
| 264 | if ( ! empty( $meta['_sale_price_dates_to'] ) ) { |
| 265 | $cached_products[ $post_id ]['to'] = $meta['_sale_price_dates_to'][0]; |
| 266 | } |
| 267 | set_transient( 'nitropack_sale_product_dates', $cached_products ); |
| 268 | } |
| 269 | } |
| 270 | /** |
| 271 | * Deletes the product from the transient cache when it is force/fully deleted. |
| 272 | * @param int $post_id The ID of the post being deleted. |
| 273 | * @return void |
| 274 | */ |
| 275 | public function remove_deleted_product_from_transient( $post_id ) { |
| 276 | $cached_products = get_transient( 'nitropack_sale_product_dates' ); |
| 277 | if ( empty( $cached_products[ $post_id ] ) ) { |
| 278 | return; |
| 279 | } |
| 280 | unset( $cached_products[ $post_id ] ); |
| 281 | set_transient( 'nitropack_sale_product_dates', $cached_products ); |
| 282 | } |
| 283 | } |