Няма описание

class-wc-shipping-legacy-flat-rate.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <?php
  2. /**
  3. * Class WC_Shipping_Legacy_Flat_Rate file.
  4. *
  5. * @package WooCommerce\Shipping
  6. */
  7. if ( ! defined( 'ABSPATH' ) ) {
  8. exit;
  9. }
  10. /**
  11. * Flat Rate Shipping Method.
  12. *
  13. * This class is here for backwards compatibility for methods existing before zones existed.
  14. *
  15. * @deprecated 2.6.0
  16. * @version 2.4.0
  17. * @package WooCommerce\Classes\Shipping
  18. */
  19. class WC_Shipping_Legacy_Flat_Rate extends WC_Shipping_Method {
  20. /**
  21. * Cost passed to [fee] shortcode.
  22. *
  23. * @var string
  24. */
  25. protected $fee_cost = '';
  26. /**
  27. * Constructor.
  28. */
  29. public function __construct() {
  30. $this->id = 'legacy_flat_rate';
  31. $this->method_title = __( 'Flat rate (legacy)', 'woocommerce' );
  32. /* translators: %s: Admin shipping settings URL */
  33. $this->method_description = '<strong>' . sprintf( __( 'This method is deprecated in 2.6.0 and will be removed in future versions - we recommend disabling it and instead setting up a new rate within your <a href="%s">Shipping zones</a>.', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=shipping' ) ) . '</strong>';
  34. $this->init();
  35. add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
  36. add_action( 'woocommerce_flat_rate_shipping_add_rate', array( $this, 'calculate_extra_shipping' ), 10, 2 );
  37. }
  38. /**
  39. * Process and redirect if disabled.
  40. */
  41. public function process_admin_options() {
  42. parent::process_admin_options();
  43. if ( 'no' === $this->settings['enabled'] ) {
  44. wp_redirect( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=options' ) );
  45. exit;
  46. }
  47. }
  48. /**
  49. * Return the name of the option in the WP DB.
  50. *
  51. * @since 2.6.0
  52. * @return string
  53. */
  54. public function get_option_key() {
  55. return $this->plugin_id . 'flat_rate_settings';
  56. }
  57. /**
  58. * Init function.
  59. */
  60. public function init() {
  61. // Load the settings.
  62. $this->init_form_fields();
  63. $this->init_settings();
  64. // Define user set variables.
  65. $this->title = $this->get_option( 'title' );
  66. $this->availability = $this->get_option( 'availability' );
  67. $this->countries = $this->get_option( 'countries' );
  68. $this->tax_status = $this->get_option( 'tax_status' );
  69. $this->cost = $this->get_option( 'cost' );
  70. $this->type = $this->get_option( 'type', 'class' );
  71. $this->options = $this->get_option( 'options', false ); // @deprecated 2.4.0
  72. }
  73. /**
  74. * Initialise Settings Form Fields.
  75. */
  76. public function init_form_fields() {
  77. $this->form_fields = include __DIR__ . '/includes/settings-flat-rate.php';
  78. }
  79. /**
  80. * Evaluate a cost from a sum/string.
  81. *
  82. * @param string $sum Sum to evaluate.
  83. * @param array $args Arguments.
  84. * @return string
  85. */
  86. protected function evaluate_cost( $sum, $args = array() ) {
  87. include_once WC()->plugin_path() . '/includes/libraries/class-wc-eval-math.php';
  88. $locale = localeconv();
  89. $decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'] );
  90. $this->fee_cost = $args['cost'];
  91. // Expand shortcodes.
  92. add_shortcode( 'fee', array( $this, 'fee' ) );
  93. $sum = do_shortcode(
  94. str_replace(
  95. array(
  96. '[qty]',
  97. '[cost]',
  98. ),
  99. array(
  100. $args['qty'],
  101. $args['cost'],
  102. ),
  103. $sum
  104. )
  105. );
  106. remove_shortcode( 'fee', array( $this, 'fee' ) );
  107. // Remove whitespace from string.
  108. $sum = preg_replace( '/\s+/', '', $sum );
  109. // Remove locale from string.
  110. $sum = str_replace( $decimals, '.', $sum );
  111. // Trim invalid start/end characters.
  112. $sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
  113. // Do the math.
  114. return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
  115. }
  116. /**
  117. * Work out fee (shortcode).
  118. *
  119. * @param array $atts Shortcode attributes.
  120. * @return string
  121. */
  122. public function fee( $atts ) {
  123. $atts = shortcode_atts(
  124. array(
  125. 'percent' => '',
  126. 'min_fee' => '',
  127. ),
  128. $atts,
  129. 'fee'
  130. );
  131. $calculated_fee = 0;
  132. if ( $atts['percent'] ) {
  133. $calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
  134. }
  135. if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
  136. $calculated_fee = $atts['min_fee'];
  137. }
  138. return $calculated_fee;
  139. }
  140. /**
  141. * Calculate shipping.
  142. *
  143. * @param array $package (default: array()).
  144. */
  145. public function calculate_shipping( $package = array() ) {
  146. $rate = array(
  147. 'id' => $this->id,
  148. 'label' => $this->title,
  149. 'cost' => 0,
  150. 'package' => $package,
  151. );
  152. // Calculate the costs.
  153. $has_costs = false; // True when a cost is set. False if all costs are blank strings.
  154. $cost = $this->get_option( 'cost' );
  155. if ( '' !== $cost ) {
  156. $has_costs = true;
  157. $rate['cost'] = $this->evaluate_cost(
  158. $cost,
  159. array(
  160. 'qty' => $this->get_package_item_qty( $package ),
  161. 'cost' => $package['contents_cost'],
  162. )
  163. );
  164. }
  165. // Add shipping class costs.
  166. $found_shipping_classes = $this->find_shipping_classes( $package );
  167. $highest_class_cost = 0;
  168. foreach ( $found_shipping_classes as $shipping_class => $products ) {
  169. // Also handles BW compatibility when slugs were used instead of ids.
  170. $shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
  171. $class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' );
  172. if ( '' === $class_cost_string ) {
  173. continue;
  174. }
  175. $has_costs = true;
  176. $class_cost = $this->evaluate_cost(
  177. $class_cost_string,
  178. array(
  179. 'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ),
  180. 'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) ),
  181. )
  182. );
  183. if ( 'class' === $this->type ) {
  184. $rate['cost'] += $class_cost;
  185. } else {
  186. $highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
  187. }
  188. }
  189. if ( 'order' === $this->type && $highest_class_cost ) {
  190. $rate['cost'] += $highest_class_cost;
  191. }
  192. $rate['package'] = $package;
  193. // Add the rate.
  194. if ( $has_costs ) {
  195. $this->add_rate( $rate );
  196. }
  197. /**
  198. * Developers can add additional flat rates based on this one via this action since @version 2.4.
  199. *
  200. * Previously there were (overly complex) options to add additional rates however this was not user.
  201. * friendly and goes against what Flat Rate Shipping was originally intended for.
  202. *
  203. * This example shows how you can add an extra rate based on this flat rate via custom function:
  204. *
  205. * add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
  206. *
  207. * function add_another_custom_flat_rate( $method, $rate ) {
  208. * $new_rate = $rate;
  209. * $new_rate['id'] .= ':' . 'custom_rate_name'; // Append a custom ID.
  210. * $new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
  211. * $new_rate['cost'] += 2; // Add $2 to the cost.
  212. *
  213. * // Add it to WC.
  214. * $method->add_rate( $new_rate );
  215. * }.
  216. */
  217. do_action( 'woocommerce_flat_rate_shipping_add_rate', $this, $rate );
  218. }
  219. /**
  220. * Get items in package.
  221. *
  222. * @param array $package Package information.
  223. * @return int
  224. */
  225. public function get_package_item_qty( $package ) {
  226. $total_quantity = 0;
  227. foreach ( $package['contents'] as $item_id => $values ) {
  228. if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
  229. $total_quantity += $values['quantity'];
  230. }
  231. }
  232. return $total_quantity;
  233. }
  234. /**
  235. * Finds and returns shipping classes and the products with said class.
  236. *
  237. * @param mixed $package Package information.
  238. * @return array
  239. */
  240. public function find_shipping_classes( $package ) {
  241. $found_shipping_classes = array();
  242. foreach ( $package['contents'] as $item_id => $values ) {
  243. if ( $values['data']->needs_shipping() ) {
  244. $found_class = $values['data']->get_shipping_class();
  245. if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
  246. $found_shipping_classes[ $found_class ] = array();
  247. }
  248. $found_shipping_classes[ $found_class ][ $item_id ] = $values;
  249. }
  250. }
  251. return $found_shipping_classes;
  252. }
  253. /**
  254. * Adds extra calculated flat rates.
  255. *
  256. * @deprecated 2.4.0
  257. *
  258. * Additional rates defined like this:
  259. * Option Name | Additional Cost [+- Percents%] | Per Cost Type (order, class, or item).
  260. *
  261. * @param null $method Deprecated.
  262. * @param array $rate Rate information.
  263. */
  264. public function calculate_extra_shipping( $method, $rate ) {
  265. if ( $this->options ) {
  266. $options = array_filter( (array) explode( "\n", $this->options ) );
  267. foreach ( $options as $option ) {
  268. $this_option = array_map( 'trim', explode( WC_DELIMITER, $option ) );
  269. if ( count( $this_option ) !== 3 ) {
  270. continue;
  271. }
  272. $extra_rate = $rate;
  273. $extra_rate['id'] = $this->id . ':' . urldecode( sanitize_title( $this_option[0] ) );
  274. $extra_rate['label'] = $this_option[0];
  275. $extra_cost = $this->get_extra_cost( $this_option[1], $this_option[2], $rate['package'] );
  276. if ( is_array( $extra_rate['cost'] ) ) {
  277. $extra_rate['cost']['order'] = $extra_rate['cost']['order'] + $extra_cost;
  278. } else {
  279. $extra_rate['cost'] += $extra_cost;
  280. }
  281. $this->add_rate( $extra_rate );
  282. }
  283. }
  284. }
  285. /**
  286. * Calculate the percentage adjustment for each shipping rate.
  287. *
  288. * @deprecated 2.4.0
  289. * @param float $cost Cost.
  290. * @param float $percent_adjustment Percent adjusment.
  291. * @param string $percent_operator Percent operator.
  292. * @param float $base_price Base price.
  293. * @return float
  294. */
  295. public function calc_percentage_adjustment( $cost, $percent_adjustment, $percent_operator, $base_price ) {
  296. if ( '+' === $percent_operator ) {
  297. $cost += $percent_adjustment * $base_price;
  298. } else {
  299. $cost -= $percent_adjustment * $base_price;
  300. }
  301. return $cost;
  302. }
  303. /**
  304. * Get extra cost.
  305. *
  306. * @deprecated 2.4.0
  307. * @param string $cost_string Cost string.
  308. * @param string $type Type.
  309. * @param array $package Package information.
  310. * @return float
  311. */
  312. public function get_extra_cost( $cost_string, $type, $package ) {
  313. $cost = $cost_string;
  314. $cost_percent = false;
  315. // @codingStandardsIgnoreStart
  316. $pattern =
  317. '/' . // Start regex.
  318. '(\d+\.?\d*)' . // Capture digits, optionally capture a `.` and more digits.
  319. '\s*' . // Match whitespace.
  320. '(\+|-)' . // Capture the operand.
  321. '\s*' . // Match whitespace.
  322. '(\d+\.?\d*)' . // Capture digits, optionally capture a `.` and more digits.
  323. '\%/'; // Match the percent sign & end regex.
  324. // @codingStandardsIgnoreEnd
  325. if ( preg_match( $pattern, $cost_string, $this_cost_matches ) ) {
  326. $cost_operator = $this_cost_matches[2];
  327. $cost_percent = $this_cost_matches[3] / 100;
  328. $cost = $this_cost_matches[1];
  329. }
  330. switch ( $type ) {
  331. case 'class':
  332. $cost = $cost * count( $this->find_shipping_classes( $package ) );
  333. break;
  334. case 'item':
  335. $cost = $cost * $this->get_package_item_qty( $package );
  336. break;
  337. }
  338. if ( $cost_percent ) {
  339. switch ( $type ) {
  340. case 'class':
  341. $shipping_classes = $this->find_shipping_classes( $package );
  342. foreach ( $shipping_classes as $shipping_class => $items ) {
  343. foreach ( $items as $item_id => $values ) {
  344. $cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $values['line_total'] );
  345. }
  346. }
  347. break;
  348. case 'item':
  349. foreach ( $package['contents'] as $item_id => $values ) {
  350. if ( $values['data']->needs_shipping() ) {
  351. $cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $values['line_total'] );
  352. }
  353. }
  354. break;
  355. case 'order':
  356. $cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $package['contents_cost'] );
  357. break;
  358. }
  359. }
  360. return $cost;
  361. }
  362. }