From c77be9c812c71b49e80666ebfebee6a8b9375f58 Mon Sep 17 00:00:00 2001 From: Dennis Ploetner Date: Sat, 22 Aug 2026 19:59:36 +0200 Subject: [PATCH] Version 3.0.1 --- .phpstan.neon.dist | 4 ++- AGENTS.md | 4 +-- MslsMenu.php | 50 +++++++++---------------------------- README.md | 2 +- composer.json | 10 +++++++- languages/mslsmenu.pot | 4 +-- readme.txt | 13 +++++----- tests/Pest.php | 14 +++-------- tests/ValidObjectTest.php | 29 ++++++++++++++++++--- tests/phpstan-bootstrap.php | 21 ++++++++++++++++ 10 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 tests/phpstan-bootstrap.php diff --git a/.phpstan.neon.dist b/.phpstan.neon.dist index 2ea6182..5ae6b2b 100644 --- a/.phpstan.neon.dist +++ b/.phpstan.neon.dist @@ -2,5 +2,7 @@ parameters: level: 5 paths: - MslsMenu.php + scanDirectories: + - vendor/lloc/multisite-language-switcher/includes bootstrapFiles: - - tests/Pest.php + - tests/phpstan-bootstrap.php diff --git a/AGENTS.md b/AGENTS.md index 786cc06..69a3047 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ - `composer qa` runs the full gate: `phpcs`, `phpstan`, then `pest`. - `composer pest` executes the Pest suite with mocked WordPress APIs. - `composer phpcs` checks the WordPress coding standards and PHP 7.4 compatibility via `.phpcs.xml.dist`; `composer phpcbf` fixes what can be fixed automatically. -- `composer analyze` runs PHPStan (level 5) against `MslsMenu.php`; keep it passing before pushing. +- `composer analyze` runs PHPStan (level 5) against `MslsMenu.php`; keep it passing before pushing. It analyses against the real Multisite Language Switcher, pulled in as a dev dependency and bootstrapped in `tests/phpstan-bootstrap.php` — the stubs in `tests/Pest.php` keep the unit tests WordPress-free but do not reflect the MSLS API. - `composer pest:coverage` enables Xdebug coverage for pull request evidence. - `composer build` runs `composer update --no-dev` and then `bin/git-release.sh`, recreating `mslsmenu/` and `mslsmenu.zip`. It strips your local dev dependencies — run `composer install` afterwards to get the tooling back. @@ -35,4 +35,4 @@ ## Release Packaging Tips - Bump the plugin header version in `MslsMenu.php` and the `Stable tag` in `readme.txt` to the same value, and add the matching `= x.y.z =` entry to the `readme.txt` changelog. WordPress.org rejects the release if header and stable tag disagree. - Run `composer build` locally only to inspect the result. The shipped artifact is built in CI: `.github/workflows/deploy.yml` runs the same `composer build`, verifies the contents and deploys them through `BUILD_DIR: mslsmenu`. -- Tag without a `v` prefix (`3.0.0`) and push the tag — that push is what triggers the deploy workflow. +- Tag without a `v` prefix (`3.0.1`) and push the tag — that push is what triggers the deploy workflow. diff --git a/MslsMenu.php b/MslsMenu.php index e722c2f..286402a 100644 --- a/MslsMenu.php +++ b/MslsMenu.php @@ -9,7 +9,7 @@ * * Plugin Name: MslsMenu * Requires Plugins: multisite-language-switcher - * Version: 3.0.0 + * Version: 3.0.1 * Plugin URI: https://wordpress.org/plugins/mslsmenu/ * Description: Adds the Multisite Language Switcher to the primary-nav-menu * Author: Dennis Ploetner @@ -82,8 +82,8 @@ public static function init( $options ): MslsMenu { return $obj; } - private function get_msls_output(): lloc\Msls\MslsOutput { - return function_exists( 'msls_output' ) ? msls_output() : lloc\Msls\MslsOutput::init(); + private function get_msls_output(): lloc\Msls\Frontend\Output { + return msls_output(); } /** @@ -102,7 +102,7 @@ public function nav_item( string $items, \stdClass $args ): string { $menu = ''; $obj = $this->get_msls_output(); - foreach ( $obj->get( (int) $this->options->mslsmenu_display, false, (int) $this->options->only_with_translation ) as $item ) { + foreach ( $obj->get( (int) $this->options->mslsmenu_display, false, (bool) $this->options->only_with_translation ) as $item ) { $menu .= $this->options->mslsmenu_before_item . $item . $this->options->mslsmenu_after_item; } @@ -129,15 +129,13 @@ public function admin_register( string $page ) { * Callback for add_settings_section in admin_register */ public function add_settings(): void { - $args = array( 'msls_admin' => lloc\Msls\MslsAdmin::init() ); - $label = __( 'Theme Location', 'mslsmenu' ); $callback = array( $this, 'theme_location' ); - add_settings_field( 'mslsmenu_theme_location', $label, $callback, $this->page, self::SID, $args ); + add_settings_field( 'mslsmenu_theme_location', $label, $callback, $this->page, self::SID ); $label = __( 'Display', 'mslsmenu' ); $callback = array( $this, 'display' ); - add_settings_field( 'mslsmenu_display', $label, $callback, $this->page, self::SID, $args ); + add_settings_field( 'mslsmenu_display', $label, $callback, $this->page, self::SID ); $fields = array( 'mslsmenu_before_output' => __( 'Text/HTML before the list', 'mslsmenu' ), @@ -148,7 +146,7 @@ public function add_settings(): void { $callback = array( $this, 'input' ); foreach ( $fields as $id => $label ) { - $args['mslsmenu_input'] = $id; + $args = array( 'mslsmenu_input' => $id ); add_settings_field( $id, $label, $callback, $this->page, self::SID, $args ); } } @@ -188,23 +186,10 @@ protected function selected( string $needle, $locations ): string { * * @param array $args */ - public function display( array $args ) { - $types = lloc\Msls\MslsLink::get_types_description(); + public function display( array $args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Signature is dictated by add_settings_field(). + $types = lloc\Msls\Link\Link::get_types_description(); $display = $this->options->mslsmenu_display ?? '0'; - /** - * Backward compatibility - */ - if ( ! class_exists( lloc\Msls\Component\Input\Select::class ) ) { - // @codeCoverageIgnoreStart - - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo $args['msls_admin']->render_select( 'mslsmenu_display', $types, $display ); - - return; - // @codeCoverageIgnoreEnd - } - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ( new lloc\Msls\Component\Input\Select( 'mslsmenu_display', $types, $display ) )->render(); } @@ -215,19 +200,6 @@ public function display( array $args ) { * @param array $args */ public function input( array $args ) { - /** - * Backward compatibility - */ - if ( ! class_exists( 'lloc\Msls\Component\Input\Text' ) ) { - // @codeCoverageIgnoreStart - - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo $args['msls_admin']->render_input( $args['mslsmenu_input'] ); - - return; - // @codeCoverageIgnoreEnd - } - $key = $args['mslsmenu_input'] ?? ''; $value = $this->options->$key ?? ''; @@ -241,7 +213,9 @@ public function input( array $args ) { add_action( 'plugins_loaded', function () { - $options = class_exists( lloc\Msls\MslsOptions::class ) ? lloc\Msls\MslsOptions::instance() : null; + // The namespaced class only exists from MSLS 3.0 on. Against an older MSLS the + // null object keeps every hook unregistered instead of fataling on a missing type. + $options = class_exists( lloc\Msls\Options\Options::class ) ? msls_options() : null; MslsMenu::init( $options ); } ); diff --git a/README.md b/README.md index 1640b8a..9e35b60 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ MslsMenu MslsMenu extends the Multisite Language Switcher by appending its language links to the navigation menus of your choice. Use it when you want the switcher inside a nav menu without editing your theme or writing a custom walker. -Multisite Language Switcher has to be installed and active first — MslsMenu is an add-on and declares that through its `Requires Plugins` header. +Multisite Language Switcher **3.0 or newer** has to be installed and active first — MslsMenu is an add-on and declares that through its `Requires Plugins` header. That header carries no version constraint, so against an older MSLS the plugin stays inactive rather than raising an error. ## Install - From WordPress: search for “MslsMenu” under Plugins → Add New and activate it. diff --git a/composer.json b/composer.json index 3acf47c..bcac911 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "wp-coding-standards/wpcs": "^3.1", "phpcompatibility/phpcompatibility-wp": "^2.1", - "lloc/composer-i18n-scripts": "^1.0" + "lloc/composer-i18n-scripts": "^1.0", + "lloc/multisite-language-switcher": "^3.0" }, "scripts": { "phpcs": "phpcs", @@ -60,5 +61,12 @@ "pestphp/pest-plugin": true, "lloc/composer-i18n-scripts": true } + }, + "extra": { + "installer-paths": { + "vendor/{$vendor}/{$name}/": [ + "type:wordpress-plugin" + ] + } } } diff --git a/languages/mslsmenu.pot b/languages/mslsmenu.pot index c3cb1af..b8d22f8 100644 --- a/languages/mslsmenu.pot +++ b/languages/mslsmenu.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: MslsMenu 3.0.0\n" +"Project-Id-Version: MslsMenu 3.0.1\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/MslsMenu\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-08-22T14:16:27+00:00\n" +"POT-Creation-Date: 2026-08-22T14:39:19+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.11.0\n" "X-Domain: mslsmenu\n" diff --git a/readme.txt b/readme.txt index 3c02f8b..92b5a24 100644 --- a/readme.txt +++ b/readme.txt @@ -6,7 +6,7 @@ Tags: multilingual, multisite, language, switcher, menu Requires at least: 6.1 Tested up to: 7.1 Requires PHP: 7.4 -Stable tag: 3.0.0 +Stable tag: 3.0.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -35,10 +35,11 @@ But this can lead to fatal errors if you don't know much about PHP, or maybe the == Changelog == -= 3.0.0 = -* Changed: version aligned with Multisite Language Switcher 3.0 - this is the add-on release for the new MSLS major version. += 3.0.1 = +* Changed: the version number now matches Multisite Language Switcher 3.0.1 - this is the add-on release for the new MSLS major version. * Changed: requires WordPress 6.1 or newer, tested up to 7.1. -* Internal: the release is deployed from the built distribution, the same artifact Plugin Check verifies. Build-only dependencies are no longer shipped. +* Changed: requires Multisite Language Switcher 3.0 or newer. MslsMenu now calls the namespaced MSLS classes instead of the pre-3.0 compatibility aliases. With an older MSLS it stays inactive instead of raising an error. +* Internal: the release is deployed from the built distribution, the same artifact Plugin Check verifies. Build-only dependencies are no longer shipped. Static analysis now runs against the real MSLS classes instead of test stubs. = 2.5.1 = * plugin check integration added @@ -90,5 +91,5 @@ But this can lead to fatal errors if you don't know much about PHP, or maybe the == Upgrade Notice == -= 3.0.0 = -Version alignment with Multisite Language Switcher 3.0. Requires WordPress 6.1 or newer. += 3.0.1 = +Version alignment with Multisite Language Switcher 3.0.1. Requires MSLS 3.0 or newer and WordPress 6.1 or newer. diff --git a/tests/Pest.php b/tests/Pest.php index ccfe6a3..5e270fc 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -7,13 +7,6 @@ public static function instance() { } } -class Admin { - - public static function init() { - return new self(); - } -} - class Link { public static function get_types_description() { @@ -50,9 +43,8 @@ public function render(): string { } } -class_alias( Options::class, 'lloc\Msls\MslsOptions' ); -class_alias( Admin::class, 'lloc\Msls\MslsAdmin' ); -class_alias( Link::class, 'lloc\Msls\MslsLink' ); -class_alias( Output::class, 'lloc\Msls\MslsOutput' ); +class_alias( Options::class, 'lloc\Msls\Options\Options' ); +class_alias( Link::class, 'lloc\Msls\Link\Link' ); +class_alias( Output::class, 'lloc\Msls\Frontend\Output' ); class_alias( Select::class, 'lloc\Msls\Component\Input\Select' ); class_alias( Text::class, 'lloc\Msls\Component\Input\Text' ); diff --git a/tests/ValidObjectTest.php b/tests/ValidObjectTest.php index 8e39cf8..1a815e4 100644 --- a/tests/ValidObjectTest.php +++ b/tests/ValidObjectTest.php @@ -21,7 +21,7 @@ function () { beforeEach( function () { - $options = \Mockery::mock( 'lloc\Msls\MslsOptions' ); + $options = \Mockery::mock( 'lloc\Msls\Options\Options' ); $options->mslsmenu_theme_location = array( 'test' ); $this->sut = \MslsMenu::init( $options ); @@ -69,8 +69,8 @@ function () { function () { $expected = ''; - $args = new \stdClass(); - $args->theme_locations = 'test'; + $args = new \stdClass(); + $args->theme_location = 'other'; $result = $this->sut->nav_item( '', $args ); @@ -123,3 +123,26 @@ function () { $this->expectOutputString( $expected ); } ); + +it( + 'appends the switcher on $sut->nav_item() when the theme location matches', + function () { + Functions\when( 'msls_output' )->justReturn( new \Output() ); + + $options = \Mockery::mock( 'lloc\Msls\Options\Options' ); + $options->mslsmenu_theme_location = array( 'primary' ); + $options->mslsmenu_display = 0; + $options->only_with_translation = false; + $options->mslsmenu_before_output = '
    '; + $options->mslsmenu_after_output = '
'; + $options->mslsmenu_before_item = '
  • '; + $options->mslsmenu_after_item = '
  • '; + + $sut = \MslsMenu::init( $options ); + + $args = new \stdClass(); + $args->theme_location = 'primary'; + + expect( $sut->nav_item( '', $args ) )->toEqual( '
    • de
    • en
    ' ); + } +); diff --git a/tests/phpstan-bootstrap.php b/tests/phpstan-bootstrap.php new file mode 100644 index 0000000..4f59036 --- /dev/null +++ b/tests/phpstan-bootstrap.php @@ -0,0 +1,21 @@ +