puff / demo
Component library Of Puff github.com/php-puff
Requires
- php: ^8.2
- ext-pcntl: *
- ext-posix: *
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
- puff/async: For Asynchronous programming
- puff/config: For Configuration management
- puff/console: For CLI application orchestration
- puff/di: For Dependency Injection
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 aspuff/exampledescription: one concise sentence describing the componentkeywords: only terms that are relevant to the packageautoload.psr-4: the production namespace and source directoryautoload-dev.psr-4: the test namespace and test directoryrequire: every package and extension used by production codesuggest: 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
suggestonly for genuinely optional integrations. - Do not depend on
puff/applicationmerely to access the container. Depend onpuff/diorpsr/containerwhen 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
finalwhere 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.