2012-08-12 14:12:52 +00:00
< ? php
2011-08-10 17:11:11 +00:00
/**
2018-03-21 23:54:10 +00:00
* WooCommerce Shipping
2012-08-12 14:12:52 +00:00
*
2011-08-10 17:11:11 +00:00
* Handles shipping and loads shipping methods via hooks .
*
2018-03-21 23:54:10 +00:00
* @ version 2.6 . 0
* @ package WooCommerce / Classes / Shipping
2012-08-12 14:12:52 +00:00
*/
2012-10-15 10:57:58 +00:00
2014-09-20 19:01:02 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
2015-12-15 15:00:45 +00:00
exit ;
2014-09-20 19:01:02 +00:00
}
2012-10-15 10:57:58 +00:00
2015-12-15 15:00:45 +00:00
/**
2018-03-21 23:54:10 +00:00
* Shipping class .
2015-12-15 15:00:45 +00:00
*/
2012-01-27 16:38:39 +00:00
class WC_Shipping {
2012-08-12 14:12:52 +00:00
2018-03-14 20:42:52 +00:00
/**
* True if shipping is enabled .
*
* @ var bool
*/
public $enabled = false ;
2012-08-14 22:43:48 +00:00
2018-03-14 20:42:52 +00:00
/**
* Stores methods loaded into woocommerce .
*
* @ var array | null
*/
public $shipping_methods = null ;
2012-08-14 22:43:48 +00:00
2018-03-14 20:42:52 +00:00
/**
* Stores the shipping classes .
*
* @ var array
*/
public $shipping_classes = array ();
2012-08-14 22:43:48 +00:00
2018-03-14 20:42:52 +00:00
/**
* Stores packages to ship and to get quotes for .
*
* @ var array
*/
public $packages = array ();
2011-12-19 14:05:32 +00:00
2013-09-12 13:41:02 +00:00
/**
2018-03-14 20:42:52 +00:00
* The single instance of the class
*
* @ var WC_Shipping
2013-09-12 13:41:02 +00:00
* @ since 2.1
*/
protected static $_instance = null ;
/**
2015-11-03 13:31:20 +00:00
* Main WC_Shipping Instance .
2013-09-12 13:41:02 +00:00
*
2014-06-19 19:43:05 +00:00
* Ensures only one instance of WC_Shipping is loaded or can be loaded .
2013-09-12 13:41:02 +00:00
*
* @ since 2.1
2014-06-19 19:43:05 +00:00
* @ return WC_Shipping Main instance
2013-09-12 13:41:02 +00:00
*/
public static function instance () {
2015-12-11 14:11:12 +00:00
if ( is_null ( self :: $_instance ) ) {
2013-09-12 13:41:02 +00:00
self :: $_instance = new self ();
2015-12-11 14:11:12 +00:00
}
2013-09-12 13:41:02 +00:00
return self :: $_instance ;
}
/**
* Cloning is forbidden .
*
* @ since 2.1
*/
public function __clone () {
2018-02-07 22:01:12 +00:00
wc_doing_it_wrong ( __FUNCTION__ , __ ( 'Cloning is forbidden.' , 'woocommerce' ), '2.1' );
2013-09-12 13:41:02 +00:00
}
/**
* Unserializing instances of this class is forbidden .
*
* @ since 2.1
*/
public function __wakeup () {
2018-02-07 22:01:12 +00:00
wc_doing_it_wrong ( __FUNCTION__ , __ ( 'Unserializing instances of this class is forbidden.' , 'woocommerce' ), '2.1' );
2013-09-12 13:41:02 +00:00
}
2017-08-18 11:51:45 +00:00
/**
* Magic getter .
*
* @ param string $name Property name .
* @ return mixed
*/
public function __get ( $name ) {
// Grab from cart for backwards compatibility with versions prior to 3.2.
2018-03-14 20:42:52 +00:00
if ( 'shipping_total' === $name ) {
2017-08-18 11:51:45 +00:00
return wc () -> cart -> get_shipping_total ();
}
2018-03-14 20:42:52 +00:00
if ( 'shipping_taxes' === $name ) {
2017-08-18 11:51:45 +00:00
return wc () -> cart -> get_shipping_taxes ();
}
}
2013-01-07 17:23:09 +00:00
/**
2015-07-16 19:55:48 +00:00
* Initialize shipping .
2013-01-07 17:23:09 +00:00
*/
public function __construct () {
2016-01-05 11:23:15 +00:00
$this -> enabled = wc_shipping_enabled ();
2015-12-15 15:00:45 +00:00
if ( $this -> enabled ) {
$this -> init ();
}
2013-01-07 17:23:09 +00:00
}
2016-07-11 14:56:35 +00:00
/**
* Initialize shipping .
*/
public function init () {
2012-05-29 14:02:18 +00:00
do_action ( 'woocommerce_shipping_init' );
}
2012-08-12 14:12:52 +00:00
2015-12-11 14:11:12 +00:00
/**
* Shipping methods register themselves by returning their main class name through the woocommerce_shipping_methods filter .
2018-03-14 20:42:52 +00:00
*
2015-12-11 14:11:12 +00:00
* @ return array
*/
public function get_shipping_method_class_names () {
2018-03-14 20:42:52 +00:00
// Unique Method ID => Method Class name.
2016-02-10 21:22:44 +00:00
$shipping_methods = array (
2016-02-10 13:21:16 +00:00
'flat_rate' => 'WC_Shipping_Flat_Rate' ,
'free_shipping' => 'WC_Shipping_Free_Shipping' ,
'local_pickup' => 'WC_Shipping_Local_Pickup' ,
2016-02-10 21:22:44 +00:00
);
2015-12-18 14:24:34 +00:00
2018-03-14 20:42:52 +00:00
// For backwards compatibility with 2.5.x we load any ENABLED legacy shipping methods here.
2015-12-18 14:24:34 +00:00
$maybe_load_legacy_methods = array ( 'flat_rate' , 'free_shipping' , 'international_delivery' , 'local_delivery' , 'local_pickup' );
foreach ( $maybe_load_legacy_methods as $method ) {
$options = get_option ( 'woocommerce_' . $method . '_settings' );
if ( $options && isset ( $options [ 'enabled' ] ) && 'yes' === $options [ 'enabled' ] ) {
$shipping_methods [ 'legacy_' . $method ] = 'WC_Shipping_Legacy_' . $method ;
}
}
2016-02-10 21:25:05 +00:00
return apply_filters ( 'woocommerce_shipping_methods' , $shipping_methods );
2015-12-11 14:11:12 +00:00
}
2012-05-29 14:02:18 +00:00
/**
2015-12-15 15:39:00 +00:00
* Loads all shipping methods which are hooked in .
* If a $package is passed some methods may add themselves conditionally and zones will be used .
2012-08-12 14:12:52 +00:00
*
2018-03-14 20:42:52 +00:00
* @ param array $package Package information .
2012-05-29 14:02:18 +00:00
* @ return array
*/
2014-02-12 11:54:43 +00:00
public function load_shipping_methods ( $package = array () ) {
2016-06-06 18:39:23 +00:00
if ( ! empty ( $package ) ) {
2016-08-24 15:55:34 +00:00
$debug_mode = 'yes' === get_option ( 'woocommerce_shipping_debug_mode' , 'no' );
2015-12-15 15:00:45 +00:00
$shipping_zone = WC_Shipping_Zones :: get_zone_matching_package ( $package );
2016-03-15 17:23:06 +00:00
$this -> shipping_methods = $shipping_zone -> get_shipping_methods ( true );
2016-06-16 10:28:53 +00:00
2018-03-14 20:42:52 +00:00
// Debug output.
2017-08-16 10:34:35 +00:00
if ( $debug_mode && ! defined ( 'WOOCOMMERCE_CHECKOUT' ) && ! defined ( 'WC_DOING_AJAX' ) && ! wc_has_notice ( 'Customer matched zone "' . $shipping_zone -> get_zone_name () . '"' ) ) {
2016-06-16 10:28:53 +00:00
wc_add_notice ( 'Customer matched zone "' . $shipping_zone -> get_zone_name () . '"' );
}
2015-12-15 15:00:45 +00:00
} else {
$this -> shipping_methods = array ();
}
2012-08-12 14:12:52 +00:00
2015-12-15 15:39:00 +00:00
// For the settings in the backend, and for non-shipping zone methods, we still need to load any registered classes here.
2015-12-15 15:00:45 +00:00
foreach ( $this -> get_shipping_method_class_names () as $method_id => $method_class ) {
$this -> register_shipping_method ( $method_class );
2015-04-13 14:32:54 +00:00
}
2012-08-12 14:12:52 +00:00
2015-12-15 15:39:00 +00:00
// Methods can register themselves manually through this hook if necessary.
2015-12-15 15:00:45 +00:00
do_action ( 'woocommerce_load_shipping_methods' , $package );
2012-08-12 14:12:52 +00:00
2018-03-14 20:42:52 +00:00
// Return loaded methods.
2015-12-15 15:00:45 +00:00
return $this -> get_shipping_methods ();
2012-05-29 14:02:18 +00:00
}
2012-08-12 14:12:52 +00:00
2012-05-29 14:02:18 +00:00
/**
2015-12-15 15:00:45 +00:00
* Register a shipping method .
2012-08-12 14:12:52 +00:00
*
2015-12-15 15:00:45 +00:00
* @ param object | string $method Either the name of the method 's class, or an instance of the method' s class .
2017-05-12 08:48:46 +00:00
*
* @ return bool | void
2012-05-29 14:02:18 +00:00
*/
2013-11-28 15:20:02 +00:00
public function register_shipping_method ( $method ) {
2015-04-13 14:32:54 +00:00
if ( ! is_object ( $method ) ) {
2015-12-16 16:24:58 +00:00
if ( ! class_exists ( $method ) ) {
return false ;
}
2012-06-03 12:13:52 +00:00
$method = new $method ();
2015-04-13 14:32:54 +00:00
}
2016-06-07 13:24:10 +00:00
if ( is_null ( $this -> shipping_methods ) ) {
$this -> shipping_methods = array ();
}
2015-12-15 15:00:45 +00:00
$this -> shipping_methods [ $method -> id ] = $method ;
2012-05-29 14:02:18 +00:00
}
2012-08-12 14:12:52 +00:00
2012-05-29 14:02:18 +00:00
/**
2015-07-16 19:55:48 +00:00
* Unregister shipping methods .
2012-05-29 14:02:18 +00:00
*/
2013-11-28 15:20:02 +00:00
public function unregister_shipping_methods () {
2016-05-26 12:03:40 +00:00
$this -> shipping_methods = null ;
2012-05-29 14:02:18 +00:00
}
/**
* Returns all registered shipping methods for usage .
*
2013-11-29 18:50:31 +00:00
* @ return array
2012-05-29 14:02:18 +00:00
*/
2013-11-28 15:20:02 +00:00
public function get_shipping_methods () {
2015-12-11 14:11:12 +00:00
if ( is_null ( $this -> shipping_methods ) ) {
$this -> load_shipping_methods ();
}
2012-05-29 14:02:18 +00:00
return $this -> shipping_methods ;
2012-08-12 14:12:52 +00:00
}
2012-04-13 11:16:24 +00:00
/**
2015-12-15 15:39:00 +00:00
* Get an array of shipping classes .
2012-04-13 11:16:24 +00:00
*
* @ return array
*/
2013-11-28 15:20:02 +00:00
public function get_shipping_classes () {
2015-04-13 14:32:54 +00:00
if ( empty ( $this -> shipping_classes ) ) {
2018-03-14 20:42:52 +00:00
$classes = get_terms (
'product_shipping_class' , array (
'hide_empty' => '0' ,
'orderby' => 'name' ,
)
);
2015-12-15 15:39:00 +00:00
$this -> shipping_classes = ! is_wp_error ( $classes ) ? $classes : array ();
2015-04-13 14:32:54 +00:00
}
2016-05-19 11:38:28 +00:00
return apply_filters ( 'woocommerce_get_shipping_classes' , $this -> shipping_classes );
2012-08-12 14:12:52 +00:00
}
2012-04-13 11:16:24 +00:00
/**
2012-08-12 14:12:52 +00:00
* Calculate shipping for ( multiple ) packages of cart items .
*
2017-07-25 14:11:32 +00:00
* @ param array $packages multi - dimensional array of cart items to calc shipping for .
* @ return array Array of calculated packages .
2012-04-13 11:16:24 +00:00
*/
2013-11-28 15:20:02 +00:00
public function calculate_shipping ( $packages = array () ) {
2017-07-25 14:11:32 +00:00
$this -> packages = array ();
2012-08-12 14:12:52 +00:00
2015-10-21 20:35:58 +00:00
if ( ! $this -> enabled || empty ( $packages ) ) {
2017-07-25 14:11:32 +00:00
return array ();
2015-10-21 20:35:58 +00:00
}
2017-07-25 14:11:32 +00:00
// Calculate costs for passed packages.
2015-12-15 16:22:32 +00:00
foreach ( $packages as $package_key => $package ) {
2016-05-06 16:00:01 +00:00
$this -> packages [ $package_key ] = $this -> calculate_shipping_for_package ( $package , $package_key );
2016-03-04 01:26:59 +00:00
}
2016-03-15 20:35:39 +00:00
/**
2017-11-02 16:16:17 +00:00
* Allow packages to be reorganized after calculating the shipping .
2016-03-15 20:35:39 +00:00
*
* This filter can be used to apply some extra manipulation after the shipping costs are calculated for the packages
* but before Woocommerce does anything with them . A good example of usage is to merge the shipping methods for multiple
* packages for marketplaces .
*
2016-03-15 23:26:46 +00:00
* @ since 2.6 . 0
2016-03-15 20:35:39 +00:00
*
* @ param array $packages The array of packages after shipping costs are calculated .
*/
2017-07-25 14:11:32 +00:00
$this -> packages = array_filter ( ( array ) apply_filters ( 'woocommerce_shipping_packages' , $this -> packages ) );
2012-08-12 14:12:52 +00:00
2017-07-25 14:11:32 +00:00
return $this -> packages ;
2011-12-02 18:54:52 +00:00
}
2011-09-06 11:11:22 +00:00
2016-08-10 11:04:52 +00:00
/**
* See if package is shippable .
2017-12-05 15:00:07 +00:00
*
* Packages are shippable until proven otherwise e . g . after getting a shipping country .
*
* @ param array $package Package of cart items .
* @ return bool
2016-08-10 11:04:52 +00:00
*/
protected function is_package_shippable ( $package ) {
2017-11-02 16:24:09 +00:00
// Packages are shippable until proven otherwise.
if ( empty ( $package [ 'destination' ][ 'country' ] ) ) {
return true ;
}
2016-08-10 11:04:52 +00:00
$allowed = array_keys ( WC () -> countries -> get_shipping_countries () );
2018-03-14 20:42:52 +00:00
return in_array ( $package [ 'destination' ][ 'country' ], $allowed , true );
2016-08-10 11:04:52 +00:00
}
2012-04-13 11:16:24 +00:00
/**
2015-11-13 20:53:44 +00:00
* Calculate shipping rates for a package ,
2012-04-13 11:16:24 +00:00
*
2015-11-13 20:53:44 +00:00
* Calculates each shipping methods cost . Rates are stored in the session based on the package hash to avoid re - calculation every page load .
2012-08-12 14:12:52 +00:00
*
2017-12-05 15:00:07 +00:00
* @ param array $package Package of cart items .
2016-05-06 16:00:01 +00:00
* @ param int $package_key Index of the package being calculated . Used to cache multiple package rates .
2017-05-15 11:50:52 +00:00
*
* @ return array | bool
2012-04-13 11:16:24 +00:00
*/
2016-05-06 16:00:01 +00:00
public function calculate_shipping_for_package ( $package = array (), $package_key = 0 ) {
2017-12-05 15:00:07 +00:00
// If shipping is disabled or the package is invalid, return false.
if ( ! $this -> enabled || empty ( $package ) ) {
2015-11-13 20:53:44 +00:00
return false ;
}
2012-08-12 14:12:52 +00:00
2017-12-05 15:00:07 +00:00
$package [ 'rates' ] = array ();
2016-08-02 16:10:26 +00:00
2017-12-05 15:00:07 +00:00
// If the package is not shippable, e.g. trying to ship to an invalid country, do not calculate rates.
if ( $this -> is_package_shippable ( $package ) ) {
// Check if we need to recalculate shipping for this package.
$package_to_hash = $package ;
2016-08-02 16:10:26 +00:00
2017-12-05 15:00:07 +00:00
// Remove data objects so hashes are consistent.
foreach ( $package_to_hash [ 'contents' ] as $item_id => $item ) {
unset ( $package_to_hash [ 'contents' ][ $item_id ][ 'data' ] );
}
2012-08-12 14:12:52 +00:00
2017-12-05 15:00:07 +00:00
$package_hash = 'wc_ship_' . md5 ( wp_json_encode ( $package_to_hash ) . WC_Cache_Helper :: get_transient_version ( 'shipping' ) );
$session_key = 'shipping_for_package_' . $package_key ;
$stored_rates = WC () -> session -> get ( $session_key );
2012-08-12 14:12:52 +00:00
2017-12-05 15:00:07 +00:00
if ( ! is_array ( $stored_rates ) || $package_hash !== $stored_rates [ 'package_hash' ] || 'yes' === get_option ( 'woocommerce_shipping_debug_mode' , 'no' ) ) {
foreach ( $this -> load_shipping_methods ( $package ) as $shipping_method ) {
if ( ! $shipping_method -> supports ( 'shipping-zones' ) || $shipping_method -> get_instance_id () ) {
$package [ 'rates' ] = $package [ 'rates' ] + $shipping_method -> get_rates_for_package ( $package ); // + instead of array_merge maintains numeric keys
}
2012-04-13 11:16:24 +00:00
}
2012-11-27 16:22:47 +00:00
2017-12-05 15:00:07 +00:00
// Filter the calculated rates.
$package [ 'rates' ] = apply_filters ( 'woocommerce_package_rates' , $package [ 'rates' ], $package );
2012-08-12 14:12:52 +00:00
2017-12-05 15:00:07 +00:00
// Store in session to avoid recalculation.
2018-03-14 20:42:52 +00:00
WC () -> session -> set (
$session_key , array (
'package_hash' => $package_hash ,
'rates' => $package [ 'rates' ],
)
);
2017-12-05 15:00:07 +00:00
} else {
$package [ 'rates' ] = $stored_rates [ 'rates' ];
}
2012-04-13 11:16:24 +00:00
}
return $package ;
2011-08-09 15:16:18 +00:00
}
2012-08-12 14:12:52 +00:00
2012-04-13 11:16:24 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get packages .
2018-03-14 20:42:52 +00:00
*
2012-04-13 11:16:24 +00:00
* @ return array
*/
2015-11-13 20:53:44 +00:00
public function get_packages () {
2013-08-14 20:00:34 +00:00
return $this -> packages ;
}
2012-04-13 11:16:24 +00:00
/**
2015-07-16 19:55:48 +00:00
* Reset shipping .
2012-08-12 14:12:52 +00:00
*
2012-04-13 11:16:24 +00:00
* Reset the totals for shipping as a whole .
*/
2013-11-28 15:20:02 +00:00
public function reset_shipping () {
2013-11-25 14:01:32 +00:00
unset ( WC () -> session -> chosen_shipping_methods );
2012-04-13 11:16:24 +00:00
$this -> packages = array ();
2011-08-09 15:16:18 +00:00
}
2012-08-12 14:12:52 +00:00
2015-12-15 15:00:45 +00:00
/**
2018-03-14 20:42:52 +00:00
* Deprecated
*
2015-12-15 15:00:45 +00:00
* @ deprecated 2.6 . 0 Was previously used to determine sort order of methods , but this is now controlled by zones and thus unused .
*/
public function sort_shipping_methods () {
2016-11-23 16:15:00 +00:00
wc_deprecated_function ( 'sort_shipping_methods' , '2.6' );
2015-12-15 15:00:45 +00:00
return $this -> shipping_methods ;
}
2014-09-20 19:01:02 +00:00
}