First steps towards introducing a dependency injection framework.
- Add PHP League's Container package via Composer. - Add an ObjectContainer class that encapsulates all the configuration and insulates the codebase from the concrete DI engine used. - Add an improved ReflectionContainer class that will allow to register individual classes as singletons while autowiring. - Use ObjectContainer to resolve the WooCommerce class, everything instantiated with "new" inside it, and all singletons that are usually obtained via WC() function. - Introduce the CustomerProvider class. - Introduce a service provider to resolve WC_Queue_Interface, this replaces the WC_Queue class. - Mark as obsolete all the replaced "instance()" methods, and the entire WC_Queue class.
This commit is contained in:
parent
86da271e6e
commit
312383ae47
|
@ -11,6 +11,7 @@
|
|||
"automattic/jetpack-autoloader": "^1.7.0",
|
||||
"automattic/jetpack-constants": "^1.1",
|
||||
"composer/installers": "1.7.0",
|
||||
"league/container": "^3.3",
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"pelago/emogrifier": "^3.1",
|
||||
"woocommerce/action-scheduler": "3.1.6",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "313a6737a05bfc27777a05bc22bb7bbe",
|
||||
"content-hash": "bf5045f466411cb446cbe44074fd7b0f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -195,6 +195,78 @@
|
|||
],
|
||||
"time": "2019-08-12T15:00:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "3.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/container.git",
|
||||
"reference": "93238f74ff5964aee27a78508cdfbdba1cd338f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/container/zipball/93238f74ff5964aee27a78508cdfbdba1cd338f6",
|
||||
"reference": "93238f74ff5964aee27a78508cdfbdba1cd338f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"psr/container": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0",
|
||||
"squizlabs/php_codesniffer": "^3.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-1.x": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "philipobenito@gmail.com",
|
||||
"homepage": "http://www.philipobenito.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency",
|
||||
"di",
|
||||
"injection",
|
||||
"league",
|
||||
"provider",
|
||||
"service"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/philipobenito",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-05-18T08:20:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maxmind-db/reader",
|
||||
"version": "v1.6.0",
|
||||
|
@ -329,6 +401,55 @@
|
|||
],
|
||||
"time": "2019-12-26T19:37:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Container\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||
"homepage": "https://github.com/php-fig/container",
|
||||
"keywords": [
|
||||
"PSR-11",
|
||||
"container",
|
||||
"container-interface",
|
||||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"time": "2017-02-14T16:28:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.4.42",
|
||||
|
@ -2521,7 +2642,7 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2020-07-14T12:35:20+00:00"
|
||||
"time": "2020-05-08T16:50:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* @version 3.4.0
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
|
@ -19,6 +21,8 @@ class WC_Checkout {
|
|||
* The single instance of the class.
|
||||
*
|
||||
* @var WC_Checkout|null
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
|
@ -49,19 +53,23 @@ class WC_Checkout {
|
|||
* @since 2.1
|
||||
* @static
|
||||
* @return WC_Checkout Main instance
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
return self::$instance = ObjectContainer::get_instance_of( __CLASS__ );
|
||||
}
|
||||
|
||||
// Hook in actions once.
|
||||
add_action( 'woocommerce_checkout_billing', array( self::$instance, 'checkout_form_billing' ) );
|
||||
add_action( 'woocommerce_checkout_shipping', array( self::$instance, 'checkout_form_shipping' ) );
|
||||
/**
|
||||
* Class constructor, hooks the appropriate actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
// Hook in actions once.
|
||||
add_action( 'woocommerce_checkout_billing', array( $this, 'checkout_form_billing' ) );
|
||||
add_action( 'woocommerce_checkout_shipping', array( $this, 'checkout_form_shipping' ) );
|
||||
|
||||
// woocommerce_checkout_init action is ran once when the class is first constructed.
|
||||
do_action( 'woocommerce_checkout_init', self::$instance );
|
||||
}
|
||||
return self::$instance;
|
||||
// woocommerce_checkout_init action is ran once when the class is first constructed.
|
||||
do_action( 'woocommerce_checkout_init', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
|
@ -28,6 +29,8 @@ class WC_Emails {
|
|||
* The single instance of the class
|
||||
*
|
||||
* @var WC_Emails
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
|
@ -46,12 +49,11 @@ class WC_Emails {
|
|||
* @since 2.1
|
||||
* @static
|
||||
* @return WC_Emails Main instance
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
return self::$_instance = ObjectContainer::get_instance_of( __CLASS__ );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* @package WooCommerce/Classes/Payment
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
|
@ -27,6 +29,8 @@ class WC_Payment_Gateways {
|
|||
*
|
||||
* @var WC_Payment_Gateways
|
||||
* @since 2.1.0
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
|
@ -37,12 +41,11 @@ class WC_Payment_Gateways {
|
|||
*
|
||||
* @since 2.1
|
||||
* @return WC_Payment_Gateways Main instance
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
return self::$_instance = ObjectContainer::get_instance_of( __CLASS__ );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
|
@ -52,6 +53,8 @@ class WC_Shipping {
|
|||
*
|
||||
* @var WC_Shipping
|
||||
* @since 2.1
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
|
@ -62,12 +65,11 @@ class WC_Shipping {
|
|||
*
|
||||
* @since 2.1
|
||||
* @return WC_Shipping Main instance
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
return self::$_instance = ObjectContainer::get_instance_of( __CLASS__ );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
use Automattic\WooCommerce\Providers\CustomerProvider;
|
||||
|
||||
/**
|
||||
* Main WooCommerce Class.
|
||||
*
|
||||
|
@ -36,6 +39,8 @@ final class WooCommerce {
|
|||
*
|
||||
* @var WooCommerce
|
||||
* @since 2.1
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
|
@ -109,6 +114,20 @@ final class WooCommerce {
|
|||
*/
|
||||
public $deprecated_hook_handlers = array();
|
||||
|
||||
/**
|
||||
* The container used for dependency injection.
|
||||
*
|
||||
* @var ObjectContainer
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* The instance of CustomerProvider to use.
|
||||
*
|
||||
* @var CustomerProvider
|
||||
*/
|
||||
private $customer_provider;
|
||||
|
||||
/**
|
||||
* Main WooCommerce Instance.
|
||||
*
|
||||
|
@ -118,12 +137,11 @@ final class WooCommerce {
|
|||
* @static
|
||||
* @see WC()
|
||||
* @return WooCommerce - Main instance.
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead, see the ObjectContainer class.
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( is_null( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
return self::$_instance = ObjectContainer::get_instance_of( __CLASS__ );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +177,10 @@ final class WooCommerce {
|
|||
/**
|
||||
* WooCommerce Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
public function __construct( ObjectContainer $container, CustomerProvider $customer_provider ) {
|
||||
$this->container = $container;
|
||||
$this->customer_provider = $customer_provider;
|
||||
|
||||
$this->define_constants();
|
||||
$this->define_tables();
|
||||
$this->includes();
|
||||
|
@ -477,8 +498,8 @@ final class WooCommerce {
|
|||
}
|
||||
|
||||
$this->theme_support_includes();
|
||||
$this->query = new WC_Query();
|
||||
$this->api = new WC_API();
|
||||
$this->query = $this->container->get( WC_Query::class );
|
||||
$this->api = $this->container->get( WC_API::class );
|
||||
$this->api->init();
|
||||
}
|
||||
|
||||
|
@ -560,13 +581,13 @@ final class WooCommerce {
|
|||
$this->load_plugin_textdomain();
|
||||
|
||||
// Load class instances.
|
||||
$this->product_factory = new WC_Product_Factory();
|
||||
$this->order_factory = new WC_Order_Factory();
|
||||
$this->countries = new WC_Countries();
|
||||
$this->integrations = new WC_Integrations();
|
||||
$this->structured_data = new WC_Structured_Data();
|
||||
$this->deprecated_hook_handlers['actions'] = new WC_Deprecated_Action_Hooks();
|
||||
$this->deprecated_hook_handlers['filters'] = new WC_Deprecated_Filter_Hooks();
|
||||
$this->product_factory = $this->container->get( WC_Product_Factory::class );
|
||||
$this->order_factory = $this->container->get( WC_Order_Factory::class );
|
||||
$this->countries = $this->container->get( WC_Countries::class );
|
||||
$this->integrations = $this->container->get( WC_Integrations::class );
|
||||
$this->structured_data = $this->container->get( WC_Structured_Data::class );
|
||||
$this->deprecated_hook_handlers['actions'] = $this->container->get( WC_Deprecated_Action_Hooks::class );
|
||||
$this->deprecated_hook_handlers['filters'] = $this->container->get( WC_Deprecated_Filter_Hooks::class );
|
||||
|
||||
// Classes/actions loaded for the frontend and for ajax requests.
|
||||
if ( $this->is_request( 'frontend' ) ) {
|
||||
|
@ -754,12 +775,12 @@ final class WooCommerce {
|
|||
public function initialize_cart() {
|
||||
// Cart needs customer info.
|
||||
if ( is_null( $this->customer ) || ! $this->customer instanceof WC_Customer ) {
|
||||
$this->customer = new WC_Customer( get_current_user_id(), true );
|
||||
$this->customer = $this->customer_provider->get_logged_in_customer();
|
||||
// Customer should be saved during shutdown.
|
||||
add_action( 'shutdown', array( $this->customer, 'save' ), 10 );
|
||||
}
|
||||
if ( is_null( $this->cart ) || ! $this->cart instanceof WC_Cart ) {
|
||||
$this->cart = new WC_Cart();
|
||||
$this->cart = $this->container->get( WC_Cart::class );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,7 +794,7 @@ final class WooCommerce {
|
|||
// Session class, handles session data for users - can be overwritten if custom handler is needed.
|
||||
$session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' );
|
||||
if ( is_null( $this->session ) || ! $this->session instanceof $session_class ) {
|
||||
$this->session = new $session_class();
|
||||
$this->session = $this->container->get( $session_class );
|
||||
$this->session->init();
|
||||
}
|
||||
}
|
||||
|
@ -815,7 +836,7 @@ final class WooCommerce {
|
|||
* @return WC_Queue_Interface
|
||||
*/
|
||||
public function queue() {
|
||||
return WC_Queue::instance();
|
||||
return $this->container->get( WC_Queue_Interface::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -824,7 +845,7 @@ final class WooCommerce {
|
|||
* @return WC_Checkout
|
||||
*/
|
||||
public function checkout() {
|
||||
return WC_Checkout::instance();
|
||||
return $this->container->get( WC_Checkout::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -833,7 +854,7 @@ final class WooCommerce {
|
|||
* @return WC_Payment_Gateways
|
||||
*/
|
||||
public function payment_gateways() {
|
||||
return WC_Payment_Gateways::instance();
|
||||
return $this->container->get( WC_Payment_Gateways::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -842,7 +863,7 @@ final class WooCommerce {
|
|||
* @return WC_Shipping
|
||||
*/
|
||||
public function shipping() {
|
||||
return WC_Shipping::instance();
|
||||
return $this->container->get( WC_Shipping::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -851,7 +872,7 @@ final class WooCommerce {
|
|||
* @return WC_Emails
|
||||
*/
|
||||
public function mailer() {
|
||||
return WC_Emails::instance();
|
||||
return $this->container->get( WC_Emails::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* @package WooCommerce/Interface
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
@ -16,6 +18,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
* Singleton for managing the WC queue instance.
|
||||
*
|
||||
* @version 3.5.0
|
||||
*
|
||||
* @deprecated 4.3.0 Use dependency injection instead to get an instance of WC_Query_Interface, see the ObjectContainer class.
|
||||
*/
|
||||
class WC_Queue {
|
||||
|
||||
|
@ -39,13 +43,7 @@ class WC_Queue {
|
|||
* @return WC_Queue_Interface
|
||||
*/
|
||||
final public static function instance() {
|
||||
|
||||
if ( is_null( self::$instance ) ) {
|
||||
$class = self::get_class();
|
||||
self::$instance = new $class();
|
||||
self::$instance = self::validate_instance( self::$instance );
|
||||
}
|
||||
return self::$instance;
|
||||
return self::$instance = ObjectContainer::get_instance_of( WC_Queue_Interface::class );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/**
|
||||
* CustomerProvider class file.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Providers
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Providers;
|
||||
|
||||
use \WC_Customer;
|
||||
|
||||
/**
|
||||
* Provides methods to retrieve customer objects.
|
||||
*/
|
||||
class CustomerProvider {
|
||||
|
||||
/**
|
||||
* Get a customer object for the currently logged in user.
|
||||
*
|
||||
* @return WC_Customer Customer object for the currently logged in user.
|
||||
*/
|
||||
public function get_logged_in_customer() {
|
||||
return new WC_Customer( get_current_user_id(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a customer object by id.
|
||||
*
|
||||
* @param int $id The id of the customer to retrieve.
|
||||
*
|
||||
* @return WC_Customer Customer object for the specified id.
|
||||
*/
|
||||
public function get_customer_by_id( int $id ) {
|
||||
return new WC_Customer( $id );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
/**
|
||||
* ObjectContainer class file.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Tools\DependencyManagement
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Tools\DependencyManagement;
|
||||
|
||||
use \League\Container\Container;
|
||||
use \Automattic\WooCommerce\Tools\DependencyManagement\ReflectionContainer as WooReflectionContainer;
|
||||
|
||||
/**
|
||||
* Dependency injection container for WooCommerce.
|
||||
*
|
||||
* This class uses PHP League's `Container` under the hood. A separate container class is used to
|
||||
* insulate the codebase from the exact DI engine used, and to encapsulate all the class and service provider registrations.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* - To register new class resolutions:
|
||||
* - Do it in the `register_classes` method, using the `container` and `reflection_container` class properties.
|
||||
* In particular, use `reflection_container->share` to register autowiring singletons.
|
||||
* No extra action is needed to use non-singleton autowiring.
|
||||
* - Or alternatively, create a new service provider inside the `ServiceProviders` folder and it will be
|
||||
* automatically registered. Service providers are recommended when instantiating the class is a complex
|
||||
* or slow process.
|
||||
*
|
||||
* - To resolve classes to instances:
|
||||
* - Ideally you just add your dependency as a typed argument in the class constructor, and it gets resolved
|
||||
* automatically (via autowiring) when the class is instantiated via the container. This is the preferred approach.
|
||||
* - If the above is not possible, you can add an argument of type `ObjectContainer` to the class constructor,
|
||||
* and then use the `get` method of the supplied instance whenever needed.
|
||||
* - For code outside classes (inside standalone functions) or in static class methods, use
|
||||
* `ObjectContainer::get_instance_of`.
|
||||
*
|
||||
* Note: autowiring means the ability to automatically resolve a class name to an instance of that class via
|
||||
* `get(TheClass::class)` or by adding a typed argument to class constructors.
|
||||
*/
|
||||
final class ObjectContainer {
|
||||
|
||||
/**
|
||||
* The singleton instance of ObjectContainer.
|
||||
*
|
||||
* @var ObjectContainer
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* The underlying dependency injection engine used.
|
||||
*
|
||||
* @var Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* The underlying reflection delegate used for autowiring.
|
||||
*
|
||||
* @var ReflectionContainer
|
||||
*/
|
||||
private $reflection_container;
|
||||
|
||||
/**
|
||||
* Get the singleton instance of ObjectContainer.
|
||||
*
|
||||
* @return ObjectContainer
|
||||
*/
|
||||
public static function instance() {
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the dependency injection engine and do all the necessary registrations.
|
||||
*/
|
||||
public static function init() {
|
||||
global $wp_actions;
|
||||
|
||||
if ( ! function_exists( 'did_action' ) || did_action( 'plugins_loaded' ) ) {
|
||||
self::_init();
|
||||
} else {
|
||||
// TODO: This is a hack to overcome the inability to use the autoloader before plugins have been loaded, remove after https://github.com/Automattic/jetpack/pull/15106 is merged.
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$wp_actions['plugins_loaded'] = 1;
|
||||
self::_init();
|
||||
unset( $wp_actions['plugins_loaded'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the dependency injection engine and do all the necessary registrations.
|
||||
*/
|
||||
public static function _init() {
|
||||
// Instantiate the underlying container and create the singleton instance of ourselves.
|
||||
$container = new Container();
|
||||
self::$instance = new self( $container );
|
||||
|
||||
// Register our singleton instance so we're able to resolve ourselves.
|
||||
$container->add( __CLASS__, self::$instance );
|
||||
|
||||
// Register the reflection container to allow autowiring.
|
||||
self::$instance->reflection_container = new WooReflectionContainer();
|
||||
$container->delegate( self::$instance->reflection_container );
|
||||
|
||||
// Perform any required manual class and service provider registration.
|
||||
self::$instance->register_classes();
|
||||
self::$instance->register_service_providers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param Container $container The instance of League\Container\Container to use for dependency injection.
|
||||
*/
|
||||
public function __construct( Container $container ) {
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register class resolutions for which default autowiring is not appropriate/enough.
|
||||
*/
|
||||
private function register_classes() {
|
||||
$singletons = array(
|
||||
\CustomerProvider::class,
|
||||
\WC_Checkout::class,
|
||||
\WC_Emails::class,
|
||||
\WC_Payment_Gateways::class,
|
||||
\WC_Shipping::class,
|
||||
\WooCommerce::class,
|
||||
);
|
||||
|
||||
foreach ( $singletons as $class_name ) {
|
||||
$this->reflection_container->share( $class_name );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all the service providers inside the ServiceProviders directory.
|
||||
*/
|
||||
private function register_service_providers() {
|
||||
$service_provider_files = array_diff( scandir( __DIR__ . '/ServiceProviders' ), array( '..', '.' ) );
|
||||
|
||||
foreach ( $service_provider_files as $file ) {
|
||||
$filename = pathinfo( $file )['filename'];
|
||||
$this->container->addServiceProvider( __NAMESPACE__ . "\\ServiceProviders\\$filename" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an instance of a class.
|
||||
*
|
||||
* @param string $class_name Class name to get an instance of.
|
||||
*
|
||||
* @return object The resolved object.
|
||||
*/
|
||||
public function get( string $class_name ) {
|
||||
return $this->container->get( $class_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an instance of a class.
|
||||
*
|
||||
* @param string $class_name Class name to get an instance of.
|
||||
*
|
||||
* @return object The resolved object.
|
||||
*/
|
||||
public static function get_instance_of( string $class_name ) {
|
||||
return self::instance()->get( $class_name );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\Tools\DependencyManagement;
|
||||
|
||||
/**
|
||||
* The original PHP League's `ReflectionContainer`, used for autowiring, provides an all-or-nothing approach
|
||||
* for registering singletons: either all the resolved objects are, or none is. This class provides a new `share`
|
||||
* method that allows to register classes to be resolved as singleton objects even if `cacheResolutions` is disabled.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Tools\DependencyManagement
|
||||
*/
|
||||
class ReflectionContainer extends \League\Container\ReflectionContainer {
|
||||
|
||||
/**
|
||||
* @var array Names of the classes to always be resolved as singletons.
|
||||
*/
|
||||
protected $classes_to_always_cache = array();
|
||||
|
||||
/**
|
||||
* Register a class to be always resolved as a singleton object, even if `cacheResolutions` is disabled.
|
||||
*
|
||||
* @param string $class_name The name of the class to register.
|
||||
*/
|
||||
public function share( string $class_name ) {
|
||||
if ( ! in_array( $class_name, $this->classes_to_always_cache, true ) ) {
|
||||
array_push( $this->classes_to_always_cache, $class_name );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function get( $id, array $args = array() ) {
|
||||
if ( true === $this->cacheResolutions || ! in_array( $id, $this->classes_to_always_cache ) ) {
|
||||
return parent::get( $id, $args );
|
||||
}
|
||||
|
||||
if ( array_key_exists( $id, $this->cache ) ) {
|
||||
return $this->cache[ $id ];
|
||||
}
|
||||
|
||||
$resolution = parent::get( $id, $args );
|
||||
$this->cache[ $id ] = $resolution;
|
||||
|
||||
return $resolution;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
/**
|
||||
* QueueServiceProvider class file.
|
||||
*
|
||||
* @package Automattic\WooCommerce\Tools\DependencyManagement\ServiceProviders
|
||||
*/
|
||||
|
||||
namespace Automattic\WooCommerce\Tools\DependencyManagement\ServiceProviders;
|
||||
|
||||
use League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
use \ReflectionClass;
|
||||
use \ReflectionException;
|
||||
use \WC_Action_Queue;
|
||||
use \WC_Queue_Interface;
|
||||
|
||||
/**
|
||||
* Service provider for WC_Queue_Interface.
|
||||
*/
|
||||
class QueueServiceProvider extends AbstractServiceProvider {
|
||||
|
||||
/**
|
||||
* The queue class that will be instantiated unless something different is specified via woocommerce_queue_class filter.
|
||||
*/
|
||||
const DEFAULT_QUEUE_CLASS = WC_Action_Queue::class;
|
||||
|
||||
/**
|
||||
* The classes/interfaces that are serviced by this service provider.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = array(
|
||||
WC_Queue_Interface::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Register the function that will create the singleton instance of WC_Queue_Interface.
|
||||
*/
|
||||
public function register() {
|
||||
$container = $this->getContainer();
|
||||
|
||||
$container->share(
|
||||
WC_Queue_Interface::class,
|
||||
function() {
|
||||
return $this->get_queue_instance();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of a class implementing WC_Queue_Interface.
|
||||
* It will be an instance of {DEFAULT_QUEUE_CLASS} unless something different is specified via woocommerce_queue_class filter.
|
||||
*
|
||||
* @return WC_Queue_Interface
|
||||
*/
|
||||
private function get_queue_instance() {
|
||||
if ( ! did_action( 'plugins_loaded' ) ) {
|
||||
$this->error_bad_timing();
|
||||
}
|
||||
|
||||
$queue_class = apply_filters( 'woocommerce_queue_class', self::DEFAULT_QUEUE_CLASS );
|
||||
$rc = null;
|
||||
try {
|
||||
$rc = new ReflectionClass( $queue_class );
|
||||
} catch ( ReflectionException $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// $rc will remain null and this will be enough to detect the error later.
|
||||
}
|
||||
|
||||
if ( is_null( $rc ) || ! $rc->implementsInterface( WC_Queue_Interface::class ) ) {
|
||||
$this->error_bad_class();
|
||||
$queue_class = self::DEFAULT_QUEUE_CLASS;
|
||||
}
|
||||
|
||||
return $this->getContainer()->get( $queue_class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a "plugins not loaded" notice.
|
||||
*/
|
||||
private function error_bad_timing() {
|
||||
wc_doing_it_wrong(
|
||||
__FUNCTION__,
|
||||
__( 'Queue instance should not be obtained before plugins_loaded.', 'woocommerce' ),
|
||||
'3.5.0'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a "invalid class name" notice.
|
||||
*/
|
||||
private function error_bad_class() {
|
||||
wc_doing_it_wrong(
|
||||
__FUNCTION__,
|
||||
sprintf(
|
||||
/* translators: %1$s: Interface name, %2$s: Default class name */
|
||||
__( 'The class attached to the "woocommerce_queue_class" filter is invalid or does not implement the %1$s interface. The default %2$s class will be used instead.', 'woocommerce' ),
|
||||
WC_Queue_Interface::class,
|
||||
self::DEFAULT_QUEUE_CLASS
|
||||
),
|
||||
'3.5.0'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Tools\DependencyManagement\ObjectContainer;
|
||||
|
||||
if ( ! defined( 'WC_PLUGIN_FILE' ) ) {
|
||||
define( 'WC_PLUGIN_FILE', __FILE__ );
|
||||
}
|
||||
|
@ -29,11 +31,33 @@ if ( ! \Automattic\WooCommerce\Autoloader::init() ) {
|
|||
}
|
||||
\Automattic\WooCommerce\Packages::init();
|
||||
|
||||
// Define a simple autoloader for the object container to work.
|
||||
// Function grabbed from https://container.thephpleague.com/3.x
|
||||
spl_autoload_register(
|
||||
function ( $class ) {
|
||||
$prefix = 'Automattic\\WooCommerce\\';
|
||||
$base_dir = __DIR__ . '/src/';
|
||||
$len = strlen( $prefix );
|
||||
if ( strncmp( $prefix, $class, $len ) !== 0 ) {
|
||||
// no, move to the next registered autoloader
|
||||
return;
|
||||
}
|
||||
$relative_class = substr( $class, $len );
|
||||
$file = $base_dir . str_replace( '\\', '/', $relative_class ) . '.php';
|
||||
if ( file_exists( $file ) ) {
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Include the main WooCommerce class.
|
||||
if ( ! class_exists( 'WooCommerce', false ) ) {
|
||||
include_once dirname( WC_PLUGIN_FILE ) . '/includes/class-woocommerce.php';
|
||||
}
|
||||
|
||||
// Initialize dependency injection.
|
||||
ObjectContainer::init();
|
||||
|
||||
/**
|
||||
* Returns the main instance of WC.
|
||||
*
|
||||
|
@ -41,7 +65,7 @@ if ( ! class_exists( 'WooCommerce', false ) ) {
|
|||
* @return WooCommerce
|
||||
*/
|
||||
function WC() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
|
||||
return WooCommerce::instance();
|
||||
return ObjectContainer::get_instance_of( WooCommerce::class );
|
||||
}
|
||||
|
||||
// Global for backwards compatibility.
|
||||
|
|
Loading…
Reference in New Issue