[吐槽]刚读了 yii3-demo, PHP 框架是怎么把 PHP (优雅的)玩死的。

2020-04-13 16:30:36 +08:00
 dvaknheo
虽然是看 yii3-demo 吐槽的,但 Laravel 等框架也适用。

业务代码里,和框架相关的代码越少才是耦合性越低啊。口口声声说解耦,但是一看代码里一堆和框架代码相关的,这能叫解耦么。

业务代码里,框架相关的代码多,意味着碰到框架相关的代码就越多

读代码,最怕的之一是:这东西怎么来的。 一两个特例马上能教人懂,多了没法马上会啊。

View 里不能做任何计算,不能 include 非 View 的代码。

Laravel 优雅的带着 PHP 优雅的 Java 化。当初 PHP 上手就干的年代,这是 PHP 的优势。

现在一堆代码,业务相关的没几行,这还是 PHP 么。

ORM 就非用不可么,Data struct 不容易理解么

有好用的文件路由不用,非得手写路由 (src/Factory/AppRouterFactory.php)
PHPer 的原则应该是 其他路由模式是文件路由的补充啊。

配置文件里一堆 use (config/web.php)

MVC 缺层都是大家的共识了, 业务逻辑层叫 Service 或 Logic 我都无所谓,但直接 Controller 里调用 ORM,以前 10k 行的 Controller 啊。 从 Controller 里把业务逻辑剥离出来,跑命令行测试不好么。

PHP 开发速度很快,运行速度也很快。 没必要为了那些优雅牺牲开发速度,运行速度。

讲真,我动力再足一点,写个同功能的 插进这些框架的 demo 中, 让大家见识一下应该用什么样的手段最符合 PHP 的快速开发模式。
16114 次点击
所在节点    PHP
126 条回复
james122333
2020-04-14 12:29:10 +08:00
话说个人一直不觉得这是优雅 优雅就要轻
开坦克觉得是优雅...
Varobjs
2020-04-14 12:36:46 +08:00
是时候推荐你的框架了楼主
encro
2020-04-14 14:51:47 +08:00
@dvaknheo


一:



<?php
// ...
echo Alert::widget()
//..
?>

我个人会选择

<?=Alert::widget(['class'=>'my-custom'])?>

更加灵活,而不是从控制器传入,
框架的核心有一条是逻辑分离,减少大脑负载,
yii model 负责规则配置、数据操作,
控制器负责接收请求、组装数据,
视图负责展示,
如果采用控制器传入,那么就不是在视图定制了,而是在控制器定制界面,
如果你连这条不清楚,
那么你的框架我非常不看好,
而且会劝别人不要用,
因为大家会找不到这个界面的代码写在哪里,
我是不会去维护这样的代码的。


二:

action="<?= $urlGenerator->generate('site/contact') ?>"


Url::to(['site/contract'])方法是可以用的。


三:

其实最希望的是 View 里 一个命名空间都不用,代码简洁得多。

前面有提到视图负责展示逻辑,你记住这句就可以了,只要没有脱离这个本质,再多命名空间,那么代码也是清晰和直白的。

代码的清晰条理性重要性很重要,你说的 view 一个命名空间不用,Yii 至少有 5 种选择:

1,将展示逻辑放在控制器;(展示逻辑不分离,不推荐)
2,隐式引用;(没有代码提示,不好 DEBUG,不直观,不推荐)
3,模板引擎;(个人认为 php 本身就是最好的模板引擎,不推荐)
4,采用 ajax ;(这个可以);
5,调用 rest 接口;(这个也可以)

你有第六种办法,还是用了其中一种?


四:

配置 rule VS 手动 debug

rule 可以自动生成(gii),改好 rule 就会错误提示一致,不会出 bug,不需要 debug 了,所以这就是自动驾驶,你现在和我说手动挡好?


五:

安全

比如内置了 jwt,rules 验证,cookie 加密,防注入等等,这些基本是目前框架标配吧。
你感觉不到,因为你看的是普通模板,不是高级模板,普通模板是隐藏了这些的,不表示它不存在,且你没有看官方文档,直接上手一个 demo 了。
比如需要配置 cookieValidationKey 你知道是做什么用的吗?
比如为什么配置文件要分为 local 和非 local ?
environments 里面的 prod 和 dev 又是做什么用的?


六:

有没有默认配置,让我的 业务工程看起来简单。


https://www.yiiframework.com/
首页 5 个步骤第一个步骤:安装 composer,下载标准模板,运行 yii serve 。
三行命令项目都跑起来了,进入开发了,你说没有默认配置?



七:
比如模拟某个人操作,没必要用 curl 模拟从他登录开始吧。
拿到 uid,直接调用 service 业务层就行了。

Model 还可以用,Console 也可以用,其他都可以用,不必重复,
Yii3 最大的不同和目的就是将 jq,bt 等前端框架从核心中分离出来。
至于这个 demo 你可以看成秀技术的,Yii 其实一直都是紧跟 PHP 版本的。


八:

php7.4 预加载,解决了一部分性能问题。但是并没有完全解决

还是请举出一个除了不能常驻内存之外的性能问题例子出来,不能说空话啊,写程序的不能空对空,得摆事实。



九:
一个请求过来,框架的 hello world 要比纯 php 的 helloworld 跑多出很多 php 代码。

现实是大家都用框架,WHY ?
都是大牛们为了优雅好玩的?
大牛们都傻?
老板也傻?
层次不同而已,你看到的是自己写得爽,对其他任来说是团队合作有一套默契的规范,能够高效解决问题。

十:

为了追求性能和模式,所谓 [WEB] 高手们抛下了 PHP 改用 go 、java 、c#,留下一个所谓“优雅”的 PHP 开发氛围。

你还是可以不用框架啊,而且你还是可以自己写一个框架啊,所以有什么好说的。
环境变了,
你以为 PHP 还是嵌入 html 中用,
其实已经经历过我前面提到过的 cake,zend,yii,laravel 多个时代了,
现在即将进入云原生时代了,
你期望 PHP 什么都能解决,
我只期望它做好 Web 即可,
至少现在开发一个人人商城,微擎这样的软件,我找不到更好的语言,
至于其他,至少我还会 Python,node,go,还在学习 C++和 C#,哪个适合我就用哪个,有什么好纠结的。



建议先看完官方手册,以及下载官方推荐的几个开源项目下来用用,然后自己动手写一个商业项目,想想这个框架适合场景,再来评价框架好坏更好。
当然还需要看下软件工程,软件思想的书籍。
知道你想骗我,想多从我这里掏点东西,自己快速学走捷径,先满足你吧。
然后有一天你会明白“欲速则不达”的道理。
pmispig
2020-04-14 15:12:26 +08:00
php 的框架什么的被 java 带歪了,越来越像 java,没有自己的特色了
james122333
2020-04-14 15:15:29 +08:00
看来还是我想的够简单 (滑稽
dvaknheo
2020-04-14 18:43:06 +08:00
@encro
1.
widget 的维护还是给程序员。你这种作法也行。
框架的核心有一条是逻辑分离,减少大脑负载。没错。

今天我找那个 ORMInfterface $orm 怎么用 App::GetOrm() 的额外方法得到,找了大半天。 自己又 debug 半天,发现全局的 $container 容器是没有的。 虽然全局都在用,但是都传递给构建方法,想自己找都没法搞。 或许是我走歪路了吧。
我大致知道 得到这个 $container 后, 可以 override 容器里的东西。

控制器负责接收请求、组装数据, 控制器做外部数据整理和输出,没错。
控制器基本不负责做业务,业务在 Service 层做。 所以我框架的控制器是不返回任何东西的。
不希望用户在 Controller 里 dump 一个东西,或者强塞东西塞不了。
而 yii3 一定要返回 ResponseInterface 这就有一个和 psr 的耦合了。
而且视图和控制器的方法基本一比一。

如果采用控制器传入,那么就不是在视图定制了,而是在控制器定制界面,
那看 demo 的吧

https://github.com/yiisoft/yii-demo/blob/master/src/Controller.php

这个 '/layout/main' 怎么解释? ViewContextInterface 又是什么东西?
为什么 这个基类 Controller 类 要实现 ViewContextInterface ?


```
abstract class Controller implements ViewContextInterface
{

public function __construct(

WebView $view
) {
$this->layout = $aliases->get('@views') . '/layout/main';
```
为什么要 $this->render() ,而不是 View::GetInstance()->render();
这个基类的还有好多东西不知道怎么冒出来就不要提了。

---- 多说一下我 DuckPHP 的作法吧
https://github.com/dvaknheo/duckphp/blob/master/template/app/Controller/Main.php
缩减无关代码。
```
<?php declare(strict_types=1);
namespace MY\Controller;
use MY\Base\Helper\ControllerHelper as C;
use MY\Service\TestService;
class Main // extends BaseController
{
public function index()
{
$var = C::H(TestService::G()->foo());
C::Show(get_defined_vars(), 'main');
}
}
```
我不知道 yii3 工程的命名空间 App 是否能改,但 DuckPHP 的命名空间是不受限制的。
多工程拼一起是可以做到的。 甚至你可以把工程变为插件让其他人引用
DuckPHP 的控制器是没继承任何类的。这就不会让人看到 $this-> 后去看这东西是不是父类里冒出来的。
而且也告诉你, 你的工程继承可以父类。
dvaknheo
2020-04-14 18:50:25 +08:00
@encro
Url::to(['site/contract'])方法是可以用的。 这种方式,应该在 demo 里显示出来。

前面有提到视图负责展示逻辑,你记住这句就可以了,只要没有脱离这个本质,再多命名空间,那么代码也是清晰和直白的。
//经常是从输出有问题,才看 View 文件。 我不希望对 HTML 做太大包装,我们要逆向追踪啊。

在 php 的 view 文件里,这三行能明白 view 文件的运作
<?php
var_dump(get_define_vars());
var_dump($this);
debug_print_backtrace(2);
?>
第二行, 超大的 View 对象。
第三行,我居然惊讶的发现 yii3 view 显示的堆栈 和 Controller 的 堆栈根不同。
dvaknheo
2020-04-14 19:01:22 +08:00
比如内置了 jwt,rules 验证,cookie 加密,防注入等等,这些基本是目前框架标配吧。

套用 yii 自己说的: [ Be Explicit ]


https://www.yiiframework.com/
首页 5 个步骤第一个步骤:安装 composer,下载标准模板,运行 yii serve 。
三行命令项目都跑起来了,进入开发了,你说没有默认配置?

然后工程文件里一坨我都不知道有什么用的配置。比如这些:

https://github.com/yiisoft/yii-demo/blob/master/config/web.php
```
// Router:
RouteCollectorInterface::class => Group::create(),
UrlMatcherInterface::class => new AppRouterFactory(),
UrlGeneratorInterface::class => UrlGenerator::class,

MiddlewareDispatcher::class => new MiddlewareDispatcherFactory(),
SessionInterface::class => [
'__class' => Session::class,
'__construct()' => [
$params['session']['options'] ?? [],
$params['session']['handler'] ?? null,
],
```
vendor/yii-web 里有个配置,然后和工程文件里的配置 array_merge 一下不行么?

DuckPHP 的作法是把能用的默认配置都注释了, 你想要的时候解除注释

七:

Model 还可以用,Console 也可以用,其他都可以用,不必重复,

这个 demo 没体现出这点,就如我说的, 我想看 Blog 页面首页有什么内容,是没有直接的业务接口的。
我知道还有 api 方式调用但是命令行测试的时候调用一个就行了啊。

--

嗯,我有些动力把 DuckPHP 插在 yii3 之前使用,看 DuckPHP 开发相同功能是怎么做的,代码能少多少了。
encro
2020-04-14 19:38:42 +08:00
@dvaknheo
既然你放出了你得框架( https://github.com/dvaknheo/duckphp ),
那么我就去看一下吧:

你的 hello world 代码,虽然只有几行,但是存在几个点,我看了之后基本不会用,也会劝别人不要用。

```
<?php
require_once __DIR__.'/../vendor/autoload.php';

class Main
{
public function index()
{
echo "hello world";
}
}
$options=[
'namespace_controller'=>'\\', // 设置控制器的命名空间为根
'skip_setting_file'=>true, // 跳过配置文件
];
DuckPHP\DuckPHP::RunQuickly($options);
```

第一:echo "hello world"; 而不是 return "hello world"?直接在控制器里面输出 html 这也是一绝了(绝迹的绝),你想让其他人都学样在控制器 echo html 吗?为什么其他框架都要采用 return 呢?

第二:namespace_controller 这是什么配置,双斜杆为根,什么奇葩?单独只让自己知道一个标准?我是不看源码心里没底。好的框架不应该直接一看就知道是什么吗?和 yii 的 'controllerNamespace' => 'api\controllers' 比一下,后者我一看就知道是什么意思,我用的着这简写吗,我在给别人还是自己挖坑吗?你真要少代码,因为反正都隐藏了,不如直接不配置,约定俗称。

第三:skip_setting_file=true 脱裤子放屁?不应该是不写配置就是自动跳过配置吗?


第四:DuckPHP\DuckPHP::RunQuickly($options)?莫非还有一个 RunSlowly($options)?有什么问题是直接一个 DuckPHP\DuckPHP::Run($options)解决不了的?


第五:Controller 写在入口文件?这得多 demo 啊?这是 micro/slim/flask 这类函数式框架的写法吧?


对于你的提到 Yii3 demo 增加了复杂度,我是同样看法的,应该可以更加简单,不妨先看看 yii2 吧,如果 yii3 真就这样,反正不用就是了,现在也在难产中,什么时候出来不知道。
我 yii 2 目前够用(除了那个和 bootstrap3 绑定外)。

对于以上评价,可能显得刻薄,请谅解。
敬佩你写开源框架的精神。

Yii3 确实被玩坏了,你的判断是对的,我也有这种感觉,团队成员现在动力不大了,因为 PHP 的发展空间就摆在这里,这个轮子不好玩了,意义不大了,我们都要承认。

PHP 要续命,目前我看只有写出一个好的基于云原生的框架,然后提供这个框架之上的大量现成程序,改下就能用。(类似 thinkphp 的 fastadmin,微擎的应用商店,这些都是中国特色,老外是不屑玩的)。
jsjscool
2020-04-14 19:39:41 +08:00
对于开发者来说不实用的问题确实存在,但是放到项目里来说这些"过度设计"是必不可少的。举个简单的例子,你们都要自己设计框架,肯定知道 Active Record 和 Data Mappers 。Data Mappers 很难用,但很多框架直接选择了他。你会选吗?如果多几个人能去思考这个问题,无数勇敢的少年将会创造奇迹。

程序员都很有个性,同一个人上周写的代码和这周写的差别都非常大。要是项目组超过 2 个人,你会发现编码的自由度越高,项目代码写的就越烂。可参考的规范越少,项目的扩展性就越差。互联网行业不要求每个程序员都能将业务代码封装成独立的 Bundle,但是每个程序员都应该要有"这块功能未来怎样能快速的变成 Bundle"的意识。而这些高级框架帮我们做的就是减少自由度,增加规范,提高内聚,这也是未来的趋势。项目允许有垃圾代码,但是这些垃圾代码影响的范围必须在可控范围内。
encro
2020-04-14 19:43:07 +08:00
CoderGeek
2020-04-14 19:51:26 +08:00
轻就减少依赖 多框架完全没觉得轻
自己玩的话怎么舒服怎么来
hantsy
2020-04-14 19:54:36 +08:00
yii 也是重写了 Prado,好像核心也是有华人。以前那段时间恰好做了 php 项目,用了 CakePHP,Prado 等。Prado 大量的模仿 Apache Tapestry ( Java 框架), 由于性能方面的问题很大,决定重写。

Yii 第一版本的时候,PHP 5 已经出来一段时间了,但是 YII 开发太保守了,第一版本居然没有 Namespace 支持,还用恶心的前缀命名,我就没跟进关注了。
encro
2020-04-14 19:57:23 +08:00
这几个才是正规项目开始的地方。

yii-base-web,yii-base-api 和 yii-base-cli 这几个参考才是正式开始的地方

demo 是某些人练手的地方,确实不友好。

https://github.com/yiisoft/docs 的 Roadmap 还有很多,不知道什么时候呢,我等了 2 年了。
hantsy
2020-04-14 20:03:45 +08:00
现在自由一点 Micro Framework 也有,Slim,Silex 等。

Symfony,ZendFramework (现在移动了 Linux 基金会下,https://getlaminas.org/ ,这个名字我都记不住)比较合适开发复杂的程序,特别是 Zend 把 Martin Flower 的企业模式基本都是实现了。Symfony, Doctrine 等完全跟上了 Java 流行框架的步伐。
weize888
2020-04-14 20:06:02 +08:00
PHP 简单易上手,基本 PHPer 都有写自己的框架。既然大家都来讨论 php 的优雅,我也来献丑一下自己两年前前写的代码。
https://github.com/weizephp/weizephp
dvaknheo
2020-04-14 20:07:28 +08:00
@encro
template 目录还有个完整的例子。 这个例子只是为了演示写在同一个文件。


一: 也是我困惑的问题,为什么要 Controller 非得要 return 一些东西?初学者最容易搞不懂的就是这个。

二: 控制器命名空间为 \ 根命名空间。因为常规下是在 ProjectNamespace\Controller 底下的。 这个小例子不需要。 这个例子就是写小型程序用的,这样其他控制器类也直接写。我应该还加上配置扩展选项,使得没服务器配置也能用。追加

$options['ext']['DuckPhp\\Ext\\RouteHookOneFileMode']=true; //内置扩展类。
$options[key_for_action']='_r';

这样可以通过 $_GET['_r'] 来做路由的控制器分发了。 url() 函数的结果 自动切换 a/b 为 _r=a/b,不需要改

三: 修改默认设置。skip_setting_file=false 目的是为了防止你传代码不传设置文件上去的时候报错。

四:

DuckPHP\DuckPHP::RunQuickly($options,$callback) =>
DuckPHP\DuckPHP::G()->init($options);
//$callback();
DuckPHP\DuckPHP::G()->run();

前者只初始化,后面才开始路由。DuckPHP 不仅仅支持整站配置,还支持非整站的路由设置, 如放到 /something/index.php 里。

还有 Swoole 兼容的插件, 把初始化部分放在主流程, 运行部分在工作协程。无需改动代码。
encro
2020-04-14 20:30:50 +08:00
@dvaknheo
我知道都能说的通,
但是一个框架或者一段代码,
如果不是明摆着的,
而是需要解释,
那么就是没人愿意看的愿意用的。


一: 也是我困惑的问题,为什么要 Controller 非得要 return 一些东西?初学者最容易搞不懂的就是这个。
所以我们要明白为什么几乎所有流行框架都用 return 呢?其实因为后面方便加 cache 层,加 layout 啊。


二: 控制器命名空间为 \ 根命名空间。因为常规下是在 ProjectNamespace\Controller 底下的。 这个小例子不需要。 这个例子就是写小型程序用的,这样其他控制器类也直接写。我应该还加上配置扩展选项,使得没服务器配置也能用。追加

$options=[
'namespace_controller'=>'\\', // 设置控制器的命名空间为根
];

你改成
$options=[
'controller_namespace'=>'Controller ',
];
这样,你的框架又少了一条潜规则(双斜杠),框架不是潜规则越少,越容易学么?


三: 修改默认设置。skip_setting_file=false 目的是为了防止你传代码不传设置文件上去的时候报错。

做多了

$options=[
'namespace_controller'=>'\\', // 设置控制器的命名空间为根
'skip_setting_file'=>true, // 跳过配置文件,防止传代码不传设置文件上去的时候报错
];

多别扭,新手关心这个干什么,这个就不是最佳实践



四:

DuckPHP\DuckPHP::RunQuickly($options,$callback) =>
DuckPHP\DuckPHP::G()->init($options);
//$callback();
DuckPHP\DuckPHP::G()->run();
前者只初始化,后面才开始路由。DuckPHP 不仅仅支持整站配置,还支持非整站的路由设置, 如放到 /something/index.php 里。
还有 Swoole 兼容的插件, 把初始化部分放在主流程, 运行部分在工作协程。无需改动代码。


试试
DuckPHP\DuckPHP::run(['mode'=>'swoole'])
或者
DuckPHP\DuckPHP::run([],DuckPHP::RUN_MODE_SWOOLE)

还有这个 DuckPHP\DuckPHP::G()又多了一个方法,写全一点就少了一个隐藏点,仍然是潜规则越少越好,我记不住,大多数人都记不住。大部分时候,显式表达由于隐式表达。

同事说给我去买个带个午餐上周二你帮我带过的,或者说给我去兰州拉面买个刀削面,当然是后者更不会出错。

这些基础不搞好,再提性能和架构都是扯。
JohnH
2020-04-14 21:03:58 +08:00
无意冒犯,看了这么多回复下来,从代码和思想上,楼主所处的阶段应该还是有命名空间以前的时代。

我想楼主并没有对任何一个框架( Laravel 、Yii2 )进行**长期、深入**的学习使用,否则你的疑惑就不是疑惑了,你自己的轮子也不会是现在的样子(说难听点,可能思维还不到 thinkphp 3.x 时期)。
dvaknheo
2020-04-14 21:04:41 +08:00
@encro
一:加 cache 层,加 layout 是在外围的事情,直接 echo 也能加,显式表达 ,性能起见 。Duckphp 没去用中间件,用路由钩子的方式。

二:这是简单模式,先懂通用模式才回到这种简单模式。 或许我应该加个 $options['mode_simple'] 组合这几个参数。 不是双斜杠啊,转义后是 \ , 根命名空间,

三:skip_setting_file 因为设置文件是不要存于版本控制里的,所以特意加的限定。而且有提醒看设置文件的作用。 (`mv -T` 不香么)

四:init() 后的各组件可以用,也是高级用法。 所以为什么给出这个公开方法。初始化之后不一定马上就开始路由了。
Swoole 支持是通过 DuckPhp\Ext\PluginForSwooleHttpd::class 这个插件开启的。

G() 这个方法是高级用户用的。 普通用户,只要懂 M,V,C,S, 这四个助手类,还有 DB,Log 对象 够了。

实现热修复就是靠这个函数。DuckPhp::G()->init() 后,DuckPhp::G() => My\Base\App;
这个可变单例我在其他地方说过多次了。 替换组件也就是 BaseClass::G(MyClass::G()); 这样后面的 BaseClass::G() => MyClass();

在开发 DuckPhp 过程中,我经常想的是:不这么做不行么?只有不得已才加上必要的功能。

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

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

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

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

© 2021 V2EX