# polylang/trunk/src/Switcher/Layout/Abstract_Layout.php

Polylang, version trunk. 158 lines.

- Page: https://pluginprobe.com/plugins/polylang/trunk/code/src/Switcher/Layout/Abstract_Layout.php
- Raw: https://pluginprobe.com/plugins/polylang/trunk/raw/src/Switcher/Layout/Abstract_Layout.php
- Modified: 2026-09-14T16:30:38+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/polylang/trunk/code/src/Switcher/Layout/Abstract_Layout.php#L10-L20`.

```php
<?php
/**
 * @package Polylang
 */

namespace WP_Syntex\Polylang\Switcher\Layout;

use PLL_Links;
use PLL_Language;
use WP_Syntex\Polylang\Switcher\Settings\Settings;
use WP_Syntex\Polylang\Switcher\Element\Abstract_Element;

defined( 'ABSPATH' ) || exit;

/**
 * Abstract class to display a certain type of language switcher.
 *
 * @since 3.9
 */
abstract class Abstract_Layout {
	/**
	 * @var Settings
	 */
	protected Settings $settings;

	/**
	 * @var PLL_Links
	 */
	protected PLL_Links $links;

	/**
	 * @var Abstract_Element[]
	 */
	protected array $elements = array();

	/**
	 * Constructor.
	 *
	 * @since 3.9
	 *
	 * @param Settings  $settings Instance of `Settings`.
	 * @param PLL_Links $links    Instance of `PLL_Links`.
	 */
	public function __construct( Settings $settings, PLL_Links $links ) {
		$this->settings = $settings;
		$this->links    = $links;

		foreach ( $this->links->model->languages->get_list() as $language ) {
			$this->elements[ $language->slug ] = $this->get_element( $language );
		}
	}

	/**
	 * Returns the markup of the switcher.
	 *
	 * @since 3.9
	 *
	 * @return string
	 */
	abstract public function get(): string;

	/**
	 * Returns the switcher's data.
	 *
	 * @since 3.9
	 *
	 * @return Abstract_Element[]
	 *
	 * @phpstan-return array<non-empty-string, Abstract_Element>
	 */
	public function get_elements(): array {
		$out      = array();
		$is_first = true;

		foreach ( $this->elements as $element ) {
			if ( $this->settings->hide_current && $element->is_current ) {
				// Hide current item.
				continue;
			}

			if ( $this->settings->hide_if_empty && $element->is_empty ) {
				// Hide empty item.
				continue;
			}

			if ( $this->settings->hide_if_no_translation && ! $element->has_translations ) {
				// Hide item with no translations.
				continue;
			}

			if ( empty( $element->url ) ) {
				// Failed to get a URL.
				continue;
			}

			if ( $is_first && ! in_array( 'lang-item-first', $element->item_classes, true ) ) {
				// This is the first element of the list, for example the first `<li>` in the `<ul>` tag.
				$is_first = false;
				$element->item_classes[] = 'lang-item-first';
			}

			$out[ $element->slug ] = $element;
		}

		return $out;
	}

	/**
	 * Returns an instance of `Abstract_Element`.
	 *
	 * @since 3.9
	 *
	 * @param PLL_Language $language Instance of `PLL_Language`.
	 * @return Abstract_Element
	 */
	abstract protected function get_element( PLL_Language $language ): Abstract_Element;

	/**
	 * Returns the list of HTML classes to add to the wrapper tag.
	 *
	 * @since 3.9
	 *
	 * @return string[]
	 */
	protected function get_wrapper_classes(): array {
		$classes = array_merge(
			$this->settings->wrapper_classes,
			array(
				'pll-switcher',
				"pll-layout-{$this->settings->layout}",
				"pll-alignment-{$this->settings->alignment}",
			)
		);

		if ( $this->settings->show_flags ) {
			$classes[] = 'pll-aspect-ratio-' . str_replace( ':', '', $this->settings->flag_aspect_ratio );
		}

		return $classes;
	}

	/**
	 * Returns the name of the tag to use as "navigation", depending if the current theme supports HTML5 features.
	 *
	 * @since 3.9
	 *
	 * @return string
	 */
	protected function get_nav_tag(): string {
		$format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml';

		/** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */
		$format = apply_filters( 'navigation_widgets_format', $format );

		return 'html5' === $format ? 'nav' : 'div';
	}
}

```
