Categories
AI SumoCoders Tech

Symfony CLI and Claude Code

If you use Symfony CLI locally and Claude Code it happens that Claude wants to run php commands. Which often leads to using the wrong PHP version as it should use symfony php instead.

The following script instructs Claude to use symfony xxx. It does this for php, composer, bin/console, vendor/bin

#!/bin/bash

# Exit quietly if the symfony binary is not available
if ! command -v symfony >/dev/null 2>&1; then
    echo "symfony binary not found, skipping symfony-cli hook"
    exit 0
fi

deny() {
    local reason=$1
    echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"$reason\"}}"
    exit 2
}

COMMAND=$(cat | jq -r '.tool_input.command // ""')

case "$COMMAND" in
    php|php\ *)
        deny "Do not use bare \`php\`. Use \`symfony php\` instead. Example: \`symfony php vendor/bin/phpstan analyse\`"
        ;;
    composer|composer\ *)
        deny "Do not use bare \`composer\`. Use \`symfony composer\` instead. Example: \`symfony composer require symfony/var-dumper\`"
        ;;
    bin/console|bin/console\ *)
        deny "Do not use bare \`bin/console\`. Use \`symfony console\` instead. Example: \`symfony console debug:router\`"
        ;;
    vendor/bin|vendor/bin\ *)
        deny "Do not use bare \`vendor/bin\`. Use \`symfony php vendor/bin\` instead. Example: \`symfony php vendor/bin/phpstan analyse\`"
        ;;
esac

exit 0

Now you need to instruct Claude to use this script. So hooks to the rescue.

In your .claude/settings.local.json (or you global config) add the following:

...
"hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "PATH/TO/use-symfony-cli.sh",
            "if": "Bash"
          }
        ]
      }
    ]
  }
...