oont-contents/plugins/woocommerce-square/includes/Framework/Square_Helper.php
2025-02-08 15:10:23 +01:00

415 lines
11 KiB
PHP

<?php
namespace WooCommerce\Square\Framework;
use WooCommerce\Square\Framework\Plugin_Compatibility;
defined( 'ABSPATH' ) || exit;
/**
* Square Helper Class
*
* The purpose of this class is to centralize common utility functions.
*
* @since 3.0.0
*/
class Square_Helper {
/** encoding used for mb_*() string functions */
const MB_ENCODING = 'UTF-8';
/** String manipulation functions (all multi-byte safe) ***************/
/**
* Returns true if the haystack string starts with needle
*
* Note: case-sensitive
*
* @since 3.0.0
* @param string $haystack
* @param string $needle
* @return bool
*/
public static function str_starts_with( $haystack, $needle ) {
if ( self::multibyte_loaded() ) {
if ( '' === $needle ) {
return true;
}
return 0 === mb_strpos( $haystack, $needle, 0, self::MB_ENCODING );
} else {
$needle = self::str_to_ascii( $needle );
if ( '' === $needle ) {
return true;
}
return 0 === strpos( self::str_to_ascii( $haystack ), self::str_to_ascii( $needle ) );
}
}
/**
* Returns true if the needle exists in haystack
*
* Note: case-sensitive
*
* @since 3.0.0
* @param string $haystack
* @param string $needle
* @return bool
*/
public static function str_exists( $haystack, $needle ) {
if ( self::multibyte_loaded() ) {
if ( '' === $needle ) {
return false;
}
return false !== mb_strpos( $haystack, $needle, 0, self::MB_ENCODING );
} else {
$needle = self::str_to_ascii( $needle );
if ( '' === $needle ) {
return false;
}
return false !== strpos( self::str_to_ascii( $haystack ), self::str_to_ascii( $needle ) );
}
}
/**
* Returns a string with all non-ASCII characters removed. This is useful
* for any string functions that expect only ASCII chars and can't
* safely handle UTF-8. Note this only allows ASCII chars in the range
* 33-126 (newlines/carriage returns are stripped)
*
* @since 3.0.0
* @param string $string string to make ASCII
* @return string
*/
public static function str_to_ascii( $string ) {
// strip ASCII chars 32 and under
$string = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW );
// strip ASCII chars 127 and higher
return filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH );
}
/**
* Truncates a given $string after a given $length if string is longer than
* $length. The last characters will be replaced with the $omission string
* for a total length not exceeding $length
*
* @since 3.0.0
* @param string $string text to truncate
* @param int $length total desired length of string, including omission
* @param string $omission omission text, defaults to '...'
* @return string
*/
public static function str_truncate( $string, $length, $omission = '...' ) {
if ( self::multibyte_loaded() ) {
// bail if string doesn't need to be truncated
if ( mb_strlen( $string, self::MB_ENCODING ) <= $length ) {
return $string;
}
$length -= mb_strlen( $omission, self::MB_ENCODING );
return mb_substr( $string, 0, $length, self::MB_ENCODING ) . $omission;
} else {
$string = self::str_to_ascii( $string );
// bail if string doesn't need to be truncated
if ( strlen( $string ) <= $length ) {
return $string;
}
$length -= strlen( $omission );
return substr( $string, 0, $length ) . $omission;
}
}
/**
* Helper method to check if the multibyte extension is loaded, which
* indicates it's safe to use the mb_*() string methods
*
* @since 3.0.0
* @return bool
*/
protected static function multibyte_loaded() {
return extension_loaded( 'mbstring' );
}
/** Array functions ***************************************************/
/**
* Insert the given element after the given key in the array
*
* Sample usage:
*
* given
*
* array( 'item_1' => 'foo', 'item_2' => 'bar' )
*
* array_insert_after( $array, 'item_1', array( 'item_1.5' => 'w00t' ) )
*
* becomes
*
* array( 'item_1' => 'foo', 'item_1.5' => 'w00t', 'item_2' => 'bar' )
*
* @since 3.0.0
* @param array $array array to insert the given element into
* @param string $insert_key key to insert given element after
* @param array $element element to insert into array
* @return array
*/
public static function array_insert_after( array $array, $insert_key, array $element ) {
$new_array = array();
foreach ( $array as $key => $value ) {
$new_array[ $key ] = $value;
if ( $insert_key === $key ) {
foreach ( $element as $k => $v ) {
$new_array[ $k ] = $v;
}
}
}
return $new_array;
}
/**
* Lists an array as text.
*
* Takes an array and returns a list like "one, two, three, and four"
* with a (mandatory) oxford comma.
*
* @since 3.0.0
*
* @param array $items items to list
* @param string|null $conjunction coordinating conjunction, like "or" or "and"
* @param string $separator list separator, like a comma
* @return string
*/
public static function list_array_items( array $items, $conjunction = null, $separator = '' ) {
if ( ! is_string( $conjunction ) ) {
$conjunction = _x( 'and', 'coordinating conjunction for a list of items: a, b, and c', 'woocommerce-square' );
}
// append the conjunction to the last item
if ( count( $items ) > 1 ) {
$last_item = array_pop( $items );
array_push( $items, trim( "{$conjunction} {$last_item}" ) );
// only use a comma if needed and no separator was passed
if ( count( $items ) < 3 ) {
$separator = ' ';
} elseif ( ! is_string( $separator ) || '' === $separator ) {
$separator = ', ';
}
}
return implode( $separator, $items );
}
/** Number helper functions *******************************************/
/**
* Format a number with 2 decimal points, using a period for the decimal
* separator and no thousands separator.
*
* Commonly used for payment gateways which require amounts in this format.
*
* @since 3.0.0
* @param float $number
* @return string
*/
public static function number_format( $number ) {
return number_format( (float) $number, 2, '.', '' );
}
/**
* Determines if an order contains only virtual products.
*
* @since 3.0.0
* @param \WC_Order $order the order object
* @return bool
*/
public static function is_order_virtual( \WC_Order $order ) {
$is_virtual = true;
foreach ( $order->get_items() as $item ) {
$product = $item->get_product();
// once we've found one non-virtual product we know we're done, break out of the loop
if ( $product && ! $product->is_virtual() ) {
$is_virtual = false;
break;
}
}
return $is_virtual;
}
/**
* Safely get sanitized data from $_POST
*
* @since 3.0.0
* @param string $key Array key to get from $_POST array.
* @param string $sanitize_callback Name of the sanitization callback function.
*
* @return string value from $_POST or blank string if $_POST[ $key ] is not set
*/
public static function get_post( $key = '', $sanitize_callback = 'sanitize_text_field' ) {
if ( ! is_callable( $sanitize_callback ) ) {
$sanitize_callback = 'sanitize_text_field';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return isset( $_POST[ $key ] ) ? call_user_func( $sanitize_callback, wp_unslash( $_POST[ $key ] ) ) : '';
}
/**
* Safely get and trim data from $_REQUEST
*
* @since 3.0.0
* @param string $key Array key to get from $_REQUEST array.
* @param string $sanitize_callback Name of the sanitization callback function.
*
* @return string value from $_REQUEST or blank string if $_REQUEST[ $key ] is not set
*/
public static function get_request( $key, $sanitize_callback = 'sanitize_text_field' ) {
if ( ! is_callable( $sanitize_callback ) ) {
$sanitize_callback = 'sanitize_text_field';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return isset( $_REQUEST[ $key ] ) ? call_user_func( $sanitize_callback, wp_unslash( $_REQUEST[ $key ] ) ) : '';
}
/**
* Add and store a notice.
*
* WC notice functions are not available in the admin
*
* @since 3.0.0
* @param string $message The text to display in the notice.
* @param string $notice_type The singular name of the notice type - either error, success or notice. [optional]
*/
public static function wc_add_notice( $message, $notice_type = 'success' ) {
if ( function_exists( 'wc_add_notice' ) ) {
wc_add_notice( $message, $notice_type );
}
}
/**
* Gets the full URL to the log file for a given $handle
*
* @since 3.0.0
* @param string $handle log handle
* @return string URL to the WC log file identified by $handle
*/
public static function get_wc_log_file_url( $handle ) {
return admin_url( sprintf( 'admin.php?page=wc-status&tab=logs&log_file=%s-%s-log', $handle, sanitize_file_name( wp_hash( $handle ) ) ) );
}
/**
* Gets the current WordPress site name.
*
* This is helpful for retrieving the actual site name instead of the
* network name on multisite installations.
*
* @since 3.0.0
* @return string
*/
public static function get_site_name() {
return ( is_multisite() ) ? get_blog_details()->blogname : get_bloginfo( 'name' );
}
/**
* Displays a notice if the provided hook has not yet run.
*
* @since 3.0.0
*
* @param string $hook action hook to check
* @param string $method method/function name
* @param string $version version the notice was added
*/
public static function maybe_doing_it_early( $hook, $method, $version ) {
if ( ! did_action( $hook ) ) {
Plugin_Compatibility::wc_doing_it_wrong( $method, "This should only be called after '{$hook}'", $version );
}
}
/**
* Triggers a PHP error.
*
* This wrapper method ensures AJAX isn't broken in the process.
*
* @since 3.0.0
* @param string $message the error message
* @param int $type Optional. The error type. Defaults to E_USER_NOTICE
*/
public static function trigger_error( $message, $type = E_USER_NOTICE ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
wc_deprecated_function( __METHOD__, '3.9.0' );
if ( wp_doing_ajax() ) {
switch ( $type ) {
case E_USER_NOTICE:
$prefix = 'Notice: ';
break;
case E_USER_WARNING:
$prefix = 'Warning: ';
break;
default:
$prefix = '';
}
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log( $prefix . $message );
} else {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
trigger_error( $message, $type );
}
}
}