internal/skills/content/php-guide/SKILL.md
PHP language guardrails, patterns, and best practices for AI-assisted development. Use when working with PHP files (.php), composer.json, or when the user mentions PHP. Provides type declaration patterns, Composer conventions, PSR standards, and testing guidelines specific to this project's coding standards.
npx skillsauth add ar4mirez/samuel php-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: PHP 8.1+, Web Applications, APIs, CLIs, Microservices
declare(strict_types=1)composer.json with version constraintscomposer validate and composer audit before committingcomposer.lock for applications (commit it), omit for librariesrequire-dev for development-only dependenciescomposer update in production (use composer install --no-dev)PascalCase classes/enums, camelCase methods/properties, UPPER_SNAKE constantsdeclare(strict_types=1) as first statement after <?php in every file?> tag in pure PHP filesuse per declaration, grouped (classes, functions, constants), alphabetized<?php
declare(strict_types=1);
namespace App\Domain\User;
use App\Domain\Exception\ValidationException;
use App\Domain\ValueObject\Email;
final class UserService
{
public function __construct(
private readonly UserRepositoryInterface $repository,
private readonly EventDispatcherInterface $dispatcher,
) {}
public function register(string $name, string $email): User
{
$emailVO = Email::fromString($email);
if ($this->repository->existsByEmail($emailVO)) {
throw ValidationException::duplicateEmail($email);
}
$user = User::create(name: $name, email: $emailVO);
$this->repository->save($user);
$this->dispatcher->dispatch(new UserRegistered($user->id));
return $user;
}
}
void)string|int) instead of mixed when possibleCountable&Iterator) for combined type constraintsType|null for nullable parameters (prefer explicit over ?Type)never return type for functions that always throw or exitmixed -- if truly needed, document why@ error suppression operatorset_error_handler at bootstrap\Exception or \RuntimeException)catch (\Throwable $e) without re-throwingprevious parameter to chain exceptions<?php
declare(strict_types=1);
namespace App\Domain\Exception;
abstract class DomainException extends \RuntimeException
{
public function __construct(
string $message,
public readonly string $errorCode = 'UNKNOWN',
int $code = 0,
?\Throwable $previous = null,
) {
parent::__construct($message, $code, $previous);
}
}
final class NotFoundException extends DomainException
{
public static function forResource(string $resource, string $id): self
{
return new self(
message: sprintf('%s with ID "%s" not found', $resource, $id),
errorCode: 'NOT_FOUND',
);
}
}
htmlspecialchars() for HTML, parameterized for SQLrealpath() + check against allowed directories)eval(), exec(), system(), passthru(), or backtick operator with user inputpassword_hash() with PASSWORD_ARGON2ID (or PASSWORD_BCRYPT minimum)session.cookie_httponly, session.cookie_secure, session.cookie_samesitemyproject/
├── src/ # Application source (PSR-4: App\)
│ ├── Domain/ # Business logic, entities, value objects
│ ├── Application/ # Use cases, command/query handlers
│ ├── Infrastructure/ # Framework, database, external services
│ └── Kernel.php
├── tests/
│ ├── Unit/ # Fast, isolated unit tests
│ ├── Integration/ # Tests with real dependencies
│ └── bootstrap.php
├── config/ # Configuration files
├── public/ # Web root (index.php entry point)
├── composer.json
├── composer.lock
├── phpunit.xml
├── phpstan.neon
└── .php-cs-fixer.php
"App\\": "src/" in composer.jsonpublic/ as the web root with a single index.php front controllerUse PHP 8.1+ features idiomatically:
switch -- strict comparison, expression-based, no fallthrough<?php
declare(strict_types=1);
enum UserRole: string
{
case Admin = 'admin';
case Editor = 'editor';
case Viewer = 'viewer';
public function label(): string
{
return match ($this) {
self::Admin => 'Administrator',
self::Editor => 'Editor',
self::Viewer => 'Viewer',
};
}
/** @return list<Permission> */
public function permissions(): array
{
return match ($this) {
self::Admin => Permission::cases(),
self::Editor => [Permission::Read, Permission::Write],
self::Viewer => [Permission::Read],
};
}
}
<?php
declare(strict_types=1);
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
public function add(self $other): self
{
if ($this->currency !== $other->currency) {
throw new \InvalidArgumentException('Cannot add different currencies');
}
return new self($this->amount + $other->amount, $this->currency);
}
}
See references/patterns.md for additional patterns: dependency injection, repository pattern, value objects, command/handler, fibers, match expressions, and tooling configurations.
*Test.php in tests/ mirroring src/ structuretest_<unit>_<scenario>_<expected> or #[Test] attribute<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\ValueObject;
use App\Domain\Exception\ValidationException;
use App\Domain\ValueObject\Email;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
final class EmailTest extends TestCase
{
#[Test]
#[DataProvider('validEmailProvider')]
public function it_accepts_valid_emails(string $input): void
{
$email = Email::fromString($input);
self::assertSame(strtolower($input), $email->toString());
}
/** @return iterable<string, array{string}> */
public static function validEmailProvider(): iterable
{
yield 'simple' => ['[email protected]'];
yield 'with subdomain' => ['[email protected]'];
yield 'with plus' => ['[email protected]'];
yield 'uppercase' => ['[email protected]'];
}
#[Test]
#[DataProvider('invalidEmailProvider')]
public function it_rejects_invalid_emails(string $input): void
{
$this->expectException(ValidationException::class);
Email::fromString($input);
}
/** @return iterable<string, array{string}> */
public static function invalidEmailProvider(): iterable
{
yield 'empty string' => [''];
yield 'no at sign' => ['userexample.com'];
yield 'no domain' => ['user@'];
yield 'no local part' => ['@example.com'];
yield 'spaces' => ['user @example.com'];
}
}
phpunit/phpunit ^10.0 -- testingphpstan/phpstan ^1.10 -- static analysis (level 8)friendsofphp/php-cs-fixer ^3.0 -- code stylemockery/mockery ^1.6 -- mockingvimeo/psalm ^5.0 -- alternative static analysis# phpstan.neon
parameters:
level: 8
paths:
- src
treatPhpDocTypesAsCertain: false
checkMissingIterableValueType: true
See references/patterns.md for full composer.json, phpstan.neon, and .php-cs-fixer.php configurations.
composer install # Install dependencies
composer validate # Validate composer.json
composer audit # Check for security vulnerabilities
php vendor/bin/phpunit # Run all tests
php vendor/bin/phpunit --coverage-text # With coverage summary
php vendor/bin/phpstan analyse # Static analysis (level 8)
php vendor/bin/psalm # Alternative static analysis
php vendor/bin/php-cs-fixer fix # Auto-fix code style
php vendor/bin/php-cs-fixer fix --dry-run --diff # Preview fixes
For detailed code examples, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.