agentleFS
ddtcorexddtcorex/maestro-skills/skills/php-dev-core/SKILL.md

php-dev-core

Skill5 starsChanged 3 months agoMITView on GitHub

This skill should be used when the user is writing generic PHP, creating a Composer package, working with PSR-4/PSR-12, running PHPStan/PHPCS, handling PHP security, or building framework-agnostic PHP logic. Foundation skill for PHP development. DEPENDENT on govard-toolbox for environment commands.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every new PHP file MUST start with strict types:

    line 25
  2. Very specific:

    Run composer dump-autoload --optimize after changing autoload maps and verify with composer validate --strict.

    line 88
  3. Very specific:

    NEVER: read $_GET, $_POST, $_REQUEST, $_SESSION, $_COOKIE directly.

    line 162

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

PHP Developer Core

Foundation skill for framework-agnostic PHP. Covers coding standards, Composer, static analysis, security, and testing across Magento 2, Laravel, Symfony, and WordPress. Pair with govard-toolbox for container and environment commands (govard up, govard sh, govard audit). For framework-specific tooling see govard-laravel, govard-symfony, govard-wordpress, or magento2-dev-core for Magento DI and service contracts.

Related Skills

REQUIRED BACKGROUND: Load govard-toolbox first for environment lifecycle and audit matrix. This skill adds language-level patterns. Framework skills (govard-laravel, govard-symfony, govard-wordpress) depend on both govard-toolbox and php-dev-core; magento2-dev-core cross-references this skill for generic PHP and owns Magento-specific DI and plugins.

1. Coding Standards & Types

Every new PHP file MUST start with strict types:

<?php
declare(strict_types=1);

PSR-12 is the default. When framework conventions diverge, PSR-12 wins for generic code.

RulePSR-12Notes
Opening tag<?php then declare(strict_types=1);Blank line after declare
BracesNext line for classes/methodsSame line for controls
VisibilityAlways explicit: public, protected, privateInclude readonly where needed
NamingClasses PascalCase, methods camelCaseConstants UPPER_SNAKE
Use groupingGrouped use for same namespaceOne use per line for clarity
Indentation4 spaces, no tabs, LFLine limit 120

PSR-4 autoloading is required. Map namespaces in composer.json:

{
  "autoload": {
    "psr-4": { "Vendor\\Package\\": "src/" }
  }
}

PHP 8.1-8.4 idioms to prefer:

  • readonly properties and readonly class for DTOs (8.1+), constructor promotion.
  • enum for closed sets instead of class constants (8.1+).
  • match over switch for exhaustive mapping; never for functions that always throw.
  • Intersection types (X&Y) and mixed where genuinely needed; avoid untyped parameters.
  • final classes by default; open only with documented reason.

Example with promotion and readonly:

<?php
declare(strict_types=1);

namespace Vendor\Package;

final readonly class UserDto
{
    public function __construct(
        public int $id,
        public string $email,
        public string $name,
    ) {}
}

function resolveStatus(string $raw): Status
{
    return match ($raw) {
        'active' => Status::Active,
        'inactive' => Status::Inactive,
        default => throw new \InvalidArgumentException("Unknown status: $raw"),
    };
}

Run composer dump-autoload --optimize after changing autoload maps and verify with composer validate --strict.

2. Composer

Composer is the single source for dependencies, autoloading, and scripts.

composer validate --strict                           # validate before commit
composer audit                                       # known vulnerabilities
composer install --no-dev --optimize-autoloader      # CI/production
composer outdated                                    # check outdated
composer update vendor/package --with-dependencies   # update with care

Version constraints: prefer ^ (caret) for semver (^8.1 allows 8.x but not 9.0). Never commit vendor/; always commit composer.lock for applications, omit for libraries.

composer.json essentials:

{
  "name": "vendor/package",
  "type": "library",
  "require": { "php": "^8.1" },
  "autoload": { "psr-4": { "Vendor\\Package\\": "src/" } },
  "scripts": {
    "lint": "phpcs --standard=PSR12 src tests",
    "analyse": "phpstan analyse -c phpstan.neon",
    "test": "phpunit --testdox"
  }
}

Private package auth belongs in auth.json (gitignored) or env vars, never in composer.json.

3. Static Analysis

Two gates: PHPCS for style, PHPStan for types. Both must pass before merge.

PHPCS (PSR-12)

vendor/bin/phpcs --standard=PSR12 src tests
govard audit run --checks lint --lint-provider govard --mode project --format json
govard audit run --checks lint --lint-provider govard --mode project  # text, capped

Fix auto-fixable violations with phpcbf --standard=PSR12 src.

PHPStan (Level 6+)

Level 6 is the minimum; raise to 8 when possible.

vendor/bin/phpstan analyse -c phpstan.neon
govard audit run --checks lint --lint-provider govard --mode project --format json

Sample phpstan.neon (full config in references/coding-standards.md):

parameters:
  level: 6
  paths: [src]
  checkMissingIterableValueType: false

Govard audit is the source of truth for CI — it runs PHPCS PSR12 plus PHPStan and the pub/media guard. The lint check is the only audit check for generic PHP; other values are rejected. Use --mode project for full scans, --mode standalone for isolated packages, --scope diff --base origin/master for PR quick scans (see govard-toolbox ## Audit). Fallback without Govard is phpcs --standard=PSR12 plus standalone phpstan. Treat every PHPStan error as a defect — add types instead of baseline ignores.

4. Security Baseline

Apply these rules to every PHP change regardless of framework.

Input — Never Trust Superglobals

NEVER read $_GET, $_POST, $_REQUEST, $_SESSION, $_COOKIE directly. Use filter_input or framework request objects.

// WRONG
$id = $_GET['id'];

// CORRECT — filter_input with explicit filter
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

// Fallback when filter_input not available
$id = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
    throw new \InvalidArgumentException('Invalid id');
}

Output — Escape on Render

Escape at the boundary where data becomes HTML, JS, URL, or CSS:

echo htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
echo '<div data-id="' . htmlspecialchars($id, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '">';
echo '<script>var name = ' . json_encode($name, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) . ';</script>';

For plain PHP templates, htmlspecialchars with ENT_QUOTES | ENT_SUBSTITUTE and explicit UTF-8 is the baseline. Framework escapers (Magento Escaper, Laravel e(), Symfony Twig autoescape) build on the same principle.

Database — Prepared Statements Only

NEVER interpolate variables into SQL. Use PDO::prepare with bound parameters:

// WRONG — SQL injection
$pdo->query("SELECT * FROM users WHERE email = '$email'");

// CORRECT — PDO::prepare with named placeholders
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->bindValue(':email', $email, \PDO::PARAM_STR);
$stmt->bindValue(':status', $status, \PDO::PARAM_STR);
$stmt->execute();
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);

// Also correct — execute with array
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $id]);

Disable emulated prepares when possible (PDO::ATTR_EMULATE_PREPARES => false).

Cryptography — Use Modern Primitives

Instead ofUse
md5(), sha1()password_hash() / password_verify() for passwords
uniqid(), rand(), mt_rand()random_bytes() / random_int() for tokens
serialize() / unserialize()json_encode() / json_decode() with JSON_THROW_ON_ERROR
$hash = password_hash($password, PASSWORD_DEFAULT);
if (!password_verify($password, $hash)) {
    throw new \RuntimeException('Invalid credentials');
}
$token = bin2hex(random_bytes(32));
$json = json_encode($data, JSON_THROW_ON_ERROR);

Store secrets in .env (never in code). Generate CSRF tokens with random_bytes and validate with hash_equals. Finding codes: PHP-SEC-xxx / PHP-ARCH-xxx — see references/security-best-practices.md.

5. Testing & Verification

Test Layout

  • PHPUnit (or Pest) under tests/ mirroring src/ via PSR-4 autoload-dev.
  • Unit tests: typed mocks, no real I/O; integration tests: real container/DB with isolation.
vendor/bin/phpunit --testdox
vendor/bin/pest --coverage
php -l src/Service/UserService.php
vendor/bin/phpcs --standard=PSR12 src tests
vendor/bin/phpstan analyse -c phpstan.neon
composer validate --strict
composer audit

Verification Checklist (Run Before Every Commit)

php -l src/**/*.php                          # 1. Syntax
vendor/bin/phpcs --standard=PSR12 src tests  # 2. Style PSR-12
vendor/bin/phpstan analyse -c phpstan.neon  # 3. Types L6+
composer validate --strict                   # 4. Manifest valid
composer audit                               # 5. No known CVEs
vendor/bin/phpunit --testdox                 # 6. Tests green
govard audit run --checks lint --lint-provider govard --mode project --format json  # 7. Govard gate

Govard audit (--checks lint) bundles PHPCS, PHPStan, and the pub/media guard. Keep it green in --scope project for releases and --scope diff --base origin/master for PRs.

Audit

For the 4-framework audit matrix (quick vs deep scope, --scope diff vs --scope project, base branch, timing, provider rules) see govard-toolbox ## Audit. This skill delegates all audit-mode and provider specifics there. Fallback without Govard is phpcs --standard=PSR12 plus phpstan analyse -c phpstan.neon at level 6+. No nesting — govard audit and govard tool are host-side commands, not container-nested (do not wrap them with govard sh -c).

References

  • references/coding-standards.md — PSR-12 checklist, phpstan.neon sample, and strict-types patterns.
  • references/security-best-practices.md — XSS, SQL injection, CSRF, and cryptography examples.
  • references/architecture-patterns.md — Service, DTO (readonly class), factory, and PSR-11 container patterns.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.