dvaknheo/duckphp
Lightweight PHP framework built around duck typing, with routing, MVC, a PDO database layer and dependency injection, configurable with minimal setup and few dependencies. Use it as a full framework or drop components into existing projects. Easy to learn, flexible to extend.
First, calibrate expectations. dvaknheo/duckphp is not a Laravel package — it is a competing full-stack PHP micro-framework whose README explicitly calls Laravel "larva." There is no ServiceProvider, no facade, no config/*.php merge, no Blade. You cannot composer require it into an existing Laravel app and "leverage" it the way you would a normal package. Adopting it means adopting a second runtime stack, so the realistic first use case is a small, standalone service (a JSON endpoint, an internal tool, a legacy-compatible page) that you run next to Laravel, not inside it.
Steps to actually get moving:
Repository: unknown, so confirm the Packagist vendor/name and the source URL from composer.json yourself. Do not trust the listed "last release: 2026-10-19"; a future-dated release is a strong signal that the metadata is stale or synthesized.examples/ / demo directory in the repo is more valuable than prose.<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';
$options = [
// plain PHP array config — this is the whole config system
];
\DuckPhp\DuckPhp::RunQuickly($options);
DuckPhp\DuckPhp (the bootstrap/RunQuickly surface), DuckPhp\Core\Kernel and DuckPhp\Core\App (lifecycle and hooks), then DuckPhp\Ext\* for anything optional.Config as array, not as files. DuckPHP's whole configuration model is the $options array you hand to RunQuickly. Treat it as the equivalent of config/ + .env and build it programmatically from environment variables in your entry point:
$options = [
'is_debug' => getenv('APP_DEBUG') === '1',
// route_map / view / db keys per the docs
];
\DuckPhp\DuckPhp::RunQuickly($options);
Keep this file under version control and keep environment reads in exactly one place — the framework will not do env resolution for you the way env() does.
Controller + PHP-template views. DuckPHP uses plain PHP files as templates, not a compiled template engine. That is the point (the author considers Smarty "evil"), and it means your view layer looks like this:
// app/view/home.php
<h1><?= htmlspecialchars($title) ?></h1>
Note the escaping is manual. Establish a e() helper in your own code on day one, or you will ship XSS. This is the single biggest habit shift from Blade.
BMVC over MVC. DuckPHP promotes a "Business / Model / View / Controller" split, with business logic in a service-ish layer rather than fat controllers. Use it: thin controller method → business class → returns a plain array → view or JSON serializer. If you write Laravel-style fat controllers here you'll get the worst of both worlds, because you don't have Laravel's conveniences to fall back on.
Database: bring your own habits. The data layer is PDO-oriented rather than a full ORM. Plan on hand-written SQL or a thin repository wrapper you author yourself. If you're coming from Eloquent, write the repository interface before the first feature so the rest of the code doesn't grow PDO calls inline.
Integration with an existing Laravel estate. The only pattern that works cleanly is the strangler/ sidecar approach:
tools.example.com or /legacy/* behind nginx).Extension points. Extend by subclassing the app/kernel classes and by dropping classes under DuckPhp\Ext\-style namespaces, then enabling them via options. Before writing an extension, grep the repo for an existing Ext class — the framework's own optional features are implemented that way and are the best documentation of the hook contract.
artisan vendor:publish, no config merge, no container binding. Any plan that involves "use DuckPHP inside Laravel" should be abandoned early.Repository: unknown plus a release date in the future means the package descriptor is unreliable. Verify on Packagist and in the actual composer.json before you invest a sprint.RunQuickly merges your $options into framework defaults. When something behaves unexpectedly, dump the merged options array first; most "the framework ignored my setting" bugs are a wrong key name or a merge order issue, not a bug.e() and use it everywhere, or add a lint rule.error_log/var_dump, and read stack traces against Core\Kernel. Add a tiny request-logging middleware of your own on day one — you'll need it.$options before touching composer.json.composer.json, vendor-lock your lockfile, and be prepared to fork. Do not put anything you can't rewrite in a week behind it.How can I help you explore Laravel packages today?