denosyscore / framework
DenoSys framework runtime core
Requires
- php: ^8.4.1
- ext-fileinfo: *
- ext-pdo: *
- filp/whoops: ^2.18
- graham-campbell/result-type: ^1.1
- laminas/laminas-diactoros: ^3.6
- laminas/laminas-httphandlerrunner: ^2.12
- league/flysystem: ^3.35.3
- league/flysystem-local: ^3.30
- monolog/monolog: ^3.9
- nesbot/carbon: ^3.10
- nyholm/psr7: ^1.8
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/log: ^3.0
- psr/simple-cache: ^3.0
- ramsey/uuid: ^4.7
- symfony/console: ^7.4
- symfony/filesystem: ^7.4.23
- symfony/mailer: ^8.0
- vlucas/phpdotenv: ^5.5
Requires (Dev)
- deptrac/deptrac: ^4.7
- friendsofphp/php-cs-fixer: ^3.89
- mockery/mockery: ^1.6
- pestphp/pest: ^4.1
- pestphp/pest-plugin-stressless: ^4.0
- pestphp/pest-plugin-type-coverage: ^4.0
- phpmd/phpmd: ^2.15 || ^3.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.1
- squizlabs/php_codesniffer: ^4.0
- symfony/var-dumper: ^8.0
Suggests
None
Provides
None
Conflicts
- aws/aws-sdk-php: <3.371.4
- guzzlehttp/guzzle: <7.15.2 || >=8.0.0,<8.0.1
- guzzlehttp/psr7: <2.12.3
- mtdowling/jmespath.php: <2.9.1
Replaces
- denosyscore/auth: v0.22.1
- denosyscore/cache: v0.22.1
- denosyscore/config: v0.22.1
- denosyscore/console: v0.22.1
- denosyscore/container: v0.22.1
- denosyscore/contracts: v0.22.1
- denosyscore/database: v0.22.1
- denosyscore/encryption: v0.22.1
- denosyscore/events: v0.22.1
- denosyscore/filesystem: v0.22.1
- denosyscore/hashing: v0.22.1
- denosyscore/http: v0.22.1
- denosyscore/queue: v0.22.1
- denosyscore/rate-limiter: v0.22.1
- denosyscore/routing: v0.22.1
- denosyscore/session: v0.22.1
- denosyscore/validation: v0.22.1
- denosyscore/view: v0.22.1
- dev-main / 0.22.x-dev
- v0.22.1
- v0.22.0
- v0.21.1
- v0.21.0
- v0.20.0
- v0.19.0
- v0.18.0
- v0.17.0
- v0.16.0
- v0.15.0
- v0.14.1
- v0.14.0
- v0.13.0
- v0.12.0
- v0.11.1
- v0.11.0
- v0.10.0
- v0.9.2
- v0.9.1
- v0.9.0
- v0.8.1
- v0.8.0
- v0.7.0
- v0.6.1
- v0.6.0
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.0
- v0.2.1
- v0.2.0
- v0.1.0
- dev-codex/console-mail-distribution
- dev-codex/fix-mailer-helper
- dev-codex/env-helper-process-values
- dev-codex/mail-provider
- dev-codex/form-mail-integration
- dev-codex/form-request-lifecycle
- dev-codex/queue-console-minor-constraints
- dev-codex/optional-env-files
This package is auto-updated.
Last update: 2026-11-13 15:25:47 UTC
README
DenoSys framework runtime core package.
Installation
composer require denosyscore/framework
Recommended App Skeleton
Use the official app skeleton for new projects:
composer create-project denosyscore/app my-app
What This Package Provides
Denosys\Applicationruntime core- Bootstrap/config/environment/routing glue under
src/ - Global framework helpers via
support/helpers.php - Every DenoSys component, developed in this repository under
packages/
Starting with 0.10.0 this repository is the DenoSys monorepo. The framework
package replaces every component, so an application that requires the
framework installs all components from this one package, at the same version.
Each component is also published on its own (for example
denosyscore/database) from a read-only split repository, for code that needs
only that component. Framework code and
public component APIs are still available under their Denosys\ namespaces.
If an application imports a third-party library directly, declare that library
in the application's own Composer requirements. PHP 8.4.1 or newer is required.
Application lifecycle
bootstrap/app.php configures the application and returns it unbooted:
return Application::configure(basePath: dirname(__DIR__)) ->withProviders([App\Providers\AppServiceProvider::class]) ->without(Denosys\Framework\Providers\ViewServiceProvider::class) // optional ->withRoutes(web: __DIR__ . '/../routes/web.php') ->create();
The first request or command calls boot(), which registers and boots every
eager provider once, with one event dispatcher, for HTTP and the console
alike. A provider that implements DeferrableProvider is registered and booted
only when one of the services it provides is first requested, so listeners it
adds in boot() do not exist before then. If boot fails, every later attempt
reports the same error, and the container cannot be replaced after boot.
Application::defaultProviders() lists the framework's providers;
without() removes some (it rejects a class that is not a default),
withProviders() adds the application's, each class is registered once
however its name is cased, and a class that does not exist or is not a provider is an
error. bootstrap/providers.php is no longer read: boot stops with an error
if it still lists providers.
Configuration
The framework loads config/*.php into a Denosys\Config\Repository
(from denosyscore/config), or from the cache written by config:cache. A
stored null is a value: get() returns it and has() reports it. Typed
readers name the path when a value is missing or has the wrong type:
$config->string('mail.from.address'); $config->section(MailSettings::class); // a ConfigSection built once
Environment variables are read only through Denosys\Config\Environment,
which config files reach with env(). Services read configuration, never the
process environment.
Environment files
EnvironmentManager::load($directory, $files) treats missing files in the
ordered filename list as optional. It loads the first present file according
to dotenv's normal precedence and emits no PHP warning when none are present.
The directory itself must exist. Run composer test to verify this contract
locally.
The global env() helper reads an explicit $_ENV value first and falls back
to the process environment through getenv(). Process overrides therefore work
in CLI configurations that do not populate $_ENV. Missing keys return the
provided default; the usual boolean and null string values are cast.
Mail provider
Denosys\Framework\Providers\MailServiceProvider is one of the default
providers, so Symfony\Component\Mailer\MailerInterface is available in every
application. The provider binds a lazy TransportInterface and mailer. Set mail.dsn for a
Symfony-supported transport, or configure mail.default as smtp or
sendmail with mail.mailers.smtp.* or mail.mailers.sendmail.path.
The global mailer() helper resolves the provider's MailerInterface binding;
it is available once the application has booted. Call send() with a Symfony
Email instance or inject MailerInterface into an application service.
Unsupported drivers throw when the mailer is resolved; they never silently
discard messages. A working transport and sender address are required before
sending account or other transactional mail.
Framework 0.7.0 requires Console 0.4.0, whose make:mail command generates
Symfony MIME Email messages accepted by this provider. Its former Markdown
option is removed because no Markdown renderer is registered by the framework.
Form requests in route handlers
The framework's registered route invocation strategy constructs a type-hinted
Denosys\Http\FormRequest subclass from the current PSR-7 request, supplies
the current authenticated user and route arguments, supplies the framework
container, and calls validate() before the handler. A denied
authorize() or invalid field rule prevents handler invocation and propagates
through the configured exception pipeline. Each invocation uses its own
request, including when the strategy and handler are reused.
Form-request subclasses should retain the package's Request constructor
contract. This behavior is provided by the framework bridge; the standalone
routing package remains independent of HTTP form requests.
HTTP routing errors
The default router returns PSR responses for routing HTTP exceptions, including 404 for unknown paths and 405 for unsupported methods. Its global HTTP exception middleware runs inside CORS so those responses retain CORS headers. Unexpected exceptions continue through the configured exception pipeline.
Framework 0.6.0 and later require database 0.4.1 or newer within that release line; CI verifies minimum and current published component combinations.
Filesystem configuration
The framework's ConfigurationInterface extends the shared
Denosys\Contracts\Config\ConfigurationReaderInterface. During application
bootstrap, the reader binding resolves the current configuration binding,
including after it is replaced. A prepared replacement container passed to
Application::setContainer() receives the reader binding if it lacks one.
FilesystemServiceProvider reads the shared interface and uses the existing
path.storage binding, keeping default local files in the storage app
directory. Framework 0.5.1 requires filesystem 0.3.2 and contracts 0.2.4 or newer
within their respective release lines. Release contracts before filesystem,
then framework, before updating a consumer's Composer lockfile.
Framework 0.5.2 raises the filesystem and Symfony Filesystem/HTTP Client
minimums to patched releases without changing the framework API.
Repository layout
| Path | Contents |
|---|---|
src/, support/, tests/ |
The framework package itself |
packages/<name>/ |
One component per directory, each with its own composer.json, README, tests and PHPStan baseline |
src/Framework/ |
Framework-owned integration: service providers (Providers/), the HTTP kernel, routing integration and framework console commands |
bin/package-ci |
Installs one package on its own, with sibling packages from this checkout, and runs its checks; layers checks package boundaries |
Every package directory is published to the read-only repository
denosyscore/<name> by the Split packages workflow. Pull requests opened
there are closed with a pointer back here.
Working on a package
bin/package-ci install packages/database bin/package-ci check database bin/package-ci quickstart database
install writes a git-ignored composer.monorepo.json that resolves sibling
packages from packages/, so a change is tested against the other packages at
the same commit. A package that uses a class it does not require fails here,
even though the root autoloader would hide it.
Package boundaries
Packages are grouped into tiers in composer.json under extra.denosys.tiers:
foundation (contracts, container, events), infrastructure (database, cache,
encryption, hashing, filesystem), HTTP (http, routing, session, validation,
rate-limiter) and features (auth, queue, view, console). A package may require
only packages in its own or a lower tier, without cycles, and its code may use
only the DenoSys packages it requires. Packages contain no service providers:
the framework's providers in src/Framework/Providers wire them together.
bin/package-ci layers
Repository workflows
- CI: framework tests, static analysis, an audit and the package boundary check. Each package is installed alone and its tests, PHPStan baseline and README quick start are run. Database tests also run against MySQL, MariaDB and PostgreSQL, and there is an HTTP and session integration check.
- Split packages: publishes
packages/*to the package repositories on every push tomainand on version tags. It needs theSPLIT_TOKENsecret. - Release: creates the GitHub release for a tag.
- Dependabot: weekly Composer updates for the root and every package, and GitHub Actions updates.
Releases
All packages share one version line (0.10). To release:
- Make sure CI passes on
main. - Push an annotated tag from that commit, for example
git tag -a v0.10.1 -m v0.10.1 && git push origin v0.10.1. - The framework release is published here. Split packages tags the package repositories: a minor release (
vX.Y.0) tags every package, and a patch release tags only packages whose directory changed since the previous tag. - Confirm the versions on Packagist before updating consumers.
Between releases, a package's requirements on other DenoSys packages use the current line (^0.10). When opening a new minor line, update extra.branch-alias in the root and every package, and those requirements, together.