| 1 |
<?php |
| 2 |
/** |
| 3 |
* Tax catalog proxy behavior. |
| 4 |
* |
| 5 |
* @package WCPOS\WooCommercePOS\API\V2\Proxy |
| 6 |
*/ |
| 7 |
|
| 8 |
namespace WCPOS\WooCommercePOS\API\V2\Proxy; |
| 9 |
|
| 10 |
use WP_REST_Request; |
| 11 |
|
| 12 |
/** |
| 13 |
* Applies tax ID narrowing and POS read permission without a v1 controller. |
| 14 |
*/ |
| 15 |
final class Taxes_Proxy_Behavior extends Scoped_Proxy_Behavior { |
| 16 |
/** POS read grant for this collection only. |
| 17 |
* |
| 18 |
* @var string |
| 19 |
*/ |
| 20 |
protected $permission_collection = 'tax_rates'; |
| 21 |
|
| 22 |
/** |
| 23 |
* The Collection Rules plan for the request in flight. |
| 24 |
* |
| 25 |
* @var int[] |
| 26 |
*/ |
| 27 |
private $include = array(); |
| 28 |
|
| 29 |
/** |
| 30 |
* The Collection Rules plan for the request in flight. |
| 31 |
* |
| 32 |
* @var int[] |
| 33 |
*/ |
| 34 |
private $exclude = array(); |
| 35 |
|
| 36 |
/** |
| 37 |
* Adjust the params forwarded to wc/v3 for this resource. |
| 38 |
* |
| 39 |
* @param array $params Query parameters to forward. |
| 40 |
* @param WP_REST_Request $request Original proxy request. |
| 41 |
* |
| 42 |
* @return array |
| 43 |
*/ |
| 44 |
public function forwarded_params( array $params, WP_REST_Request $request ): array { |
| 45 |
foreach ( array( 'include', 'exclude' ) as $param ) { |
| 46 |
if ( isset( $params[ $param ] ) ) { |
| 47 |
$this->{$param} = wp_parse_id_list( $params[ $param ] ); |
| 48 |
$params[ 'wcpos_' . $param ] = $this->{$param}; |
| 49 |
unset( $params[ $param ] ); |
| 50 |
} |
| 51 |
} |
| 52 |
|
| 53 |
return $params; |
| 54 |
} |
| 55 |
|
| 56 |
/** |
| 57 |
* Install this resource's hooks and return their removal tuples. |
| 58 |
* |
| 59 |
* @return array<int, array{0: string, 1: callable, 2: int}> |
| 60 |
*/ |
| 61 |
protected function install(): array { |
| 62 |
$bindings = array(); |
| 63 |
|
| 64 |
if ( array() === $this->include && array() === $this->exclude ) { |
| 65 |
return $bindings; |
| 66 |
} |
| 67 |
|
| 68 |
/* |
| 69 |
* Stays installed for the whole forward, and is unwound by around()'s |
| 70 |
* finally rather than by removing itself on first match. |
| 71 |
* |
| 72 |
* v1's method self-removes, but it removes the array callback |
| 73 |
* `array( $this, 'wcpos_tax_add_include_exclude_to_sql' )` — while the |
| 74 |
* proxy lane registered a CLOSURE wrapping that method, so the removal |
| 75 |
* never matched and the filter went on to narrow the pagination COUNT |
| 76 |
* query too. Self-removing here really does unhook, which left the |
| 77 |
* count unfiltered and the reported total too high. |
| 78 |
* `test_cashier_include_filter_limits_pagination_totals` pins this. |
| 79 |
* |
| 80 |
* Re-application is safe: each invocation receives a fresh query string, |
| 81 |
* and the strpos guard scopes it to tax-rate queries. |
| 82 |
*/ |
| 83 |
$query_filter = function ( string $query ): string { |
| 84 |
global $wpdb; |
| 85 |
|
| 86 |
if ( false === strpos( $query, "{$wpdb->prefix}woocommerce_tax_rates" ) ) { |
| 87 |
return $query; |
| 88 |
} |
| 89 |
if ( array() !== $this->include ) { |
| 90 |
$query = self::insert_where( $query, "{$wpdb->prefix}woocommerce_tax_rates.tax_rate_id IN (" . implode( ',', $this->include ) . ')' ); |
| 91 |
} |
| 92 |
if ( array() !== $this->exclude ) { |
| 93 |
$query = self::insert_where( $query, "{$wpdb->prefix}woocommerce_tax_rates.tax_rate_id NOT IN (" . implode( ',', $this->exclude ) . ')' ); |
| 94 |
} |
| 95 |
|
| 96 |
return $query; |
| 97 |
}; |
| 98 |
add_filter( 'query', $query_filter, 10, 1 ); |
| 99 |
$bindings[] = array( 'query', $query_filter, 10 ); |
| 100 |
|
| 101 |
return $bindings; |
| 102 |
} |
| 103 |
|
| 104 |
/** |
| 105 |
* Insert one condition into the WooCommerce tax-rate SQL. |
| 106 |
* |
| 107 |
* @param string $query SQL query. |
| 108 |
* @param string $condition WHERE condition. |
| 109 |
* |
| 110 |
* @return string |
| 111 |
*/ |
| 112 |
private static function insert_where( string $query, string $condition ): string { |
| 113 |
if ( false !== strpos( $query, 'WHERE' ) ) { |
| 114 |
return str_replace( 'WHERE', "WHERE $condition AND", $query ); |
| 115 |
} |
| 116 |
|
| 117 |
$position = strpos( $query, 'ORDER BY' ); |
| 118 |
return false !== $position |
| 119 |
? substr_replace( $query, " WHERE $condition ", $position, 0 ) |
| 120 |
: $query . " WHERE $condition"; |
| 121 |
} |
| 122 |
} |
| 123 |
|