MCPInstall

php

Modern PHP programming patterns

Installs
76
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/miles990/claude-software-skills --skill php
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads php automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

PHP

Overview

Modern PHP (7.4+/8.x) patterns including typed properties, attributes, and modern OOP features.


Modern PHP Features

Type System

<?php

declare(strict_types=1);

// Typed properties (PHP 7.4+)
class User
{
    public string $id;
    public string $email;
    public ?string $name = null;
    public bool $active = true;
    public array $roles = [];
    public DateTimeImmutable $createdAt;

    // Constructor promotion (PHP 8.0+)
    public function __construct(
        public readonly string $email,
        public readonly string $name,
        private string $password
    ) {
        $this->id = uniqid();
        $this->createdAt = new DateTimeImmutable();
    }
}

// Union types (PHP 8.0+)
function process(string|int $value): string|int
{
    return is_string($value) ? strtoupper($value) : $value * 2;
}

// Intersection types (PHP 8.1+)
function processIterable(Countable&Iterator $items): int
{
    return count($items);
}

// Return types
function findUser(string $id): ?User
{
    return $this->repository->find($id);
}

function getUsers(): array
{
    return $this->repository->findAll();
}

// Never return type (PHP 8.1+)
function fail(string $message): never
{
    throw new RuntimeException($message);
}

// Nullable types
function setName(?string $name): void
{
    $this->name = $name;
}

Attributes (PHP 8.0+)

<?php

use Attribute;

// Define attribute
#[Attribute(Attribute::TARGET_PROPERTY)]
class Column
{
    public function __construct(
        public string $name,
        public string $type = 'string',
        public bool $nullable = false
    ) {}
}

#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $path,
        public string $method = 'GET'
    ) {}
}

#[Attribute(Attribute::TARGET_CLASS)]
class Entity
{
    public function __construct(
        public string $table
    ) {}
}

// Using attributes
#[Entity(table: 'users')]
class User
{
    #[Column(name: 'id', type: 'uuid')]
    public string $id;

    #[Column(name: 'email', type: 'string')]
    public string $email;

    #[Column(name: 'name', nullable: true)]
    public ?string $name;
}

class UserController
{
    #[Route(path: '/users', method: 'GET')]
    public function index(): array
    {
        return $this->userService->getAll();
    }

    #[Route(path: '/users/{id}', method: 'GET')]
    public function show(string $id): User
    {
        return $this->userService->find($id);
    }
}

// Reading attributes
$reflection = new ReflectionClass(User::class);
$attributes = $reflection->getAttributes(Entity::class);

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    echo $instance->table; // 'users'
}

Enums (PHP 8.1+)

<?php

// Basic enum
enum Status
{
    case Pending;
    case Active;
    case Inactive;

    public function label(): string
    {
        return match ($this) {
            self::Pending => 'Pending Review',
            self::Active => 'Active',
            self::Inactive => 'Inactive',
        };
    }
}

// Backed enum (with values)
enum Role: string
{
    case Admin = 'admin';
    case Moderator = 'moderator';
    case User = 'user';

    public function permissions(): array
    {
        return match ($this) {
            self::Admin => ['read', 'write', 'delete', 'admin'],
            self::Moderator => ['read', 'write', 'delete'],
            self::User => ['read', 'write'],
        };
    }

    public static function fromString(string $value): self
    {
        return self::from($value);
    }

    public static function tryFromString(string $value): ?self
    {
        return self::tryFrom($value);
    }
}

// Usage
$status = Status::Active;
echo $status->label(); // "Active"

$role = Role::Admin;
echo $role->value; // "admin"

$userRole = Role::from('user');
$permissions = $userRole->permissions();

Object-Oriented Patterns

Traits

<?php

// Trait definition
trait Timestampable
{
    protected ?DateTimeImmutable $createdAt = null;
    protected ?DateTimeImmutable $updatedAt = null;

    public function getCreatedAt(): ?DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function getUpdatedAt(): ?DateTimeImmutable
    {
        return $this->updatedAt;
    }

    public function touch(): void
    {
        $now = new DateTimeImmutable();
        $this->createdAt ??= $now;
        $this->updatedAt = $now;
    }
}

trait SoftDeletable
{
    protected ?DateTimeImmutable $deletedAt = null;

    public function delete(): void
    {
        $this->deletedAt = new DateTimeImmutable();
    }

    public function restore(): void
    {
        $this->deletedAt = null;
    }

    public function isDeleted(): bool
    {
        return $this->deletedAt !== null;
    }
}

// Using traits
class Document
{
    use Timestampable;
    use SoftDeletable;

    public function __construct(
        public string $title,
        public string $content
    ) {
        $this->touch();
    }
}

// Trait conflict resolution
trait A
{
    public function hello(): string
    {
        return 'Hello from A';
    }
}

trait B
{
    public function hello(): string
    {
        return 'Hello from B';
    }
}

class MyClass
{
    use A, B {
        A::hello insteadof B;
        B::hello as helloFromB;
    }
}

Interfaces and Abstract Classes

<?php

// Interface
interface Repository
{
    public function find(string $id): ?object;
    public function findAll(): array;
    public function save(object $entity): void;
    public function delete(string $id): void;
}

// Generic-style interface
interface Collection
{
    public function add(mixed $item): void;
    public function remove(mixed $item): bool;
    public function contains(mixed $item): bool;
    public function count(): int;
    public function toArray(): array;
}

// Abstract class
abstract class BaseRepository implements Repository
{
    public function __construct(
        protected PDO $pdo,
        protected string $table
    ) {}

    abstract protected function hydrate(array $row): object;

    public function find(string $id): ?object
    {
        $stmt = $this->pdo->prepare(
            "SELECT * FROM {$this->table} WHERE id = :id"
        );
        $stmt->execute(['id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        return $row ? $this->hydrate($row) : null;
    }

    public function findAll(): array
    {
        $stmt = $this->pdo->query("SELECT * FROM {$this->table}");
        return array_map(
            fn(array $row) => $this->hydrate($row),
            $stmt->fetchAll(PDO::FETCH_ASSOC)
        );
    }
}

// Concrete implementation
class UserRepository extends BaseRepository
{
    public function __construct(PDO $pdo)
    {
        parent::__construct($pdo, 'users');
    }

    protected function hydrate(array $row): User
    {
        return new User(
            id: $row['id'],
            email: $row['email'],
            name: $row['name']
        );
    }

    public function findByEmail(string $email): ?User
    {
        // Custom method
    }
}

Error Handling

<?php

// Custom exceptions
class AppException extends Exception
{
    public function __construct(
        string $message,
        public readonly string $code,
        public readonly array $context = [],
        ?Throwable $previous = null
    ) {
        parent::__construct($message, 0, $previous);
    }
}

class ValidationException extends AppException
{
    public function __construct(
        public readonly array $errors,
        ?Throwable $previous = null
    ) {
        parent::__construct(
            'Validation failed',
            'VALIDATION_ERROR',
            ['errors' => $errors],
            $previous
        );
    }
}

class NotFoundException extends AppException
{
    public function __construct(
        string $resource,
        string $id,
        ?Throwable $previous = null
    ) {
        parent::__construct(
            "{$resource} not found: {$id}",
            'NOT_FOUND',
            ['resource' => $resource, 'id' => $id],
            $previous
        );
    }
}

// Result pattern
readonly class Result
{
    private function __construct(
        public mixed $value,
        public ?string $error,
        public bool $success
    ) {}

    public static function success(mixed $value): self
    {
        return new self($value, null, true);
    }

    public static function failure(string $error): self
    {
        return new self(null, $error, false);
    }

    public function map(callable $fn): self
    {
        if (!$this->success) {
            return $this;
        }
        return self::success($fn($this->value));
    }

    public function flatMap(callable $fn): self
    {
        if (!$this->success) {
            return $this;
        }
        return $fn($this->value);
    }
}

// Usage
function createUser(array $data): Result
{
    if (empty($data['email'])) {
        return Result::failure('Email is required');
    }

    try {
        $user = new User($data['email'], $data['name'] ?? '');
        $this->repository->save($user);
        return Result::success($user);
    } catch (Exception $e) {
        return Result::failure($e->getMessage());
    }
}

Collections and Arrays

<?php

// Array functions
$users = [
    ['name' => 'Alice', 'age' => 30],
    ['name' => 'Bob', 'age' => 25],
    ['name' => 'Charlie', 'age' => 35],
];

// Filter
$adults = array_filter($users, fn($u) => $u['age'] >= 30);

// Map
$names = array_map(fn($u) => $u['name'], $users);

// Reduce
$totalAge = array_reduce($users, fn($sum, $u) => $sum + $u['age'], 0);

// Find
$bob = array_filter($users, fn($u) => $u['name'] === 'Bob');
$bob = current($bob); // Get first match

// Sort
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);

// Group by (custom)
function groupBy(array $items, string $key): array
{
    return array_reduce($items, function ($groups, $item) use ($key) {
        $groups[$item[$key]][] = $item;
        return $groups;
    }, []);
}

// Collection class
class Collection implements Countable, IteratorAggregate
{
    public function __construct(private array $items = []) {}

    public static function from(array $items): self
    {
        return new self($items);
    }

    public function map(callable $fn): self
    {
        return new self(array_map($fn, $this->items));
    }

    public function filter(callable $fn): self
    {
        return new self(array_filter($this->items, $fn));
    }

    public function reduce(callable $fn, mixed $initial = null): mixed
    {
        return array_reduce($this->items, $fn, $initial);
    }

    public function first(): mixed
    {
        return $this->items[array_key_first($this->items)] ?? null;
    }

    public function count(): int
    {
        return count($this->items);
    }

    public function getIterator(): Traversable
    {
        return new ArrayIterator($this->items);
    }

    public function toArray(): array
    {
        return $this->items;
    }
}

// Usage
$result = Collection::from($users)
    ->filter(fn($u) => $u['age'] >= 30)
    ->map(fn($u) => $u['name'])
    ->toArray();

Dependency Injection

<?php

// Interface definition
interface LoggerInterface
{
    public function info(string $message, array $context = []): void;
    public function error(string $message, array $context = []): void;
}

interface UserRepositoryInterface
{
    public function find(string $id): ?User;
    public function save(User $user): void;
}

// Service with constructor injection
class UserService
{
    public function __construct(
        private readonly UserRepositoryInterface $repository,
        private readonly LoggerInterface $logger,
        private readonly EventDispatcher $events
    ) {}

    public function createUser(string $email, string $name): User
    {
        $this->logger->info('Creating user', ['email' => $email]);

        $user = new User($email, $name);
        $this->repository->save($user);

        $this->events->dispatch(new UserCreated($user));

        return $user;
    }
}

// Simple container
class Container
{
    private array $bindings = [];
    private array $instances = [];

    public function bind(string $abstract, callable $concrete): void
    {
        $this->bindings[$abstract] = $concrete;
    }

    public function singleton(string $abstract, callable $concrete): void
    {
        $this->bindings[$abstract] = function ($c) use ($abstract, $concrete) {
            if (!isset($this->instances[$abstract])) {
                $this->instances[$abstract] = $concrete($c);
            }
            return $this->instances[$abstract];
        };
    }

    public function get(string $abstract): mixed
    {
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]($this);
        }

        return $this->resolve($abstract);
    }

    private function resolve(string $class): object
    {
        $reflection = new ReflectionClass($class);
        $constructor = $reflection->getConstructor();

        if (!$constructor) {
            return new $class();
        }

        $params = array_map(
            fn(ReflectionParameter $p) => $this->get($p->getType()->getName()),
            $constructor->getParameters()
        );

        return $reflection->newInstanceArgs($params);
    }
}

Related Skills

  • [[backend]] - Laravel, Symfony
  • [[database]] - Doctrine, Eloquent
  • [[testing]] - PHPUnit, Pest

FAQ

What does the php skill do?

Modern PHP programming patterns

How do I install php?

Run npx skills add https://github.com/miles990/claude-software-skills --skill php in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/php/ (personal) or .claude/skills/php/ (project).

Which AI agents can use php?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is php safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains php?

It is published by miles990 in miles990/claude-software-skills. It has 76 recorded installs.

More from miles990/claude-software-skills

View all →

code-quality

miles990/claude-software-skills

Clean code principles, SOLID, and code review practices

devops-cicd

miles990/claude-software-skills

CI/CD pipelines, infrastructure as code, and deployment strategies

DevOps & Cloud606 installs

game-development

miles990/claude-software-skills

Game development patterns, architectures, and best practices

Media & Creative411 installs

project-management

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

389 installs

e-commerce

miles990/claude-software-skills

E-commerce platforms, payment processing, and shopping cart patterns

Backend & APIs376 installs

saas-platforms

miles990/claude-software-skills

Open the skill to read its SKILL.md instructions.

362 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs