diff --git a/.distignore b/.distignore
index 50a350c3..04d2425f 100644
--- a/.distignore
+++ b/.distignore
@@ -21,6 +21,7 @@
/src
/tests
AGENTS.md
+CLAUDE.md
Changelog.md
Diagrams.md
README.md
diff --git a/.gitignore b/.gitignore
index 68b9a68d..e0b109ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,17 +4,18 @@
.phpunit.cache
.phpunit.result.cache
.vscode/
-composer.lock
-composer.phar
+CLAUDE.md
assets/js/msls-widget-block/
assets/js/msls.js
+composer.lock
+composer.phar
multisite-language-switcher.zip
multisite-language-switcher/
node_modules/
out/
phpunit.xml.bak
-tests/coverage/
tests/coverage.xml
+tests/coverage/
tests/playwright-report/
tests/playwright-results/
tests/playwright/.env.local
diff --git a/.phpstan.neon.dist b/.phpstan.neon.dist
index 8e41d58a..3a711d24 100644
--- a/.phpstan.neon.dist
+++ b/.phpstan.neon.dist
@@ -1,9 +1,7 @@
parameters:
- level: 6
+ level: 8
paths:
- MultisiteLanguageSwitcher.php
- includes
bootstrapFiles:
- tests/phpstan/bootstrap.php
- ignoreErrors:
- - '/^Class lloc\\Msls\\MslsWidget extends generic class WP_Widget but does not specify its types: T$/'
\ No newline at end of file
diff --git a/.wp-env.json b/.wp-env.json
index 3a407b1f..596deecf 100644
--- a/.wp-env.json
+++ b/.wp-env.json
@@ -1,9 +1,4 @@
{
- "phpVersion": "8.3",
- "multisite": true,
- "plugins": [
- "."
- ],
"env": {
"tests": {
"config": {
@@ -19,5 +14,10 @@
},
"mappings": {
"wp-content/plugins/multisite-language-switcher": "."
- }
+ },
+ "multisite": true,
+ "phpVersion": "8.3",
+ "plugins": [
+ "."
+ ]
}
diff --git a/MultisiteLanguageSwitcher.php b/MultisiteLanguageSwitcher.php
index 5e992ea2..86e13a40 100644
--- a/MultisiteLanguageSwitcher.php
+++ b/MultisiteLanguageSwitcher.php
@@ -49,201 +49,9 @@
define( 'MSLS_PLUGIN_PATH', plugin_basename( __FILE__ ) );
define( 'MSLS_PLUGIN__FILE__', __FILE__ );
+ require_once __DIR__ . '/includes/functions.php';
require_once __DIR__ . '/includes/deprectated.php';
- /**
- * Get the output for using the links to the translations in your code
- *
- * @package Msls
- * @param mixed $attr
- * @return string
- */
- function msls_get_switcher( $attr ): string {
- $arr = is_array( $attr ) ? $attr : array();
- $obj = apply_filters( 'msls_get_output', null );
-
- return ! is_null( $obj ) ? strval( $obj->set_tags( $arr ) ) : '';
- }
-
- /**
- * Output the links to the translations in your template
- *
- * You can call this function directly like that
- *
- * if ( function_exists ( 'the_msls' ) )
- * the_msls();
- *
- * or just use it as shortcode [sc_msls]
- *
- * @package Msls
- * @uses get_the_msls
- *
- * @param string[] $arr
- */
- function msls_the_switcher( array $arr = array() ): void {
- // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- echo msls_get_switcher( $arr );
- }
-
- /**
- * Gets the URL of the country flag-icon for a specific locale
- *
- * @param string $locale
- *
- * @return string
- */
- function msls_get_flag_url( string $locale ): string {
- return ( new \lloc\Msls\MslsOptions() )->get_flag_url( $locale );
- }
-
- /**
- * Gets the description for a blog for a specific locale
- *
- * @param string $locale
- * @param string $preset
- *
- * @return string
- */
- function msls_get_blog_description( string $locale, string $preset = '' ): string {
- $blog = msls_blog( $locale );
-
- return $blog ? $blog->get_description() : $preset;
- }
-
- /**
- * Gets the permalink for a translation of the current post in a given language
- *
- * @param string $locale
- * @param string $preset
- *
- * @return string
- */
- function msls_get_permalink( string $locale, string $preset = '' ): string {
- $url = null;
- $blog = msls_blog( $locale );
-
- if ( $blog ) {
- $options = \lloc\Msls\MslsOptions::create();
- $url = $blog->get_url( $options );
- }
-
- return $url ?? $preset;
- }
-
- /**
- * Looks for the MslsBlog instance for a specific locale
- *
- * @param string $locale
- *
- * @return \lloc\Msls\MslsBlog|null
- */
- function msls_blog( string $locale ): ?\lloc\Msls\MslsBlog {
- return msls_blog_collection()->get_blog( $locale );
- }
-
- /**
- * Gets the MslsBlogCollection instance
- *
- * @return \lloc\Msls\MslsBlogCollection
- */
- function msls_blog_collection(): \lloc\Msls\MslsBlogCollection {
- return \lloc\Msls\MslsBlogCollection::instance();
- }
-
- /**
- * Gets the MslsOptions instance
- *
- * @return \lloc\Msls\MslsOptions
- */
- function msls_options(): \lloc\Msls\MslsOptions {
- return \lloc\Msls\MslsOptions::instance();
- }
-
- /**
- * Gets the MslsContentTypes instance
- *
- * @return \lloc\Msls\MslsContentTypes
- */
- function msls_content_types(): \lloc\Msls\MslsContentTypes {
- return \lloc\Msls\MslsContentTypes::create();
- }
-
- /**
- * Gets the MslsPostType instance
- *
- * @return \lloc\Msls\MslsPostType
- */
- function msls_post_type(): \lloc\Msls\MslsPostType {
- return \lloc\Msls\MslsPostType::instance();
- }
-
- /**
- * Gets the MslsTaxonomy instance
- *
- * @return \lloc\Msls\MslsTaxonomy
- */
- function msls_taxonomy(): \lloc\Msls\MslsTaxonomy {
- return \lloc\Msls\MslsTaxonomy::instance();
- }
-
- /**
- * Gets the MslsOutput instance
- *
- * @return \lloc\Msls\MslsOutput
- */
- function msls_output(): \lloc\Msls\MslsOutput {
- return \lloc\Msls\MslsOutput::create();
- }
-
- /**
- * Retrieves the MslsOptionsPost instance.
- *
- * @param int $id
- * @return \lloc\Msls\MslsOptionsPost
- */
- function msls_get_post( int $id ): \lloc\Msls\MslsOptionsPost {
- return new \lloc\Msls\MslsOptionsPost( $id );
- }
-
- /**
- * Retrieves the MslsOptionsTax instance.
- *
- * Determines the current query based on conditional tags:
- * - is_category
- * - is_tag
- * - is_tax
- *
- * @param int $id
- * @return \lloc\Msls\OptionsTaxInterface
- */
- function msls_get_tax( int $id ): \lloc\Msls\OptionsTaxInterface {
- return \lloc\Msls\MslsOptionsTax::create( $id );
- }
-
- /**
- * Retrieves the MslsOptionsQuery instance.
- *
- * Determines the current query based on conditional tags:
- * - is_day
- * - is_month
- * - is_year
- * - is_author
- * - is_post_type_archive
- *
- * @return ?\lloc\Msls\MslsOptionsQuery
- */
- function msls_get_query(): ?\lloc\Msls\MslsOptionsQuery {
- return \lloc\Msls\MslsOptionsQuery::create();
- }
-
- /**
- * Trivial void function for actions that do not return anything.
- *
- * @return void
- */
- function msls_return_void(): void {
- }
-
lloc\Msls\MslsPlugin::init();
lloc\Msls\MslsCli::init();
}
diff --git a/includes/Component/Input/Text.php b/includes/Component/Input/Text.php
index 149d7279..f8789fbd 100644
--- a/includes/Component/Input/Text.php
+++ b/includes/Component/Input/Text.php
@@ -14,7 +14,7 @@ final class Text extends Component {
protected $key;
/**
- * @var string
+ * @var ?string
*/
protected $value;
@@ -48,7 +48,7 @@ public function render(): string {
return sprintf(
'',
esc_attr( $this->key ),
- esc_attr( $this->value ),
+ esc_attr( (string) $this->value ),
$this->size,
$this->readonly // phpcs:ignore WordPress.Security.EscapeOutput
);
diff --git a/includes/ContentImport/ContentImporter.php b/includes/ContentImport/ContentImporter.php
index 761ba89f..d13432e0 100644
--- a/includes/ContentImport/ContentImporter.php
+++ b/includes/ContentImport/ContentImporter.php
@@ -60,7 +60,7 @@ class ContentImporter extends MslsRegistryInstance {
* @param ?MslsMain $main
*/
public function __construct( ?MslsMain $main = null ) {
- $this->main = ! is_null( $main ) ? $main : MslsMain::create();
+ $this->main = $main ?? MslsMain::create();
}
/**
@@ -175,7 +175,7 @@ protected function pre_flight_check() {
/**
* Parses the source blog and post IDs from the $_POST array validating them.
*
- * @return int[]|bool
+ * @return array{int, int}|false
*/
public function parse_sources() {
if ( ! MslsRequest::has_var( 'msls_import' ) ) {
@@ -202,7 +202,7 @@ protected function get_the_blog_post_ID( $blog_id ) {
$id = get_the_ID();
- if ( ! empty( $id ) ) {
+ if ( false !== $id && $id > 0 ) {
restore_current_blog();
return $id;
@@ -225,11 +225,11 @@ protected function get_the_blog_post_ID( $blog_id ) {
* @param int $blog_id
* @param array $data
*
- * @return bool|int
+ * @return int
*/
protected function insert_blog_post( $blog_id, array $data = array() ) {
if ( empty( $data ) ) {
- return false;
+ return 0;
}
switch_to_blog( $blog_id );
@@ -242,7 +242,7 @@ protected function insert_blog_post( $blog_id, array $data = array() ) {
}
$this->handle( true );
- $this->has_created_post = $post_id > 0 ? $post_id : false;
+ $this->has_created_post = $post_id > 0 ? $post_id : 0;
restore_current_blog();
@@ -312,12 +312,12 @@ public function import_content( ImportCoordinates $import_coordinates, array $po
$importers = Map::instance()->make( $import_coordinates );
}
- if ( is_null( $this->get_logger() ) ) {
- $this->set_logger( new ImportLogger( $import_coordinates ) );
+ if ( is_null( $this->logger ) ) {
+ $this->logger = new ImportLogger( $import_coordinates );
}
- if ( is_null( $this->get_relations() ) ) {
- $this->set_relations( new Relations( $import_coordinates ) );
+ if ( is_null( $this->relations ) ) {
+ $this->relations = new Relations( $import_coordinates );
}
if ( ! empty( $importers ) ) {
@@ -341,8 +341,8 @@ public function import_content( ImportCoordinates $import_coordinates, array $po
* Fires after the import ran.
*
* @param ImportCoordinates $import_coordinates
- * @param ImportLogger $logger
- * @param Relations $relations
+ * @param ?ImportLogger $logger
+ * @param ?Relations $relations
*
* @since TBD
*/
@@ -353,8 +353,8 @@ public function import_content( ImportCoordinates $import_coordinates, array $po
*
* @param array $post_fields
* @param ImportCoordinates $import_coordinates
- * @param ImportLogger $logger
- * @param Relations $relations
+ * @param ?ImportLogger $logger
+ * @param ?Relations $relations
*/
return apply_filters(
'msls_content_import_data_after_import',
diff --git a/includes/ContentImport/ImportLogger.php b/includes/ContentImport/ImportLogger.php
index 23d6749f..ac8d5c53 100644
--- a/includes/ContentImport/ImportLogger.php
+++ b/includes/ContentImport/ImportLogger.php
@@ -128,7 +128,7 @@ protected function build_nested_array( $path, $what = '' ): array {
);
$data = json_decode( $json, true );
- return $data;
+ return is_array( $data ) ? $data : array();
}
/**
@@ -137,9 +137,7 @@ protected function build_nested_array( $path, $what = '' ): array {
* @return string[]
*/
protected function build_path( string $where ): array {
- $where_path = explode( $this->levels_delimiter, $where );
-
- return $where_path;
+ return explode( $this->levels_delimiter, $where );
}
/**
diff --git a/includes/ContentImport/MetaBox.php b/includes/ContentImport/MetaBox.php
index 9457cef6..606fe6bb 100644
--- a/includes/ContentImport/MetaBox.php
+++ b/includes/ContentImport/MetaBox.php
@@ -24,7 +24,7 @@ class MetaBox extends MslsRegistryInstance {
*/
public function render(): void {
$post = get_post();
- $mydata = new MslsOptionsPost( $post->ID );
+ $mydata = new MslsOptionsPost( $post->ID ?? 0 );
$languages = MslsOptionsPost::instance()->get_available_languages();
$current = MslsBlogCollection::get_blog_language( get_current_blog_id() );
$languages = array_diff_key( $languages, array( $current => $current ) );
@@ -139,7 +139,7 @@ protected function inline_thickbox_html( $output = true, array $data = array() )
' ) {
/* translators: %s: list of languages */
$format = __( 'This post is also available in %s.', 'multisite-language-switcher' );
+ $output = '';
if ( has_filter( 'msls_filter_string' ) ) {
/**
diff --git a/includes/MslsMain.php b/includes/MslsMain.php
index c27fa8d4..a876a3bc 100644
--- a/includes/MslsMain.php
+++ b/includes/MslsMain.php
@@ -38,7 +38,7 @@ final public function __construct( MslsOptions $options, MslsBlogCollection $col
$this->collection = $collection;
}
- public static function create(): object {
+ public static function create(): static {
return new static( msls_options(), msls_blog_collection() );
}
diff --git a/includes/MslsOutput.php b/includes/MslsOutput.php
index 7d3061e3..3f9c4177 100644
--- a/includes/MslsOutput.php
+++ b/includes/MslsOutput.php
@@ -24,7 +24,7 @@ class MslsOutput extends MslsMain {
const MSLS_GET_TAGS_HOOK = 'msls_output_get_tags';
- public static function init(): object {
+ public static function init(): static {
_deprecated_function( __METHOD__, '2.9.2', 'MslsOutput::create' );
return self::create();
@@ -201,6 +201,54 @@ public function set_tags( array $arr = array() ): MslsOutput {
return $this;
}
+ /**
+ * Gets structured language data for all available translations
+ *
+ * @param bool $filter When true, only returns languages with existing translations
+ *
+ * @return array
+ */
+ public function get_languages( bool $filter = false ): array {
+ $blogs = $this->collection->get_filtered( $filter );
+
+ if ( ! $blogs ) {
+ return array();
+ }
+
+ $mydata = MslsOptions::create();
+ $languages = array();
+
+ foreach ( $blogs as $blog ) {
+ $language = $blog->get_language();
+ $is_current = $this->collection->is_current_blog( $blog );
+
+ if ( $is_current ) {
+ $url = $mydata->get_current_link();
+ } else {
+ switch_to_blog( $blog->userblog_id );
+
+ $url = $mydata->get_permalink( $language );
+
+ restore_current_blog();
+ }
+
+ if ( empty( $url ) ) {
+ continue;
+ }
+
+ $languages[] = array(
+ 'locale' => $language,
+ 'alpha2' => $blog->get_alpha2(),
+ 'url' => $url,
+ 'label' => $blog->get_description(),
+ 'flag_url' => $this->options->get_flag_url( $language ),
+ 'current' => $is_current,
+ );
+ }
+
+ return $languages;
+ }
+
/**
* Returns true if the requirements not fulfilled
*
diff --git a/includes/MslsPostTag.php b/includes/MslsPostTag.php
index 2a825c63..0814a857 100644
--- a/includes/MslsPostTag.php
+++ b/includes/MslsPostTag.php
@@ -54,8 +54,9 @@ public static function suggest(): void {
*
* @since 0.9.9
*/
- $args = (array) apply_filters( 'msls_post_tag_suggest_args', $args );
- foreach ( get_terms( $args ) as $term ) {
+ $args = (array) apply_filters( 'msls_post_tag_suggest_args', $args );
+ $terms = get_terms( $args );
+ foreach ( is_array( $terms ) ? $terms : array() as $term ) {
/**
* Manipulates the term object before using it
*
@@ -187,7 +188,7 @@ public function the_input( ?\WP_Term $tag, string $title_format, string $item_fo
if ( $mydata->has_value( $language ) ) {
$term = get_term( $mydata->$language, $type );
- if ( is_object( $term ) ) {
+ if ( $term instanceof \WP_Term ) {
$icon->set_href( (int) $mydata->$language );
$value = $mydata->$language;
$title = $term->name;
diff --git a/includes/MslsShortCode.php b/includes/MslsShortCode.php
index 4e5cf2a1..9f8ec31f 100644
--- a/includes/MslsShortCode.php
+++ b/includes/MslsShortCode.php
@@ -12,17 +12,16 @@ public static function init(): void {
/**
* Renders output using the widget's output
*
- * @return string|false
+ * @return string
*/
- public static function render_widget() {
+ public static function render_widget(): string {
if ( msls_options()->is_excluded() ) {
return '';
}
ob_start();
the_widget( MslsWidget::class );
- $output = ob_get_clean();
- return $output;
+ return (string) ob_get_clean();
}
}
diff --git a/includes/MslsSqlCacher.php b/includes/MslsSqlCacher.php
index 0e10bfec..85ad0038 100644
--- a/includes/MslsSqlCacher.php
+++ b/includes/MslsSqlCacher.php
@@ -93,13 +93,16 @@ public function __get( string $name ) {
* @return mixed
*/
public function __call( string $method, array $args ) {
+ /** @var callable $callback */
+ $callback = array( $this->db, $method );
+
if ( 'get_' !== substr( $method, 0, 4 ) ) {
- return call_user_func_array( array( $this->db, $method ), $args );
+ return call_user_func_array( $callback, $args );
}
$result = wp_cache_get( $this->cache_key, self::CACHE_GROUP );
if ( false === $result ) {
- $result = call_user_func_array( array( $this->db, $method ), $args );
+ $result = call_user_func_array( $callback, $args );
wp_cache_set( $this->cache_key, $result, self::CACHE_GROUP, $this->expire );
}
diff --git a/includes/deprectated.php b/includes/deprectated.php
index 8af510c6..f2e5b36b 100644
--- a/includes/deprectated.php
+++ b/includes/deprectated.php
@@ -32,6 +32,7 @@ function get_the_msls( $attr ): string {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
function the_msls( array $arr = array() ): void {
_deprecated_function( __FUNCTION__, '2.10.1', 'msls_the_switcher' );
+
msls_the_switcher( $arr );
}
diff --git a/includes/functions.php b/includes/functions.php
new file mode 100644
index 00000000..b4c31430
--- /dev/null
+++ b/includes/functions.php
@@ -0,0 +1,216 @@
+set_tags( $arr ) ) : '';
+}
+
+/**
+ * Output the links to the translations in your template
+ *
+ * You can call this function directly like that
+ *
+ * if ( function_exists ( 'msls_the_switcher' ) )
+ * msls_the_switcher();
+ *
+ * or just use it as shortcode [sc_msls]
+ *
+ * @package Msls
+ * @uses get_the_msls
+ *
+ * @param string[] $arr
+ */
+function msls_the_switcher( array $arr = array() ): void {
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo msls_get_switcher( $arr );
+}
+
+/**
+ * Gets the URL of the country flag-icon for a specific locale
+ *
+ * @param string $locale
+ *
+ * @return string
+ */
+function msls_get_flag_url( string $locale ): string {
+ return ( new \lloc\Msls\MslsOptions() )->get_flag_url( $locale );
+}
+
+/**
+ * Gets the description for a blog for a specific locale
+ *
+ * @param string $locale
+ * @param string $preset
+ *
+ * @return string
+ */
+function msls_get_blog_description( string $locale, string $preset = '' ): string {
+ $blog = msls_blog( $locale );
+
+ return $blog ? $blog->get_description() : $preset;
+}
+
+/**
+ * Gets the permalink for a translation of the current post in a given language
+ *
+ * @param string $locale
+ * @param string $preset
+ *
+ * @return string
+ */
+function msls_get_permalink( string $locale, string $preset = '' ): string {
+ $url = null;
+ $blog = msls_blog( $locale );
+
+ if ( $blog ) {
+ $options = \lloc\Msls\MslsOptions::create();
+ $url = $blog->get_url( $options );
+ }
+
+ return $url ?? $preset;
+}
+
+/**
+ * Looks for the MslsBlog instance for a specific locale
+ *
+ * @param string $locale
+ *
+ * @return \lloc\Msls\MslsBlog|null
+ */
+function msls_blog( string $locale ): ?\lloc\Msls\MslsBlog {
+ return msls_blog_collection()->get_blog( $locale );
+}
+
+/**
+ * Gets the MslsBlogCollection instance
+ *
+ * @return \lloc\Msls\MslsBlogCollection
+ */
+function msls_blog_collection(): \lloc\Msls\MslsBlogCollection {
+ return \lloc\Msls\MslsBlogCollection::instance();
+}
+
+/**
+ * Gets the MslsOptions instance
+ *
+ * @return \lloc\Msls\MslsOptions
+ */
+function msls_options(): \lloc\Msls\MslsOptions {
+ return \lloc\Msls\MslsOptions::instance();
+}
+
+/**
+ * Gets the MslsContentTypes instance
+ *
+ * @return \lloc\Msls\MslsContentTypes
+ */
+function msls_content_types(): \lloc\Msls\MslsContentTypes {
+ return \lloc\Msls\MslsContentTypes::create();
+}
+
+/**
+ * Gets the MslsPostType instance
+ *
+ * @return \lloc\Msls\MslsPostType
+ */
+function msls_post_type(): \lloc\Msls\MslsPostType {
+ return \lloc\Msls\MslsPostType::instance();
+}
+
+/**
+ * Gets the MslsTaxonomy instance
+ *
+ * @return \lloc\Msls\MslsTaxonomy
+ */
+function msls_taxonomy(): \lloc\Msls\MslsTaxonomy {
+ return \lloc\Msls\MslsTaxonomy::instance();
+}
+
+/**
+ * Gets the MslsOutput instance
+ *
+ * @return \lloc\Msls\MslsOutput
+ */
+function msls_output(): \lloc\Msls\MslsOutput {
+ return \lloc\Msls\MslsOutput::create();
+}
+
+/**
+ * Retrieves the MslsOptionsPost instance.
+ *
+ * @param int $id
+ * @return \lloc\Msls\MslsOptionsPost
+ */
+function msls_get_post( int $id ): \lloc\Msls\MslsOptionsPost {
+ return new \lloc\Msls\MslsOptionsPost( $id );
+}
+
+/**
+ * Retrieves the MslsOptionsTax instance.
+ *
+ * Determines the current query based on conditional tags:
+ * - is_category
+ * - is_tag
+ * - is_tax
+ *
+ * @param int $id
+ * @return \lloc\Msls\OptionsTaxInterface
+ */
+function msls_get_tax( int $id ): \lloc\Msls\OptionsTaxInterface {
+ return \lloc\Msls\MslsOptionsTax::create( $id );
+}
+
+/**
+ * Retrieves the MslsOptionsQuery instance.
+ *
+ * Determines the current query based on conditional tags:
+ * - is_day
+ * - is_month
+ * - is_year
+ * - is_author
+ * - is_post_type_archive
+ *
+ * @return ?\lloc\Msls\MslsOptionsQuery
+ */
+function msls_get_query(): ?\lloc\Msls\MslsOptionsQuery {
+ return \lloc\Msls\MslsOptionsQuery::create();
+}
+
+/**
+ * Gets structured language data for all available translations
+ *
+ * Returns an array of language entries with locale, alpha2 code, URL,
+ * label, flag URL, and whether it's the current language. This provides
+ * programmatic access to the same data used by the switcher output,
+ * without any HTML rendering.
+ *
+ * @param bool $filter When true, only returns languages with existing translations
+ *
+ * @return array
+ */
+function msls_get_languages( bool $filter = false ): array {
+ return msls_output()->get_languages( $filter );
+}
+
+/**
+ * Trivial void function for actions that do not return anything.
+ *
+ * @return void
+ */
+function msls_return_void(): void {
+}
diff --git a/tests/phpunit/TestMslsOutput.php b/tests/phpunit/TestMslsOutput.php
index 280f5f7d..86f6a816 100644
--- a/tests/phpunit/TestMslsOutput.php
+++ b/tests/phpunit/TestMslsOutput.php
@@ -387,6 +387,107 @@ public function test_get_skips_empty_url(): void {
$this->assertEquals( array(), ( new MslsOutput( $options, $collection ) )->get( 0 ) );
}
+ private function stub_options_create(): void {
+ Functions\expect( 'is_admin' )->andReturn( false );
+ Functions\expect( 'is_front_page' )->andReturn( false );
+ Functions\expect( 'is_search' )->andReturn( false );
+ Functions\expect( 'is_404' )->andReturn( false );
+ Functions\expect( 'is_category' )->andReturn( false );
+ Functions\expect( 'is_tag' )->andReturn( false );
+ Functions\expect( 'is_tax' )->andReturn( false );
+ Functions\expect( 'is_date' )->andReturn( false );
+ Functions\expect( 'is_author' )->andReturn( false );
+ Functions\expect( 'is_post_type_archive' )->andReturn( false );
+ Functions\expect( 'get_queried_object_id' )->andReturn( 42 );
+ Functions\expect( 'get_option' )->andReturn( array() );
+ }
+
+ public function test_get_languages_empty(): void {
+ $collection = \Mockery::mock( MslsBlogCollection::class );
+ $collection->shouldReceive( 'get_filtered' )->once()->with( false )->andReturn( array() );
+
+ $options = \Mockery::mock( MslsOptions::class );
+
+ $this->assertEquals( array(), ( new MslsOutput( $options, $collection ) )->get_languages() );
+ }
+
+ public function test_get_languages(): void {
+ $blog_de = \Mockery::mock( MslsBlog::class );
+ $blog_de->userblog_id = 2;
+ $blog_de->shouldReceive( 'get_language' )->andReturn( 'de_DE' );
+ $blog_de->shouldReceive( 'get_alpha2' )->andReturn( 'de' );
+ $blog_de->shouldReceive( 'get_description' )->andReturn( 'Deutsch' );
+
+ $blog_en = \Mockery::mock( MslsBlog::class );
+ $blog_en->userblog_id = 1;
+ $blog_en->shouldReceive( 'get_language' )->andReturn( 'en_US' );
+ $blog_en->shouldReceive( 'get_alpha2' )->andReturn( 'en' );
+ $blog_en->shouldReceive( 'get_description' )->andReturn( 'English' );
+
+ $options = \Mockery::mock( MslsOptions::class );
+ $options->shouldReceive( 'get_flag_url' )->andReturnUsing(
+ function ( string $language ): string {
+ return "https://example.com/flags/{$language}.png";
+ }
+ );
+
+ $collection = \Mockery::mock( MslsBlogCollection::class );
+ $collection->shouldReceive( 'get_filtered' )->once()->with( false )->andReturn( array( $blog_de, $blog_en ) );
+ $collection->shouldReceive( 'is_current_blog' )->with( $blog_de )->andReturn( false );
+ $collection->shouldReceive( 'is_current_blog' )->with( $blog_en )->andReturn( true );
+
+ $this->stub_options_create();
+ Functions\expect( 'switch_to_blog' )->once()->with( 2 );
+ Functions\expect( 'restore_current_blog' )->once();
+ Functions\expect( 'home_url' )->andReturn( 'https://example.com/' );
+ Functions\expect( 'get_permalink' )->andReturn( 'https://example.com/post' );
+
+ $result = ( new MslsOutput( $options, $collection ) )->get_languages();
+
+ $this->assertCount( 2, $result );
+
+ $this->assertEquals( 'de_DE', $result[0]['locale'] );
+ $this->assertEquals( 'de', $result[0]['alpha2'] );
+ $this->assertEquals( 'Deutsch', $result[0]['label'] );
+ $this->assertNotEmpty( $result[0]['url'] );
+ $this->assertEquals( 'https://example.com/flags/de_DE.png', $result[0]['flag_url'] );
+ $this->assertFalse( $result[0]['current'] );
+
+ $this->assertEquals( 'en_US', $result[1]['locale'] );
+ $this->assertEquals( 'en', $result[1]['alpha2'] );
+ $this->assertEquals( 'English', $result[1]['label'] );
+ $this->assertTrue( $result[1]['current'] );
+ }
+
+ public function test_get_languages_skips_empty_url(): void {
+ $blog = \Mockery::mock( MslsBlog::class );
+ $blog->userblog_id = 2;
+ $blog->shouldReceive( 'get_language' )->andReturn( 'fr_FR' );
+
+ $options = \Mockery::mock( MslsOptions::class );
+ $options->shouldReceive( 'get_flag_url' )->andReturn( '' );
+
+ $collection = \Mockery::mock( MslsBlogCollection::class );
+ $collection->shouldReceive( 'get_filtered' )->once()->andReturn( array( $blog ) );
+ $collection->shouldReceive( 'is_current_blog' )->with( $blog )->andReturn( false );
+
+ $this->stub_options_create();
+ Functions\expect( 'switch_to_blog' )->once()->with( 2 );
+ Functions\expect( 'restore_current_blog' )->once();
+ Functions\expect( 'home_url' )->andReturn( '' );
+
+ $this->assertEmpty( ( new MslsOutput( $options, $collection ) )->get_languages() );
+ }
+
+ public function test_get_languages_filter(): void {
+ $collection = \Mockery::mock( MslsBlogCollection::class );
+ $collection->shouldReceive( 'get_filtered' )->once()->with( true )->andReturn( array() );
+
+ $options = \Mockery::mock( MslsOptions::class );
+
+ $this->assertEquals( array(), ( new MslsOutput( $options, $collection ) )->get_languages( true ) );
+ }
+
public function test_init(): void {
Functions\expect( '_deprecated_function' )->once();