• PHP8.2后,动态属性将被弃用,而多年前在开发php框架时需要根据调用内容来生成动态属性,该这么办呢?
  • PHP4时代对象引用使用&,PHP5之后对象是引用传递,因为框架开发比较早,当时为了适配,运用了&符号,而升级到8之后会提示重载了;
  • Mysql5.6之后使用GROUP BY子句对数据进行分组,但在SELECT列表中包含了未被分组的非聚合列,那么将提示Fatal error: Syntax error or access violation: 1055 Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column

内容不断添加中~~

PHP部分

1. 动态属性

原本直接写

class A{public function setValue(){$this->num = 1;}}

这里的num属性正规写,就必须要

class A{public $num;public function setValue(){$this->num = 1;}}

同样,实例化后原本直接写即可

class A{public function setValue(){$this->num = 1;}}$x = new A();$x->num = 1;

但现在升级为8.0以后还这样写,就会出现警告提示PHP Deprecated: Creation of dynamic property A::$num is deprecated in ...,而且未来该写法将被放弃。

那么我们就要利用魔术方法将所谓的动态属性写给一个数组来解决这个问题。

class A{private $_dynamicProperty = array();public function __get($name) {//判断是否存在该属性对应键名if (array_key_exists($name, $this->_dynamicProperty)) {return $this->_dynamicProperty[$name];}return NULL;}//写入数组,属性名即为数组键名public function __set($name, $value) {//将原本的属性名写入数组键名,调用的时候利用__get()方法直接去数组里获取$this->_dynamicProperty[$name] = $value;}//属性都存储在_dynamicProperty中,那么各应用模块中如果要用到isset时就需要调用本魔术方法//否则类似isset($obj->num)的操作去判断属性是否存在将会失败public function __isset($name) {return isset($this->_dynamicProperty[$name]);}}$x = new A();$x->num = 1;

2. 对象引用和重载错误

重载指多个名字相同,但参数不同的函数在同一作用域并存的现象。因为PHP早期以灵活著称,所以传统意义上的重载将可能导致不同函数的覆盖从而出错。

我这里举例主要针对的是自研框架的错误。
当时开发框架时处于php4和php5期间,而php4重复同一个对象的操作需要用到&来作为引用,而到了php5,对象就是引用方式来使用的。以往用&符号引用对象不会有问题,但是到了php8之后,由于动态属性的已经变异靠__set()、__get()和数组来共同实现了,那么再用&符号去引用就显得多余并且会出错了。
考虑到现在已经不需要再兼容PHP4了,所以我们的代码用PHP5及目前版本8.0都能接受的方式来重改。

我们下面循序渐进的了解引用。

原代码:

class A{public $num;public function setValue($n=0){$this->num = $n;}}$x = new A();$y = & $x;$y->setValue(5);echo $y->num;echo $x->num;//结果// 5// 5

现PHP8.2后需提前告知$num属性存在,否则出错:

class A{public $num;public function setValue($n=0){$this->num = $n;}}$x = new A();$y = $x;$y->setValue(5);echo $y->num;echo $x->num;// 结果// 5// 5

《PHP对象赋值给变量的两种方式的区别,一般赋值和引用赋值?》
这里有一个小知识:加了&,就是对该变量的引用,没有加则还是对象的引用,如下案例:

class A{public $num;public function setValue($n=0){$this->num = $n;}}$x = new A();$y = $x;$z = & $x;$y->setValue(5);echo $y->num;echo $x->num;echo $z->num;// 结果// 5// 5// 5// 此刻原本对象引用的变量$x的值改为null,那么引用它的$z的值也改变了,而$y因为是引用的对象,其值没有改变$x = null;var_dump($y);var_dump($x);var_dump($z);// 结果// object(A)#24 (1) { ["num"]=> int(5) }// NULL// NULL

这篇文章 《PHP的stdClass的理解》 虽然是针对基类的学习,但其中提到了对象的引用,可以学习。

参考:
《PHP:对象和引用》
《PHP重载》

了解了上面的基础知识后,我们来看一段会发生重载属性的代码

class A {private $data = [];public function __get($name) {if (!isset($this->data[$name])) {$this->data[$name] = null;}return $this->data[$name];}public function __set($name, $value) {$this->data[$name] = $value;}}$x = new A();$x->value = 'Initial Value';// 下面这行代码会触发 "Indirect modification of overloaded property" 错误$alias = 'value';$aliasValue = & $x->$alias;$aliasValue = 'Modified Value';// 结果// PHP Notice:Indirect modification of overloaded property A::$value has no effect in ...

对象已经是引用了,其内部的属性再用引用方式则弹出重载错误,PHP4那会儿为了节省资源,用&引用,但目前不需要了。
解决办法很简单:那就是去掉&即可~~哈哈,就是这么简单。

class A {private $data = [];public function __set($name, $value) {$this->data[$name] = $value;}public function __get($name) {return $this->data[$name];}}$x = new A();$x->value = 'Initial Value';// 通过引用整个对象来避免 "Indirect modification of overloaded property" 错误$alias = 'value';$aliasValue = $x->$alias;// 获取属性值,不使用引用$aliasValue = 'Modified Value';// 修改属性值var_dump($x->value);// 输出 "Modified Value"

Mysql部分

1. 因group聚合产生的错误

错误警告类似于:

PHP Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.c.papername’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by in …

错误原因:

  • 使用了 GROUP BY 子句对数据进行分组,但在 SELECT 列表中包含了未被分组的非聚合列 test.c.papername。
  • 数据库模式设置为 sql_mode=only_full_group_by,该模式要求在 GROUP BY 子句中列出的列必须是所有非聚合列的函数依赖。

解决方案:

  1. 添加列到 GROUP BY 子句:
    test.c.papername 等涉及到的所有列添加到 GROUP BY 子句中,确保所有非聚合列都参与分组。
SELECT c.papername, COUNT(*) AS countFROM paper as cJOIN papercategory AS b ON c.id=b.id...GROUP BY b.categoryid //原本必须要整合的字段GROUP BY c.papername, //SELECT中提到GROUP BY c.paperid, // ORDER中提到GROUP BY ...// 添加其他分组列...ORDER BY c.paperid

注意昏头:
group by一定要存在能明确区分每行数据的标志,比如id,如果没有group分组id只分组papername,那么极可能因两个papername名称重名而合并,实际他们是两份完全不同的内容。

  1. 使用聚合函数:
    如果不想在 GROUP BY 子句中包含 test.c.papername,可以使用聚合函数(如 MAX、MIN、SUM、AVG)来处理它:
SELECT MAX(c.papername) AS papername, COUNT(*) AS countFROM paper as cJOIN papercategory AS b ON c.id=b.id...GROUP BY b.categoryid //原本必须要整合的字段
  1. 调整 sql_mode:
    如果不想遵循 only_full_group_by 模式的严格要求,可以调整数据库的 sql_mode 设置:

在 MySQL 配置文件(my.cnf 或 my.ini)中找到 sql_mode 选项,并将 ONLY_FULL_GROUP_BY 从列表中移除。

或者,在 MySQL 会话中使用以下命令:

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
  1. 注意:
    调整 sql_mode 可能会影响其他 SQL 语句的行为,因此请谨慎操作,并在测试环境中进行验证。
    通常建议遵循 only_full_group_by 模式的规范,以确保数据的一致性和完整性。
    请根据您的具体查询和需求选择合适的解决方案。如果您可以提供更多代码或信息,我会更准确地帮助您解决问题。

  2. 结论:
    我选择了第一种方案,宁可吃力点,把所有因涉及到group而出错的代码给改了,
    第二种方案有隐患,参考《聊聊MySql8.0中的group by 和 max函数取最新(优)一条记录的问题》
    第三种方案万一服务器数据库不给修改就不好弄了,而且每次装机都可能涉及到修改数据库默认数值,这对于一个框架来说是不可取的。

2. datatime不允许“0000-00-00 00:00:00”

  1. 默认状态下是不允许的,和上面一样改

在 MySQL 配置文件(my.cnf 或 my.ini)中找到 sql_mode 选项,并将 NO_ZERO_IN_DATE,NO_ZERO_DATE 从列表中移除。


2. 将所有原来0000-00-00 00:00:00改成 0000-01-01 00:00:00
我也是选择了这种方式,一切以默认为准,批量修改代码吧。