Help us improve
Share bugs, ideas, or general feedback.
From marko-skills
Creates Marko plugins — classes that intercept method arguments or return values via #[Before] and #[After] attributes, without subclassing. Auto-discovered from src/.
npx claudepluginhub marko-php/marko --plugin marko-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/marko-skills:create-pluginThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **This skill is the canonical specification for a Marko plugin. Do not inspect existing plugins in this project to infer structure — siblings may have drifted from spec. Copy the templates from `assets/` verbatim, substitute placeholders, and stop.**
Implements Magento 2 plugins (interceptors) with before, after, and around methods to modify class behavior without inheritance. Use for extending core or third-party modules.
Scaffold a new Marko Composer package with composer.json, namespaced src/, Pest tests, and optional module.php.
Guides plugin architecture, component selection, and directory structure for Claude Code, Codex CLI, and Antigravity. Use when planning, creating, or reviewing a plugin.
Share bugs, ideas, or general feedback.
This skill is the canonical specification for a Marko plugin. Do not inspect existing plugins in this project to infer structure — siblings may have drifted from spec. Copy the templates from
assets/verbatim, substitute placeholders, and stop.
A plugin is a class that intercepts the input or output of a public method on any
other class — without replacing that class. Plugins are Marko's fine-grained
extensibility primitive. They are auto-discovered from any module's src/
directory; no manual registration is needed.
Use a plugin when the user wants to modify arguments to, or the result from, a public method on a class without rewriting or subclassing it. Common cases:
For total replacement of a class, use a Preference instead — that is a separate
skill (marko-create-preference). Plugins and Preferences are complementary; only
Preferences swap entire implementations.
Marko supports exactly two plugin types. A third type that wraps the call is intentionally absent — anything it could do is expressed as a Before (short-circuit) or After (result transformation), keeping the call stack debuggable.
| Attribute | When | What it does |
|---|---|---|
#[Before] | Before the target method | Modify args, short-circuit, or pass through |
#[After] | After the target method | Receive and modify the return value |
| Return value | Effect |
|---|---|
null | Pass-through — original method runs with original args |
array | Replace arguments — original method runs with these args |
| Any other non-null value | Short-circuit — original method is NOT called |
After plugins receive $result (the return value of the original method or a
prior After plugin) as their first parameter, followed by the (possibly modified)
original arguments. Each After plugin's return value feeds the next After plugin
in sort order — always return the (possibly modified) result.
sortOrder is defined on the method-level attribute, not the class. Lower
numbers run first; negatives are valid; default is 0.
#[Before(sortOrder: -10)] // runs before plugins at default 0
#[After(sortOrder: 100)] // runs after lower-priority Afters
Determine which class and public method to intercept. Plugins cannot intercept
protected or private methods, and the target class must not be final.
Copy assets/PluginClass.php.tmpl verbatim. Substitute:
| Placeholder | Value |
|---|---|
{{Vendor}} | Composer vendor namespace (e.g., App) |
{{Name}} | Module name (e.g., Blog) |
{{TargetClass}} | Unqualified class name (e.g., PostRepository) |
Place the file at src/Plugins/{{TargetClass}}Plugin.php inside the module.
Add the correct use statement for the fully-qualified target class.
Remove any #[Before] or #[After] methods that are not needed for this plugin —
the template shows both for illustration.
Name each plugin method identically to the target method it intercepts (or use the
method: argument on the attribute to override when names would collide).
Apply the correct return semantics from the table above.
Copy assets/PluginTest.php.tmpl verbatim. Substitute the same placeholders.
Adjust test cases to reflect the actual behavior being intercepted (pass-through, argument modification, short-circuit, result enrichment).
src/ — src/Plugins/ is conventional.module.php — discovery is automatic.After writing files, expect LSP diagnostics from marko-lsp. Resolve all
diagnostics before declaring the plugin complete — diagnostics are the verification
gate. Then call the find_plugins_targeting MCP tool with the target class to
confirm the new plugin is discovered.
public on the target classfinal (Marko avoids final for this reason)readonly when they have no mutable statedeclare(strict_types=1) alwaysmarko-create-modulemarko-create-preference)#[Observer], a different mechanism