All skills
jeffallan avatar

/php-pro

@efebc44
by jeffallanjeffallan/claude-skills12k stars
1,127

Use when building PHP applications with modern PHP 8.3+ features, Laravel, or Symfony frameworks. Invokes strict typing, PHPStan level 9, async patterns with Swoole, and PSR standards. Creates controllers, configures middleware, generates migrations, writes PHPUnit/Pest tests, defines typed DTOs and value objects, sets up dependency injection, and scaffolds REST/GraphQL APIs. Use when working with Eloquent, Doctrine, Composer, Psalm, ReactPHP, or any PHP API development.

Use this Skill: https://skilld.dev/gh/jeffallan/claude-skills/php-pro

This session only. Nothing lands on disk.

referencesasync-patterns.md

≈2.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Async PHP Patterns

Swoole HTTP Server

<?php

declare(strict_types=1);

use Swoole\HTTP\Server;
use Swoole\HTTP\Request;
use Swoole\HTTP\Response;

$server = new Server('0.0.0.0', 9501);

$server->set([
    'worker_num' => 4,
    'max_request' => 10000,
    'task_worker_num' => 2,
    'enable_coroutine' => true,
]);

$server->on('start', function (Server $server) {
    echo "Swoole HTTP server started at http://0.0.0.0:9501\n";
});

$server->on('request', function (Request $request, Response $response) {
    $response->header('Content-Type', 'application/json');

    match ($request->server['request_uri']) {
        '/api/users' => handleUsers($request, $response),
        '/api/health' => $response->end(json_encode(['status' => 'healthy'])),
        default => $response->status(404)->end(json_encode(['error' => 'Not found'])),
    };
});

function handleUsers(Request $request, Response $response): void
{
    // Coroutine for concurrent DB queries
    go(function () use ($response) {
        $users = queryDatabase('SELECT * FROM users LIMIT 10');
        $response->end(json_encode(['data' => $users]));
    });
}

$server->start();

Swoole Coroutines

<?php

declare(strict_types=1);

use Swoole\Coroutine;
use Swoole\Coroutine\Http\Client;

// Concurrent HTTP requests
Coroutine\run(function () {
    $results = [];

    // Create multiple coroutines
    $wg = new Coroutine\WaitGroup();

    $urls = [
        'https://api.example.com/users',
        'https://api.example.com/posts',
        'https://api.example.com/comments',
    ];

    foreach ($urls as $url) {
        $wg->add();
        go(function () use ($url, &$results, $wg) {
            $client = new Client(parse_url($url, PHP_URL_HOST), 443, true);
            $client->set(['timeout' => 5]);
            $client->get(parse_url($url, PHP_URL_PATH));

            $results[$url] = [
                'status' => $client->statusCode,
                'body' => $client->body,
            ];

            $client->close();
            $wg->done();
        });
    }

    $wg->wait();

    print_r($results);
});

Swoole Async MySQL

<?php

declare(strict_types=1);

use Swoole\Coroutine;
use Swoole\Coroutine\MySQL;

Coroutine\run(function () {
    $mysql = new MySQL();

    $connected = $mysql->connect([
        'host' => '127.0.0.1',
        'port' => 3306,
        'user' => 'root',
        'password' => 'password',
        'database' => 'test',
    ]);

    if (!$connected) {
        throw new \RuntimeException($mysql->connect_error);
    }

    // Async query
    $result = $mysql->query('SELECT * FROM users WHERE active = 1');

    foreach ($result as $row) {
        echo "User: {$row['name']}\n";
    }

    // Prepared statements
    $stmt = $mysql->prepare('SELECT * FROM users WHERE id = ?');
    $stmt->execute([42]);
    $user = $stmt->fetchAll();

    $mysql->close();
});

Swoole Channel (Communication)

<?php

declare(strict_types=1);

use Swoole\Coroutine;
use Swoole\Coroutine\Channel;

Coroutine\run(function () {
    $channel = new Channel(10); // Buffer size: 10

    // Producer
    go(function () use ($channel) {
        for ($i = 1; $i <= 5; $i++) {
            $channel->push("Task {$i}");
            echo "Produced: Task {$i}\n";
            Coroutine::sleep(0.5);
        }
        $channel->close();
    });

    // Consumer
    go(function () use ($channel) {
        while (true) {
            $task = $channel->pop();
            if ($task === false && $channel->errCode === SWOOLE_CHANNEL_CLOSED) {
                break;
            }
            echo "Consumed: {$task}\n";
            Coroutine::sleep(1);
        }
    });
});

ReactPHP Event Loop

<?php

declare(strict_types=1);

require 'vendor/autoload.php';

use React\EventLoop\Loop;
use React\Http\Message\Response;
use Psr\Http\Message\ServerRequestInterface;

// HTTP Server
$server = new React\Http\HttpServer(function (ServerRequestInterface $request) {
    return new Response(
        200,
        ['Content-Type' => 'application/json'],
        json_encode([
            'method' => $request->getMethod(),
            'uri' => (string) $request->getUri(),
            'timestamp' => time(),
        ])
    );
});

$socket = new React\Socket\SocketServer('0.0.0.0:8080');
$server->listen($socket);

echo "Server running at http://0.0.0.0:8080\n";

// Periodic timer
Loop::addPeriodicTimer(5.0, function () {
    echo "Heartbeat: " . date('H:i:s') . "\n";
});

// One-time timer
Loop::addTimer(10.0, function () {
    echo "This runs once after 10 seconds\n";
});

ReactPHP Async MySQL

<?php

declare(strict_types=1);

require 'vendor/autoload.php';

use React\MySQL\Factory;
use React\MySQL\QueryResult;

$factory = new Factory();

$connection = $factory->createLazyConnection('root:password@localhost/database');

$connection->query('SELECT * FROM users WHERE active = 1')
    ->then(
        function (QueryResult $result) {
            echo "Found " . count($result->resultRows) . " users\n";
            foreach ($result->resultRows as $row) {
                echo "User: {$row['name']}\n";
            }
        },
        function (\Exception $error) {
            echo "Error: " . $error->getMessage() . "\n";
        }
    );

// Prepared statements
$connection->query('SELECT * FROM users WHERE id = ?', [42])
    ->then(function (QueryResult $result) {
        $user = $result->resultRows[0] ?? null;
        var_dump($user);
    });

ReactPHP Promises

<?php

declare(strict_types=1);

use React\Promise\Promise;
use React\Promise\Deferred;
use function React\Promise\all;

// Creating promises
function fetchUser(int $id): Promise
{
    $deferred = new Deferred();

    // Simulate async operation
    Loop::addTimer(1.0, function () use ($deferred, $id) {
        $deferred->resolve([
            'id' => $id,
            'name' => "User {$id}",
        ]);
    });

    return $deferred->promise();
}

// Using promises
fetchUser(42)
    ->then(function ($user) {
        echo "Got user: {$user['name']}\n";
        return fetchUserPosts($user['id']);
    })
    ->then(function ($posts) {
        echo "Got " . count($posts) . " posts\n";
    })
    ->catch(function (\Exception $error) {
        echo "Error: " . $error->getMessage() . "\n";
    });

// Parallel promises
all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3),
])->then(function ($users) {
    echo "Fetched " . count($users) . " users\n";
});

PHP Fibers (Native PHP 8.1+)

<?php

declare(strict_types=1);

// Simple async function using fibers
function async(callable $callback): Fiber
{
    return new Fiber($callback);
}

function await(Fiber $fiber): mixed
{
    if (!$fiber->isStarted()) {
        return $fiber->start();
    }
    if ($fiber->isTerminated()) {
        return $fiber->getReturn();
    }
    return $fiber->resume();
}

// Simulate async I/O
function fetchData(string $url): Fiber
{
    return async(function () use ($url) {
        echo "Fetching: {$url}\n";
        Fiber::suspend('pending');

        // Simulate network delay
        sleep(1);

        return "Data from {$url}";
    });
}

// Usage
$fiber1 = fetchData('https://api.example.com/users');
$fiber2 = fetchData('https://api.example.com/posts');

await($fiber1);
await($fiber2);

$result1 = await($fiber1);
$result2 = await($fiber2);

echo "{$result1}\n";
echo "{$result2}\n";

Amphp Framework

<?php

declare(strict_types=1);

require 'vendor/autoload.php';

use Amp\Http\Server\HttpServer;
use Amp\Http\Server\Request;
use Amp\Http\Server\Response;
use Amp\Http\Server\Router;
use Amp\Socket\Server as SocketServer;
use function Amp\async;
use function Amp\Future\await;

// HTTP Server with Amphp
$router = new Router();

$router->addRoute('GET', '/api/users', function (Request $request): Response {
    // Concurrent database queries
    $users = await([
        async(fn() => queryUsers()),
        async(fn() => queryUserStats()),
    ]);

    return new Response(
        status: 200,
        headers: ['content-type' => 'application/json'],
        body: json_encode(['users' => $users[0], 'stats' => $users[1]]),
    );
});

$server = new HttpServer(
    servers: [SocketServer::listen('0.0.0.0:8080')],
    requestHandler: $router,
);

$server->start();

Quick Reference

Technology Use Case Performance
Swoole High-performance servers, WebSockets Very High
ReactPHP Event-driven apps, real-time High
Amphp Modern async framework High
Fibers Native async (PHP 8.1+) Medium
Generators Simple async patterns Medium
Feature Swoole ReactPHP Amphp
Coroutines Yes No (Promises) Yes (Fibers)
HTTP Server Built-in Via package Via package
WebSockets Built-in Via package Via package
Extension Required Not required Not required
Learning Curve Medium Low Medium

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk CRITICAL
  • Gen Agent Trust Hub16d

    This skill provides a comprehensive environment for senior PHP development, including patterns for Laravel, Symfony, and asynchronous programming with Swoole and ReactPHP. While automated scanners flagged the documentation link and SKILL.md file, manual analysis indicates the content consists of standard enterprise PHP development patterns and references to the author's own GitHub documentation pages.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    6 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at efebc44. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 5 months ago
Other metadata
metadata
{
  "author": "https://github.com/Jeffallan",
  "version": "1.1.0",
  "domain": "language",
  "triggers": "PHP, Laravel, Symfony, Composer, PHPStan, PSR, PHP API, Eloquent, Doctrine",
  "role": "specialist",
  "scope": "implementation",
  "output-format": "code",
  "related-skills": "fullstack-guardian, fastapi-expert"
}
  • Testing
  • php
  • laravel
  • symfony
  • phpstan
  • orm
  • dependency-injection
  • rest-api
  • graphql
  • typed-dto

README badge

README badge for jeffallan/claude-skills/php-pro

Writes PHP 8.3+ code with strict typing, PHPStan level 9 validation, and enterprise patterns for Laravel or Symfony. Generates typed DTOs, services with dependency injection, controllers, migrations, and PHPUnit/Pest tests enforcing 80%+ coverage before delivery.

Generated from the current SKILL.md.

Does this skill support PHP versions before 8.3?
No. PHP Pro requires PHP 8.3+ and enforces modern language features like readonly properties, enums, and strict typing.
What frameworks does this skill work with?
Laravel and Symfony are the primary targets. The skill includes reference guides for both frameworks' patterns (services, repositories, DI, events, commands).
Does this skill enforce static analysis?
Yes. PHPStan level 9 is mandatory before delivery, and all code must pass with zero errors. Tests must achieve 80%+ coverage.
Can this skill set up async code?
Yes. The skill includes patterns for Swoole, ReactPHP, and fibers for async PHP workflows.
What testing frameworks does this skill use?
PHPUnit and Pest. The skill includes test structure templates and enforces 80%+ code coverage before delivery.

Generated from the current SKILL.md. These answers refresh after source changes.