craft-php-guidelines

craft-php-guidelines is a skill for Claude Code from michtio/craftcms-claude-skills. It costs 336 tokens per session (4,132 once invoked), scanned A, original, MIT.

A set of PHP coding standards for Craft CMS 5 plugins and modules, covering documentation, structure, tools, and common implementation choices.

In plain words
What is it for?
Use it when writing or reviewing Craft PHP, adding documentation, running code-quality checks, scaffolding code, or resolving questions about project conventions.
Why use it?
It gives developers consistent rules for writing and reviewing PHP, including project conventions that may not be obvious from PHP's type system alone.

Skill for Claude Code

Written for Claude Code: shipped in a Claude Code plugin.

Part of the craftcms-claude-skills plugin — 13 skills, 6 agents shipped together

Good fit Use it when writing or reviewing Craft PHP, adding documentation, running code-quality checks, scaffolding code, or resolving questions about project conventions.

Compare 6 skills from other repositories ↓
Install with agentmods
npx agentmods add skills/michtio/craftcms-claude-skills/craft-php-guidelines
Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

Any agent
npx skills add michtio/craftcms-claude-skills --skill craft-php-guidelines
Clone the repo
git clone --depth 1 https://github.com/michtio/craftcms-claude-skills

Made for: Claude Code.

Or install craftcms-claude-skills, the plugin that ships this one along with the rest of its 13 skills, 6 agents.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for craft-php-guidelines

README.md
[![agentmods](https://agentmods.dev/badge/skills/michtio/craftcms-claude-skills/craft-php-guidelines/github.svg)](https://agentmods.dev/skills/michtio/craftcms-claude-skills/craft-php-guidelines)
Your own site
<a href="https://agentmods.dev/skills/michtio/craftcms-claude-skills/craft-php-guidelines"><img src="https://agentmods.dev/badge/skills/michtio/craftcms-claude-skills/craft-php-guidelines/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for craft-php-guidelines

Your own site · 80×15
<a href="https://agentmods.dev/skills/michtio/craftcms-claude-skills/craft-php-guidelines"><img src="https://agentmods.dev/badge/skills/michtio/craftcms-claude-skills/craft-php-guidelines.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 336 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 4,132 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe. Third-party audits
  • NVIDIA SkillSpector pass 7 Sept 2026
How audits are shown
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00336 $0.04132
Opus 5 $0.00168 $0.02066
Sonnet 5 $0.00067 $0.00826
Haiku 4.5 $0.00034 $0.00413

Measured 10d ago against content hash b24a6f20f81a, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-10, from the pricing page.

Security

Grade A, and why

craft-php-guidelines scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 10d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

skills/craft-php-guidelines/SKILL.md · 180 lines

How it starts

The opening of the file, as written. The whole thing — 180 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Craft CMS 5 PHP Guidelines

Complete PHP coding standards and conventions for Craft CMS 5 plugin and module development. These extend Craft's official coding guidelines with project-specific conventions.

Core principles: PHPDocs on everything — classes, methods, and properties — regardless of type hints. No declare(strict_types=1) in plugin source files (matching Craft core convention).

Companion Skills — Always Load Together

  • craftcms — Architecture patterns, element lifecycle, controllers, events, migrations. Required for any Craft plugin or module development.
  • ddev — All commands run through DDEV. Required for running ECS, PHPStan, scaffolding, and tests.

Documentation

When unsure about a convention, WebFetch the coding guidelines page for the authoritative answer.

Common Pitfalls

  • addSelect() is the convention in beforePrepare() — safely additive when multiple extensions contribute columns.
  • $_instances is not a Craft convention — private properties use underscore prefix but meaningful names like $_items, $_sections.
  • Records use the same class name as models (namespace distinguishes). Alias when importing both: use ...\records\MyEntity as MyEntityRecord;.
  • Queue jobs have no "Job" suffixResaveElements, not ResaveElementsJob.
  • declare(strict_types=1) is NOT used in plugin source files. Only in standalone config files like ecs.php.
  • @author goes on classes and methods only — never on properties. (Craft core puts @author at the class level only; placing it on methods too is this project's house convention, not core style.)
  • Don't use string|null — use ?string (short nullable notation).
  • Forget parent::defineRules() and you lose all inherited validation.
  • Using [$this, '_validateFoo'] callable arrays or inline closures in defineRules() — Craft core uses string method names: [['attr'], 'validateAttr']. The validator method is public, no underscore — Yii invokes it by name.
  • DateTimeHelper in elements/queries, Carbon in services — never mix in the same class.
  • Parsing a raw DB datetime with strtotime() or new DateTime() — those columns are naive UTC strings and the process timezone is system.timeZone, so the result is off by the full offset on any non-UTC install. Parse with an explicit UTC zone. See Date Handling below.
  • Missing @throws chains — document exceptions from called methods too, not just your own throws.
  • Using magic property access ($plugin->settings, $app->view) instead of explicit getters ($plugin->getSettings(), $app->getView()) — PHPStan can't resolve __get() calls, so magic access passes at runtime but fails static analysis. Always use explicit getters for Yii2 components and Craft plugin properties.
  • Calling Craft-specific methods directly on Craft::$app (Craft::$app->getConfig()) — PHPStan can't resolve them because the static type is Yii's base union. Narrow with a typed local: /** @var \craft\web\Application $app */ $app = Craft::$app;. Don't use @phpstan-ignore-line.
  • Duplicating contract constants as private const across multiple classes with "keep in lockstep" comments — PHPStan can't detect drift. Declare public const on the owning service, reference as OwnerService::CONSTANT_NAME everywhere else. This applies specifically to permission handles: a handle like 'my-plugin:manage-settings' is a contract string referenced from registration (EVENT_REGISTER_PERMISSIONS), the controller gate (requirePermission()), and the nav check (->can()); a bare literal drifts silently and a typo passes for admins (who hold every permission) while denying everyone else. Declare it as a public const on the controller that enforces it — SettingsController::PERMISSION_MANAGE_SETTINGS — and reference the const everywhere. (Craft core uses bare literals here; the const is a deliberately stricter house rule. See the craftcms skill's permissions.md.)
  • Writing the same authorization check separately in a CP controller, a console command, and a GraphQL resolver — they drift, and the surface that drifts is the one nobody tests. One shared gate method called by every surface, with a test per surface. Console is not exempt (a documented cron path with no permission check is an unauthenticated capability), and GraphQL schema scope is not the plugin's permission matrix. See references/authorization-parity.md.
  • Assuming Craft prevents self-approval / self-review — it has no such concept, and peer permissions are the opposite axis. Write the guard into the shared gate, orthogonal to role checks, with an explicit bypass permission. See references/authorization-parity.md.
  • Shipping ../* path repositories in a plugin's composer.json — resolution works only on the author's disk. Unpublished sibling deps get a vcs entry; Packagist deps need nothing; composer.lock stays gitignored for plugins. Prove it with composer config --global repositories (expect empty) then a no-lock composer update --dry-run. See references/tooling.md (Composer Hygiene).
  • Using Db::parseParam() for a literal comparison — a leading or trailing * becomes a SQL LIKE wildcard, so a uniqueness check on a stored pattern silently becomes a prefix match. Use a raw andWhere(['col' => $value]). See the craftcms skill's architecture.md.
  • Registering EVENT_REGISTER_ELEMENT_TYPES / EVENT_REGISTER_FIELD_TYPES inside a getIsCpRequest() (or other request-context) branch in init() — component-type registration must run in every context (CP, console, site) or the type disappears from getAllElementTypes() in console/queue requests, and Gc::hardDeleteElements() silently stops purging its trashed rows. Register unconditionally; only CP-rendering/routing (URL rules, asset bundles, nav) may be gated. See the craftcms skill's events.md → "Registration scope".

Read the full file on GitHub · 180 lines

Files

What ships with it

6 files beside SKILL.md in the same directory: the scripts, references and assets a skill reads on demand. Not counted in the per-session cost; read them before you install if any of them is executable.

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 10d ago First seen · 180 lines · 336 tokens per session scan A b24a6f20f81a

Subscribe to this mod's changes

craft-php-guidelines is a skill published in the GitHub repository michtio/craftcms-claude-skills (78 stars, last pushed 7d ago), licensed MIT. It adds 336 tokens to every session and 4,132 once invoked, about $0.0017 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

phx-deps-audit

Audit Hex deps for supply-chain security risk — bidi chars, compile-time exec, maintainer changes, typosquats, CVEs. Use after mix deps.update, when checking if a package upgrade is safe, or reviewing mix.lock PR diffs.

oliver-kriska/claude-elixir-phoenix · 58 tokens

deps-update

Bump outdated Hex deps — inventory, snapshot changelogs, update, fix breaks, split reviewable PRs (patches bundled, majors solo). Use to upgrade/bump Elixir dependencies or when versions fall behind. NOT for deps.get failures (/phx:investigate).

oliver-kriska/claude-elixir-phoenix · 60 tokens

deps-vet

Record a vetted Hex package version in hexvet.exs after a security review — manages the audit ledger, not the scanner. Use to approve a dep after /phx:deps-audit findings or to initialize hexvet.exs.

oliver-kriska/claude-elixir-phoenix · 53 tokens

phx-deps-update

Bump outdated Hex deps — inventory, snapshot changelogs, update, fix breaks, split reviewable PRs (patches bundled, majors solo). Use to upgrade/bump Elixir dependencies or when versions fall behind. NOT for deps.get failures (phx-investigate).

oliver-kriska/claude-elixir-phoenix · 62 tokens

phx-plan

Plan features spanning multiple domains: billing (Stripe), auth (RBAC), real-time (Presence), webhooks, jobs (Oban). Use when designing interconnected systems or converting review findings into tasks.

oliver-kriska/claude-elixir-phoenix · 44 tokens

phx-full

Run a portable sequential plan-work-verify-review-compound lifecycle. Use optional generic workers only when Amp makes them available.

oliver-kriska/claude-elixir-phoenix · 28 tokens