Kotlin, IntelliJ Platform Gradle Plugin 2.x, PhpStorm 2024.3+ (no until-build cap).
Version 0.4.0 is a PSI-first rebuild driven by the Publisher field review in
../xoops-support/00_docs/ (Addendum §B is the plan this
release implements).
- TUTORIAL.md — feature-by-feature walkthrough with expected results
- CHANGELOG.md — release history; rendered into the plugin's "What's New" panel at build time
- tasks/todo.md — the 0.4.0 plan and its review
The sibling xoops-support plugin applied 1,043 findings to a real module and left it
broken in seven different ways while every file still passed php -l. The cause was
structural: inspections and fixes worked on regex over file text. This plugin is built
so that class of failure cannot ship:
- PSI, not text. Receivers, statement boundaries, include targets, attribute context, comment/string/injected-fragment status all come from PhpStorm's PHP parser.
- No stored offsets. A fix gets the problem's PSI element and computes its edit from the live tree at apply time, after whatever earlier fixes in the batch did. Every fix re-validates the condition before editing and declines silently if it no longer holds.
- Golden files. Each inspection ships with a fixture module tree, an asserted problem
count, a whole-file
*.after.phponce every fix of the family has been applied through the IDE, and a second highlighting pass that must offer no further fix (idempotence).gradlew testruns the lot (src/test/kotlin/org/xoops/support/XoopsQuickFixTest.kt, fixtures generated bytools/gen_fixtures.py). - Unknown is not a finding. A
$sqlthe plugin cannot resolve is not a "mutating query"; a header file it has not read does not "already call" anything.
PhpStorm 2025.3 never hot-swaps an installed plugin through "Install Plugin from Disk": in
PluginInstaller.installFromDisk the dynamic path is taken only when no previous install exists
(oldPath == null && DynamicPlugins.allowLoadUnloadWithoutRestart(...)), otherwise the zip is staged
for the next restart. The plugin itself is unload-safe (every extension point it uses is dynamic;
idea.log shows "loaded without restart in 69 ms" on enable). Two ways to stay live:
- Uninstall, then install from disk. Both steps are dynamic for this plugin.
- Local plugin repository.
updatePlugins.xmlin this folder points at the current dev zip. Settings > Plugins > gear > Manage Plugin Repositories > addfile:///<path to this folder>/updatePlugins.xml, then the Installed tab offers "Update", which unloads the old version and loads the new one in place. Each build needs a higher version:gradlew buildPlugin -PbuildNumber=Nproduces0.7.2.N; update theversionandurlinupdatePlugins.xmlto match.
| Feature | Where | What it does |
|---|---|---|
| Settings page | Settings → Languages & Frameworks → XOOPS Support | Enable switch for the project, startup balloon, tool-window auto-scan (off by default), Core version override. Auto reads include/version.php of the nearest web root and the page shows what it found; a value such as 2.5.9 is what the version-gated quick-fixes (isResultSet() 2.5.11, exec() 2.5.12) see - the only way to tell the plugin about the target core of a standalone module checkout. |
| New → XOOPS Module… (also Tools → XOOPS Support) | project view context menu | Dialog with dirname validation and a layout choice. Legacy: manifest, index page, English language files, index template. Hybrid adds preloads/autoloader.php (PSR-4 XoopsModules\Cap\ → class/), class/Helper.php on Xmf\Module\Helper, include/common.php, and an Xmf\Module\Admin admin side. Every generated file passes this plugin's inspections; a test asserts it for both layouts. |
| Tools → XOOPS Support | main menu | Show XOOPS Project Info (scanner summary), Refresh XOOPS Overview (opens the tool window and rescans), New XOOPS Module…. |
| Ctrl+B / Find Usages on language constants | PHP strings, Smarty templates | '_MI_DEMO_NAME' as a string (manifest options, constant(), defined()) and $smarty.const._MD_X in a template navigate to the define(); several language packs give a chooser, same module and English first. Find Usages on the define() lists those uses. A bare _MI_X in PHP already resolves through PhpStorm. |
The plugin registers 52 inspections: security (CSRF, superglobals, SQL concatenation, escaping, tokens), the
database layer (queryF(), exec() vs query(), isResultSet() guards, installer SQL), module files and
bootstrap (XOOPS_ROOT_PATH guard, admin/menu.php autoloader, form loader, preload globals), templates and
Smarty (delimiters, registration, Smarty 3 / 5 migrations), language files, and deprecated core APIs.
INSPECTIONS.md lists every one of them with its default level, what it reports, what the
quick-fix changes and when it stays silent. It is written from the same descriptions PhpStorm shows in the
inspection settings (src/main/resources/inspectionDescriptions/), so the two stay in step.
Plus, unchanged from 0.3: project detection, the XOOPS tool window, language-constant completion and five live templates.
cd phpstorm-plugin
./gradlew test # golden-file suite + descriptor consistency
./gradlew buildPlugin # build/distributions/xoops-support-1.0.0-alpha.5.zip
./gradlew runIde # sandboxed PhpStorm with the plugin installed
Requirements: a JDK on PATH (the build provisions its own JDK 21 toolchain through the Foojay resolver) and network access on the first build (PhpStorm 2024.3.5 platform, cached afterwards). Install the zip with Settings → Plugins → gear → Install Plugin from Disk….
- Write it in
src/main/kotlin/org/xoops/support/inspections/on top ofXoopsInspection,psi/Php.ktandfix/Edits.kt. A fix derives everything fromdescriptor.psiElementand re-checks its precondition before editing. - Register it in
META-INF/plugin.xml, add its page totools/gen_descriptions.pyand run the script. - Add a fixture case to
tools/gen_fixtures.py(a real module layout, the.after.phpwhole file) and acheck(...)line toXoopsQuickFixTest. The descriptor test fails until step 2 is complete; the golden test fails until the fix is right and idempotent.
build.gradle.kts platform 2024.3.5, PHP plugin, test framework
src/main/kotlin/org/xoops/support/
psi/Php.kt, psi/XoopsPaths.kt shared PSI queries and path policy
fix/Edits.kt live-PSI document edits, XoopsFix base
inspections/ one file per inspection family
completion/, toolwindow/, XoopsProjectDetector.kt, XoopsSettings.kt
src/main/resources/META-INF/plugin.xml registrations
src/main/resources/inspectionDescriptions/ generated by tools/gen_descriptions.py
src/test/kotlin/org/xoops/support/ XoopsQuickFixTest, PluginDescriptorTest
src/test/resources/fixtures/ generated by tools/gen_fixtures.py