Web 框架 DuckPHP 发布

2019-12-11 22:01:36 +08:00
 dvaknheo

代码: https://github.com/dvaknheo/duckphp 作者 QQ: 85811616 官方 QQ 群: 714610448

安装

composer require dvaknheo/duckphp # 用 require 
./vendor/bin/duckphp --help     # 查看有什么指令
./vendor/bin/duckphp --create   # --full # 创建工程

为什么叫 DuckPHP ? 取名鸭子类型的典故。 动态语言中经常提到鸭子类型,所谓鸭子类型就是:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子( If it walks like a duck and quacks like a duck, it must be a duck )。鸭子类型是编程语言中动态类型语言中的一种设计风格,一个对象的特征不是由父类决定,而是通过对象的方法决定的。

Web 框架这么多,为什么要用你的框架。 首先,DuckPHP 所有组件都可以替换,这是现代 PHP 框架必备的品质, 出现什么安全问题,替换官方组件就可以。不必等着 composer update。

其次,DuckPHP 本着普通程序员和核心程序员两个层级的使用观点,普通程序员根本不会触及到 DuckPHP 的代码,只要简单的教学就能使用。 核心程序员,可以由浅及深扩展甚至魔改框架。

这也涉及到另一个特点,DuckPHP 更自由开放, 不会让你的 工程代码限制于 App 或 app 命名空间。 你写的 DuckPHP 工程 可以作为 插件用于其他 DuckPHP 工程( 旧框架没这个功能。

DuckPHP 支持 composer,但无引用第三方组件。无引用第三方组件对系统的可靠性很重要。比如某框架用了第三方旧组件,或许会引发安全问题不可控。

DuckPHP 的适应性也是很强的,不像某些框架,只能用于全站。DuckPHP 可以在网站子目录里使用,如果不支持 path_info,也可以调整。 一般的框架的配合是很难处理的。 但是 DuckPHP 很容易插入其他框架里提前使用,你也很容易在 DuckPhp 不处理的时候接其他框架。

DuckPHP 的代码层次分明, 用主类把其他所有独立类连在一起。你很容易可以写 DuckPHP 的扩展。你可以很容易读懂。template 目录,附带了好些 demo,但文档还没跟上。 工程方面,做了 phpunit 全覆盖测试。 用了 psr-2 的标准格式化代码。希望有一小撮人能搞起来, 杜绝我现在这种闭门造车的情况。

末了,贴一份完全 demo 源文件在 template/public/demo.php

<?php declare(strict_types=1);
/**
 * DuckPHP
 * From this time, you never be alone~
 */
namespace {
    require_once(__DIR__.'/../../autoload.php');        // @DUCKPHP_HEADFILE
    //头文件可以自行修改。
}
// 以下部分是核心程序员写。
namespace MySpace\Base
{
    use \DuckPhp\Core\View;
    use \DuckPhp\Ext\CallableView;

    // 默认的 View 不支持函数调用,我们这里替换他。
    class App extends \DuckPhp\App
    {
        protected function onInit()
        {
            // 本例特殊,这里演示函数调用的   CallableView 代替系统的 View
            $this->options['callable_view_class'] = 'MySpace\View\Views';
            View::G(CallableView::G());
            
            ////
            return parent::onInit();
        }
    }
    //服务基类, 为了 XXService::G() 可变单例。
    class BaseService
    {
        use \DuckPhp\SingletonEx;
    }
    // 模型基类, 为了 XXModel::G() 可变单例。
    class BaseModel
    {
        use \DuckPhp\SingletonEx;
    }
} // end namespace
// 助手类
namespace MySpace\Base\Helper
{
    class ControllerHelper extends \DuckPhp\Helper\ControllerHelper
    {
        // 一般不需要添加东西,继承就够了
    }
    class ServiceHelper extends \DuckPhp\Helper\ServiceHelper
    {
        // 一般不需要添加东西,继承就够了
    }
    class ModelHelper extends \DuckPhp\Helper\ModelHelper
    {
        // 一般不需要添加东西,继承就够了
    }
    class ViewHelper extends \DuckPhp\Helper\ViewHelper
    {
        // 一般不需要添加东西,继承就够了
    }
} // end namespace
// 以下部分是普通程序员写的。不再和 DuckPhp 的类有任何关系。
namespace MySpace\Controller {

    use MySpace\Base\Helper\ControllerHelper as C;
    use MySpace\Service\MyService;

    class Main
    {
        public function __construct()
        {
            //设置页眉页脚。
            C::setViewWrapper('header', 'footer');
        }
        public function index()
        {
            //获取数据
            $output = "Hello, now time is " . C::H(MyService::G()->getTimeDesc());
            $url_about = C::URL('about/me');
            C::Show(get_defined_vars(), 'main_view'); //显示数据
        }
    }
    class about
    {
        public function me()
        {
            $url_main = C::URL('');
            C::setViewWrapper('header', 'footer');
            C::Show(get_defined_vars());
        }
    }
} // end namespace
namespace MySpace\Service
{
    use MySpace\Base\Helper\ServiceHelper as S;
    use MySpace\Base\BaseService;
    use MySpace\Model\MyModel;

    class MyService extends BaseService
    {
        public function getTimeDesc()
        {
            return "<" . MyModel::G()->getTimeDesc() . ">";
        }
    }

} // end namespace
namespace MySpace\Model
{
    use MySpace\Base\Helper\ModelHelper as M;
    use MySpace\Base\BaseModel;

    class MyModel extends BaseModel
    {
        public function getTimeDesc()
        {
            return date(DATE_ATOM);
        }
    }

}
// 把 PHP 代码去掉看,这是可预览的 HTML 结构
namespace MySpace\View {
    class Views
    {
        public function header($data)
        {
            extract($data); ?>
            <html>
                <head>
                </head>
                <body>
                <header style="border:1px gray solid;">I am Header</header>
    <?php
        }

        public function main_view($data)
        {
            extract($data); ?>
            <h1><?=$output?></h1>
            <a href="<?=$url_about?>">go to "about/me"</a>
    <?php
        }
        public function about_me($data)
        {
            extract($data); ?>
            <h1> OK, go back.</h1>
            <a href="<?=$url_main?>">back</a>
    <?php
        }
        public function footer($data)
        {
            ?>
            <footer style="border:1px gray solid;">I am footer</footer>
        </body>
    </html>
    <?php
        }
    }
} // end namespace
// 以下部分是核心程序员写。
// 这里是入口,单一文件下要等前面类声明
namespace {
    $options = [];
    $options['namespace'] = rtrim('MySpace\\', '\\'); //项目命名空间为 MySpace,  你可以随意命名
    $options['is_debug'] = true;  // 开启调试模式
    
    $options['skip_app_autoload'] = true; // 本例特殊,跳过 app 用的 autoload 免受干扰
    $options['skip_setting_file'] = true; // 本例特殊,跳过设置文件
    
    \DuckPhp\App::RunQuickly($options, function () {
    });
} // end namespace

DuckPHP 的前身是 DNMVCS,因为太拗口,所以改名了。这个名字应该可以吧。

5647 次点击
所在节点    PHP
29 条回复
mokeyjay
2019-12-11 22:16:20 +08:00
大致看完后有 3 点个人的想法:
1、单字母函数 /方法很丑,仿佛回到了 N 年前的 ThinkPHP
2、demo 中还出现了不遵守 PSR 规范的情况
3、php 参杂 html ……你认真的?
dvaknheo
2019-12-11 22:28:44 +08:00
因为要一个文件展示所有东西,所以用 php 参杂 html。 正常情况下是写在 view 文件夹里的。

Model 中可以全 static 方法, 反正也不会改变。
Service 中的 G 方法。 在 test/JsonRpcExtTest 里有个例子:

TestService::G(JsonRpcExt::Wrap(TestService::class));

$data=TestService::G()->foo();

把本地调用,一下子变成远程调用。

普通程序员用到的方面。
Service 层必须要用 G 方法而不能用 静态方法的,只有
SessionService 这个特殊的用于管理 Session 的 Service 了。

核心程序员用一个 G 方法 替换掉默认的类,总比配置 超长东西容易记住。
比如容器别名之类,你还得去找这个容器的别名是什么来替换。

use ControllerHelper as C。这个单字母类, 是为了减少输入。
jfcherng
2019-12-11 22:29:53 +08:00
命名風格太亂了


protected $exceptionHandlers
protected $default_exception_handler
public function assignExceptionHandler
public function on_error_handler
public static function Hook
public static function RunQuickly
public function _Parameters
trait Core_Handler
trait HelperTrait

我感覺所有能用的風格都用上了...
dvaknheo
2019-12-11 22:44:53 +08:00
protected $exceptionHandlers // 内部变量,可 override
protected $default_exception_handler // 内部变量,不建议 override。
public function assignExceptionHandler // 对外动态方法
public function on_error_handler // 不得不公开动态方法,不建议外部使用
public static function Hook // 对外静态方法
public static function RunQuickly // 对外静态方法
public function _Parameters // 不得不公开静态方法,不建议外部使用
trait Core_Handler // 下划线本文件内的功能分块。
trait HelperTrait // Trait 后缀 表示是 Trait
agdhole
2019-12-11 23:07:20 +08:00
有 minggeJS 内味了
hefish
2019-12-12 08:06:42 +08:00
看了 demo,不想跟进。很讨厌单字母方法。 另外还分核心程序员,普通程序员,可能是为了展示可替代的特性,但觉着麻烦。
inclulu
2019-12-12 09:52:22 +08:00
@agdhole 哈哈,还有人记得 minggeJS,一晃好多年了
dvaknheo
2019-12-12 09:58:58 +08:00
鉴于单字母方法很令人讨厌。那么 template/public/demo2.php 就把单字母去掉了。
核心程序员,普通程序员。 那就改为核心代码和业务代码吧。
还有,因为有人懒得用设置服务器,我们也加了不用设置服务器就能看到结果的方法。
分出的 四种 Helper 一个人写代码也没必要这么折腾,那就用 App 的静态方法够了。
这样就只有两个地方用到 DuckPhp 命名空间了。
我只改动了 demo,没去改 DuckPhp 的代码。


```php
<?php declare(strict_types=1);
/**
* DuckPHP
* From this time, you never be alone~
*/
namespace {
require_once(__DIR__.'/../../autoload.php'); // @DUCKPHP_HEADFILE
//头文件可以自行修改。
}

// 以下部分是核心代码。
namespace MySpace\Base
{
// 默认的 View 不支持函数调用,我们这里替换他。
class App extends \DuckPhp\App
{
protected function onInit()
{
// 本例特殊,这里演示函数调用的 扩展 CallableView 代替系统的 View
$this->options['ext']['DuckPhp\Ext\CallableView'] = [
'callable_view_class' => 'MySpace\View\Views',
];
////
return parent::onInit();
}
}
} // end namespace
// 以下部分是业务代码
namespace MySpace\Controller //控制器
{
use MySpace\Base\App;
use MySpace\Service\MyService;

class Main
{
public function __construct()
{
App::setViewWrapper('header', 'footer');//设置页眉页脚。
}
public function index() //主页
{
//获取数据
$output = "Hello, now time is " . App::H(MyService::getTimeDesc());
$url_about = App::URL('about/me');
App::Show(get_defined_vars(), 'main_view'); //显示数据
}
}
class about
{
public function me() //about/me
{
$url_main = App::URL('');
App::setViewWrapper('header', 'footer');
App::Show(get_defined_vars());// 默认的 view 名称没了。
}
}
} // end namespace
namespace MySpace\Service
{
use MySpace\Base\App;
use MySpace\Model\MyModel;

class MyService
{
public static function getTimeDesc()
{
return "<" . MyModel::getTimeDesc() . ">";
}
}

} // end namespace
namespace MySpace\Model
{
use MySpace\Base\App;

class MyModel
{
public static function getTimeDesc()
{
return date(DATE_ATOM);
}
}

}
// 把 PHP 代码去掉看,这是可预览的 HTML 结构
namespace MySpace\View
{
class Views
{
public function header($data)
{
extract($data); ?>
<html>
<head>
</head>
<body>
<header style="border:1px gray solid;">I am Header</header>
<?php
}

public function main_view($data)
{
extract($data); ?>
<h1><?=$output?></h1>
<a href="<?=$url_about?>">go to "about/me"</a>
<?php
}
public function about_me($data)
{
extract($data); ?>
<h1> OK, go back.</h1>
<a href="<?=$url_main?>">back</a>
<?php
}
public function footer($data)
{
?>
<footer style="border:1px gray solid;">I am footer</footer>
</body>
</html>
<?php
}
}
} // end namespace
// 以下部分是核心代码
// 入口文件,默认在 public/index.php
namespace
{
$options = [];
$options['namespace'] = rtrim('MySpace\\', '\\'); //项目命名空间为 MySpace, 你可以随意命名
$options['is_debug'] = true; // 开启调试模式

$options['skip_app_autoload'] = true; // 本例特殊,跳过 app 用的 autoload 免受干扰
$options['skip_setting_file'] = true; // 本例特殊,跳过设置文件

//没设置服务器,那就用 _r 作为路由吧
$options['ext']['DuckPhp\Ext\RouteHookOneFileMode']=[
'key_for_action' => '_r',
'key_for_module' => '',
];

\DuckPhp\App::RunQuickly($options);
} // end namespace
```
weirdo
2019-12-12 10:29:27 +08:00
看到 github 页面里那个 和其他框架简单对比 的表格。。
哈哈哈哈 ,楼主加油!
sadfQED2
2019-12-12 14:05:51 +08:00
我觉得吧,现在 php 的框架没必要造了,足够多了,我们公司的底层框架开源六七年了,就 60 多个 star,还都是我们内部员工。(我们注册用户今年刚刚过亿,全是这套框架撑起来了)
dvaknheo
2019-12-12 15:01:21 +08:00
@sadfQED2 求一下你们框架的地址。
现在的国内 PHP7 可用框架并不多。也就只有 Laravel 和 TP 两个可选而已。

Laravel 的厉害之处在于宣传。这也就是在代码之外的功夫。
我现在碰到的也不是代码问题,虽然有些 todo 列表。
文档是最大的问题,我对我写的文档没信心,总觉得写的不知道能看懂。
如果有人问起,我能说有什么缺的。
但目前就是缺乏反馈的状态。
yxzblue
2019-12-12 15:33:38 +08:00
有点看不懂
sadfQED2
2019-12-12 16:20:40 +08:00
@dvaknheo 在 github 上开源了,但是没写任何解释,没写任何 wiki,你不是我们公司的话,或许很难上手
ben1024
2019-12-12 16:22:47 +08:00
加油 ing
sadfQED2
2019-12-12 16:53:36 +08:00
@dvaknheo 地址 https://github.com/zhangchu/ko 真的太难找了,我翻了半天才翻出来
dvaknheo
2019-12-12 17:36:32 +08:00
@sadfQED2 过了一下,成不了 CodeIgniter 那样的通用框架。 最别扭的是 方法名都加上了返回类型前缀,和 PHP 的能省则省相冲突。php 的命名空间 5.3 就出来了。如果刚开始没用命名空间还可以接受。

不能替换默认组件。额外好些功能,有些功能甚至要设置数据库表结构才能用。可以拆分到其他项目。

DuckPHP 提供的 Logger ,Pager 都是最小化的,如果有更好解决方案可以替换的,甚至 DB 类都可以替换(教程文档就有替换成 thinphp-orm 的。

说起来我里面也有 facades 的扩展。 DuckPhp\Ext\FacadesAutoLoader。 我宁愿用单字母静态方法而不愿用 facades 的一个原因就是 facades 的东西不好调试。
sadfQED2
2019-12-12 17:43:31 +08:00
@dvaknheo 有些问题和我们历史遗留代码有关,业务代码迭代快 10 年了,框架不是随便能改的。而且公司自己的框架,只为自己公司项目考虑,没必要替换组件
terrywater
2019-12-24 15:02:06 +08:00
请问一下:工具是为了解决问题而生,你的框架,比 Yii2 框架的优势体现在哪里?我在介绍里面没有看出来

对于你的描述里面的框架的优势,yii2 框架也有这些优势,而且实现的非常好,现在的能满足自己所用,为什么要写框架呢?

如果是做技术研究,提升技能,本人还是支持的

如果是做公司的业务,本人是反对的。
terrywater
2019-12-24 15:05:52 +08:00
看了一下您的介绍: https://github.com/dvaknheo/duckphp/blob/master/tutorial.md

加入了 service 层,关于这个,本人认为可以在 yii2 的基础上,自己加一层就行了,封装起来就可以了,这样比自己造一个框架实用的多

fecmall,基于 yii2 开发的真正开源商城,在 yii2 框架上封装了 services 层: http://www.fecmall.com/doc/fecshop-guide/develop/cn-2.0/guide-fecmall-service-abc.html
ywisax
2019-12-24 23:16:03 +08:00
@sadfQED2 这框架,隐约有点 kohana 的影子。。。

@dvaknheo 楼主如果目的是研究的话,对自身挺有益处的。不过个人觉得有些地方是走歪了。举个例子,“无第三方依赖,你不必担心第三方依赖改动而大费周折”,这在比较正式项目开发中没你想象那么重要,可以通过锁死版本来规避。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/628156

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX