Wrappers

In WordPress development, it’s often necessary to extend or build on the functionalities of an existing plugin, for instance, WooCommerce.

So in your theme or custom plugin, you use functions provided by WooCommerce, that not only makes sense, it’s the only way to avoid having to write these functions your self.

For instance, to check if a product exists in WooCommerce you could use:
wc_get_product()->exists()
Which returns ‘true’ if the product exists.

Issues with direct usage of WooCommerce functions

Unit testing

In order to unit test your plugin you would have to mock the wc_get_product() function as well as mock the object it returns and provide it with a method exists().

WooCommerce updates

At some point, WooCommerce might get updated in such a way that the functions you use in your custom plugin now have a different signature.
That would break your plugin and you will have to update every class or file where you have a called a WooCommerce function.

Code duplication

Since you are coding against an existing code base, you would have to do at least some defensive programming because your code cannot assume every returned value is correct in the new context it creates.

This can become tedious very quickly because you might have to repeat code over and over again to ensure the code works in all contexts.

The “lazy” Solution

A possible solution might be to write a wrapper function, basically, a function that uses the WooCommerce function you need and which has error handling built-in.

function my_prefix_get_product() {
    if( wc_get_product()->exists() ) {
        return wc_get_product();
    } else {
        return false;
    }
}

This can be useful because wc_get_product() can return 3 types:
boolean ( false )
WC_Product
null
And in our use case, we only cared about WC_Product as a return type.

The downside to this, however, is that the code of the custom plugin is still coupled to the WooCommerce code base.

The best solution

The easiest way to decouple your code from the WooCommerce code base is to create an extra layer in between them, using dependency injection.

Dependency injection is a fancy way to describe a simple concept.
Instead of using the functions or classes directly in your own class, you pass them as an argument to a method of your class.

For our WooCommerce example, we create a class with a method get_product() that provides access to the wc_get_product() function of WC.

class Wc_Functions_Wrapper {
    public function get_product() {
        if( wc_get_product()->exists() ) {
            return wc_get_product();
        } else {
            return false;
        }
    }

    public function get_product_id() {
        if( $this->get_product() ) {
            return $this->get_product()->get_id();
        } else {
            return false;
        }
    }
}

Product_Customizer is the class that is dependant on WooCommerce functions, notice how the constructor takes an instance of Wc_Functions_Wrapper as an argument.
This will make all the methods of Wc_Functions_Wrapper available in our class Product_Customizer.

class Product_Customizer {
    private $wc_functions;

    public function __construct( Wc_Functions_Wrapper $wrapper ) {
        $this->wc_functions = $wrapper;
    }

    public function add_custom_product_meta() {
        $product_id = $this->wc_functions->get_product_id();
        update_meta( $product_id, 'meta_key', 'meta_value');
    }
}

A simple example to use our Product_Customizer class:

$wrapper = new Wc_Functions_Wrapper();
$product_customizer = new Product_Customizer( $wrapper );
$product_customizer->add_custom_product_meta()

Using this concept of wrappers and dependency injection our business logic code is decoupled from the WooCommerce code/functions.