schedule2019-12-25

PHP(5.6→7.3)とLaravel(5.1→5.8)のアップグレード作業の手順

サーバ移設に伴って、PHPとLaravelをアップグレードしたときのメモ。 この記事では対応した順番に書いている。

アップグレードの工数は私でおおよそ1週間(40h)はかかってる。 サービスの規模や作業者にも依りますが、Laravel 5.1から5.8へ大幅にアップグレードしているためどのバージョンでの変更が原因か切り分けが難しかった。バージョン毎に結構変わっている。 2回目以降は割とすんなりとできる。

初めて作業する方は1人では辛いので複数人でやって欲しい。

環境

PHPのバージョンを5.6から7.3に上げます。 現行のLaravelのバージョンは5.1ですがPHP7.3に対応しているのがLaravel 5.7以上であるため、こちらもアップグレードします。

  • OS: CentOS 7(変わらず)
  • PHP: 5.6 → 7.3
  • Laravel 5.1 → 5.8
  • MySQL5.6
  • composer 1.9.1

アップグレードの作業はローカルのVagrantに立てたPHP 7.3の環境で行います。

アップグレード作業の方針

方針は目的のPHPのバージョンに合わせてライブラリの依存関係を解消していきます。

  1. composer.jsonを修正して、ライブラリのバージョンを上げる
  2. composer updateしてみる
  3. 「composer.jsonに書いてあるライブラリはこのバージョンだと依存関係を解消できない」みたいなエラーが出る
  4. packagist.orgでバージョンと依存関係を調べつつ1.に戻る

こんなサイクルを回しながらLaravelのアップグレードを進めた。

LaravelのdocにUpgarade Guideがあります。 今回、5.1から5.8まで一気に上げたのでかなりの変更が入っていた(5.6からconfigにlogging.phpが追加された等)。 全て読むのは大変ですが、頭の片隅にあると対応しやすくなる。

PHPに関しては都度エラーを解消していく。

また、キャッシュにも悩まされるのでLaravel キャッシュクリア系コマンドなども都度実行してみるといい。

作業のメモ

以下はおおよそ時系列順にやったことを書き連ねる。

composer.jsonを修正する

ひとまずcomposer.jsonのPHPのバージョンのみ変更して進める。

    "require": {
-        "php": ">=5.5.9",
+        "php": ">=7.3",
        "laravel/framework": "5.1.*",
        "laravelcollective/html": "5.1.*",
        "barryvdh/laravel-debugbar": "^2.2",
        "intervention/image": "^2.3"
    },

Carbonをアップグレードする

Laravel5.8から(?)Carbon1からCarbon2が必要になっていた。 Laravel Upgrade Guid #Carbon 2.0

composer.jsonを修正してから、composer installでのエラー。

Generating autoload files
Carbon 1 is deprecated, see how to migrate to Carbon 2.
https://carbon.nesbot.com/docs/#api-carbon-2
    You can run './vendor/bin/upgrade-carbon' to get help in updating carbon and other frameworks and libraries that depend on it.
> php artisan clear-compiled
> php artisan optimize
Generating optimized class loader
Compiling common classes

Carbon 2にアップデートして下さいよーとのメッセージ。 ./vendor/bin/upgrade-carbonを実行するとcomposerのバージョンに合わせて依存関係をアップデートしていった。

Do you want us to try the following upgrade:
  - nesbot/carbon: ^2.0.0
  - laravel/framework: ^5.8.0
[Y/N] y

途中でLaravelのフレームワークとcarbonのアップグレートするか聞かれ、yで進めた。

[Y/N] y
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: don't install laravel/framework v5.8.36
    - Conclusion: don't install laravel/framework v5.8.35
    - Conclusion: don't install laravel/framework v5.8.34
    - Conclusion: don't install laravel/framework v5.8.33
この後バージョンの依存関係を解決できなかったといった内容が続く

依存関係が解決できなかったようです。 composer.jsonにはLaravel5.8とその他の記述が加わっていた。

    "require": {
        "php": ">=7.3",
-        "laravel/framework": "5.1.*",
+        "laravel/framework": "^5.8.0",
        ...
    },
    "require-dev": {
        ...
+        "barryvdh/laravel-debugbar": "2.*@dev",
+        "composer/composer": "^1.9"
    }

ライブラリのバージョンを上げていく

composer updateすると{ライブラリ名} requires illuminate/support 5.0.* といった内容が吐き出される。 これは、composer.jsonに書かれているライブラリのバージョンが足りていないよってことなので一個ずつ今あるバージョンを調べて変更する。

packagist.orgでライブラリのバージョンと必要な依存関係を調べられます。 例えば、barryvdh/laravel-debugbarこちらに依存関係が書いてある。 "^2.2"としていたけど3系が出ているようなので"^3.0"とした。

チルダはメジャーバージョンが同じでマイナーバージョンの変更をする。 "^2.2"なら対象となるバージョンは2.2以上3.0未満となる。 composer.jsonの書き方は以下が詳しい。

Composerの導入&使い方(初心者の復習用)

Generating autoload filesでのエラー

Laravel5.3

phpspec/phpspecのバージョンを上げた時に起きたエラー。

PHP Warning:  Declaration of App\Exceptions\Handler::renderHttpException(Symfony\Component\HttpKernel\Exception\HttpException $e) should be compatible with Illuminate\Foundation\Exceptions\Handler::renderHttpException(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e) in /var/laravel/app/Exceptions/Handler.php on line 12
PHP Fatal error:  Uncaught Error: Class name must be a valid object or a string in /var/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php:144
Stack trace:
#0 [internal function]: Illuminate\Foundation\Exceptions\Handler->Illuminate\Foundation\Exceptions\{closure}(0, 'Symfony\\Compone...')
#1 /var/laravel/bootstrap/cache/compiled.php(6226): call_user_func(Object(Closure), 0, 'Symfony\\Compone...')
#2 /var/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(145): Illuminate\Support\Arr::first(Array, Object(Closure))
#3 /var/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php(102): Illuminate\Foundation\Exceptions\Handler->shouldntReport(Object(ReflectionException))
#4 /var/laravel/app/Exceptions/Handler.php(33): Illuminate\Foundation\Exceptions\Handler->report(Object(ReflectionException))
#5 /var/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(378): App\Exceptions\Handler->report(Object(ReflectionException))
#6 /var/laravel/vendo in /var/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php on line 144

bootstrap/cache/compiled.phpを削除したらOK。

bootstrap/autoload.phpの場所がかわってたようです。 Laravel5.3 upgrade guide

ディレクトリは以下のようにした。

$ tree -a bootstrap/
bootstrap/
├── app.php
├── autoload.php
└── cache
    ├── .gitignore
    └── packages.php

参考:stack-overflow

HandlerでのHttpExceptionInterface

app/Exceptions/Handler.phpでエラー処理をしており、継承しているクラスが変わっていた。

In Handler.php line 12:

  Declaration of App\Exceptions\Handler::renderHttpException(App\Exceptions\H
  ttpExceptionInterface $e) should be compatible with Illuminate\Foundation\E
  xceptions\Handler::renderHttpException(Symfony\Component\HttpKernel\Excepti
  on\HttpExceptionInterface $e)

オーバライドしている関数renderHttpException()の引数を修正する。

app/Exceptions/Handler.php

- use Symfony\Component\HttpKernel\Exception\HttpException;
+ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

中略
-    protected function renderHttpException(HttpException $e)
+    protected function renderHttpException(HttpExceptionInterface $e)
    {

ServiceProviderのエラー

Laravel 5.2

更にcomposer updateをすすめると以下のエラーが出た。

In ProviderRepository.php line 208:

  Class 'Illuminate\Routing\ControllerServiceProvider' not found

Script php artisan clear-compiled handling the post-install-cmd event returned with error code 1

Service Providers

The Illuminate\Foundation\Providers\ArtisanServiceProvider should be removed from your service provider list in your app.php configuration file.

The Illuminate\Routing\ControllerServiceProvider should be removed from your service provider list in your app.php configuration file.
Laravel 5.2 Upgrade Guide

5.2では2つのServiceProviderを取り除くように指示がある。config/app.phpの該当箇所を削除。

    'providers' => [
        // ... ,
-        Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
-        Illuminate\Routing\ControllerServiceProvider::class,
    ]

参考:composer updateでartisanコマンドのエラーが出る時の対処法

Application Service Providersのエラー

Laravel 5.3

In EventServiceProvider.php line 8:

  Declaration of App\Providers\EventServiceProvider::boot(Illuminate\Contract
  s\Events\Dispatcher $events) should be compatible with Illuminate\Foundatio
  n\Support\Providers\EventServiceProvider::boot()


Script php artisan clear-compiled handling the post-update-cmd event returned with error code 1
  • EventServiceProvider
  • RouteServiceProvider
  • AuthServiceProvider の3つのクラスのboot()の引数がLaravel 5.3から無くなりました。 (指定された引数の呼び出しは、代わりに同等のファサードを使用するように変換できる) Laravel 5.3 Upgrade Guide #Application Service Providers

app/Providers/EventServiceProvider.php

-    public function boot(DispatcherContract $events)
+    public function boot()
    {
-        parent::boot($events);
+        parent::boot();

        //
    }

同様に他のboot()も修正する。

その他のエラー対処など

直接アップグレードに関わりはないけど、起きたエラーについてもまとめておく。

Vagrantの影響

1回目のcomposerインストールでいきなりエラーが出た。

$ composer install
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 68 installs, 0 updates, 0 removals
  - Installing kylekatarnls/update-helper (1.2.0): Loading from cache
Plugin installation failed, rolling back
  - Removing kylekatarnls/update-helper (1.2.0)

                                                                                  
  [RuntimeException]                                                              
  Could not delete /var/laravel/vendor/kylekatarnls/update-helper/src/UpdateHelper:  

こちらはVarant特有のエラーらしく、以下のコマンドで回避した。2回目からは不要です。

composer install --no-plugins

参考:Windows+Vagrantでcomposer installすると"Could not delete /vagrant/vendor/"エラーが発生する場合の対処

databaseディレクトリ

[RuntimeException]                                                                            
  Could not scan for classes inside "database" which does not appear to be a file nor a folder  

databaseディレクトリ下にfactoriesディレクトリを作成すると解消できた。

【Laravel】composer実行時のエラー「Could not scan for classes inside “database/factories”」の対処法

.gitignore

ディレクトリ構成が変わるため.gitignoreも更新しておく。 現行+githubの設定で取り敢えず良いかと。

composerのアップデート終わり

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> php artisan clear-compiled
Compiled services and packages files removed!
> php artisan optimize
Configuration cache cleared!
Configuration cached successfully!
Route cache cleared!
Routes cached successfully!
Files cached successfully!

以上でライブラリの依存関係が解消できた。この後は、ソースコードをPHP7.3に対応させていく。

composer.jsonは下記のようになった。

    "require": {
-        "php": ">=5.5.9",
-        "laravel/framework": "5.1.*",
-        "laravelcollective/html": "5.1.*",
-        "barryvdh/laravel-debugbar": "^2.2",
+        "php": ">=7.3",
+        "laravel/framework": "^5.8.0",
+        "laravelcollective/html": "^5.1",
        "barryvdh/laravel-debugbar": "^3.0",
        "intervention/image": "^2.3"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
-        "phpunit/phpunit": "~4.0",
-        "phpspec/phpspec": "~2.1",
-        "barryvdh/laravel-debugbar": "2.*@dev"
+        "phpunit/phpunit": "^7.0",
+        "phpspec/phpspec": "^5.0",
+        "barryvdh/laravel-debugbar": "^3.0",
+        "composer/composer": "^1.9"
    },

500エラーを解消していく

ここからlaravelが吐き出すログを見ながら500エラーを解消していく。

設定ファイルlogging.phpを追加

Laravel 5.6

[2019-12-25 19:40:16] laravel.EMERGENCY: Unable to create configured logger. Using emergency logger. {"exception":"[object] (InvalidArgumentException(code: 0): Log [] is not defined. at /var/laravel/vendor/laravel/framework/src/Illuminate/Log/LogManager.php:168)
[stacktrace]
#0 /var/laravel/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(102): Illuminate\\Log\\LogManager->resolve(NULL)
#1 /var/laravel/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(90): Illuminate\\Log\\LogManager->get(NULL)
#2 /var/laravel/vendor/laravel/framework/src/Illuminate/Log/LogManager.php(500): Illuminate\\Log\\LogManager->driver()
...

Laravel 5.6からconfig/logging.phpが追加されました。 Laravel 5.6 Upgrade Guide #Logging

config/logging.phpを作成して公式のgithubのconfig/logging.phpをコピペして追加。

更に、config/app.phpのLogの記述を削除した。

-     'log' => 'daily',
-     'log_max_files' => 360,

設定ファイルもキャッシュするためphp artisan config:clearしておくと良い。

production.ERROR: Please provide a valid cache path.

Laravel 5.7

[2020-01-15 18:24:48] production.ERROR: Please provide a valid cache path. {"exception":"[object] (InvalidArgumentException(code: 0): Please provide a valid cache path. at /var/laravel/vendor/laravel/framework/src/Illuminate/View/Compilers/Compiler.php:36)
[stacktrace]
#0 /var/laravel/vendor/laravel/framework/src/Illuminate/View/ViewServiceProvider.php(141): Illuminate\\View\\Compilers\\Compiler->__construct(Object(Illuminate\\Filesystem\\Filesystem), false)
#1 /var/laravel/vendor/laravel/framework/src/Illuminate/Container/Container.php(785): Illuminate\\View\\ViewServiceProvider->Illuminate\\View\\{closure}(Object(Illuminate\\Foundation\\Application), Array)

新しいディレクトリstorage/framework/cacheが必要になった。 Laravel 5.7 Upgrade Guide #Cache

まず、config/cache.phpが変わっていたので修正する。

<?php
+ use Illuminate\Support\Str;

return [
// ...
    'stores' => [


        'file' => [
            'driver' => 'file',
-            'path'   => storage_path('framework/cache'),
+            'path'   => storage_path('framework/cache/data'),
        ],
// ...
    ],

-    'prefix' => `laravel`,
+    'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),

prefixとキャッシュドライバーをfileとしていたのでその変更を載せた。 他にmemcachedredisの設定が変わっており、dynamodbが追加されていた。

参考から、ディレクトリと.gitignoreを作るコマンドを紹介する。

# configからstorage_pathで設定しているパスを確認する
$ find config/ -type f | xargs grep storage_path | grep framework
config/cache.php:            'path'   => storage_path('framework/cache/data'),
config/session.php:    'files' => storage_path('framework/sessions'),
config/view.php:    'compiled' => realpath(storage_path('framework/views')),

# ないディレクトリを作成する (-p は無ければ親ディレクトリも作る)
$ mkdir -p storage/framework/cache/data
$ mkdir -p storage/framework/sessions
$ mkdir -p storage/framework/views

# gitignore
$ vi storage/framework/cache/.gitignore
*
!data/
!.gitignore

# gitignoreを配置
$ cp storage/framework/cache/.gitignore storage/framework/cache/data/.gitignore
$ cp storage/framework/cache/.gitignore storage/framework/sessions/.gitignore
$ cp storage/framework/cache/.gitignore storage/framework/views/.gitignore

# config clear
$ php artisan config:clear

参考:ComposerでPlease provide a valid cache pathとなったときの対応方法-Qiita

設定ファイルのキャッシュ

DBの接続エラーに対して.envを見直して実行したが、全くつながらなかった。 数時間悩んでから、Logのホストやデータベース名が変わって無いことに気づいた。(ログの'mysql:host=hostname...', 'database'のとこ)

[2019-12-25 20:09:40] production.ERROR: SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (SQL: select * from `sessions` where `id` = WGIZxhW8Md1jgxqA7I9IqK4K0VajYoW9tHRyxl96 limit 1) {"exception":"[object] (Illuminate\\Database\\QueryException(code: 2002): SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known (SQL: select * from `sessions` where `id` = WGIZxhW8Md1jgxqA7I9IqK4K0VajYoW9tHRyxl96 limit 1) at /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connection.php:664, PDOException(code: 2002): SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known at /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70, PDOException(code: 0): PDO::__construct(): php_network_getaddresses: getaddrinfo failed: Name or service not known at /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:70)
Stack trace:
#0 /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php(70): PDO->__construct('mysql:host=hostname...', 'database', 'CEsr3TWLZzP8Ypb...', Array)
#1 /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php(46): Illuminate\\Database\\Connectors\\Connector->createPdoConnection('mysql:host=hostname...', 'database', 'CEsr3TWLZzP8Ypb...', Array)
#2 /var/laravel/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php(24): Illuminate\\Database\\Connectors\\Connector->createConnection('mysql:host=hostname...', Array, Array)
...

設定もキャッシュするんですね。。。 php artisan config:clearで設定のキャッシュを消したら無事繋がりました。

Laravel キャッシュクリア系コマンドなど

config/auth.phpの変更

Laravel 5.2

Auth guard [] is not defined.のエラー。

こちらは設定ファイルをconfig/auth.phpに置き換えて解消した。(以降のバージョンで変更なし)

Laravel 5.8 Upgrade Guide #Authentication

テーブルsessionsの変更

続いて、SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user_id' in 'field list' (SQL: update sessions ・・・とのエラー。 DBでsessionを管理しており、sessionsテーブルのカラムが現在id,payload,last_activityの3つだけ。user_idip_adress, user_agentのカラムが足りていない。カラムを足すのも良いが、session情報は消えても構わないので削除して再度作成する。

php artisan session:tableでsessionsのマイグレーションファイルを作成。マイグレートする。

[vagrant@localhost laravel]$ php artisan migrate:status
+------+-----------------------------------------+-------+
| Ran? | Migration                               | Batch |
+------+-----------------------------------------+-------+
| No   | 2019_12_26_174943_create_sessions_table |       |
+------+-----------------------------------------+-------+
[vagrant@localhost laravel]$ php artisan migrate
Migrating: 2019_12_26_174943_create_sessions_table

In Connection.php line 664:
                                                                                                                                                                                       
  SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `sessions` add unique `sessions_id_unique`(`id`))  

このエラーはMySQLに起因するもの。 MySQL 5.6だとconfig/database.phpで設定していた文字コードに対応していないため、utf8にしておく。

        'mysql' => [
-            'charset' => 'utf8mb4',
-            'collation' => 'utf8mb4_unicode_ci',
+            'charset' => 'utf8',
+            'collation' => 'utf8_unicode_ci',

参考;Laravel5.4以上、MySQL5.7.7未満 でusersテーブルのマイグレーションを実行すると Syntax error が発生する

sessionの設定もconfig/session.phpに置き換える。

UnauthorizedException

Laravel 5.3

app/Exceptions/Handler.phpで認証系のハンドリングを拡張していたため遭遇したエラー。

thrown {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalErrorException(code: 1): Uncaught Error: Call to undefined method App\\Exceptions\\Handler::isAuthorizationException() in /var/laravel/app/Exceptions/Handler.php:50

Illuminate\Auth\Access\UnauthorizedExceptionがリネームしてIlluminate\Auth\Access\AuthorizationExceptionとなった。 公式にも「不運」にもとかかれている。 laravel 5.3 Upgrade Guide

isUnauthorizedException()にあたる関数も無くなっていたので、下のように自作しておいた。

/**
     * Exception type is AuthorizationException;
     *
     * @param  \Exception  $e
     * @return boolean
     */
    private static function isAuthorizationException(Exception $e){
        return $e == AuthorizationException::class;
    }

Bladeの変数代入

Bladeのコメント{{--コメント--}}がPHPのコードに変換されると<?php /*コメント*/ ?>となることを活かして、次の書き方で変数に代入していた。

{{--*/ $var = 'test' /*--}}

// 上を変換すると <?php /**/ $var = 'test' /**/ ?> となる

この書き方は、Laravel5.4では既に使えないため<?php $var = 'test'; ?>と書きかえ正常に動作した。 また、この記述が数百カ所あるため正規表現で置換する。

// 検索文字
\{\{-- *\*\/(.+)\/\* *--\}\}

// 置換文字
<?php $1; ?>

参考:[Laravel5.4ではbladeファイル内で{{--*/ $hoge = 'hoge' /*--}}といった書き方で変数代入できない]{https://qiita.com/madayo/items/667beaec1121ec6dd13a}

Bladeの"or"オペレーター

Laravel 5.7

Bladeで変数が宣言されていない場合に代わりの値を置く構文orが廃止され、??に変わった。

例として変数$fooが宣言されていない場合にdefault`を表示する場合の違い。

// Laravel 5.6...
{{ $foo or 'default' }}

// Laravel 5.7...
{{ $foo ?? 'default' }}

ちなみに??Null合体演算子null coalescing operator)と呼ばれ、C#やSwiftなど広く使われます。 Laravelでの変更もPHP7からNull合体演算子が組み込まれたためかと。

参考

Call to undefined method Illuminate\Database\Eloquent\Builder::lists()

Laravel 5.3

Eloquentモデルでカラムのコレクションを返すlists()Laravel5.3から削除された。 代わりにpluck()に置き換えると良い。

Call to undefined method App\Models\Hoge::lists()のようにIlluminate\Database\Eloquent\Modelを継承したクラスも修正する。

Class 'Illuminate\Pagination\BootstrapThreePresenter' not found (View:..blade.php)

Laravel 5.3

ページネーションのクラスBootstrapThreePresenterがLaravel5.3から削除されたようです。 ここはカスタムして利用していたためかなり痛い。。。

Illuminate\Pagination\Paginatorで置き換え改修しました。

コントローラーのコンストラクタの順序

Laravel 5.3

Laravel 5.3未満ではコントローラーのコンストラクターでミドルウエアで得たセッション変数や認証されたユーザにアクセスできた。 これは、ミドルウェアの実行後にコントローラークラスが生成されるためであった。

Laravel 5.3未満の順序

  1. index.php
  2. middlewareの処理
  3. controllerの生成
  4. controllerの処理

この順序はフレームワークの明示的な機能を意図したものではなく、Laravel 5.3以降ではコントローラークラスの生成がミドルウェアの処理の前に行うようになった。

Laravel 5.3以降の順序

  1. index.php
  2. controllerの生成 <- 順序が変わった
  3. middlewareの処理
  4. controllerの処理

コントローラーのコンストラクタで処理したい場合は、クロージャーベースのミドルウェアを直接利用する方法がある。 実装例は下のリンク先にあります。ただし、Laravel5.3.4以降で利用できます。

laravel 5.3 Upgrade GuideのSession In The Constructorの箇所

PHPコードのバージョンアップ

ここからPHP5.6からPHP7.3になったことで仕様変更または非推奨になった関数の対応をしていった。

compact(): Undefined variable:

PHP7.3

PHP7.3からcompact関数が未定義の渡された変数をエラーとするよう変更となった。 PHP RFC: Make compact function reports undefined passed variables

ControllerからViewへ変数を渡すためにcompact()を利用している。

$foo = 'hoge';
$bar = 'piyo';
return view('/', compact('foo', 'bar');

// 以下の書き方と同等
return view('/', ['foo' => $foo, 'bar' => $bar]);

この時、未定義の変数を渡すとエラーが出る。 ($barが宣言されていない場合はcompact(): Undefined variable: bar`)

参考:[PHP]local.ERROR: compact(): Undefined variable:エラーについて

compact関数の宣言していない変数の扱いについて調べた記事はこちら

Function mcrypt_get_block_size() is deprecated

PHP7.1

DESなどのブロック暗号をサポートするMcryptはPHP7.1.0から非推奨になったhttps://www.php.net/manual/ja/intro.mcrypt.php

この機能の代替として、OpenSSLを利用する。 暗号化方式にMCRYPT_BLOWFISHでモードにMCRYPT_MODE_CBCを指定しているため、OpenSSLでも同等の方式を充てる。

参考:PHP 7.2で消えるMcryptの扱い

count(): Parameter must be an array or an object that implements Countable

PHP7.2

count()の引数にはカウント可能 Countable な配列かオブジェクトを渡してよとのエラー。

今回はnullが渡されていたためエラーになっていた。 以前のバージョンではcount(NULL)0が帰っていていたが、PHP7.2からで警告を発生させるようなった。

解決方法はcount()に渡す前にis_countable()でCountableかチェックを行うようにし、emptyなどで対応できるものは変更した。

if(is_countable($hoge)){
  $piyo = count($hoge);
}

動的型付けの悪い部分が悪さした。。。

また、if分の中で使われている場面も多い。一行で書きたいので演算子の優先順位に従って、||の左側で確認する。

…と実行してみたがエラーが出た。まじか。

// エラーが出た。。。
if(is_countable($hoge) || count($hoge) > 10){
  // count(): Parameter must be an array or an object that implements Countable
}

上の書き方でエラーが出たため、(ちょっと気にくわないが)下のようにした。 Bladeも同様。

if(is_countable($hoge)){ /*count()のエラー回避*/ }
elseif(count($hoge) > 10){
  // do something
}

// Blade
@if(!is_countable($hoge)) {{-- count()のエラー回避 --}}
@elseif(count($hoge) > 1 )
  <div>do something</div>
@endif

参考