# duckphp
**Repository Path**: dvaknheo/duckphp
## Basic Information
- **Project Name**: duckphp
- **Description**: DuckPhp 是一个 PHP 框架。 回归 PHPer 的简单化
- **Primary Language**: PHP
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 140
- **Forks**: 36
- **Created**: 2020-09-16
- **Last Updated**: 2026-07-11
## Categories & Tags
**Categories**: webframework
**Tags**: None
## README
# DuckPhp
[English](README.md) | [中文](README-zh.md)
- 版本:v1.3.4 / 为 v1.3.5 准备
- 作者 QQ:85811616
- 官方 QQ 群:714610448
Gitee 仓库地址:https://gitee.com/dvaknheo/duckphp
Github 仓库地址:https://github.com/dvaknheo/duckphp
## 一、DuckPhp 是什么
DuckPhp 是一个零依赖、全组件可替换、部署与协作都很灵活的库模式 PHP 框架。
DuckPhp 的名字来源:
> 鸭子类型(Duck Typing):如果它看起来像鸭子、游起来像鸭子、叫起来像鸭子,那它就是鸭子。
## 二、特点
### 使用 Composer 安装
```
composer require dvaknheo/duckphp # 使用 require
./vendor/bin/duckphp new
```
第一句命令说明 DuckPhp 是库模式的框架,而不是由一堆库拼装起来的。
**DuckPhp 以库方式引入**,所以项目骨架不像其他框架那样包含一大堆不可删除的文件。
**DuckPhp 零依赖。** 你不需要担心第三方包的变更。它无需引入 101 个包就能工作,稳定性完全可控。
`./vendor/bin/duckphp new` 命令会复制骨架文件,并根据你在 `composer.json` 中设置的 `src` 命名空间替换项目命名空间。
**DuckPhp 不限制你的项目命名空间。**
你也可以不依赖骨架文件来编写代码。
### 示例 1
#### 创建示例
我们用一个简单的例子来快速了解 DuckPhp。
在项目目录中创建文件 `sample1.php`:
```php
__DIR__,
'controller_welcome_class' => MyWelcomeController::class,
'callable_view_class' => MyView::class,
// ...
];
//@override
public function onInited()
{
Helper::setViewHeadFoot('', '');
}
public function action_index()
{
$words = MyBusiness::_()->getTime();
Helper::Show(['words' => $words], 'main');
}
public function view_main($data)
{
$url = __url('');
echo "You are visit: $url; {$data['words']}";
}
public function getTime()
{
return "Hello, now is <" . MyModel::_()->getData() . '>';
}
public function getData()
{
return DATE(DATE_ATOM);
}
}
MyApp::RunQuickly([]);
```
将 `sample1.php` 作为服务器运行:
```bash
php sample1.php run --host 127.0.0.1 --port 9628 --path-document .
```
这使用了 PHP 内置服务器。你也可以直接运行 PHP 内置服务器:
```bash
php -S 127.0.0.1:9628 -t .
```
访问 `http://127.0.0.1:9628/sample1.php`,结果如下:
```
You are visit: /sample1.php; Hello, now is <2026-07-11T07:08:05+00:00>
```
#### 说明
这里的入口类是 `DuckPhp\DuckPhpAllInOne`。
流程如下:
- `MyApp::RunQuickly()` → `MyWelcomeController::action_index()` → `MyBusiness::getTime()` → `MyModel::getData()`
- `MyApp::RunQuickly()` → `MyWelcomeController::action_index()` → `Helper::Show()` → `MyView::view_main()`
详细说明:
- 应用入口 `MyApp::RunQuickly()` 演示了应用选项 `MyApp->$options`。
- 应用入口路由到 `MyWelcomeController::action_index()`。
- 控制器层 `MyWelcomeController::action_index()` 调用业务层的 `MyBusiness::getTime()`。
- 业务层的 `MyBusiness::getTime()` 调用 Model 层的 `MyModel::getData()`。
- Model 层的 `MyModel::getData()` 获取当前时间。
- 控制器层 `MyApp::action_index()` 调用助手类的 `Helper::Show()` 用于显示输出。
- `Helper::Show()` 调用 View 层的 `MyApp::view_main()` 最终显示输出。
- View 层用 `__url()` 全局函数获得当前 URL。
### 示例 1 展示的 DuckPhp 部分特点
从这个例子中,我们可以看到 DuckPhp 的特点:
**DuckPhp 同时支持 Web 模式和命令行模式**
*不建议使用 PHP 内置命令行 Web 服务器;推荐把 nginx 或 Apache 的 `document_root` 指向 `public` 目录,按常规方式部署。*
**DuckPhp 不限制你的目录,支持全站路由、局部路径路由和无 PATH_INFO 路由**
> 现在许多 PHP 框架一个域名只能放一个应用。DuckPhp 回归 PHP 快速开发的本源。
> DuckPhp 不需要修改服务器配置也能使用,也支持放在子目录里。`DuckPhpAllInOne` 类相比 `DuckPhp` 类默认启用了无 PATH_INFO 路由。
DuckPhp 通过 Composer 包 `dvaknheo/workermanhttpd` 扩展支持 Workerman,不需要修改项目代码即可运行,未来还会支持更多平台。
**DuckPhp 不限制你的项目的命名空间**
> 示例代码使用 `MyApp` 作为命名空间。
**DuckPhp 不需要大量配置文件**
> 它的配置大多使用默认值,通过调整选项可以得到不同行为。
> 这里的 `$options` 就是应用选项,你可以打开调试模式。有很多应用选项可用,具体请查看文档。
**DuckPhp 不需要手动路由**
自动路由在大多数情况下已经足够。如果不够,你也可以编写自己的路由。
**DuckPhp 非侵入式,避免全局函数冲突**
> 只有少数以 `__` 开头的全局函数。你也可以重写它们。
### 示例 2:嵌入另一个项目
**DuckPhp 应用可以将其他 DuckPhp 应用作为子应用嵌入**
这是 DuckPhp 的一个重要特点。你无需对现有的 DuckPhp 应用进行二次开发。你可以直接将其作为插件使用。
示例文件 `sample2.php`:
```php
[
ChildApp::class => [
'controller_url_prefix' => 'child/',
],
]
];
public function action_index()
{
$url_child = __url('child/index');
echo "I'm Parent. Goto child";
}
}
ParentApp::RunQuickly([]);
```
在上述同一服务器下运行。
访问 `http://127.0.0.1:9628/sample2.php`,结果如下:
`I'm Parent. Goto child`。点击链接跳转到子应用,内容为 `I'm child.`。
这里,`ParentApp` 和 `ChildApp` 都是独立的 DuckPhp 应用。`ParentApp` 将 `ChildApp` 作为子应用使用。
如果你不想为 API 编写用户系统,可以嵌入 Composer 包 `dvaknheo/duckadmin` 的用户系统。然后通过 `Helper::UserId()` 获取用户 ID,通过 `Helper::AdminId()` 获取管理员 ID。
这只是一个简单的嵌入演示。子应用涉及静态资源、应用间通信、组件共享等复杂问题,详情请参阅文档。
### 示例 3:替换组件
```php
' . CoreHelper::_H($str) . '';
}
}
class ExtApp extends DuckPhpAllInOne
{
//@override
public function onInited()
{
CoreHelper::_(MyCoreHelper::_());
ExtApp::setViewHeadFoot('', '');
}
public function action_index()
{
ExtApp::Show([], 'main');
}
public function view_main($data)
{
echo __h('');
}
}
$options = [
'path' => __DIR__,
];
ExtApp::RunQuickly($options);
```
在上述同一服务器下运行。
访问 `http://127.0.0.1:9628/sample3.php`,输出如下:
```html
<h!>
```
这个例子替换了 `__h()` 的实现,展示了 DuckPhp 的灵活性。
**作为现代 PHP 库,DuckPhp 使所有组件都可替换。** 如果你对默认实现不满意,可以轻松切换到另一个实现,即使它需要第三方依赖。DuckPhp 使用可变单例,因此调用接口不变,而实现可以更改。这意味着你可以在不修改框架的情况下修复问题或切换组件。
**DuckPhp 应用拥有清晰的堆栈跟踪,便于调试。** 你可以通过 `debug_print_backtrace(2)` 快速定位问题。使用大量中间件的框架通常堆栈跟踪不够清晰。
### 文档
[DuckPHP 框架文档](docs/zh/index.md):安装、路由、数据库、部署等完整指南。
## 三、常规项目
当你使用 `./vendor/bin/duckphp new` 创建项目时,会得到以下骨架文件。更多详情请参见 `RULES.md`。
```
project/
├── composer.json
├── config/
│ └── DuckPhpSettings.config.php # 全局设置
├── public/
│ └── index.php # Web 入口
├── src/
│ ├── Controller/ # 控制器层:HTTP/CLI 请求入口
│ │ ├── Base.php
│ │ ├── ConsoleCommand.php # CLI 子命令示例(默认未启用)
│ │ ├── ExceptionReporter.php # 异常报告器(默认未启用)
│ │ ├── Helper.php
│ │ ├── MainController.php
│ │ ├── Session.php # Session 管理
│ │ ├── SomeAction.php # Action 示例
│ │ └── testController.php # 测试控制器
│ ├── Business/ # 业务层:业务逻辑
│ │ ├── Base.php
│ │ ├── DemoBusiness.php # Business 示例
│ │ ├── Helper.php
│ │ └── SomeService.php # Service 示例
│ ├── Model/ # 模型层:数据访问
│ │ ├── Base.php
│ │ └── DemoModel.php # Model 示例
│ └── System/ # 系统层:应用配置与异常
│ ├── App.php # 应用核心配置
│ ├── BusinessException.php # Business 异常(默认未启用)
│ ├── ControllerException.php # Controller 异常(默认未启用)
│ └── ProjectException.php # 项目异常基类(默认未启用)
├── view/ # 视图目录
│ ├── _sys/ # 系统视图
│ │ ├── error_404.php
│ │ └── error_500.php
│ └── main.php # 默认视图示例
├── runtime/ # 运行时目录(日志等)
├── cli.php # CLI 入口
├── RULES.md # 规则说明文件
└── vendor/
```
> **注意**:
> - `SomeAction.php`、`testController.php`、`DemoBusiness.php`、`SomeService.php` 和 `DemoModel.php` 是示例文件。在实际项目中,删除它们并编写适合业务的类似类。
> - `ConsoleCommand.php`、`ExceptionReporter.php`、`BusinessException.php`、`ControllerException.php` 和 `ProjectException.php` 默认禁用。你可以:
> - 删除不需要的文件以简化项目。
> - 在 `src/System/App.php` 中取消注释对应的选项(`cli_command_classes`、`exception_reporter`、`exception_for_project` / `exception_for_business` / `exception_for_controller`)来启用功能。
>
> `runtime/` 目录需要写入权限。
**DuckPhp 项目层次清晰,没有跨层调用。**
System → Controller → Business → Model
DuckPhp 用户分为两个角色:`业务开发者` 和 `核心开发者`。
- `业务开发者` 只需要研究业务代码。
- `核心开发者` 负责研究系统核心代码。
> 看完助手类教程后,`业务开发者` 就可以开始写业务代码了。不懂的地方可以问 `核心开发者`。
## 四、简单教程
// 后续补充:写一个增删改查示例
## 五、其他特点
DuckPhp 支持 Composer,但也可以在没有 Composer 的情况下运行。DuckPhp 是一个 Composer 库,因此不需要独立的脚手架项目。
> 拥有自己的 loader 是可能的,但项目意义不大。
`DuckPhp\Core\App` 是 `DuckPhp` 的子框架。某些情况下,你可以直接使用 `DuckPhp\Core\App`。
DuckPhp 的控制器易于切换。它们独立于其他类,简单易懂。
DuckPhp 的路由也可以单独使用。
> 在实际项目中,这三个很少分开使用。
DuckPhp 支持扩展。这些扩展可以是独立的,不一定要和 DuckPhp 一起使用。
> 任何支持 `init([], $context)` 的类都可以作为扩展使用。
DuckPhp 可以做到你的应用和 DuckPhp 系统代码只有一行关联。你的业务代码基本和 DuckPhp 系统代码无关。你只需要研究业务代码,不需要研究框架代码。
> 这是通过更改选项实现的。
DuckPhp 有一个扩展可以阻止你在控制器中直接编写 SQL。有些框架通过牺牲性能来防止开发者犯错。DuckPhp 以几乎无性能损失的方式做到这一点。
> 只是目前作用不大。
DuckPhp 松散耦合,扩展灵活,易于修改。
> DuckPhp 的数据库类很简单,你可以轻松替换它。
DuckPhp 类尽量保持无状态。
DuckPhp 组件之间不直接相互引用,因此你可以通过 `var_dump(AnyComponent::_())` 来查看。
### 开发理念
DuckPhp 代码简洁,不做多余事情。最新版本默认 demo 运行根据 CodeCoverage 覆盖统计,只需要行数 376 / 4381(v1.2.13-dev)执行行数/总可执行行数。
DuckPhp 框架的设计原则:这个东西必须框架自带吗?不自带可以吗?
DuckPhp 每次发布都通过全代码覆盖的测试,因此具有很强的健壮性。
DuckPhp 没有 ORM,也不屏蔽 SQL,根据日志查 SQL 方便多了。它自己简单封装了 PDO。你也可以使用自己的 DB 类,或用第三方 ORM(教程里有使用 ThinkPHP-DB 的例子。
DuckPhp 不带模板引擎,PHP 本身就是模板引擎。
DuckPhp 不写 Widget,因为这和 MVC 分离原则相违背。
## 六、DuckPhp 的版本历程
起初,这是想搞个简单的 PHP Web 框架。现在使用方式简单,但内部设计并不简单。
+ 1.0.* 系列版本是前身 DNMVCS 单文件模式的版本
+ 1.1.* 系列版本是前身 DNMVCS 拆分成多文件的版本
+ 1.2.* 系列版本是改名 DuckPhp 后的版本,随着思想的变化,或许会有大的变更
+ 1.3.* 系列版本是计划开始有人大规模使用后的稳定版本,将会对历史负责
1.3 版本最大的变化是增加了相位概念,使得各应用之间相互插入也无影响。
1.3.4 增加了 Docker 支持,修复了多语言支持,为 1.3.5 准备。
## 七、 DuckPhp 下一步计划
