V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dvaknheo
V2EX  ›  PHP

DuckPhp 1.2.4 发布,终极架构,文档完善了

  •  
  •   dvaknheo · 2020-05-16 10:14:21 +08:00 · 5980 次点击
    这是一个创建于 1412 天前的主题,其中的信息可能已经有所发展或是发生改变。
    项目地址: https://github.com/dvaknheo/duckphp
    作者 QQ: 85811616
    官方 QQ 群: 714610448

    phpstan 第 7 级检查通过。php-cs-fixer 代码风格通过。phpunit 全覆盖测试通过。

    名称统一成了 DuckPhp,和命名空间一样。

    重复一下 DuckPhp 的特性:
    + 可以在不改动库文件下,替换所有实现。
    我用 CodeIgniter 的时候,基本上没见到个 CodeIgniter 系统代码是官方原版的。都是各种魔改。
    Composer 时代之后,直接改 vendor 的文件确实是不可取的
    所以满足在不改动库文件下,替换所有实现这是对现代框架的基本要求。不实现这个目标的框架没存在意义。

    + 可以非固定全站使用的框架。
    很多框架都要要求你配置整站才能使用,DuckPhp 很灵活,不需要配置成整站。
    甚至启用自带扩展能做到连 Web 服务器都不用配置的单一入口文件模式。
    还有一种玩法是 A ,B 两个项目独立开发,然后合在同一个服务器上。
    其他框架很麻烦,命名空间都是用 App,路由处理还要折腾很多。
    DuckPhp 不同项目不同命名空间,B 项目作为 A 项目的插件使用。

    + DuckPhp 秉着 代码和 DuckPhp 关联越少越好的原则
    核心工程师写的非业务核心代码才会和 DuckPhp 耦合。
    应用工程师写的业务代码是不和 DuckPhp 耦合的。

    + DuckPhp 是无第三方依赖的单一的 Composer Library
    这点很重要么? 很重要。引用的第三方组件出了 Bug 人家升级了怎么办。
    Library 则很方便的插入第三方框架里使用。
    DuckPhp 工程的初始化就是 复制 template 目录,修改相应模板。
    甚至可以直接运行 template 目录。

    --

    从 CURD 角度的应用工程师角度来看,基本不需要动什么。

    使用 DuckPhp 的感觉就是,默认的就已经足够了。

    应用工程里那些高级的东西,让核心工程师来做。
    而核心工程师呢,如果不满足,那么就调选项。
    如果选项还不行,那就入口类里调整。
    更高级的是自己做组件和替换默认组件。
    对于应用工程师,就看那些 Helper 函数多出来什么就够了。
    var_dump(ControllerHelper::GetExtendStaticMethodList());

    最高级的玩法是把当前工程 A 作为扩展给 B 工程复用。
    也没什么难度,入口类里加额外代码就是。(引入 AppPluginTrait)



    1.2.3 版本,我在 yii3 demo 上内嵌了个 DuckPhp 作为实现版本

    https://github.com/dvaknheo/yii-demo

    在入口 index.php 处拦截如果 DuckPhp 能实现,则用 DuckPhp 的实现版本,否则继续
    所有 DuckPhp 版本的实现的文件,都在一个目录下,可以对比一下。
    https://github.com/dvaknheo/yii-demo/tree/for_duckphp/app
    之前也折腾过 laravel 的 demo, 结果丫自己的 auth demo 我看的都绕半天,何况其他小白。

    用 phpunit-codecoverage 看了一下 thinkphp 5.1 hello world 。1052 / 19241 行代码。
    也就是不干什么事情都至少有 1000 行代码空跑了。DuckPhp 是 339/5252 行,所以 DuckPhp 是高效的。
    当然,这只是对性能的粗略估计。比如 一个 autoloader 的 file_exists 判断性能上就能顶很多行
    (尤其是我在 wsl1 环境下 IO 性能惨不忍睹。
    至于 Laravel,我有空看一下是不是跑了一万行代码。

    DuckPhp 的 swoole 支持是有的,只不过现在不作为重点了,workerman 有空的话我再看看。
    DuckPhp 切换成这些模式是无缝的。 只是我不觉得 web 不是 swoole/workerman 该做的主战场。

    现在想找个能很好的演示 DuckPhp 的工程来做。演示一下 DuckPhp 的魅力。
    41 条回复    2020-05-21 12:13:32 +08:00
    littleylv
        1
    littleylv  
       2020-05-16 10:25:49 +08:00   ❤️ 6
    其他还没看,你这个名字能改一下吗? Php 看着超级别扭。专有名词不用按照驼峰规范来的,直接 DuckPHP 没问题的,DuckPhp 反而看着别扭
    lifeintools
        2
    lifeintools  
       2020-05-16 10:50:50 +08:00
    大概看了 一圈 没看懂。。希望介绍能言简意赅一些
    lscho
        3
    lscho  
       2020-05-16 11:08:57 +08:00 via iPhone
    同一楼,太别扭
    xnode
        4
    xnode  
       2020-05-16 11:12:10 +08:00
    可以叫 DoubleDuck 中文 双鸭
    jqh
        5
    jqh  
       2020-05-16 11:44:37 +08:00
    + DuckPhp 秉着 代码和 DuckPhp 关联越少越好的原则
    核心工程师写的非业务核心代码才会和 DuckPhp 耦合。
    应用工程师写的业务代码是不和 DuckPhp 耦合的。
    ------------------------------------------------------------
    这样做的意义在哪里?用框架不就是为了不重复造轮子吗?框架提供稳定成熟的基础功能,让工程师专注于业务的开发,你让核心工程师去造这些轮子我敢说大部分质量都是很糟糕的,至少我待过的公司中使用自研框架(无框架)的公司,代码质量都是不怎么样的。毕竟一个成熟的开源框架,每个细节的设计都是经过成千上万个项目检验提炼后的成果,很多公司所谓自研核心组件就是想当然和幼稚无意义的重复实现。

    ControllerHelper::GetExtendStaticMethodList()
    -------------------------------------------------------------
    还自创代码风格??这么多千奇百怪的风格不累吗?自动加载都知道用 composer PSR4 规范了?编码风格也不能统一一下?

    至于 Laravel,我有空看一下是不是跑了一万行代码。
    --------------------------------------------------------------
    现在还有纠结代码行数的,实际上绝大多数用 PHP 的公司都没必要纠结这点代码行数,开发效率和稳定性才是重中之重
    littleylv
        6
    littleylv  
       2020-05-16 11:52:05 +08:00
    + DuckPhp 是无第三方依赖的单一的 Composer Library
    这点很重要么? 很重要。引用的第三方组件出了 Bug 人家升级了怎么办。

    --------------------------------------------------------------


    这点我不敢苟同。现在那么多的优秀组件(特别是 symfony 家的组件),那么多好的轮子,为什么一定要自己造。“引用的第三方组件出了 Bug”? symfony 家社区那么多用户,有 bug 也是很快发现或很快修复。再说了别人有 bug,你自己造的轮子也有可能有 bug 呀
    james122333
        7
    james122333  
       2020-05-16 12:45:52 +08:00
    支持楼主造轮子
    可以证明自己不是搬运工
    成千上万项目检验只能说给非专业的人听
    littleylv
        8
    littleylv  
       2020-05-16 13:05:28 +08:00
    “php-cs-fixer 代码风格通过。”

    --------------------------------------------------------------

    我不知道楼主用的谁家的代码风格,我稍微看了代码 https://github.com/dvaknheo/duckphp/blob/master/src/Ext/DBManager.php:

    -- psr-1 规定 方法名称 必须 符合 camelCase 式的小写开头驼峰命名规范。:
    public static function CloseAllDB()
    public static function DB_W()
    public function _DB($tag = null)
    public function _DB_W()
    public function _onException()
    public function OnException()

    -- psr-1 规定 类的属性名 应该 在一定的范围内保持一致:
    protected $before_get_db_handler = null;
    protected $use_context_db_setting = true;
    protected $beforeQueryHandler = null;
    dvaknheo
        9
    dvaknheo  
    OP
       2020-05-16 13:10:55 +08:00
    @littleylv 我系统命名空间是用 DuckPhp,所以统一成 DuckPhp 。 要不,还得把命名空间全调整成 DuckPHP ?


    @jqh 不是说核心工程师造轮子,是核心工程师调轮子。应用工程师一个 DuckPhp 命名空间的东西也用不到。
    DuckPhp 用 phpstan 检查过规范,phpunit 单元覆盖测试 100% 。

    ControllerHelper::GetExtendStaticMethodList() => 这里只是个简写, 其实直接在 MyProject\Base\Helper\ControllerHelper 里字节加就行。 这是写插件的时候的额外方法。

    是有自动加自的。

    现在还有纠结代码行数的 。
    ////
    fpm 模式下,空跑上万行和 和 100 行区别大了。 而且上万行的代码,意味着核心工程师想调整写功能也不好找。


    “引用的第三方组件出了 Bug”? symfony 家社区那么多用户,有 bug 也是很快发现或很快修复。

    ////
    有这么一个框架,引用了低版本的 symfony 组件。symfony 后来升级了。

    再说了别人有 bug,你自己造的轮子也有可能有 bug 呀
    ////
    所以现代框架就必须要有出 Bug 的时候可以不硬改系统代码,在工程里改正的方法啊。
    dvaknheo
        10
    dvaknheo  
    OP
       2020-05-16 13:21:29 +08:00
    @littleylv .php_cs 文件, 就额外几条规则。你这里说的,没用 php-cs-fixer fix 检查出来。

    ```
    <?php
    $header = <<<'EOF'
    DuckPhp
    From this time, you never be alone~
    EOF;
    $finder = PhpCsFixer\Finder::create()
    ->files()
    ->in(__DIR__.'/src')
    ->name('*.php')
    ;
    return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules([
    '@PSR2' => true,
    'header_comment' => [
    'commentType' => 'PHPDoc',
    'header' => $header,
    'separate' => 'none',
    'location' => 'after_declare_strict',
    ],
    'declare_strict_types' => true,
    'binary_operator_spaces'=>true,
    ])
    ->setFinder($finder)
    ->setUsingCache(false);
    ```

    public function _DB($tag = null) 这样的方法是对应 public function DB($tag = null) 的。


    用于外部回调。 下划线开始的代码,算是一种特殊风格。

    public function _onException()
    public function OnException() 刚注意到这应该用 _OnException 。 代码多了,有些地方没注意得过来。

    欢迎大家到 Github 上提 Issue 或者在 QQ 群里讨论
    iidestiny
        11
    iidestiny  
       2020-05-16 14:42:30 +08:00
    楼主是不是对 Laravel 有什么偏见。。。
    dvaknheo
        12
    dvaknheo  
    OP
       2020-05-16 17:15:00 +08:00
    @iidestiny
    Laravel 不能节省运行效率和开发效率。 违背了 PHP 简洁之道。

    随手举例子

    + blade 模板 违背了 PHP 代码就是视图的原则
    + 滥用 ArrayIterator foreach, 使得没法 dump
    + orm 使得调试更麻烦了
    + 退化到路由表了。有简单的文件路由不用。
    + 中间件使得调用关系复杂化。
    HiCode
        13
    HiCode  
       2020-05-16 17:33:07 +08:00
    看着心疼楼主,又不是人民币,没办法得到别人的喜欢的。

    我就不爱做这事了……
    wowiwj
        14
    wowiwj  
       2020-05-16 17:57:49 +08:00 via iPhone
    laravel 不能节省开发效率和运行效率的结论从哪里得出来的?感觉楼主可能对 php 有什么误解,最好拉一些实际例子列举一些自己轮子优势,这样大家好坏看起来更直观一些,也更有说服力
    dvaknheo
        15
    dvaknheo  
    OP
       2020-05-16 18:27:40 +08:00
    @wowiwj
    laravel 的运行效率,已经有太多例子了。
    laravel 的开发效率:不要指望一个智力水平只有高中的 php 新手去使用并了解其中原理。

    最好拉一些实际例子列举一些自己轮子优势。
    ////
    我 fork 了一个 yii3 demo 的分支,用 DuckPhp 1.2.3 实现的。输出和 yii3 的一模一样。

    除了入口文件,其他工程文件都在同一个目录下

    https://github.com/dvaknheo/yii-demo/tree/for_duckphp/app

    可以看 yii3 还是 DuckPhp 更容易明白。DuckPhp 更加朴实无华,不用那么花哨就解决问题。

    之前在我本机弄了 laravel 自带的 auth 的版本,发现 laravel auth 连我都没能弄清楚,怎么可能会有国内项目用他那套做验证,都是自己从头起一套更容易。 后面是卡在验证器部分了。 要在不用 laravel 框架的情况下实现和 laravel 做表现一致的验证。后面回头做 DuckPhp 的代码了就没管这个。

    @HiCode 谢谢。我希望能有一小撮人搞起。在写文档的时候,总没有底气不知道别人能否看明白。DuckPhp 的精妙之处,在使用之后会明白的。但是我做得出来,说出来就一塌糊涂。DuckPhp 也有些地方,总感觉可以做得更好,但是总是缺一个人提醒一下。
    rootx
        16
    rootx  
       2020-05-16 18:30:30 +08:00
    支持楼主!
    DavidNineRoc
        17
    DavidNineRoc  
       2020-05-16 18:49:34 +08:00
    随便点开了一个文件,看到了 Core_App
    所以楼主是用 sanke 还是 camel 呢。

    对于楼主说的话,我就随便反驳几个。
    1. Laravel 不能节省开发效率,这句话你是框架第一人。你写个队列任务,写个数据库操作
    2. 简单的文件路由,你都知道简单了,为什么还要纠结这个问题呢,文件路由怎么实现 path parameter 呢。
    3. 如果一个项目没有中间件,我大概就知道这个项目怎么做登录验证的那种模块了,估计就是每个文件 include auth.php
    4. 前面的人也说了,你居然相信你会超过 symfony 家,别人一个人我就不说了,一个社区你想怎么比

    从 CURD 角度的应用工程师角度来看,你都觉得框架不好了,我觉得不用原生 PHP 的都是在放屁 |(-_-)|
    explore365
        18
    explore365  
       2020-05-16 20:08:48 +08:00
    支持楼主
    dvaknheo
        19
    dvaknheo  
    OP
       2020-05-16 21:06:51 +08:00
    @DavidNineRoc

    1 队列任务不是 Web 框架必备的部分,队列或者应该是单独的一个库。数据库也不是。但是作为常用组件,DuckPhp 通过默认扩展的方式提供主从数据库的支持。另一个默认扩展,就是分页了。 分页在各应用里绝对都是很折腾的方式,所以 DuckPhp 也带了基础的分页,并很容易改写。

    2 文件路由,namespace 分割,如果有更特殊需求,加路由钩子。

    3 路由前后钩子。中间件搞得堆栈混乱一堆。中间件还存在为调用顺序折腾的事。 如果路由钩子多起来,那么我会提供调用方式调整的方法,但是希望是手动。

    4 一个社区也是由一小撮人慢慢发展起来的。并不是说 symfony 不好。为什么能推广开的是 laravel 而不是 symfony 我很奇怪。 比如 html 编码。 我提供了一个默认方式,zend framework 有更好的方式,你可以很容易的去替换成 zend framework 。symfony 的例子意思是可靠性最好不要建立在 第三方基础上,而是自己有实现,也可替换成第三方实现。

    因为我闭门造车,很多小的细节不可避免的有问题,目前就是希望能有一小撮人一起来讨论,做好。
    dvaknheo
        20
    dvaknheo  
    OP
       2020-05-16 21:22:36 +08:00
    @DavidNineRoc
    3. 如果一个项目没有中间件,我大概就知道这个项目怎么做登录验证的那种模块了,估计就是每个文件 include auth.php

    这个问题我再说明一下,我刚才答错了。
    DuckPhp 项目,推荐的是用 PHPer 最习惯的方式,在控制器基类构造函数里做权限判断。好处是你从代码看起的时候不会发懵:为什么这里会有个验证? 这也就是 DuckPhp 尽量避免的,这玩意从哪里来。

    我答成了使用第三方类 auth 类做验证了。
    如 template 目录下的例子。

    http://duckphp.demo.dev/full/public/u/index.php/login

    对应的文件应该是 template/full/public/u/app/Controller/Main.php (暂时别吐槽为什么文件这么长了。
    但是没看到 login 方法。
    因为在
    template/full/public/u/app/Base/App.php 的 oninit 里有这么几句。
    $this->options['ext']['UserSystemDemo\Base\App'] = true;
    $path = realpath($this->options['path'].'../../auth/');
    $this->assignPathNamespace($path, 'UserSystemDemo');
    后两句是 autoload 添加 'UserSystemDemo' 命名空间。
    重点是第一句
    把 template/full/public/auth 这个项目作为扩展来用于 auth.
    template/full/public/auth/Base/App.php 只需要一句,就把这个独立工程可插件化了。
    use AppPluginTrait;

    独立运行 auth 项目的入口是
    http://duckphp.demo.dev/full/public/auth.php
    ruoge3s
        21
    ruoge3s  
       2020-05-16 21:33:08 +08:00
    # 20 楼那个,在构造器里加权限判断,耦合太严重了。😄
    ruoge3s
        22
    ruoge3s  
       2020-05-16 21:35:43 +08:00
    1. 建议楼主可以多看看一看设计模式。
    2. 自己写一些框架组件没什么问题的。有些东西自己写了,才会更能感受到别人写的一部分东西确实很优秀。借鉴人家优秀的东西,让自己的框架、组件更优秀,让自己更优秀吧。
    dvaknheo
        23
    dvaknheo  
    OP
       2020-05-16 22:53:29 +08:00
    @ruoge3s 控制器的构造器就是干这样的活的,还觉得耦合严重。。。
    控制器又不需要继承系统基本类,一看就明白。
    给你看个耦合严重的控制器例子吧。

    ```
    namespace App\Http\Controllers;

    use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
    use Illuminate\Foundation\Bus\DispatchesJobs;
    use Illuminate\Foundation\Validation\ValidatesRequests;
    use Illuminate\Routing\Controller as BaseController;

    class Controller extends BaseController
    {
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
    }
    ```
    这时候,你就的去看 BaseController 里有什么,会影响到什么。
    还有 AuthorizesRequests DispatchesJobs ValidatesRequests 都有什么功能。
    似乎这些不管也无所谓。

    控制器基本不会复用,复用的是 service,业务逻辑层。

    keep it simple stupid.
    dvaknheo
        24
    dvaknheo  
    OP
       2020-05-16 23:10:07 +08:00
    DuckPhp 1.2.3 到 1.2.4 版本有个显著改变是之前组件都没继承基类,实现什么接口。只使用 SingletonEx trait 做可变单例。

    按 MyComponent::G()->init(array $optioins, $context=null): this;
    这样的约定初始化(可以想象成 ServiceProvider.

    1.2.4 之后,各组件使用 MyComponent extends DuckPhp\Core\ComponentBase , ComponentBase implements ComponentInterface,这样的方式,多了一个继承,内部实现一个接口。

    实际上你编写扩展,也只需要满足约束 MyComponent::G()->init(array $options, $context=null): this; 就行了,没必要 实现 ComponentInterface 的所有接口

    ComponentBase 帮你过滤你只需要的 $options 选项。 拆分 init() 为 initOptions($options) initContext() 两个空方法以便于你的继承。

    以上这些,只是核心工程师需要了解的。应用工程师用不到。
    影响应用工程师的是少量选项,方法名又因为名称不满意,细调了,不兼容了。

    核心工程师有没有会被版本升级恶心的事呢? 有,继承系统类的时候,或许系统类的类型会调整成强类型,签名不同了。

    最后,还可能会有什么问题呢?
    Helper 的函数名字冲突,比如你在旧版本的 Helper 加了个函数, 升级后函数名相同,但签名不同,这也是不太可能出现的情况。
    beastk
        25
    beastk  
       2020-05-16 23:54:06 +08:00 via iPhone
    其实我还在用 php5.2
    jqh
        26
    jqh  
       2020-05-17 10:52:19 +08:00
    @dvaknheo

    不是说核心工程师造轮子,是核心工程师调轮子。应用工程师一个 DuckPhp 命名空间的东西也用不到。
    DuckPhp 用 phpstan 检查过规范,phpunit 单元覆盖测试 100% 。
    --------------------------------------------------------
    “应用工程师一个 DuckPhp 命名空间的东西也用不到”,你这句话翻译一下就是:这个框架基本什么基础功能都不提供。

    那你让“核心工程师”调什么?你有提供 HTTP 请求和响应处理功能有吗?配置文件功能有吗?文件处理功能有吗?缓存工具有吗?参数表单验证工具有吗?还有更多基础功能你都没有,那你怎么让“核心工程师”不造轮子?

    最终的结果就是各种所谓的“核心工程师”拿到你的框架就是群魔乱舞各种瞎写,最后出来的就是惨不忍睹、很难维护的糟糕代码,这也是很多所谓自研框架公司的现状。


    + blade 模板 违背了 PHP 代码就是视图的原则
    + 滥用 ArrayIterator foreach, 使得没法 dump
    + orm 使得调试更麻烦了
    + 退化到路由表了。有简单的文件路由不用。
    + 中间件使得调用关系复杂化。
    --------------------------------------------------------
    PHP 代码是视图的原则??这都 2020 年了,您还崇尚 HTML 和 PHP 混编呢?这写出来的代码能维护?大清早就灭亡了亲;
    orm 使得调试更麻烦了,laravel 提供了多种方式可以让 ORM 转化成 sql 调试,调试虽然麻烦了一点,但 ORM 带来的便利程度远远大于麻烦程度;
    退化到路由表了。有简单的文件路由不用,中间件使得调用关系复杂化,从这个描述就说明,你根本没理解 laravel 路由和中间件的好用之处。

    中间件最大的好处就是能把各种与业务无直接关联的代码抽象出来,放在中间件里面,每个业务控制器 action 就可以通过配置轻松增减、切换各种不同的中间件,而不需要改动业务的一行代码。比如日志收集、登陆验证、接口权限判断等等,我甚至可以实现各种逻辑不同的登陆中间件,随意切换,如果你把这些跟业务无关的代码放在控制器中,那最终只会造成你代码杂乱、臃肿和难以维护。
    那么 laravel 的路由结合中间件的配置,简直不要太好用,我可以轻松给各种路由进行分组;尤其是方便了开发者写扩展包,可以以最大的自由度定义扩展包路由,以及其实用的中间件,简直太牛逼了。其他的大部分框架,都没有这么好用的路由,也难怪 laravel 的生态如此强大,可以说 laravel 的高质量的第三包扩展包的数量可以吊打任何一款 PHP 框架。

    可见一个成熟的设计是多么重要,细节和生态才是一个框架的根本。laravel 的方方面面的设计,其实都是兼顾了普通开发者和第三方扩展包开发者的需求的,但很可惜,很多人并不理解其中的意义。

    而楼主说的中间件使得调用关系复杂化,这完全可以说是你的水平不够,中间件的配置入口就是公共配置和路由配置两种,能复杂到哪去?堆栈调用复杂?我宁愿看多几十行堆栈调用,也不愿写业务代码和与业务无关代码混杂在一起像乱麻一样的代码。


    之前在我本机弄了 laravel 自带的 auth 的版本,发现 laravel auth 连我都没能弄清楚,怎么可能会有国内项目用他那套做验证
    --------------------------------------------------------
    你这个把我看笑了,你是看不起其他程序员,还是太看得起自己?? laravel auth 你都整不明白,还好意思大言不惭说这个不行那个不行?建议楼主保持谦逊的态度,对不懂的东西不要乱评判,看了楼主的发帖纪录,就是一直在评判自己没搞懂的东西
    dvaknheo
        27
    dvaknheo  
    OP
       2020-05-17 11:33:29 +08:00
    @jqh
    “应用工程师一个 DuckPhp 命名空间的东西也用不到”,你这句话翻译一下就是:这个框架基本什么基础功能都不提供。
    ----
    是有这么一个基本用不到的功能:是你可以无缝替换成另一个框架。 翻译就是无耦合。

    HTTP 请求, $_GET,$_POST 还没死绝呢。PHP 官方又没出一个自己的 psr 接口。PHP 为什么比 Java 容易明白没?
    Framework 和 Library 的区别知道么。 非得所有的都要要你 framework 里的么?
    没关系,核心工程师怎么偷换实现都不会乱, 反正应用工程师看不到。

    PHP 代码是视图的原则??这都 2020 年了,您还崇尚 HTML 和 PHP 混编呢?这写出来的代码能维护?大清早就灭亡了亲;
    那么非 PHP 的视图模板里写 sql 你见过没。Widget 里一个超级对象你见过没。PHP 视图的原则是不做计算,只输出,而不是混编。
    [Smarty 出了这么多年,PHPer 又退化到用 PHP 来写视图模板了]

    orm 使得调试更麻烦了,laravel 提供了多种方式可以让 ORM 转化成 sql 调试,调试虽然麻烦了一点,但 ORM 带来的便利程度远远大于麻烦程度;
    调试是重要工作。而且 orm 需要多学一层,所以又过滤了一部分懒人。

    PHP 最大的好处之一就是随时改业务代码。
    DuckPhp 好处之一就是你可以把你的中间件做成独立的工程独立使用。应该说是可以把独立工程变成“中间件”,之前回答里都有了,我上面没演示的是 view 和 配置,也可以 重载。
    DuckPhp 好处之二就是这些东西都是显式表达的,你不会看到“哇靠,这东西从哪里冒出来的,究竟错在哪里”。

    如果你把业务代码放在控制器里,那等到 10k 行控制器的时候维护起来就痛苦死了。

    日志收集,登陆验证,接口权限判断这些,都是核心工程师的职责。所以他们会告诉应用工程师:继承这个控制器基类就够了。想改这些,在基类里改就是。

    我水平不够,所以只能用 PHP 这种语言。现在高水平的都去用 Java ,Go 了。
    所以我写的框架也只是适用于那些想用 PHP 快速开发的人。
    [Laravel 是很优雅的框架,不应该使用 PHP 这种不优雅的语言,应该使用更好的语言来写。]

    Laravel 默认的 auth 代码的流程我后来是弄清楚了。搞那么复杂的目的是为了有效的过滤智力低下的懒惰的 CURD 程序员。
    nicoljiang
        28
    nicoljiang  
       2020-05-17 11:39:34 +08:00
    Laravel 是很优雅的框架,不应该使用 PHP 这种不优雅的语言,应该使用更好的语言来写。

    +1
    Laravel 的作者继续耕耘 .NET 方向多好
    jqh
        29
    jqh  
       2020-05-17 12:37:47 +08:00
    @dvaknheo

    ```php
    public function postLogin(Request $request)
    {
    $vaptcha = Vaptcha::make();

    // 验证验证码是否正确
    if (! $vaptcha->validate()) {
    // 验证不通过,返回错误提示信息到前端
    return $this->error($vaptcha->getError());
    }

    $credentials = $request->only([$this->username(), 'password']);
    $remember = (bool) $request->input('remember', false);

    // 验证参数
    $validator = Validator::make($credentials, [
    $this->username() => 'required',
    'password' => 'required',
    ]);

    if ($validator->fails()) {
    return $this->validationErrorsResponse($validator);
    }

    // 登陆并返回成功信息
    if ($this->guard()->attempt($credentials, $remember)) {
    return $this->sendLoginResponse($request);
    }

    // 返回失败信息
    return $this->validationErrorsResponse([
    $this->username() => $this->getFailedLoginMessage(),
    ]);
    }
    ```

    这里我截取一段使用 laravel auth 实现的登陆功能,换成你的说法,就是你口中“应用工程师”该写业务代码,多简洁易懂,这代码不好维护吗?跟业务无关吗?

    而其中$this->guard()->attempt 这样的 auth 底层实现,就是你口中“核心工程师”写的登陆的轮子,框架内置的轮子不比大部分“核心”工程师写的优质、好用许多吗?

    laravel 这样设计的更强大之处在于,“应用工程师”甚至不必关心登陆验证的数据存储细节,例如你是想使用 session 登陆、还是 auth2.0 登陆、还是缓存 token 之类的等等,都可以通过配置文件和中间件随意切换,不需要改动一行业务代码,这不是更简单了吗?而登陆到底是要用 session 还是 auth2,这个可以交给“核心工程师”去实现。

    而且不仅如此,laravel 的几乎所有组件都是这样的模式,系统把大部分功能都抽象成了一个统一的简单易用的功能接口,“应用工程师”写业务代码并不需要关心这些功能的具体实现,只需要简单的调用数行代码就行,可以把专注点放在业务上,而这些底层组件可以采用第三方扩展包也可以由“核心工程师”自己编写,然后通过配置文件切换而不用影响业务代码。

    你所考虑的,laravel 早就想到了。
    dvaknheo
        30
    dvaknheo  
    OP
       2020-05-17 14:02:54 +08:00
    @jqh 这是一个例子恰好演示了其糟糕性。
    单是返回就有四种可能

    return $this->error($vaptcha->getError());
    return $this->validationErrorsResponse($validator);
    return $this->sendLoginResponse($request);
    return $this->validationErrorsResponse([$this->username() => $this->getFailedLoginMessage(),]);

    而且注意到的是,按常理,最后一个才是正常返回。取反放最后就是。
    但是这样还没完。
    validationErrorsResponse 接受的参数又是 Validator ,又是 array 。还好 php 是弱类型。
    可是 $this->getFailedLoginMessage() 又从哪里来? 是否前面的调用会影响状态。
    $this->guard()->attempt($credentials, $remember)

    这个 guard() 也是怎么冒出来的,为什么要和控制器耦合,这个 attepm 词也很少见。
    为什么不是 this->attempt($credentials, $remember)

    下面是我重构的结果。

    ```
    class MyController
    {
    public function postLogin(Request $request)
    {
    // 输入处理,放前面,不必节省性能。
    $remember = (bool) $request->input('remember', false);
    $credentials = $request->only([$key_username, 'password']);
    //这也算输入吧
    $key_username= $this->username();

    //验证码服务
    $error = VaptchaService::make()->validate();
    if ($error) {
    // 验证不通过
    return $this->error($error);
    }

    //兼容,如果 service 有方式获得这个 $guard,则不需要输入。
    $guard = $this->guard();

    //登录服务
    $error = LoginService::make()->login($guard,$key_username,$credentials,$remeber);

    if($error){
    if(is_array($error)){
    $error[$key_username] = $this->getFailedLoginMessage();
    }
    return $this->validationErrorsResponse($error);
    }
    return $this->sendLoginResponse($request);
    }
    }
    class VaptchaService
    {
    // 省略 make 静态方法。
    public function validate()
    {
    // 验证验证码是否正确
    if (! Vaptcha::make()->validate()) {
    return $vaptcha->getError();
    }
    return null;
    }
    }
    class LoginService
    {
    // 省略 make 静态方法
    public function login($guard,$key_username,$credentials,$remember)
    {
    // 验证参数
    $validator = Validator::make($credentials, [
    $key_username => 'required',
    'password' => 'required',
    ]);
    if ($validator->fails()) {
    return $validator;
    }
    // 登录验证
    $flag=$guard->attempt($credentials, $remember);
    if (!$flag) {
    return [ $key_username => "something wrong"];
    }
    return null;
    }
    }
    ```

    如果使用 DuckPhp 你大概会这么写。

    ```
    class MyController
    {
    // 登录页面
    public function login()
    {
    C::Show([],'login');
    }
    //处理 post
    public function do_login()
    {
    // 输入处理,放前面,不必节省性能。
    $remember = (bool) C::POST('remember', false);
    $credentials = C::SG()->_POST();

    $error=null;
    try{
    //验证码服务
    VaptchaService::G()->validate();
    //登录服务,返回用户信息。
    $info = LoginService::G()->login($credentials,$remeber);
    //保存 session 数据
    SessionService::G()->setLoginUserInfo($info);

    C::Show(get_defined_vars(),'login-done');
    }catch(\Throwable $ex){
    $error = $ex->getMessage();
    C::Show(get_defined_vars(),'login');
    }
    }
    }
    ```
    为什么要调 3 个服务解决问题,而不是一个,
    第一个是处理验证码的,和 web 平台有关的特殊服务
    第二个是主要业务,测试的时候,可以命令行运行,获得信息,不必通过 web
    第三个是 session 系统管理,管控所有 session 系统,也是和 web 平台有关的特殊服务
    jqh
        31
    jqh  
       2020-05-17 14:39:34 +08:00
    @dvaknheo 你暴露了,就凭你不知道 guard 和 attempt 方法还说自己弄懂了 laravel auth....,guard 和 attempt 就是 auth 的组成部分。
    jqh
        32
    jqh  
       2020-05-17 14:42:29 +08:00
    return $this->error($vaptcha->getError());
    return $this->validationErrorsResponse($validator);
    return $this->sendLoginResponse($request);
    return $this->validationErrorsResponse([$this->username() => $this->getFailedLoginMessage(),]);

    这些只是响应数据的方法,没必要展示出来而已。

    而你这些 service 只是业务代码的结构约定划分,跟框架没有半毛钱关系,我如果愿意也可以这样划分。而你这个 sessionservice 就是画蛇添足,使用了 laravel auth,业务层根本不必关心登陆验证是要使用 session 还是 token,这些根本不必跟业务代码耦合。
    dvaknheo
        33
    dvaknheo  
    OP
       2020-05-17 15:05:19 +08:00
    @jqh 问题是为什么一个简单的登录,会搞出那么多东西出来? 真的会有人重写这个 guard 的实现么?
    控制器的 $this->guard() 要查找多少代码理解。这也就是 Laravel 不能用 类名 /方法 文件路由的原因。
    我刚才查了手头的 laravel 6 的 例子,没找到你这段代码,不知道你这是 laravel 几的版本。

    业务层必须能用命令行运行,最好是无状态的。Web 控制器层不做业务。 控制器不能 dump 起来一堆方法都不知道干什么的。

    不要指望应用工程师通读了 Illuminate\Auth 和 Illuminate\Foundation\Auth 之后才开始工作。
    love51money
        34
    love51money  
       2020-05-18 08:55:14 +08:00
    关注。楼主辛苦。
    yxn1910
        35
    yxn1910  
       2020-05-18 10:02:17 +08:00
    我一直有个很巨大的疑问,都已经用上 laravel 的人,相比于直接上 spring boot 有哪些非用不可的理由?
    1.相较其他框架更大开发效率?
    2.相较其他框架更快代码执行效率?
    3.巨大的投入产出比?
    4.投入时间进行学习后更利于找到工作?
    5.更加优雅?
    DavidNineRoc
        36
    DavidNineRoc  
       2020-05-18 11:39:50 +08:00
    @yxn1910
    你说的都没用,最直接的公司用 PHP
    iidestiny
        37
    iidestiny  
       2020-05-21 09:55:49 +08:00
    @dvaknheo 看来楼主是 TP 用得比较多么吗?
    iidestiny
        38
    iidestiny  
       2020-05-21 10:02:42 +08:00
    @jqh 赞👍 评价得很到位。laravel auth 那条我确实是笑了。。。。
    iidestiny
        39
    iidestiny  
       2020-05-21 10:09:17 +08:00
    还是给楼主一个赞,坚持自己。
    dvaknheo
        40
    dvaknheo  
    OP
       2020-05-21 10:51:51 +08:00
    @iidestiny 我是 CodeIgniter 出身的,
    一条原则就是,能不使用系统自带的就不用系统自带的。
    也就是我的想法:框架只做最少的事情。
    CodeIgniter 的优点是你不会怕:这东西哪里来的。
    缺点嘛,太多了没必要说了。

    TP 我用得不如 Laravel 多。但 Laravel 没有我能感觉到的优点
    如果说有,那就是有人说的,现在给 laravel 做的第三方包比较多。
    拿来就用,不必关心细节。
    而之前 TP 之类,都是给项目而不能直接使用。
    DuckPhp 一个卖点就是你可以直接把你的项目做为第三方包。

    laravel 的 auth 真的很差啊
    而且我不认为 auth 这些东西应该放在默认的框架里。

    其他框架的用户系统 demo 都很容易懂,很容易改。
    laravel 的用户系统 demo 是你们水平不够不要改。
    iidestiny
        41
    iidestiny  
       2020-05-21 12:13:32 +08:00
    @dvaknheo laravel auth 用的还是比较多的,作者之所以把登录封装成几个区块是为了让用户能根据自己的业务需求去覆盖来实现。如果没特别的业务需求直接就可以用框架自带的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3162 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 12:30 · PVG 20:30 · LAX 05:30 · JFK 08:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.