Automattic\WooCommerce\Internal\StockNotifications

StockNotifications{} │ WC 1.0└─ RegisterHooksInterface

The controller for the stock notifications.

No Hooks.

Usage

$StockNotifications = new StockNotifications();
// use class methods

Methods

  1. public init_hooks()
  2. public maybe_init_services()
  3. public on_feature_enabled_changed( $feature_id, $enabled = false )
  4. public on_install_or_update()
  5. public register()
  6. public register_data_stores( $data_stores )
  7. private static is_alpha_enabled()
  8. private static is_enabled()

StockNotifications{} code WC 11.2.1

class StockNotifications implements RegisterHooksInterface {

	/**
	 * The feature that gates the whole Back in Stock Notifications experience.
	 */
	public const FEATURE_NAME = 'customer_stock_notifications';

	/**
	 * The option that stores the feature's on/off state.
	 *
	 * Declared as `option_key` in the feature definition, so this constant is the
	 * single source of truth rather than a copy of a derived name.
	 */
	public const ENABLE_OPTION_NAME = 'woocommerce_feature_customer_stock_notifications_enabled';

	/**
	 * Register the hooks that must exist regardless of the feature's state.
	 *
	 * The services are wired up in `maybe_init_services()`, behind the feature check.
	 * The feature-change listener cannot live there: during the request that turns the
	 * feature on it is still off, so it would never observe its own activation.
	 *
	 * @internal
	 */
	public function register(): void {
		add_action( 'init', array( $this, 'maybe_init_services' ), 1 );
		add_action( 'woocommerce_installed', array( $this, 'on_install_or_update' ) );
		add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this, 'on_feature_enabled_changed' ), 10, 2 );
	}

	/**
	 * Set up the data retention tasks when WooCommerce is installed or updated.
	 */
	public function on_install_or_update() {
		if ( ! self::is_enabled() ) {
			return;
		}

		wc_get_container()->get( DataRetentionController::class )->on_woo_install_or_update();
	}

	/**
	 * Set up or tear down the feature's side effects when it is toggled.
	 *
	 * Toggling from the Features screen fires no install event, so the daily data
	 * retention task and the rewrite rules have to be handled here.
	 *
	 * Params are untyped and coerced in the body because any third-party code can
	 * fire this action with fewer or differently-typed arguments.
	 *
	 * @param mixed $feature_id The feature that changed.
	 * @param mixed $enabled    Whether the feature is now enabled.
	 *
	 * @internal
	 */
	public function on_feature_enabled_changed( $feature_id, $enabled = false ): void {
		if ( self::FEATURE_NAME !== $feature_id ) {
			return;
		}

		$enabled = filter_var( $enabled, FILTER_VALIDATE_BOOLEAN );

		// The My Account endpoint appears or disappears with the feature.
		update_option( 'woocommerce_queue_flush_rewrite_rules', 'yes' );

		$data_retention_controller = wc_get_container()->get( DataRetentionController::class );

		if ( $enabled ) {
			$data_retention_controller->on_woo_install_or_update();
		} else {
			$data_retention_controller->clear_daily_task();
		}
	}

	/**
	 * Wire up the services, unless the feature is disabled.
	 *
	 * Runs on `init` priority 1: after the textdomain is loaded, and before
	 * `WC_Install::check_version()` (priority 5) fires `woocommerce_installed`.
	 *
	 * @internal
	 */
	public function maybe_init_services(): void {
		if ( ! self::is_enabled() ) {
			return;
		}

		add_filter( 'woocommerce_data_stores', array( $this, 'register_data_stores' ) );

		$container = wc_get_container();
		$container->get( EmailManager::class );
		$container->get( StockSyncController::class );
		$container->get( NotificationsProcessor::class );
		$container->get( PrivacyEraser::class );
		$container->get( DataRetentionController::class );
		$container->get( Telemetry::class );

		$container->get( NotificationManagementService::class );
		$container->get( MyAccountEndpoint::class );

		// The settings filters must attach outside admin too, or the REST settings
		// API (`is_admin()` is false there) never sees the feature's settings.
		$container->get( SettingsController::class );

		if ( is_admin() ) {
			$container->get( AdminManager::class );
		}

		// Frontend only.
		if ( ! is_admin() || wp_doing_ajax() ) {
			$container->get( EmailActionController::class );
			$container->get( ProductPageIntegration::class );
			$container->get( FormHandlerService::class );
		}
	}

	/**
	 * Register hooks and services.
	 *
	 * Declared without a native return type on purpose: the method it restores had
	 * none, and adding one would fatal any subclass that overrides it.
	 *
	 * @deprecated 11.2.0 Replaced by maybe_init_services().
	 *
	 * @internal
	 *
	 * @return void
	 */
	public function init_hooks() {
		wc_deprecated_function( __METHOD__, '11.2.0', __CLASS__ . '::maybe_init_services()' );

		if ( did_action( 'init' ) ) {
			$this->maybe_init_services();
			return;
		}

		// Alpha-era callers ran this on plugins_loaded. Defer so the feature check
		// runs after the textdomain is loaded, or the feature definitions would be
		// built untranslated and WordPress would flag the early translation call.
		add_action( 'init', array( $this, 'maybe_init_services' ), 1 );
	}

	/**
	 * Register the data stores, unless the feature is disabled.
	 *
	 * @param array $data_stores Data stores.
	 * @return array
	 */
	public function register_data_stores( $data_stores ) {
		// WC_Data_Store::__construct() re-applies this filter on every data store load,
		// so re-check the feature: it can be switched off after this callback was
		// attached at `init`. Read the option directly rather than through
		// feature_is_enabled(): a data store can be loaded before `init`, and building
		// the translated feature definitions that early is not safe.
		if ( 'yes' !== get_option( self::ENABLE_OPTION_NAME, 'no' ) && ! self::is_alpha_enabled() ) {
			return $data_stores;
		}

		if ( ! is_array( $data_stores ) ) {
			return $data_stores;
		}

		$data_stores['stock_notification'] = wc_get_container()->get( StockNotificationsDataStore::class );
		return $data_stores;
	}

	/**
	 * Check whether the feature is enabled.
	 *
	 * Must not run before `init`: feature_is_enabled() builds the translated feature
	 * definitions on first use. Every caller is hooked at `init` or later.
	 *
	 * @return bool
	 */
	private static function is_enabled(): bool {
		return FeaturesUtil::feature_is_enabled( self::FEATURE_NAME ) || self::is_alpha_enabled();
	}

	/**
	 * Check whether the site opted in through the alpha constant.
	 *
	 * Only bridges the gap until wc_update_1120_migrate_stock_notifications_alpha_constant()
	 * has copied the constant into the feature option. The database version is bumped
	 * after every 11.2.0 callback has run, so once it reaches 11.2.0 the Features screen
	 * toggle is the only switch.
	 *
	 * @return bool
	 */
	private static function is_alpha_enabled(): bool {
		if ( ! Constants::is_true( 'WOOCOMMERCE_BIS_ALPHA_ENABLED' ) ) {
			return false;
		}

		return version_compare( (string) get_option( 'woocommerce_db_version', '0' ), '11.2.0', '<' );
	}
}