puff/demo

Component library Of Puff github.com/php-puff

Maintainers

Package info

github.com/php-puff/demo

pkg:composer/puff/demo

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-08-10 06:51 UTC

This package is auto-updated.

Last update: 2026-08-10 06:53:54 UTC


README

This package is a minimal starting point for building reusable components for Puff (PHP Unison Fiber Framework). It provides the Composer metadata, namespace layout, and test structure needed for a component that can be developed independently and installed into a Puff application.

Puff targets PHP 8.2 or later and favors small packages, explicit dependencies, PSR contracts, Fiber-safe state, and automatic package discovery.

Requirements

  • PHP 8.2 or later
  • Composer 2
  • The PHP extensions required by the component
  • PHPUnit 11 for tests

The current template declares ext-pcntl and ext-posix. Keep them only when the component manages processes or signals; a general-purpose component should remove both requirements.

Creating a Component

Copy this directory, then update the following values in composer.json:

  • name: the Composer package name, such as puff/example
  • description: one concise sentence describing the component
  • keywords: only terms that are relevant to the package
  • autoload.psr-4: the production namespace and source directory
  • autoload-dev.psr-4: the test namespace and test directory
  • require: every package and extension used by production code
  • suggest: optional integrations that are not required at runtime

Use a focused package structure:

example/
├── composer.json
├── LICENSE
├── README.md
├── src/
│   ├── Example.php
│   └── ExampleServiceProvider.php
└── tests/
    └── ExampleTest.php

Add configuration, contracts, middleware, factories, or adapters only when the component actually needs them. Avoid mirroring the structure of a full application inside a library.

Composer Template

The following manifest is a practical baseline. Replace the example names and declare only the dependencies used by the component:

{
    "name": "puff/example",
    "description": "A concise description of the Puff component",
    "type": "library",
    "keywords": [
        "php",
        "puff",
        "fiber"
    ],
    "license": "MIT",
    "require": {
        "php": "^8.2"
    },
    "require-dev": {
        "friendsofphp/php-cs-fixer": "^3.0",
        "phpstan/phpstan": "^2.0",
        "phpunit/phpunit": "^11.0"
    },
    "autoload": {
        "psr-4": {
            "Puff\\Example\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Puff\\Example\\Tests\\": "tests/"
        }
    },
    "prefer-stable": true,
    "config": {
        "preferred-install": "dist",
        "sort-packages": true
    },
    "scripts": {
        "test": "phpunit tests"
    }
}

Do not add a local repositories entry to a component manifest. Configure the Puff monorepo path repository once in the developer's global Composer settings:

composer global config repositories.puff \
    '{"type":"path","url":"../*","options":{"symlink":true}}' --json

Composer will then symlink local component packages while developing them. A published package remains installable from its normal Composer repository.

Dependency Rules

Keep component boundaries explicit:

  • Depend on PSR interfaces when they provide the required boundary.
  • Declare every production dependency under require; do not rely on a root application's transitive dependencies.
  • Put test and analysis tools under require-dev.
  • Use suggest only for genuinely optional integrations.
  • Do not depend on puff/application merely to access the container. Depend on puff/di or psr/container when that is the actual contract.
  • Do not introduce global mutable state. Request-specific state must be scoped to the current Fiber or passed explicitly.
  • Avoid circular dependencies. Lower-level packages must not import their higher-level adapters.

Automatic Package Discovery

Puff discovers component integration points from the installed package's Composer metadata. This avoids maintaining provider lists in the application.

Service Providers

Use a service provider when the component needs to register services, aliases, factories, or scoped objects in the Puff container:

{
    "extra": {
        "puff": {
            "providers": [
                "Puff\\Example\\ExampleServiceProvider"
            ]
        }
    }
}

The provider should extend Puff\Di\ServiceProvider and keep registration lightweight. Expensive resources should be created lazily by container factories rather than during package discovery.

<?php
declare(strict_types=1);
/*
 * PHP Unison Fiber Framework
 * https://github.com/php-puff/example
 * https://github.com/php-puff/example/issues
 * Copyright (c) Puff
 */

namespace Puff\Example;

use Puff\Di\ServiceProvider;

final class ExampleServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(Example::class);
    }
}

Applications

Only server or runtime components should register an application module. Add the class under extra.puff.apps and implement Puff\Application\Contract:

{
    "extra": {
        "puff": {
            "apps": [
                "Puff\\Example\\ExampleApplication"
            ]
        }
    }
}

Ordinary libraries should not depend on puff/application or register an app.

Coding Standards

All component code should follow these conventions:

  • Start every PHP file with declare(strict_types=1);.
  • Follow PSR-12 formatting.
  • Use native PHP 8.2 types for parameters, properties, and return values.
  • Mark value objects and stateless services final where extension is not an intentional API.
  • Prefer constructor injection and explicit contracts over service location.
  • Keep public APIs small and document array shapes or generics with PHPDoc.
  • Treat Fiber concurrency like thread concurrency: shared mutable properties, static caches, and process-wide request state require deliberate isolation.
  • Validate data at trust boundaries and never expose secrets in exceptions or logs.

Testing and Quality Checks

Install dependencies and run the component test suite:

composer install
composer test

Before publishing a component, also validate its manifest, formatting, and types:

composer validate --strict
vendor/bin/php-cs-fixer fix --dry-run --diff
vendor/bin/phpstan analyse src tests --level=8
vendor/bin/phpunit tests

Tests should cover the component's public behavior and failure paths. Components that hold request state or perform asynchronous work must also include tests for Fiber isolation, cancellation, cleanup, and concurrent execution.

Local Integration

After configuring the global path repository, require the component from the Puff application:

composer require puff/example:dev-main

When the path repository is symlinked, changes made in the component are immediately visible to the application. Regenerate Composer metadata after changing namespaces, autoloaded files, providers, or apps:

composer dump-autoload

Release Checklist

  • The package name, namespace, description, keywords, and license are correct.
  • Production and optional dependencies are classified correctly.
  • No component-local path repository is committed.
  • Composer validation succeeds without warnings.
  • PSR-12 formatting and PHPStan level 8 checks pass.
  • The complete PHPUnit suite passes on PHP 8.2.
  • Fiber-local resources are released after completion or failure.
  • Public APIs and any automatic discovery metadata are documented.
  • A license file and changelog are included before the first public release.

License

Puff components are released under the MIT License unless a component explicitly states otherwise.