# 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 下一步计划 ![logo](duckphp.svg)