<?php
/**
 * WooCommerce Plugin Framework
 *
 * This source file is subject to the GNU General Public License v3.0
 * that is bundled with this package in the file license.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.html
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@skyverge.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade the plugin to newer
 * versions in the future. If you wish to customize the plugin for your
 * needs please refer to http://www.skyverge.com
 *
 * @package   SkyVerge/WooCommerce/Plugin/Classes
 * @author    SkyVerge
 * @copyright Copyright (c) 2013-2024, SkyVerge, Inc.
 * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
 */

namespace SkyVerge\WooCommerce\PluginFramework\v6_0_1;

defined( 'ABSPATH' ) or exit;

if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_1\\SV_WC_Plugin_Dependencies' ) ) :


/**
 * Plugin dependencies handler.
 *
 * @since 5.2.0
 */
#[\AllowDynamicProperties]
class SV_WC_Plugin_Dependencies {


	/** @var array required PHP extensions */
	protected $php_extensions = array();

	/** @var array required PHP functions */
	protected $php_functions = array();

	/** @var array required PHP settings */
	protected $php_settings = array();

	/** @var SV_WC_Plugin plugin instance */
	protected $plugin;


	/**
	 * Constructs the class.
	 *
	 * @since 5.2.0
	 *
	 * @param SV_WC_Plugin $plugin plugin instance
	 * @param array $args {
	 *     PHP extension, function, and settings dependencies
	 *
	 *     @type array $php_extensions PHP extension dependencies
	 *     @type array $php_functions  PHP function dependencies
	 *     @type array $php_settings   PHP settings dependencies
	 * }
	 */
	public function __construct( SV_WC_Plugin $plugin, $args = array() ) {

		$this->plugin = $plugin;

		$dependencies = $this->parse_dependencies( $args );

		$this->php_extensions = (array) $dependencies['php_extensions'];
		$this->php_functions  = (array) $dependencies['php_functions'];
		$this->php_settings   = (array) $dependencies['php_settings'];

		// add the action & filter hooks
		$this->add_hooks();
	}


	/**
	 * Parses the dependency arguments and sets defaults.
	 *
	 * @since 5.2.0
	 *
	 * @param array $args dependency args
	 * @return array
	 */
	private function parse_dependencies( $args ) {

		$dependencies = wp_parse_args( $args, array(
			'php_extensions' => array(),
			'php_functions'  => array(),
			'php_settings'   => array(),
		) );

		$default_settings = array(
			'suhosin.post.max_array_index_length'    => 256,
			'suhosin.post.max_totalname_length'      => 65535,
			'suhosin.post.max_vars'                  => 1024,
			'suhosin.request.max_array_index_length' => 256,
			'suhosin.request.max_totalname_length'   => 65535,
			'suhosin.request.max_vars'               => 1024,
		);

		// override any default settings requirements if the plugin specifies them
		if ( ! empty( $dependencies['php_settings'] ) ) {
			$dependencies['php_settings'] = array_merge( $default_settings, $dependencies['php_settings'] );
		}

		return $dependencies;
	}


	/**
	 * Adds the action & filter hooks.
	 *
	 * @since 5.2.0
	 */
	protected function add_hooks() {

		// add the admin dependency notices
		add_action( 'admin_init', array( $this, 'add_admin_notices' ) );
	}


	/**
	 * Adds the admin dependency notices.
	 *
	 * @since 5.2.0
	 */
	public function add_admin_notices() {

		$this->add_php_extension_notices();
		$this->add_php_function_notices();
		$this->add_php_settings_notices();

		$this->add_deprecated_notices();
	}


	/**
	 * Adds notices for any missing PHP extensions.
	 *
	 * @since 5.2.0
	 */
	public function add_php_extension_notices() {

		$missing_extensions = $this->get_missing_php_extensions();

		if ( count( $missing_extensions ) > 0 ) {

			$message = sprintf(
				/* translators: Placeholders: %1$s - plugin name, %2$s - a PHP extension/comma-separated list of PHP extensions */
				_n(
					'%1$s requires the %2$s PHP extension to function. Contact your host or server administrator to install and configure the missing extension.',
					'%1$s requires the following PHP extensions to function: %2$s. Contact your host or server administrator to install and configure the missing extensions.',
					count( $missing_extensions ),
					'woocommerce-plugin-framework'
				),
				esc_html( $this->get_plugin()->get_plugin_name() ),
				'<strong>' . implode( ', ', $missing_extensions ) . '</strong>'
			);

			$this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-extensions', $message, 'error' );
		}
	}


	/**
	 * Adds notices for any missing PHP functions.
	 *
	 * @since 5.2.0
	 */
	public function add_php_function_notices() {

		$missing_functions = $this->get_missing_php_functions();

		if ( count( $missing_functions ) > 0 ) {

			$message = sprintf(
				/* translators: Placeholders: %1$s - plugin name, %2$s - a PHP function/comma-separated list of PHP functions */
				_n(
					'%1$s requires the %2$s PHP function to exist.  Contact your host or server administrator to install and configure the missing function.',
					'%1$s requires the following PHP functions to exist: %2$s.  Contact your host or server administrator to install and configure the missing functions.',
					count( $missing_functions ),
					'woocommerce-plugin-framework'
				),
				esc_html( $this->get_plugin()->get_plugin_name() ),
				'<strong>' . implode( ', ', $missing_functions ) . '</strong>'
			);

			$this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-missing-functions', $message, 'error' );
		}
	}


	/**
	 * Adds notices for any incompatible PHP settings.
	 *
	 * @since 5.2.0
	 */
	public function add_php_settings_notices() {

		if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) {

			$bad_settings = $this->get_incompatible_php_settings();

			if ( count( $bad_settings ) > 0 ) {

				$message = sprintf(
					/* translators: Placeholders: %s - plugin name */
					__( '%s may behave unexpectedly because the following PHP configuration settings are required:' ),
					'<strong>' . esc_html( $this->get_plugin()->get_plugin_name() ) . '</strong>'
				);

				$message .= '<ul>';

					foreach ( $bad_settings as $setting => $values ) {

						$setting_message = '<code>' . $setting . ' = ' . $values['expected'] . '</code>';

						if ( ! empty( $values['type'] ) && 'min' === $values['type'] ) {

							$setting_message = sprintf(
								/* translators: Placeholder: %s - a PHP setting value */
								__( '%s or higher', 'woocommerce-plugin-framework' ),
								$setting_message
							);
						}

						$message .= '<li>' . $setting_message . '</li>';
					}

				$message .= '</ul>';

				$message .= __( 'Please contact your hosting provider or server administrator to configure these settings.', 'woocommerce-plugin-framework' );

				$this->add_admin_notice( 'wc-' . $this->get_plugin()->get_id_dasherized() . '-incompatibile-php-settings', $message, 'warning' );
			}
		}
	}


	/**
	 * Gets any deprecated warning notices.
	 *
	 * @since 5.2.0
	 */
	protected function add_deprecated_notices() {

		// add a notice for PHP < 7.4
		if ( version_compare( PHP_VERSION, '7.4.0', '<' ) ) {

			$message = '<p>';

			// for translation purposes, it's easier to keep these strings separate to avoid many tabs and newlines in the compiled pot file
			$lines = [
				/* translators: Placeholders: %1$s - <strong> HTML tag, %2$s - </strong> HTML tag */
				sprintf( __( 'Hey there! We\'ve noticed that your server is running %1$san outdated version of PHP%2$s, which is the programming language that WooCommerce and its extensions are built on.', 'woocommerce-plugin-framework' ), '<strong>', '</strong>' ),
				/* translators: Placeholders: %1$s - <strong> HTML tag, %2$s - </strong> HTML tag */
				sprintf( __( 'The PHP version that is currently used for your site is no longer maintained, nor %1$sreceives security updates%2$s; newer versions are faster and more secure.', 'woocommerce-plugin-framework' ), '<strong>', '</strong>' ),
				/* translators: Context: User is running an outdated PHP Version a plugin is not compatible with. Placeholders: %s - the plugin name */
				sprintf( __( 'As a result, %s no longer supports this version and you should upgrade PHP as soon as possible.', 'woocommerce-plugin-framework' ), esc_html( $this->get_plugin()->get_plugin_name() ) ),
				/* translators: Context: The host can update PHP version for the user. Placeholders: %1$s - <a> HTML tag, %2$s - </a> HTML tag */
				sprintf( __( 'Your hosting provider can do this for you. %1$sHere are some resources to help you upgrade%2$s and to explain PHP versions further.', 'woocommerce-plugin-framework' ), '<a href="https://wordpress.org/support/update-php/">', '</a>' ),
			];

			$message .= implode( '<br>', $lines);
			$message .= '</p>';

			$this->add_admin_notice( 'sv-wc-deprecated-php-version', $message, 'error' );
		}
	}


	/**
	 * Adds an admin notice.
	 *
	 * @since 5.2.0
	 *
	 * @param string $id notice ID
	 * @param string $message notice message
	 * @param string $type notice type
	 */
	protected function add_admin_notice( $id, $message, $type = 'info' ) {

		$notice_class = $type;

		// translate the types into WP notice classes
		switch ( $type ) {

			case 'error':
				$notice_class = 'notice-error';
			break;

			case 'warning':
				$notice_class = 'notice-warning';
			break;

			case 'info':
				$notice_class = 'notice-info';
			break;

			case 'success':
				$notice_class = 'notice-success';
			break;
		}

		$this->get_plugin()->get_admin_notice_handler()->add_admin_notice( $message, $id, array(
			'notice_class' => $notice_class,
		) );
	}


	/**
	 * Returns the active scripts optimization plugins.
	 *
	 * Returns a key-value array where the key contains the plugin file identifier and the value is the name of the plugin.
	 *
	 * @since 5.7.0
	 *
	 * @return array
	 */
	public function get_active_scripts_optimization_plugins() {

		/**
		 * Filters script optimization plugins to look for.
		 *
		 * @since 5.7.0
		 *
		 * @param array $plugins an array of file identifiers (keys) and plugin names (values)
		 */
		$plugins = (array) apply_filters( 'wc_' . $this->get_plugin()->get_id() . '_scripts_optimization_plugins', [
			'async-javascript.php' => 'Async JavaScript',
			'autoptimize.php'      => 'Autoptimize',
			'wp-hummingbird.php'   => 'Hummingbird',
			'sg-optimizer.php'     => 'SG Optimizer',
			'w3-total-cache.php'   => 'W3 Total Cache',
			'wpFastestCache.php'   => 'WP Fastest Cache',
			'wp-rocket.php'        => 'WP Rocket',
		] );

		$active_plugins = [];

		foreach ( $plugins as $filename => $plugin_name ) {

			if ( $this->get_plugin()->is_plugin_active( $filename ) ) {

				$active_plugins[ $filename ] = $plugin_name;
			}
		}

		return $active_plugins;
	}


	/**
	 * Returns true if any of the known scripts optimization plugins is active.
	 *
	 * @since 5.7.0
	 *
	 * @return bool
	 */
	public function is_scripts_optimization_plugin_active() {

		return ! empty( $this->get_active_scripts_optimization_plugins() );
	}


	/** Getter methods ********************************************************/


	/**
	 * Gets any missing PHP extensions.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_missing_php_extensions() {

		$missing_extensions = [];

		foreach ( $this->get_php_extensions() as $extension ) {

			if ( ! extension_loaded( $extension ) ) {
				$missing_extensions[] = $extension;
			}
		}

		return $missing_extensions;
	}


	/**
	 * Gets the required PHP extensions.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_php_extensions() {

		return $this->php_extensions;
	}


	/**
	 * Gets any missing PHP functions.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_missing_php_functions() {

		$missing_functions = [];

		foreach ( $this->get_php_functions() as $function ) {

			if ( ! extension_loaded( $function ) ) {
				$missing_functions[] = $function;
			}
		}

		return $missing_functions;
	}


	/**
	 * Gets the required PHP functions.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_php_functions() {

		return $this->php_functions;
	}


	/**
	 * Gets any incompatible PHP settings.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_incompatible_php_settings() {

		$incompatible_settings = [];

		if ( function_exists( 'ini_get' ) ) {

			foreach ( $this->get_php_settings() as $setting => $expected ) {

				$actual = ini_get( $setting );

				if ( ! $actual ) {
					continue;
				}

				if ( is_int( $expected ) ) {

					// determine if this is a size string, like "10MB"
					$is_size = ! is_numeric( substr( $actual, -1 ) );

					$actual_num = $is_size ? wc_let_to_num( $actual ) : $actual;

					if ( $actual_num < $expected ) {

						$incompatible_settings[ $setting ] = [
							'expected' => $is_size ? size_format( $expected ) : $expected,
							'actual'   => $is_size ? size_format( $actual_num ) : $actual,
							'type'     => 'min',
						];
					}

				} elseif ( $actual !== $expected ) {

					$incompatible_settings[ $setting ] = [
						'expected' => $expected,
						'actual'   => $actual,
					];
				}
			}
		}

		return $incompatible_settings;
	}


	/**
	 * Gets the required PHP settings.
	 *
	 * @since 5.2.0
	 *
	 * @return array
	 */
	public function get_php_settings() {

		return $this->php_settings;
	}


	/**
	 * Gets the plugin instance.
	 *
	 * @since 5.2.0
	 *
	 * @return SV_WC_Plugin
	 */
	protected function get_plugin() {

		return $this->plugin;
	}


}


endif;
