<?php
/**
 * Singleton trait which implements Singleton pattern in any class in which this trait is used.
 *
 * @package wpRigel\Pollify
 */

declare(strict_types=1);

namespace wpRigel\Pollify\Traits;

trait Singleton {

	/**
	 * Protected class constructor to prevent direct object creation
	 *
	 * This is meant to be overridden in the classes which implement
	 * this trait. This is ideal for doing stuff that you only want to
	 * do once, such as hooking into actions and filters, etc.
	 */
	protected function __construct() {
	}

	/**
	 * Prevent object cloning
	 */
	final protected function __clone() {
	}

	/**
	 * This method returns new or existing Singleton instance
	 * of the class for which it is called. This method is set
	 * as final intentionally, it is not meant to be overridden.
	 *
	 * @param mixed ...$args Optional arguments to pass when instantiating the class.
	 *
	 * @return object Singleton instance of the class.
	 */
	final public static function get_instance( ...$args ) {

		/**
		 * Collection of instances.
		 *
		 * @var array
		 */
		static $instances = [];

		/**
		 * If this trait is implemented in a class which has multiple
		 * sub-classes then static::$_instances will be overwritten with the most recent
		 * sub-class instance. Thanks to late static binding
		 * we use get_called_class() to grab the called class name, and store
		 * a key=>value pair for each `classname => instance` in self::$_instances
		 * for each sub-class.
		 */
		$called_class = get_called_class();

		// Generate a unique key based on the class name and arguments.
		$key = $called_class . maybe_serialize( $args );

		if ( ! isset( $instances[ $key ] ) ) {

			$instances[ $key ] = new $called_class( ...$args );

			/**
			 * Dependent items can use the pollify_features_singleton_init_{$called_class} hook to execute code
			 */
			do_action( sprintf( 'pollify_features_singleton_init_%s', $called_class ) ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

		}

		return $instances[ $key ];
	}
} // End trait
