Source: Services/Service.php

<?php
/**
 * Abstract class for all services
 */

namespace Classifai\Services;

abstract class Service {

	/**
	 * @var string The settings page slug
	 */
	protected $menu_slug;

	/**
	 * @var string The display name for the service.
	 */
	protected $display_name;

	/**
	 * @var array Array of provider classes for this service
	 */
	protected $providers;

	/**
	 * @var array Array of provider instances.
	 */
	public $provider_classes;

	/**
	 * @var string[] array Array of feature classes for this service
	 */
	public $features = [];

	/**
	 * @var \Classifai\Features\Feature[] Array of feature instances.
	 */
	public $feature_classes = [];

	/**
	 * Service constructor.
	 *
	 * @param string $display_name Name that appears in menu item and page title.
	 * @param string $menu_slug    Slug for the settings page.
	 * @param array  $providers    Array of provider classes for this service
	 */
	public function __construct( string $display_name, string $menu_slug, array $providers ) {
		$this->menu_slug    = $menu_slug;
		$this->display_name = $display_name;
		$this->providers    = $providers;
	}

	/**
	 * Init the Providers for this service.
	 */
	public function init() {
		/**
		 * Filter the list of providers for the service.
		 *
		 * @since 1.3.0
		 * @hook {$this->menu_slug}_providers
		 *
		 * @param {array} $this->providers Array of available providers for the service.
		 *
		 * @return {array} The filtered available providers.
		 */
		$this->providers = apply_filters( "{$this->menu_slug}_providers", $this->providers );

		if ( ! empty( $this->providers ) && is_array( $this->providers ) ) {
			foreach ( $this->providers as $provider ) {
				if ( class_exists( $provider ) ) {
					$this->provider_classes[] = new $provider();
				}
			}
			$this->register_providers();
		}

		/**
		 * Filter the list of features for the service.
		 *
		 * @since 3.0.0
		 * @hook {$this->menu_slug}_features
		 *
		 * @param {array} $this->features Array of available features for the service.
		 *
		 * @return {array} The filtered available features.
		 */
		$this->features = apply_filters( "{$this->menu_slug}_features", $this->features );

		if ( ! empty( $this->features ) && is_array( $this->features ) ) {
			foreach ( $this->features as $feature ) {
				if ( class_exists( $feature ) ) {
					$feature_instance        = new $feature();
					$this->feature_classes[] = $feature_instance;
					$feature_instance->setup();
				}
			}
		}

		add_filter( 'classifai_debug_information', [ $this, 'add_service_debug_information' ] );
	}

	/**
	 * Initializes the functionality for this services providers
	 */
	public function register_providers() {
		if ( ! empty( $this->provider_classes ) ) {
			foreach ( $this->provider_classes as $provider ) {
				if ( method_exists( $provider, 'register' ) ) {
					$provider->register();
				}
			}
		}
	}

	/**
	 * Get the menu slug
	 *
	 * @return string
	 */
	public function get_menu_slug(): string {
		return $this->menu_slug;
	}

	/**
	 * Get the display name
	 *
	 * @return string
	 */
	public function get_display_name(): string {
		return $this->display_name;
	}

	/**
	 * Render the start of a settings page. The rest is added by the providers
	 */
	public function render_settings_page() {
		$base_url       = add_query_arg(
			array(
				'page' => 'classifai',
				'tab'  => $this->get_menu_slug(),
			),
			admin_url( 'tools.php' )
		);
		$active_feature = $this->feature_classes ? $this->feature_classes[0]::ID : '';
		$active_feature = isset( $_GET['feature'] ) ? sanitize_text_field( wp_unslash( $_GET['feature'] ) ) : $active_feature; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		?>
		<div class="classifai-content">
			<?php
			include_once CLASSIFAI_PLUGIN_DIR . '/includes/Classifai/Admin/templates/classifai-header.php';
			?>

			<div class="classifai-wrap wrap wrap--nlu">
				<h2><?php echo esc_html( $this->display_name ); ?></h2>

				<?php
				if ( empty( $this->feature_classes ) ) {
					echo '<p>' . esc_html__( 'No features available for this service.', 'classifai' ) . '</p>';
					echo '</div></div>';
					return;
				}
				?>

				<?php
				/**
				 * Fires before the feature tab navigation.
				 *
				 * @since 3.0.0
				 * @hook classifai_before_feature_nav
				 *
				 * @param {string} $active_feature Current active feature.
				 */
				do_action( 'classifai_before_feature_nav', $active_feature );
				?>

				<h2 class="nav-tab-wrapper">
					<?php foreach ( $this->feature_classes as $feature_class ) : ?>
						<a href="<?php echo esc_url( add_query_arg( 'feature', $feature_class::ID, $base_url ) ); ?>" class="nav-tab <?php echo $feature_class::ID === $active_feature ? 'nav-tab-active' : ''; ?>"><?php echo esc_html( $feature_class->get_label() ); ?></a>
					<?php endforeach; ?>
				</h2>

				<?php settings_errors(); ?>

				<div class="classifai-nlu-sections">
					<?php
					/**
					 * Fires before the settings form for a feature.
					 *
					 * @since 3.0.0
					 * @hook classifai_before_feature_settings_form
					 *
					 * @param {string} $active_feature Current active feature.
					 */
					do_action( 'classifai_before_feature_settings_form', $active_feature );
					?>

					<form method="post" action="options.php">
					<?php
						settings_fields( 'classifai_' . $active_feature );
						do_settings_sections( 'classifai_' . $active_feature );
						submit_button();
					?>
					</form>

					<?php
					/**
					 * Fires after the settings form for a feature.
					 *
					 * @since 3.0.0
					 * @hook classifai_after_feature_settings_form
					 *
					 * @param {string} $active_feature Current active feature.
					 */
					do_action( 'classifai_after_feature_settings_form', $active_feature );
					?>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Adds plugin debug information to be printed on the Site Health screen.
	 *
	 * @since 1.4.0
	 *
	 * @param array $debug_information Array of associative arrays corresponding to lines of debug information.
	 * @return array Array with lines added.
	 */
	public function add_service_debug_information( array $debug_information ): array {
		return array_merge( $debug_information, $this->get_service_debug_information() );
	}

	/**
	 * Provides debug information for the service.
	 *
	 * @since 1.4.0
	 *
	 * @return array Array of associative arrays representing lines of debug information.
	 */
	public function get_service_debug_information(): array {
		$make_line = function ( $feature ) {
			return [
				'label' => sprintf( '%s', $feature->get_label() ),
				'value' => $feature->get_debug_information(),
			];
		};

		return array_map( $make_line, $this->feature_classes );
	}
}