Registers new event hooks and creates static handler methods in src/Plugin.php with appropriate category guards.

Install

mkdir -p .claude/skills/plugin-hook && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12041" && unzip -o skill.zip -d .claude/skills/plugin-hook && rm skill.zip

Installs to .claude/skills/plugin-hook

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Adds a new event hook to src/Plugin.php — registers in getHooks() and creates the static GenericEvent handler. Use when user says 'add hook', 'handle event', 'new plugin event', or adds a lifecycle action (activate/deactivate/change IP/reactivate). Do NOT use for modifying existing handlers or adding inc functions.
316 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Decide event and handler names for a new hook
  • Register the new hook in `getHooks()` in `src/Plugin.php`
  • Add the handler method to `src/Plugin.php`
  • Update `tests/PluginTest.php` with the new hook key and method count
  • Ensure handler methods are `public static` and accept `GenericEvent $event`

How it works

The skill adds a new event hook to `getHooks()` and creates a corresponding static handler method in `src/Plugin.php`, then updates the test file to reflect these changes.

Inputs & outputs

You give it
Event name and handler name for a new plugin event
You get back
Modified `src/Plugin.php` with a new hook and handler, and updated `tests/PluginTest.php`

When to use plugin-hook

  • Add a new plugin event hook
  • Register lifecycle actions
  • Create static event handlers

About this skill

plugin-hook

Critical

  • All handler methods MUST be public static and accept exactly one GenericEvent $event parameter — testHookMethodsArePublicAndStatic and testEventHandlerMethodSignatures will fail otherwise.
  • Always guard the handler body with if ($event['category'] == get_service_define('PARALLELS')) before any logic.
  • Always call $event->stopPropagation() as the last statement inside the category guard.
  • Hook keys use self::$module . '.event_name' (e.g. 'licenses.change_ip') — never hardcode the module string.
  • The one exception is 'function.requirements' which uses a literal key, not the module prefix.
  • Run composer test after every change — a missing method or wrong signature will break testAllHookMethodsExist and testClassMethodCount.

Instructions

  1. Decide the event name and handler name. Event name is snake_case (e.g. change_ip). Handler is PascalCase prefixed with get (e.g. getChangeIp). Verify no existing entry in getHooks() already covers this event.

  2. Register the hook in getHooks() inside src/Plugin.php. Add one line to the returned array:

    self::$module.'.change_ip' => [__CLASS__, 'getChangeIp'],
    

    If the new event should share an existing handler (like reactivategetActivate), point to the existing method name instead of creating a new one. Verify the array key is unique before proceeding.

  3. Add the handler method to src/Plugin.php after the last existing handler, before the closing }:

    /**
     * @param \Symfony\Component\EventDispatcher\GenericEvent $event
     */
    public static function getChangeIp(GenericEvent $event)
    {
        if ($event['category'] == get_service_define('PARALLELS')) {
            $serviceClass = $event->getSubject();
            myadmin_log(self::$module, 'info', 'Parallels <Action Description>', __LINE__, __FILE__, self::$module, $serviceClass->getId());
            // ... call function_requirements() then the inc function ...
            $event->stopPropagation();
        }
    }
    

    The use Symfony\Component\EventDispatcher\GenericEvent; import is already present at the top — do not add it again.

  4. If the handler calls an inc function, add function_requirements('your_function') before the call and register the function path in getRequirements():

    $loader->add_requirement('your_function', '/../src/parallels.inc.php');
    

    Verify the function exists in src/parallels.inc.php before registering.

  5. Update tests/PluginTest.php — add the new hook key to the $expectedKeys array in testGetHooksContainsExpectedKeys() and bump the count in testClassMethodCount() if a new method was added. Run composer test to confirm all tests pass.

Examples

User says: "Add a hook for licenses.change_ip that logs the old and new IP"

Actions taken:

getHooks() addition in src/Plugin.php:

self::$module.'.change_ip' => [__CLASS__, 'getChangeIp'],

New handler method in src/Plugin.php:

public static function getChangeIp(GenericEvent $event)
{
    if ($event['category'] == get_service_define('PARALLELS')) {
        $serviceClass = $event->getSubject();
        myadmin_log(self::$module, 'info', 'IP Change - (OLD:'.$serviceClass->getIp().") (NEW:{$event['newip']})", __LINE__, __FILE__, self::$module, $serviceClass->getId());
        // perform IP change logic here
        $event['status'] = 'ok';
        $event['status_text'] = 'The IP Address has been changed.';
        $event->stopPropagation();
    }
}

Result: composer test passes; testGetHooksContainsExpectedKeys and testAllHookMethodsExist both green.

Common Issues

  • testClassMethodCount fails with "Expected 7, got 8": You added a new handler but did not update the count assertion in tests/PluginTest.php. Change the assertCount(7, ...) to the new total.
  • testAllHookMethodsExist fails: The method name string in getHooks() does not exactly match the declared method name. Check spelling and PascalCase — getChangeIpgetChangeIP.
  • testEventHandlerMethodSignatures fails with "parameter should be GenericEvent": The handler is missing the GenericEvent $event type hint, or the use statement is absent (it lives at the top of src/Plugin.php — do not remove it).
  • stopPropagation not called: Event will continue dispatching to other plugins and produce duplicate side-effects. Always place $event->stopPropagation() as the final statement inside the category guard.
  • function_requirements not registered: If activate_parallels or a new inc function is called but not registered in getRequirements(), it will throw a fatal error at runtime. Verify $loader->add_requirement('fn_name', '...') is present.

When not to use it

  • For modifying existing event handlers
  • For adding `inc` functions directly without registration

Limitations

  • Handler methods must be `public static` and accept `GenericEvent $event`
  • Requires guarding handler body with `if ($event['category'] == get_service_define('PARALLELS'))`
  • Requires calling `$event->stopPropagation()` as the last statement inside the category guard

How it compares

This skill automates the structured addition of new event hooks and their handlers, including test updates, which is more guided than manually inserting code and updating tests.

Compared to similar skills

plugin-hook side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
plugin-hook (this skill)04moNo flagsIntermediate
write-script-php13moNo flagsIntermediate
import16moNo flagsBeginner
controller16moNo flagsBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry