<?php
/**
* Tax Code list service
* Retrieves from API, stores and formats Stripe Tax Codes
*
* @package Stripe\StripeTaxForWooCommerce\Stripe
*/
namespace Stripe\StripeTaxForWooCommerce\Stripe;
defined( 'ABSPATH' ) || exit;
use Exception;
/**
* Class provides tax code list
*/
class TaxCodeList {
use StripeClientTrait;
const CACHE_GROUP = 'stripe-tax-for-woocommerce';
const CACHE_KEY = 'tax-codes';
const TABLE_NAME = STRIPE_TAX_FOR_WOOCOMMERCE_DB_PREFIX . 'tax_codes';
/**
* Stripe API key
*
* @var string $stripe_secret_key Stripe API secret key
*/
protected $stripe_secret_key;
/**
* Creates TaxCodeList object
*
* @param string $stripe_secret_key Stripe API secret key.
*/
public function __construct( string $stripe_secret_key ) {
$this->stripe_secret_key = $stripe_secret_key;
}
/**
* Formats array of tax_code objects converting "{name}" property of each object in "{id} - {localized_name}" format
*
* @param array $items Array of tax_code objects with "id" and "name" mandatory properties.
*
* @return array Array of tax_code objects in "{id} - {localized_name}" format
*/
protected function format_items( array $items ): array {
$formatted_items = array();
foreach ( $items as $tax_code => $tax_code_object ) {
$formatted_items[ $tax_code ] = new \stdClass();
$formatted_items[ $tax_code ]->id = $tax_code;
$formatted_items[ $tax_code ]->name = $tax_code . ' - ' . $tax_code_object->name;
}
return $formatted_items;
}
/**
* Formats associative array of tax_codes converting value into "{id} - {localized_name}" format
*
* @param array $items Array of tax_code objects with "id" and "name" mandatory properties.
*
* @return array Array of tax_code objects in "{id} - {localized_name}" format
*/
protected function format_items_key_value( array $items ): array {
$formatted_items = array();
foreach ( $items as $tax_code => $tax_code_name ) {
$formatted_items[ $tax_code ] = $tax_code . ' - ' . $tax_code_name;
}
return $formatted_items;
}
/**
* Tries to return tax codes from WordPress object cache
*
* @return array Cached tax codes
*/
protected function get_from_object_cache(): array {
$items = wp_cache_get( static::CACHE_KEY, static::CACHE_GROUP );
if ( $items ) {
return $items;
} else {
return array();
}
}
/**
* Tries to return tax codes from database
*
* @return array Associative array of objects, where each array's item key equals value od first column in database
*/
protected function get_from_db(): array {
global $wpdb;
if (
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->get_results(
$wpdb->prepare(
'SELECT %i, %i FROM %i',
array(
'id',
'name',
static::TABLE_NAME,
)
),
OBJECT_K
)
) {
return $wpdb->get_results(
$wpdb->prepare(
'SELECT %i, %i FROM %i',
array(
'id',
'name',
static::TABLE_NAME,
)
),
OBJECT_K
);
} else {
return array();
}
}
/**
* Saves tax codes into object cache
*
* @param array $items Tax codes.
*
* @return void
*/
protected function set_to_object_cache( array $items ): void {
wp_cache_flush_group( static::CACHE_GROUP );
wp_cache_set( static::CACHE_KEY, $items, static::CACHE_GROUP, 2 * HOUR_IN_SECONDS );
}
/**
* Tries to receive tax codes using Stripe API
*
* @return array Associative array of tax codes, where each array's item key equals tax_code id.
* @throws Exception If data is not correct.
* @see https://stripe.com/docs/api/tax_codes/list
*/
protected function get_from_api_call(): array {
$stripe = $this->get_stripe_client( $this->stripe_secret_key );
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$api_response = $stripe->taxCodes->all( array( 'limit' => 100 ) );
if ( ! isset( $api_response->object ) || 'list' !== $api_response->object ) {
throw new Exception( esc_html__( 'Unexpected response from Stripe', 'stripe-tax-for-woocommerce' ) . ': ' . wp_json_encode( $api_response ) );
}
$counter = 0;
$tax_codes = array();
foreach ( $api_response->autoPagingIterator() as $tax_code ) {
$tax_codes[ $tax_code->id ] = $tax_code;
++$counter;
if ( $counter > 10000 ) {
throw new Exception( esc_html__( 'Too many tax codes', 'stripe-tax-for-woocommerce' ) );
}
}
return $tax_codes;
}
/**
* Truncates tax codes table
*
* @return void
*/
protected function truncate_table(): void {
global $wpdb;
$wpdb->query(
$wpdb->prepare( 'TRUNCATE %i', static::TABLE_NAME )
);
}
/**
* Saves tax codes to database
*
* @param array $items_array Array of tax code objects.
*
* @return void
*/
protected function set_to_db( array $items_array ): void {
global $wpdb;
foreach ( $items_array as $object ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
$wpdb->prepare(
'INSERT %i (%i, %i, %i) VALUES (%s, %s, %s)',
array(
static::TABLE_NAME,
'id',
'name',
'description',
$object->id,
$object->name,
$object->description,
)
)
);
}
}
/**
* Receive all tax codes
*
* @param bool $skip_object_cache Skips object cache and tries to receive tax_codes from database or Stripe API, depending on $skip_database_cache parameter.
* @param bool $skip_database_cache Skips database cache and tries to receive tax codes from Stripe API if object cache skipped or empty.
*
* @return array Array of tax codes
* @throws Exception If data is not correct.
*/
public function get( bool $skip_object_cache = false, bool $skip_database_cache = false ): array {
if ( ! $skip_object_cache ) {
$items = $this->get_from_object_cache();
if ( $items ) {
return $items;
}
}
if ( ! $skip_database_cache ) {
$items = $this->get_from_db();
if ( $items ) {
$this->set_to_object_cache( $items );
return $items;
}
}
$items = $this->get_from_api_call();
if ( $items ) {
$this->truncate_table();
$this->set_to_db( $items );
$this->set_to_object_cache( $items );
}
return $items;
}
/**
* Gets formatted and localized tax code list, where tax code name equals "{id} - {name}"
*
* @param bool $skip_object_cache Skips object cache and tries to receive tax_codes from database or Stripe API, depending on $skip_database_cache parameter.
* @param bool $skip_database_cache Skips database cache and tries to receive tax codes from Stripe API if object cache skipped or empty.
*
* @return array Formatted and localized tax code list, where tax code name equals "{id} - {name}"
* @throws Exception If data is not correct.
*/
public function get_formatted( bool $skip_object_cache = false, bool $skip_database_cache = false ): array {
return $this->format_items( $this->get( $skip_object_cache, $skip_database_cache ) );
}
/**
* Returns tax codes as associative array, there key equals tax code id and value equals tax code name
*
* @param bool $skip_object_cache Skip object cache.
* @param bool $skip_database_cache Skip database cache.
*
* @return array Tax codes as associative array, there key equals tax code id and value equals tax code name.
* @throws Exception If data is not correct.
*/
public function get_as_key_value( bool $skip_object_cache = false, bool $skip_database_cache = false ): array {
$cached_value = wp_cache_get( 'tax-codes-key-value', static::CACHE_GROUP );
if ( $cached_value && ! $skip_object_cache ) {
return $cached_value;
}
$items = $this->get( $skip_object_cache, $skip_database_cache );
$cached_value = array_combine( array_keys( $items ), array_column( $items, 'name' ) );
wp_cache_set( 'tax-codes-key-value', $cached_value, static::CACHE_GROUP, 2 * HOUR_IN_SECONDS );
return $cached_value;
}
/**
* Returns tax codes as associative array, where key equals tax code id and value equals {id} - {name}
*
* @param bool $skip_object_cache Skips object cache and tries to receive tax_codes from database or Stripe API, depending on $skip_database_cache parameter.
* @param bool $skip_database_cache Skips database cache and tries to receive tax codes from Stripe API if object cache skipped or empty.
*
* @return array Tax codes as associative array, where key equals tax code id and value equals {id} - {name}
* @throws Exception If data is not correct.
*/
public function get_as_key_value_formatted( bool $skip_object_cache = false, bool $skip_database_cache = false ): array {
return $this->format_items_key_value( $this->get_as_key_value( $skip_object_cache, $skip_database_cache ) );
}
/**
* Formats single
*
* @param string $tax_code Tax code.
* @param string $api_key API key.
*
* @return mixed
* @throws Exception If data is not correct.
*/
public static function format_single( $tax_code, $api_key ) {
// @phpstan-ignore-next-line
$tax_code_list = new static( $api_key );
return $tax_code_list->get()[ $tax_code ]->name;
}
}