PHP language conventions, modern idioms, and type system. Invoke whenever task involves any interaction with PHP code — writing, reviewing, refactoring, debugging, or understanding PHP projects.
Applies strict PHP 8.5+ conventions for writing, reviewing, and refactoring code with modern syntax and type safety.
npx claudepluginhub xobotyi/cc-foundryThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/concurrency.mdreferences/oop.mdreferences/packaging.mdreferences/typing.mdStrict types, explicit contracts, no magic. If a class needs a docblock to explain what its properties do, the properties are named wrong.
PHP 8.5+ is the baseline. Use modern syntax unconditionally — union types, enums, readonly
classes, property hooks, named arguments, match, pipe operator. No backward compatibility
with older PHP versions unless the project explicitly requires it.
Every PHP file starts with declare(strict_types=1).
Extended examples, packaging workflows, and detailed rationale for the rules below live in
references/.
| Topic | Reference | Contents |
|---|---|---|
| Type declarations, union/intersection/DNF, strict_types, variance | typing.md | Full type system coverage, nullable patterns, typed properties and constants, coercion rules |
| Interfaces, traits, readonly, property hooks, enums, visibility | oop.md | OOP patterns, abstract classes, constructor promotion, lazy objects, magic methods, #[Override] |
| Fibers, generators, async patterns, event loops | concurrency.md | Fiber API, generator coroutines, comparison table, async library guidance |
| Composer, PSR-4 autoloading, project structure, file headers | packaging.md | composer.json templates, version constraints, project layouts, namespace conventions |
| Entity | Style | Examples |
|---|---|---|
| Classes, interfaces, traits, enums | PascalCase | UserService, Renderable, Status |
| Methods, functions | camelCase | findById, getFullName |
| Properties, variables | camelCase | $userName, $isActive |
| Constants (class and global) | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_LOCALE |
| Namespaces | PascalCase segments | App\Http\Controller |
| Enum cases | PascalCase | Status::Active, Suit::Hearts |
$userCount not $n. Short names ($i, $k, $v) only in
tiny scopes (loops, array operations).$car->make not $car->carMake.is/has/can/should prefix: $isValid, $hasAccess.HttpClient not HTTPClient, JsonParser not JSONParser.
Treat abbreviations and acronyms as regular words — uppercase first letter only (PER-CS).PHP 8.5+ provides a complete type system. Use it everywhere.
declare(strict_types=1) in every file. No exceptions.bool, int, float, string. Never boolean, integer,
double.|: string|int, Foo|null. Prefer ?T for single-type nullable.&: Countable&Traversable. Class/interface types only.array|(ArrayAccess&Traversable) — union of intersections in parentheses.void return: annotate on functions that return nothing.never return: functions that always throw or exit.mixed — it disables type safety. Use object when you mean "any object."
Use mixed only at true interop boundaries with untyped code.null last in unions: string|int|null, not null|string|int.callable cannot be used as a property type. Use Closure instead.class Config
{
public const int MAX_RETRIES = 3;
public const string DEFAULT_LOCALE = 'en';
protected const float TAX_RATE = 0.21;
}
mixed return can be narrowed to any type in a subclass.See typing.md for the complete type system reference.
string or int) when the value must interoperate with external
systems (JSON, database, API).from() throws on invalid value; tryFrom() returns null. Choose based on
whether invalid input is a caller error or expected.new'd.Status::{$name} (8.3+) for variable-based case resolution.enum Status: string
{
case Active = 'active';
case Inactive = 'inactive';
case Suspended = 'suspended';
public function label(): string
{
return match ($this) {
self::Active => 'Active',
self::Inactive => 'Inactive',
self::Suspended => 'Suspended',
};
}
}
class User
{
public function __construct(
public readonly string $name,
private string $email,
protected int $age = 0,
) {}
}
public protected(set) for publicly readable,
internally writable properties.get/set logic on properties. Use instead of trivial
getter/setter methods. Incompatible with readonly.use statement per trait, each on its own line.#[Override] (8.3+) on every method that overrides a parent or implements an
interface method. Catches signature drift at compile time.super() equivalent: always use parent::method(). Never hardcode grandparent
class names.__get, __set) in new code. Typed properties with
hooks are strictly better.__toString() — define when string conversion has meaningful semantics.__invoke() — for single-method objects that act as callables.__serialize() / __unserialize() — prefer over __sleep() / __wakeup().readonly class with constructor promotion. Immutable by default.ReflectionClass::newLazyGhost().createUser(name: 'John', admin: true).match over switch — match is an expression, uses strict comparison, and
does not fall through.$result = $input
|> trim(...)
|> strtolower(...)
|> ucfirst(...);
... syntax: array_map(strlen(...), $strings).fn($x) => $x * 2. Arrow functions capture
by value, not by reference.use to capture outer variables.
Prefer static function / static fn when $this is not needed.catch (InvalidArgumentException)
not catch (Exception).catch (\Throwable) at arbitrary depths. Use at application boundaries
only (controllers, CLI entry points, queue workers).throw is an expression (8.0+): $value ?? throw new InvalidArgumentException().throw new AppException('context', previous: $e) preserves the
original cause.class AppException extends \RuntimeException {}
class NotFoundException extends AppException {}
class ValidationException extends AppException {}
\RuntimeException subtree for application errors. \LogicException subtree
for programming errors (wrong arguments, unimplemented methods)."user not found: invalid ID format".#[Deprecated] attribute (8.4+) on functions, methods, and constants to emit
E_USER_DEPRECATED when called.#[NoDiscard] attribute (8.5+) on functions whose return value must be consumed.
Use (void) cast to intentionally suppress.finally for unconditional cleanup. But prefer RAII-style patterns (destructors,
resource wrappers) when possible."Hello, {$name}".sprintf() for complex formatting: sprintf('Item %d: %s', $id, $name).<<<'EOT') when no interpolation needed.str_contains(), str_starts_with(), str_ends_with() (8.0+) — never strpos
with === false for substring checks.mb_trim(), mb_ltrim(), mb_rtrim() (8.4+) for multibyte-safe trimming.mb_ucfirst(), mb_lcfirst() (8.4+) for multibyte-safe case conversion."".join() equivalent: implode(', ', $parts) for building strings from arrays.$arr = [1, 2, 3]. Never array().array_map(), array_filter(), array_reduce() for functional transforms.array_find(), array_find_key() (8.4+) — find first element matching a callback.array_any(), array_all() (8.4+) — existence/universal checks.array_first(), array_last() (8.5+) — get first/last value without resetting
the internal pointer.$merged = [...$defaults, ...$overrides].[$first, $second] = $array or ['key' => $value] = $assoc.$result = match ($status) {
Status::Active => 'active',
Status::Inactive, Status::Suspended => 'inactive',
default => throw new \UnexpectedValueException("Unknown status: {$status->value}"),
};
match is an expression — it returns a value. Use instead of switch.===) — no type coercion.default unless provably exhaustive. Unmatched
value throws UnhandledMatchError.strlen(...), $obj->method(...),
ClassName::method(...).fn) for single-expression closures — auto-captures by value.static fn, static function) when $this is not needed —
saves memory, prevents accidental binding.Closure::bind() and Closure::fromCallable() for advanced callable manipulation.Closure in property types (not callable).composer.json is the single source of truth for project metadata, dependencies,
autoloading, and scripts.^ constraints for dependencies: "vendor/package": "^2.0".autoload-dev for test namespaces.my-project/
├── composer.json
├── composer.lock
├── src/
│ └── ... (PSR-4: App\)
├── tests/
│ ├── Unit/
│ ├── Integration/
│ └── bootstrap.php
├── config/
├── public/
│ └── index.php
└── var/
├── cache/
└── log/
src/ — application code, one class per file, PSR-4 mapped.tests/ — mirrors src/ structure with Unit/ and Integration/ separation.public/ — web root, single entry point (index.php).var/ — generated files (cache, logs). Git-ignored.vendor/ — Composer dependencies. Git-ignored.<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\User;
use App\Repository\UserRepository;
use Psr\Log\LoggerInterface;
Order: <?php tag, blank line, declare(strict_types=1), blank line, namespace, blank
line, use imports (classes, then functions, then constants), blank line, code. No leading
backslash on imports.
See packaging.md for composer.json templates and PSR-4 mapping.
PER Coding Style is the baseline. These are conventions, not tool configuration.
if, for, while, etc.).match arms, use lists.abstract/final, visibility, static, readonly.new Foo() — always use parentheses when instantiating (even without arguments),
unless immediately chaining: new Foo()->method().| and &. Parentheses for DNF without internal
spaces.class NotFoundException extends AppException {}When writing PHP code: apply all conventions silently — don't narrate each rule. If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
When reviewing PHP code: cite the specific violation and show the fix inline. Don't lecture — state what's wrong and how to fix it.
Bad: "According to PHP best practices, you should use strict_types
declaration at the top of every file..."
Good: "Missing declare(strict_types=1)."
An Intelephense LSP server is configured for .php and .phtml files. Always use LSP tools
for code navigation instead of Grep or Glob. LSP understands PHP's namespace system, type
inference, scope rules, and Composer autoload boundaries — text search does not.
| Task | LSP Operation | Why LSP over text search |
|---|---|---|
| Find where a function/class is defined | goToDefinition | Resolves use statements, aliases, namespace paths |
| Find all usages of a symbol | findReferences | Scope-aware, no false positives from string matches |
| Get type signature or docs | hover | Instant type info without reading source files |
| List all symbols in a file | documentSymbol | Structured output — classes, methods, constants |
| Find a symbol by name across project | workspaceSymbol | Searches all namespaces and Composer dependencies |
| Find concrete classes implementing an interface | goToImplementation | Knows the type hierarchy |
| Find what calls a function | incomingCalls | Precise call graph across namespace boundaries |
| Find what a function calls | outgoingCalls | Structured dependency map |
Grep/Glob remain appropriate for: text in comments, string literals, log messages, TODO markers, config values, env vars, file name patterns, URLs, error message text — anything that isn't a PHP identifier.
When spawning subagents for PHP codebase exploration, instruct them to use LSP tools. Subagents have access to the same LSP server.
The coding skill governs workflow (discovery, planning, verification); this skill governs PHP implementation choices. The phpunit skill governs testing conventions — both are active simultaneously when writing PHP tests.
Strict types everywhere. Types on everything. If PHP can check it at compile time, make it do so.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.