From 5b518f6e206d35fc39aeaa5d68bcde458f200f25 Mon Sep 17 00:00:00 2001 From: Ben Selby Date: Wed, 25 Feb 2026 19:19:13 +0000 Subject: [PATCH] Provide the ability to have platform specific paths in settings Resolves #183 --- README.md | 24 +++++++++++ phpcs.py | 23 ++++++++++- phpcs.sublime-settings | 42 ++++++++++++++++++++ tests/test_phpcs.py | 90 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 176 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59e304f..10fd865 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,30 @@ Your .project file should look something like this: Of course this is a example to apply Drupal code sniffer. This could be anything. Whatever you can have on this package settings it can be overwritten under the settings -> phpcs +### Multi-Platform Settings + +If you use this plugin across multiple operating systems (e.g. syncing settings between Windows and Linux), you can specify platform-specific values for any path setting by using a dictionary instead of a string. The supported platform keys are `"windows"`, `"linux"`, and `"osx"`. You can also specify a `"default"` key as a fallback for platforms not explicitly listed. + +```json +{ + "phpcs_executable_path": { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "linux": "/usr/bin/phpcs", + "osx": "/usr/local/bin/phpcs" + }, + "php_cs_fixer_executable_path": { + "windows": "c:\\xampp\\php\\bin\\php-cs-fixer", + "default": "/usr/local/bin/php-cs-fixer" + }, + "phpcs_php_prefix_path": { + "windows": "c:\\xampp\\php\\bin\\php", + "default": "" + } +} +``` + +This works for all path settings: `phpcs_executable_path`, `phpcs_php_prefix_path`, `phpcs_php_path`, `php_cs_fixer_executable_path`, `phpcbf_executable_path`, and `phpmd_executable_path`. Plain string values continue to work as before. + ## FAQ ### What do I do when I get "OSError: [Errno 8] Exec format error"? diff --git a/phpcs.py b/phpcs.py index 8622ce2..a9fc217 100644 --- a/phpcs.py +++ b/phpcs.py @@ -75,9 +75,28 @@ def load(self): def get(self, key): if key in self.project_settings: - return self.project_settings.get(key) + raw = self.project_settings.get(key) else: - return self.settings.get(key) + raw = self.settings.get(key) + return self._resolve_platform_value(raw) + + def _resolve_platform_value(self, value): + """ + If value is a dict, resolve it to the value for the current platform. + Supports keys: "osx", "linux", "windows", and "default" as a fallback. + If value is not a dict, return it as-is for backward compatibility. + """ + if isinstance(value, dict) and any( + k in value for k in ("osx", "linux", "windows", "default") + ): + platform = sublime.platform() + if platform in value: + return value[platform] + elif "default" in value: + return value["default"] + else: + return "" + return value def set(self, key, value): if key in self.project_settings: diff --git a/phpcs.sublime-settings b/phpcs.sublime-settings index 29dc4f5..9f24822 100644 --- a/phpcs.sublime-settings +++ b/phpcs.sublime-settings @@ -33,6 +33,13 @@ // The path to the php executable. // Needed for windows, or anyone who doesn't/can't make phars // executable. Avoid setting this if at all possible + // + // You can use a dictionary to specify different paths per platform: + // "phpcs_php_prefix_path": { + // "windows": "c:\\xampp\\php\\bin\\php", + // "linux": "/usr/bin/php", + // "osx": "/usr/local/bin/php" + // } "phpcs_php_prefix_path": "", // Options include: @@ -61,6 +68,13 @@ // It seems python/sublime cannot always find the phpcs application // If empty, then use PATH version of phpcs, else use the set value + // + // You can use a dictionary to specify different paths per platform: + // "phpcs_executable_path": { + // "windows": "c:\\xampp\\php\\bin\\phpcs", + // "linux": "/usr/bin/phpcs", + // "osx": "/usr/local/bin/phpcs" + // } "phpcs_executable_path": "", // Additional arguments you can specify into the application @@ -84,6 +98,13 @@ "php_cs_fixer_show_quick_panel": false, // Path to where you have the php-cs-fixer installed + // + // You can use a dictionary to specify different paths per platform: + // "php_cs_fixer_executable_path": { + // "windows": "c:\\xampp\\php\\bin\\php-cs-fixer", + // "linux": "/usr/local/bin/php-cs-fixer", + // "osx": "/usr/local/bin/php-cs-fixer" + // } "php_cs_fixer_executable_path": "", // Additional arguments you can specify into the application @@ -98,6 +119,13 @@ "phpcbf_show_quick_panel": false, // Path to where you have the phpcbf installed + // + // You can use a dictionary to specify different paths per platform: + // "phpcbf_executable_path": { + // "windows": "c:\\xampp\\php\\bin\\phpcbf", + // "linux": "/usr/local/bin/phpcbf", + // "osx": "/usr/local/bin/phpcbf" + // } "phpcbf_executable_path": "", // Additional arguments you can specify into the application @@ -121,6 +149,13 @@ // It seems python/sublime cannot always find the php application // If empty, then use PATH version of php, else use the set value + // + // You can use a dictionary to specify different paths per platform: + // "phpcs_php_path": { + // "windows": "c:\\xampp\\php\\bin\\php", + // "linux": "/usr/bin/php", + // "osx": "/usr/local/bin/php" + // } "phpcs_php_path": "", // What is the regex for the linter? Has to provide a named match for 'message' and 'line' @@ -136,6 +171,13 @@ // It seems python/sublime cannot always find the phpmd application // If empty, then use PATH version of phpmd, else use the set value + // + // You can use a dictionary to specify different paths per platform: + // "phpmd_executable_path": { + // "windows": "c:\\xampp\\php\\bin\\phpmd", + // "linux": "/usr/local/bin/phpmd", + // "osx": "/usr/local/bin/phpmd" + // } "phpmd_executable_path": "", // Additional arguments you can specify into the application diff --git a/tests/test_phpcs.py b/tests/test_phpcs.py index 76574b1..2eea02a 100644 --- a/tests/test_phpcs.py +++ b/tests/test_phpcs.py @@ -3,7 +3,7 @@ import sublime -from Phpcs.phpcs import Sniffer +from Phpcs.phpcs import Pref, Sniffer class TestSniffer(TestCase): @@ -37,3 +37,91 @@ def test_we_can_parse_phpcs_standards_output(self, shell_mock): expected = ["One", "NeutronStandard", "Two", "Three"] self.assertEqual(expected, standards) + + +class TestPrefPlatformResolution(TestCase): + def setUp(self): + self.pref = Pref() + + def test_string_value_passes_through_unchanged(self): + result = self.pref._resolve_platform_value("/usr/bin/phpcs") + self.assertEqual("/usr/bin/phpcs", result) + + def test_empty_string_passes_through_unchanged(self): + result = self.pref._resolve_platform_value("") + self.assertEqual("", result) + + def test_non_platform_dict_passes_through_unchanged(self): + value = {"--standard": "PSR2", "-n": ""} + result = self.pref._resolve_platform_value(value) + self.assertEqual(value, result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="osx") + def test_dict_resolves_to_osx_value(self, _): + value = { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "linux": "/usr/bin/phpcs", + "osx": "/usr/local/bin/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("/usr/local/bin/phpcs", result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="linux") + def test_dict_resolves_to_linux_value(self, _): + value = { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "linux": "/usr/bin/phpcs", + "osx": "/usr/local/bin/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("/usr/bin/phpcs", result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="windows") + def test_dict_resolves_to_windows_value(self, _): + value = { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "linux": "/usr/bin/phpcs", + "osx": "/usr/local/bin/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("c:\\xampp\\php\\bin\\phpcs", result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="osx") + def test_dict_falls_back_to_default_when_platform_missing(self, _): + value = { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "default": "/usr/bin/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("/usr/bin/phpcs", result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="osx") + def test_dict_returns_empty_string_when_platform_and_default_missing(self, _): + value = { + "windows": "c:\\xampp\\php\\bin\\phpcs", + "linux": "/usr/bin/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("", result) + + @patch("Phpcs.phpcs.sublime.platform", return_value="linux") + def test_dict_prefers_platform_over_default(self, _): + value = { + "linux": "/usr/bin/phpcs", + "default": "/opt/phpcs", + } + result = self.pref._resolve_platform_value(value) + self.assertEqual("/usr/bin/phpcs", result) + + def test_none_value_passes_through(self): + result = self.pref._resolve_platform_value(None) + self.assertIsNone(result) + + def test_list_value_passes_through(self): + value = ["Sniffer", "Fixer"] + result = self.pref._resolve_platform_value(value) + self.assertEqual(["Sniffer", "Fixer"], result) + + def test_boolean_value_passes_through(self): + result = self.pref._resolve_platform_value(True) + self.assertTrue(result)