Sometimes code needs to change; without doing so you can end up with a non-consistent, bloated mess. When changing things such as functions and hooks however, you do have to consider backwards compatibility so that code which relies on the old things doesn’t just stop breaking without explanation.
In WooCommerce major releases we often have to deal with this problem – in this post I’ll explain how to deprecate code, and how we dealt with it whilst developing 2.1.
How to deprecate code with a warning
There are 3 deprecated functions in WordPress core for telling the user something is being done wrong (when WP_DEBUG is enabled). These are _deprecated_function
, _deprecated_argument
and _deprecated_file
.
You can pass each the name of the function/arg/file and the version in which it was deprecated. With files and functions you can also pass in the replacement (if it exists) and this will also inform the user. Args and files can also be passed some additional explanation via a message (I’ve been pushing for this to be added to deprecated_function for 10 months too, but I’m not making much headway -.-).
[php]_deprecated_function( $function, $version, $replacement );[/php]
[php]_deprecated_argument( $function, $version, $message );[/php]
[php]_deprecated_file( $file, $version, $replacement = null, $message = ” );[/php]
Here is an example in context from WooCommerce:
[php]_deprecated_argument( ‘WC_Checkout::process_checkout()’, ‘2.1’, ‘The “shiptobilling” field is deprecated. The template files are out of date’ );[/php]
If triggered this will output:
NOTICE: WC_Checkout::process_checkout() was called with an argument that is <strong>deprecated</strong> since version 2.1! The “shiptobilling” field is deprecated. The template files are out of date
Deprecating WC functions
To keep things tidy, we moved most deprecated functions to wc-deprecated-functions.php
where we declared the old function with a notice and a call to the new function.
Here is an example from that file:
[php]function woocommerce_readfile_chunked( $file, $retbytes = true ) {
_deprecated_function( ‘woocommerce_readfile_chunked’, ‘2.1’, ‘WC_Download_Handler::readfile_chunked()’ );
return WC_Download_Handler::readfile_chunked( $file, $retbytes );
}[/php]
As you can see, this calls the new method, meaning old code won’t break (for now). If the user has WP_DEBUG
on, they will be notified.
Since they are also in a single file, when we do remove them (most likely after major 2 point releases) they are easy to locate.
Deprecating WC methods
Methods need to be kept in the same class and cannot be moved. The setup is similar to the functions though. Here is an example from the WooCommerce main class:
[php]public function force_ssl( $content ) {
_deprecated_function( ‘Woocommerce->force_ssl’, ‘2.1’, ‘WC_HTTPS::force_https_url’ );
return WC_HTTPS::force_https_url( $content );
}[/php]
Depreciating WC filter hooks
When filter names change, there isn’t a built in way to handle this. In 2.1, I built a simple filter mapper, to map the old filter’s hooked in functions to the new filter. This prevents hooked in code from breaking.
Here is a sample:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Handle renamed filters | |
*/ | |
global $wc_map_deprecated_filters; | |
$wc_map_deprecated_filters = array( | |
'woocommerce_cart_item_class' => 'woocommerce_cart_table_item_class' | |
); | |
foreach ( $wc_map_deprecated_filters as $new => $old ) | |
add_filter( $new, 'woocommerce_deprecated_filter_mapping' ); | |
function woocommerce_deprecated_filter_mapping( $data, $arg_1 = '', $arg_2 = '', $arg_3 = '' ) { | |
global $wc_map_deprecated_filters; | |
$filter = current_filter(); | |
if ( isset( $wc_map_deprecated_filters[ $filter ] ) ) | |
if ( has_filter( $wc_map_deprecated_filters[ $filter ] ) ) { | |
$data = apply_filters( $wc_map_deprecated_filters[ $filter ], $data, $arg_1, $arg_2, $arg_3 ); | |
_deprecated_function( 'The ' . $wc_map_deprecated_filters[ $filter ] . ' filter', '2.1', $filter ); | |
} | |
return $data; | |
} |
In the above example, we’re mapping woocommerce_cart_table_item_class
to the new filter called woocommerce_cart_item_class
. To map more, they are just defined as key/value pairs in the $wc_map_deprecated_filters
array. This appears to work well.
Soft deprecation
Some functions were just renamed for consistency in 2.1, losing the woocommerce_
prefix for a shorter wc_
. To avoid making too much work for devs we decided to “soft deprecate” them. That is, define them as an alias for now, so going forward the new function names are used, and then in a few versions deprecate them. It would be better (for us) to remove them all now, but to ease the transition we thought it best to keep the old ones there without notices.
All in the name of progress
Changes to functions can be frustrating for developers, but in order to progress they are a necessary evil. Using deprecated functions at least keep code functioning giving developers a chance to update their code, and shouldn’t affect live sites anyway (because live stores should never have debugging visible!).
Leave a Reply