ツイート
シェア
LINEで送る
B! はてぶでブックマーク
Pocketでブックマーク
RSSフィード

PHP8, 配列やクラスオブジェクトの算術/ビット演算でエラーになる

php
イラストダウンロードサイト【イラストAC】
の画像をもとに加工しています。

PHP8では、配列やクラスオブジェクトの算術演算、ビット演算で型チェックが厳しくなりました。

警告だったものがエラーになります。

PHP8はデータの厳格化がポイントなので演算にも影響します。

『そんな計算ないわー』と思っていた人にとっては当たり前の話。

2020年11月26日、5年ぶりにPHPのメジャーバージョンが上がりました。PHP8です。

メジャーアップデートだけに変更点も大きいです。配列やクラスオブジェクトの算術演算、ビット演算の型チェックを厳しくしたのもそう。

PHP7までも、『そんな計算ないっしょ』だったものはエラーになっていましたが、一部、警告で済んでいたものがありました。

PHP8では、ありえない計算をしたものはUncaught TypeErrorの例外をスローするように統一されています。

言葉で説明するよりも、実際に動かしたほうが『百聞は一見にしかず』なので、サンプルコードで進めていきます。

配列の算術演算

これまでも配列の算術演算はできました。足し算(+)だけは。

配列で足し算をすると配列の結合になります。これはPHP8でも変わりません。

そのほかのありえない算術演算の挙動がPHP8から変わります。

配列の結合(+)

配列の結合の結果は変わりません。

サンプルコード
<?php

var_dump( [ 1, 2 ] + [ 10, 20, 30 ] );
実行結果
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(30)
}

- , * , / : ErrorからTypeErrorへ変更

引き算(-)、掛け算(*)、割り算(/)はPHP7でもエラーですが、PHP8ではエラーの種類が変わりUncaught TypeErrorになります。

サンプルコード
<?php

var_dump( [ 1, 2 ] - [ 10, 20, 30 ] );
PHP7の実行結果
PHP Fatal error:  Unsupported operand types in /home/vagrant/sample.php on line 3
PHP8の実行結果
PHP Fatal error:  Uncaught TypeError: Unsupported operand types: array - array in /home/vagrant/sample.php:3
Stack trace:
#0 {main}
  thrown in /home/vagrant/sample.php on line 3

サンプルは引き算ですが、掛け算、割り算でも同じ結果になります。

正確には、PHP8でのTypeErrorは例外です。投げられた例外をキャッチして独自のエラーロジックを書くことができる。

『配列とintではどうか?』と思うかもしれませんが、どちらかの計算対象の型(オペランド)が配列だったら同じ結果になります。

% , ** : ややこしかった挙動がエラー例外に

剰余(%, 〇〇を△△で割った余り)、累乗(**, 〇〇の△△乗)はこれまで、ややこしい動きをしていました。

サンプルコード
<?php

var_dump( [ 1, 2 ] % [ 10, 20, 30 ] );
PHP7の実行結果
int(0)

ありえない計算をしているのに結果は整数の0。

言わんとすることは分かる。曖昧な比較(==)ではfalseを返しているのと一緒だし。

ただ剰余では正しい計算の結果0との区別がつきません。

(余りが0と結果が同じ。)

この不都合な結果がPHP8では修正され、Uncaught TypeErrorになります。

PHP8の実行結果
PHP Fatal error:  Uncaught TypeError: Unsupported operand types: array % array in /home/vagrant/sample.php:3
Stack trace:
#0 {main}
  thrown in /home/vagrant/sample.php on line 3

配列のビット演算

配列のビット演算はbooleanの計算結果が出ます(シフト演算はint)。配列を格納するメモリのアドレスに値が入っていて、それをビット単位にすると0と1の集合体として見るため。

PHP8では、すべてのビット演算でUncaught TypeErrorになります。

サンプルコード
<?php

var_dump( [ 1, 2 ] & [ 10, 20, 30 ] );
PHP7の実行結果
int(1)
PHP8の実行結果
PHP Fatal error:  Uncaught TypeError: Unsupported operand types: array & array in /home/vagrant/sample.php:3
Stack trace:
#0 {main}
  thrown in /home/vagrant/sample.php on line 3

クラスオブジェクトの算術演算

PHP7まで、クラスオブジェクトで算術演算をすると、警告(Notice)が出るだけで処理をつづけます。

クラスオブジェクトを格納するメモリアドレスの値を無理やりintに変換して。もちろん、その値に意味はありません。

これもPHP8では、すべての演算子でUncaught TypeErrorになります。

サンプルコード
<?php

class Class1 {
    public $number = 1;
}
class Class2 {
    public $number = 2;
}

var_dump( ( new Class1() ) + ( new Class2() ) );
var_dump( ( new Class1() ) - ( new Class2() ) );
var_dump( ( new Class1() ) / ( new Class2() ) );
var_dump( ( new Class1() ) * ( new Class2() ) );
var_dump( ( new Class1() ) % ( new Class2() ) );
var_dump( ( new Class1() ) ** ( new Class2() ) );
PHP7の実行結果
PHP Notice:  Object of class Class1 could not be converted to number in /home/vagrant/sample.php on line 10
PHP Notice:  Object of class Class2 could not be converted to number in /home/vagrant/sample.php on line 10
int(2)
PHP Notice:  Object of class Class1 could not be converted to number in /home/vagrant/sample.php on line 11
PHP Notice:  Object of class Class2 could not be converted to number in /home/vagrant/sample.php on line 11
int(0)
PHP Notice:  Object of class Class1 could not be converted to number in /home/vagrant/sample.php on line 12
PHP Notice:  Object of class Class2 could not be converted to number in /home/vagrant/sample.php on line 12
int(1)
PHP Notice:  Object of class Class1 could not be converted to number in /home/vagrant/sample.php on line 13
PHP Notice:  Object of class Class2 could not be converted to number in /home/vagrant/sample.php on line 13
int(1)
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 14
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 14
int(0)
PHP Notice:  Object of class Class1 could not be converted to number in /home/vagrant/sample.php on line 15
PHP Notice:  Object of class Class2 could not be converted to number in /home/vagrant/sample.php on line 15
int(1)
PHP8の実行結果
PHP Fatal error:  Uncaught TypeError: Unsupported operand types: Class1 + Class2 in /home/vagrant/sample.php:10
Stack trace:
#0 {main}
  thrown in /home/vagrant/sample.php on line 10

クラスオブジェクトのビット演算

PHP7までのクラスオブジェクトのビット演算は、算術演算と同じで警告を出して処理をつづけます。

オブジェクトの値を無理やりintに変換するのも同じ。

これもPHP8では、すべての演算子でUncaught TypeErrorになります。

サンプルコード
<?php

class Class1 {
    public $number = 1;
}
class Class2 {
    public $number = 2;
}

var_dump( ( new Class1() ) & ( new Class2() ) );
var_dump( ( new Class1() ) | ( new Class2() ) );
var_dump( ( new Class1() ) ^ ( new Class2() ) );
var_dump( ( new Class1() ) << ( new Class2() ) );
var_dump( ( new Class1() ) >> ( new Class2() ) );
PHP7の実行結果
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 10
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 10
int(1)
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 11
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 11
int(1)
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 12
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 12
int(0)
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 13
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 13
int(2)
PHP Notice:  Object of class Class1 could not be converted to int in /home/vagrant/sample.php on line 14
PHP Notice:  Object of class Class2 could not be converted to int in /home/vagrant/sample.php on line 14
int(0)
PHP8の実行結果
PHP Fatal error:  Uncaught TypeError: Unsupported operand types: Class1 & Class2 in /home/vagrant/sample.php:10
Stack trace:
#0 {main}
  thrown in /home/vagrant/sample.php on line 10

なんでこんな変更をしたのか? 答えは簡単です。おかしな計算をさせないため。それなりに結果を出してしまうとバグに気づきません。

というかこれを許しているところがスゴい。昔のC言語でキャストを使ってこんなことしてたような? クラスじゃなくて構造体だったけど。今はどうなってるか分かりませんが。

意味を考えてプログラミングしている人はPHP8にアップグレードしてもエラーにならないでしょう。

しかし、『とりあえず動いているからOK』にしている人は要注意。エラーになるので処理が止まってしまいます。

(修正が必要。)

前の投稿
PHP8, 関数のパラメータの型を守らないとエラーになる。戻り値も。
PHP8, is_numeric, 判定が変わり後ろ空白がtrueに
次の投稿
コメントを残す

*