PHPのバージョンを7から8にした時の覚書

# PHPのメジャーバージョンアップ

PHPが、7.3.5から8.1.10へマイナーバージョンが上がることになった。
(*これを書いている数年前の話なので、少し古い)

それに伴って社内のみで稼働しているWebツールを改修することになった。

https://openstandia.jp/oss_info/php/version/

マイナーバージョンは軽微なものがおおいが、メジャーバージョンを更新するとなると結構コードの改修なんかが必要になることがある。

# PHPってなんだ

当時は、そもそもPHPというのを触ったのがほぼ初めてだった。
（初めての時、誰かの書いたコードを参考に、微修正するようなところから始まるのはよくあると思う。）

PHPは、サーバーで動くスクリプトで、動的にホームページを作成する言語だ。
チャットとか、掲示板とか、……というほど高度なことをしているわけではなかったが、社内でスケジュールの管理をするようなソフトである。
DBと連携はしているがやっていることはそんなに高度でもないし、影響範囲がせいぜい社内である。
（大事な仕事だけど、いざってときに迷惑をかけるのが身内かお客さんかというので結構プレッシャーは違う）。

### 個人の感想

触ってみて思ったのは、こう、If これこれならこのHTML、ElseならこのHTMLという感じで、「単にそん時そん時でHTMLを組み立てているような感じか～」というものだった。

```php
<?php
if ($loggedIn) {
    echo '<nav>Logged in menu</nav>';
} else {
    echo '<nav>Guest menu</nav>';
}
?>
```

このように条件分岐で動的にHTMLを生成する方法は、コードの動きを追いやすい反面、複雑なページ構成になると煩雑になることがある。

# どういう変更があったのか
PHP 7.3.x から PHP 7.4.x への移行

https://www.php.net/manual/ja/migration74.php

PHP 7.4.x から PHP 8.0.x への移行

https://www.php.net/manual/ja/migration80.php#migration80


まずドキュメントで変更点を確認し、使っていそうな関数を秀丸エディタのGrep検索で洗い出して、地道に調べて関数を書き換えていった。次に、追いきれなかった分を修正していった。
（難しいところは確認を取りつつ……。）

### 配列でない値を配列スタイルでアクセスした場合
>null, bool, int, float または resource 型を ($null["key"] のように) 配列としてアクセスしようとすると、警告が生成されるようになりました。

```php
<?php
$val = null;
// PHP 7.4まではサイレントエラーだったが、8.0以降では警告が出る
echo $val["key"];
?>
```

### get_declared_classes()
>get_declared_classes() 関数は、まだインスタンス化されていない無名クラスを返さなくなりました。

```php
<?php
var_dump(get_declared_classes());
PHP 8.0以降では、上記の関数はインスタンス化されていない無名クラスを含めない
```

これは、デバッグ用にechoさせていた時に苦しんだもので、とくに動きに差はなかったはず。

### fn キーワード
>fn は、予約語になりました。特に、関数名やクラス名として使うことはできなくなりました。メソッド名やクラスの定数名の場合は使用できます。

古い関数名を修正することになった。

追記：エラーになる例ではない例を出していたので修正。関数名の時にだめ(指摘ありがとう！)

### クラス名と同じ名前のメソッド
>・クラス名と同じ名前のメソッドは、コンストラクタと解釈されなくなりました。 __construct() メソッドを代わりに使って下さい。

```php
<?php
// PHP 7.x ではコンストラクタとして機能していたが、PHP 8.0以降では __construct を使用する必要がある
class MyClass {
    public function MyClass() {
        // コンストラクタの処理
    }
}
?>
```

->__construct() を使用するようにした。

### each()関数の削除
>・each() 関数は削除されました。 代わりに foreach や ArrayIterator を使うべきです。
class\old\calender.php(49): 		while(list( ,$w) = each($this->wday)){
class\old\calender.php(54): 		while(list( ,$week) = each($this->_calendar)){
class\old\calender.php(58): 			while(list( ,$day) = each($week)){

```php
<?php
// PHP 7.x では有効だったが、PHP 8.0以降では削除された
$array = [1, 2, 3];
while (list($key, $value) = each($array)) {
    echo "$key: $value\n";
}

// 代わりに foreach を使うべき
foreach ($array as $key => $value) {
    echo "$key: $value\n";
}
?>

```

関数を持ち替えた。

## ほか、安全のために要求される引数が増えた関数や、Nullチェックが必須になったものが多かった。

こういった関数を地道に書き換えて行って、動作確認した。

### 例1: substr() 関数
PHP 8.0では、substr()関数でNullを渡すとTypeErrorが発生する。

PHP 7.x まで:

```php
<?php
echo substr(null, 0, 1); // 出力: (空文字列)
?>
```

PHP 8.0以降:

```php
<?php
try {
    echo substr(null, 0, 1); // TypeErrorが発生
} catch (TypeError $e) {
    echo 'Error: ' . $e->getMessage();
}
?>
```

### 例2: str_replace() 関数
PHP 8.0では、str_replace()関数も同様に、引数としてnullが渡されるとTypeErrorが発生する。

PHP 7.x まで:

```php
<?php
echo str_replace('a', 'b', null); // 出力: (空文字列)
?>
```

PHP 8.0以降:

```php
<?php
try {
    echo str_replace('a', 'b', null); // TypeErrorが発生
} catch (TypeError $e) {
    echo 'Error: ' . $e->getMessage();
}
?>
```

### 例3: array_merge() 関数
PHP 8.0では、引数としてnullを渡すとTypeErrorが発生する。

PHP 7.x まで:

```php
<?php
$array1 = ['a' => 1];
$array2 = null;
$result = array_merge($array1, $array2); // 出力: ['a' => 1]
print_r($result);
?>
```

PHP 8.0以降:

```php
<?php
$array1 = ['a' => 1];
$array2 = null;
try {
    $result = array_merge($array1, $array2); // TypeErrorが発生
} catch (TypeError $e) {
    echo 'Error: ' . $e->getMessage();
}
?>
```