Skip to content

Put every plugin class into its own FOG\Plugins\<Plugin> namespace - #33

Merged
mastacontrola merged 1 commit into
mainfrom
namespace-the-plugins
Aug 31, 2026
Merged

Put every plugin class into its own FOG\Plugins\<Plugin> namespace#33
mastacontrola merged 1 commit into
mainfrom
namespace-the-plugins

Conversation

@mastacontrola

Copy link
Copy Markdown
Member

Companion to FOGProject/fogproject#1535, which teaches core to resolve a namespaced plugin class. That PR must merge, release and be pinned first — a plugin declaring FOG\Plugins\… does not resolve on a core without its autoload arm.

What this does

All 176 class-declaring files across 15 plugins now declare:

namespace FOG\Plugins\<Ucfirst-plugin-directory-name>;

ldap/FOG\Plugins\Ldap, helloworld/FOG\Plugins\Helloworld. Mechanical ucfirst() of the directory name — no lookup table, no exceptions. The subdirectory (class/, pages/, hooks/, events/, reports/, tasks/, capone's reg-task/) is not part of it: every class in a plugin shares one flat namespace, because FOGController::getManager() is qualify(shortName($this) . 'Manager') and a model and its manager have to resolve together.

File layout is untouched — the discovery suffixes are how a page, hook, event, report or task is found.

Why the diff is one line per file

  • All 403 core references were already leading-backslash FQCNs (\FOG\Base\FOGController), pinned by tests/core-references-are-qualified.test.php. A leading backslash means the same thing inside a namespace, so not one of them changed.
  • Every catch was already qualified — 24 \Exception, one \TypeError, one \Throwable.
  • self::getClass('X') string literals are untouched (~150 of them). Core's FOGBase::qualify() resolves a plugin short name to its new FQCN, so they keep working as written.
  • A per-file sweep for bare PHP built-ins and cross-plugin bare references found nothing to fix.
  • No class_alias() is added anywhere — that is the point of the core change.

Also in here

  • helloworld/class/helloworld.class.php had a stale docblock claiming the class is found by PHP's default spl_autoload lowercasing the filename. That fallback was removed from core (ADR 0013 §2b) because it let a plugin shadow a core class. It now describes Initiator::autoload() and the new namespace.
  • tests/oidc-flow-safety.test.php and tests/oidc-provider-safety.test.php require OIDC plugin source directly and called OIDC/OIDCManager/OIDCGroupManager/OIDCUserGrantManager bare. Both now qualify those references — without it, two tests went from green to a fatal Class "OIDC" not found.

Gate

tests/plugins-are-namespaced.test.php (new) asserts, for every class file under every plugin directory: exactly one namespace declaration; that it equals FOG\Plugins\ + the plugin directory, case-insensitively (PHP namespaces are, and a third-party plugin may spell it MyPlugin for directory myplugin); and that the file declares no class_alias(). It runs with no fogproject checkout, like its sibling.

Mutation-verified, both arms observed red:

- ldap/class/ldapmanager.class.php: declares no namespace (expected fog\plugins\ldap)
- ldap/class/ldapmanager.class.php: declares namespace 'FOG\Plugins\Oidc', expected 'fog\plugins\ldap' (case-insensitive)

Restored by copy, not git checkout --, and verified byte-identical.

Verification

  • All 176 touched files plus the three test files pass php -l.
  • Full suite 14/14.
  • Loaded against the modified core with this tree as an external plugin root: all 176 classes resolve under their FQCN, 176 unique short names, and every bare name qualifies to its own FQCN.

Still outstanding, and it needs a running server: render every plugin page, install/uninstall a plugin, hit a plugin REST route, run a plugin task through PluginRunner.

Every class/interface/trait-declaring file under a plugin directory
(*.class.php, *.hook.php, *.page.php, *.event.php, *.report.php,
*.task.php -- 176 files across 15 plugins) now declares
`namespace FOG\Plugins\<Ucfirst-plugin-directory-name>;`, mechanically
derived from the plugin's directory name with no exceptions. The
subdirectory (class/, pages/, hooks/, events/, reports/, tasks/,
capone's odd reg-task/) is not part of it, so every class in a plugin
shares one flat namespace.

Core references stay exactly as they were -- already leading-backslash
FQCNs (fogproject ADR 0013 §2/§13) -- and self::getClass() string
literals are untouched, since core's FOGBase::qualify() already
resolves a plugin short name to its new FQCN. A per-file bare-builtin
and cross-plugin-reference sweep found nothing to fix; the source was
already clean.

Fixed the stale helloworld docblock claiming the class is found by
PHP's default spl_autoload lowercasing the filename -- that fallback
was removed from core (ADR 0013 §2b) because it let a plugin shadow a
core class. It documents Initiator::autoload() and the new namespace
instead.

tests/oidc-flow-safety.test.php and tests/oidc-provider-safety.test.php
require the OIDC plugin's source directly and call OIDC/OIDCManager/
OIDCGroupManager/OIDCUserGrantManager by bare name -- both now qualify
those references, matching the FQCN convention everywhere else.

Added tests/plugins-are-namespaced.test.php: for every class file under
every plugin directory, asserts exactly one namespace declaration,
that it matches FOG\Plugins\<Plugin> case-insensitively (a third-party
plugin may spell it differently from a strict ucfirst()), and that the
file declares no class_alias(). Mutation-verified by copying
ldap/class/ldapmanager.class.php aside, removing its namespace line
(went red: "declares no namespace"), restoring, setting it to a wrong
namespace (went red: "declares namespace 'FOG\Plugins\Oidc', expected
'fog\plugins\ldap'"), then restoring the original file byte-for-byte.

Co-Authored-By: Claude <noreply@anthropic.com>
@mastacontrola
mastacontrola merged commit 8e6c007 into main Aug 31, 2026
2 checks passed
@mastacontrola
mastacontrola deleted the namespace-the-plugins branch September 1, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant