PHPerのための「Laravel10の新機能」を語り合う PHP TechCafe ======================== Connpass: https://rakus.connpass.com/event/273053/ # PHPer's NEWS ::: info **[Python 遅いものたち](https://toriidao.hateblo.jp/entry/2023/01/24/121216)** PHPに遅いイメージはないが、この処理だけは遅いとかあったりするのだろうか? ::: ::: info **[Python(書くのが)速いものたち](https://qiita.com/hibit/items/32696071932e713eff60)** Pythonは便利記法が多いが、PHPは少ないというイメージが... ::: ::: info **[PHPUnit 10 has been released](https://phpunit.de/announcements/phpunit-10.html)** イベントシステムが導入されたようですが、1ユーザ的にはあまりインパクトはなさそう?(周辺ツールの開発者にとっては影響がありそう) それよりメタデータの付与方法の変更や、Warning等が例外にならなくなった点の方がインパクトが大きそうです。 ::: ::: info **[PHPUnit 10 Change Log](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-10.0.md)** ::: ::: info **[PHP Core Roundup #9](https://thephp.foundation/blog/2023/01/30/php-core-roundup-9/)** PHP8.2がリリース。 PHP8.3のRFCもどんどん進んでいるようです。 ::: ::: info **[OSSセキュリティMeetup第一回開催](https://prtimes.jp/main/html/rd/p/000000198.000042042.html)** ::: ::: info **[Laravel 10🐿がリリースされたのだ🎉【Laravel 10 新機能】](https://qiita.com/7mpy/items/e92dc2e89d9fcf678220)** ::: ::: info **[Windows 10上の「IE11」、2月14日で完全無効化 Edgeに強制リダイレクトへ](https://www.itmedia.co.jp/news/articles/2302/14/news104.html)** ::: ::: info **[さようなら、全てのインターネット・エクスプローラー](https://www.itmedia.co.jp/news/articles/2206/17/news073.html)** ありがとう... ありがとう... ::: ::: info **[【PHP8.3】 クラス定数とENUMを文字列から探せるようになる](https://qiita.com/rana_kualu/items/d0dc17543f4b89db30cb)** ENUMが使えたらと思うことがあるので、はやくPHP8.1の世界に行きたい ::: # 特集:Laravel10の新機能 * 過去のLaravel回のまとめ * [PHPerによるPHPerのための「Laravel8を中心に語り合う」TechCafe](https://hackmd.io/@S051_ovFTzmLW3plu6ehiw/BkiwDx9IP?view) * [PHPerのための「Laravel/PHP8/Dockerで開発環境作りを語り合う」TechCafe](https://hackmd.io/@hrxVDayfRGeTR-1JBV3QLA/H1NeB2asD?view) * [PHPerのための「2020年のPHP/Laravel振り返り+2021年」を語るTechCafe](https://hackmd.io/@hrxVDayfRGeTR-1JBV3QLA/H1dA4TGku?view) * [PHPerのための「Laravel 入門を語り合う」PHP TechCafe ](https://hackmd.io/BKYlg2FKRVCdULZ0TNFG7w?view) * [PHPerのための「2021年のPHP/Laravel振り返り+2022年」を語るTechCafe](https://hackmd.io/X8XLpiC7SEWsbW3kpfE4iA?view) * [PHPerのための「Laravel 9 について語る」PHPTechCafe](https://hackmd.io/nYcZeh-9Rl-EtKdopnAhLQ?view) ## Laravelのリリースサイクルについて * LaravelはLaravel8以降1年に1回のメジャーバージョンアップになっている ## PHP8.0系の非対応 * Laravel10はPHP8.1以上が必要 ## [Laravel Pennant](https://laravel.com/docs/10.x/pennant) * 新しいファーストパーティパッケージ * フィーチャーフラグを追加する * Compoer 経由でインストールする ### フィーチャーフラグとは?? * [FeatureToggle戦略と運用方法](https://speakerdeck.com/kubotak/featuretogglezhan-lue-toyun-yong-fang-fa) * [これでリリースも怖くない フィーチャートグルを導入入門](https://speakerdeck.com/nagano/php-conference-japan-2021) * [フィーチャートグルを使って素早く価値を検証する 早く安全に失敗し学ぶために](https://speakerdeck.com/akki_megane/huitiyatoguruwo-shi-tutesu-zao-kujia-zhi-wojian-zheng-suru) * [ローンチから16年目のWebサービスに、どうやってフィーチャートグルを導入したか、運用しているか](https://speakerdeck.com/meihei3/phpcon2022-e259c9cb-1462-4e0a-b22c-20327532bbee) ### 導入手順 Compoer でいれるだけ ```bash $ composer require laravel/pennant $ php artisan vendor:publish --provider="Laravel\Pennant\PennantServiceProvider" $ php artisan migrate ``` ### 機能利用可否の定義 通常、各機能の利用可否は、**現在認証されているユーザ**に対して確認される。 (ほかのオブジェクトを指定することも可能。) #### サービスプロバイダで定義する方法 ```php= <?php namespace App\Providers; use App\Models\User; use Illuminate\Support\Lottery; use Illuminate\Support\ServiceProvider; use Laravel\Pennant\Feature; // ★ class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. */ public function boot(): void { // 新しいAPIについて Feature::define('new-api', fn (User $user) => match (true) { // 内部メンバーは利用可能 $user->isInternalTeamMember() => true, // トラフィックの多いユーザは利用不可 $user->isHighTrafficCustomer() => false, // それ以外の場合、1/100の確率で利用可能 default => Lottery::odds(1 / 100), }); } } ``` #### クラスでの定義 * 機能フラグをクラスで定義することも可能 * artisanコマンドから雛形を作成できる ```bash $ php artisan pennant:feature NewApi ``` ```php= <?php namespace App\Features; use Illuminate\Support\Lottery; class NewApi { /** * Resolve the feature's initial value. */ public function resolve(User $user): mixed { // 新しいAPIについて return match (true) { // 内部メンバーは利用可能 $user->isInternalTeamMember() => true, // トラフィックの多いユーザは利用不可 $user->isHighTrafficCustomer() => false, // それ以外の場合、1/100の確率で利用可能 default => Lottery::odds(1 / 100), }; } } ``` ### 機能フラグの利用方法 `Feature::active('KEY名')` で ON/OFF を取得できる ```php= <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Pennant\Feature; class PodcastController { /** * Display a listing of the resource. */ public function index(Request $request): Response { return Feature::active('new-api') // 定義した条件通りにtrue/falseが返される ? $this->resolveNewApiResponse($request) : $this->resolveLegacyApiResponse($request); } // ... } ``` #### クラスに定義している場合 フィーチャーフラグを管理しているクラス名を指定する必要がある。 ```php= <?php namespace App\Http\Controllers; use App\Features\NewApi; use Illuminate\Http\Request; use Illuminate\Http\Response; use Laravel\Pennant\Feature; class PodcastController { /** * Display a listing of the resource. */ public function index(Request $request): Response { return Feature::active(NewApi::class)  // ★ ? $this->resolveNewApiResponse($request) : $this->resolveLegacyApiResponse($request); } // ... } ``` ### Blade内での利用方法 `@feature` ディレクティブを使ってBladeで使用することも可能 ```htmlembedded= @feature('new-api') <!-- 'site-redesign' is active --> @else <!-- 'site-redesign' is inactive --> @endfeature ``` ------------------------------------ ## [Native type declarations in Laravel 10 skeleton](https://laravel.com/docs/10.x/releases#types) * DeepLでの日本語訳 > アプリケーションのスケルトンとスタブのタイプヒントは、Nuno Maduro氏によって提供されました。 > > Laravelの最初のリリースでは、当時のPHPで利用可能なすべてのタイプヒントの機能を利用しました。しかし、その後、プリミティブ型ヒントの追加、戻り値の型、ユニオン型など、多くの新機能がPHPに追加されました。 > Laravel 10.xでは、アプリケーションスケルトンとフレームワークで利用されるすべてのスタブを徹底的に更新し、すべてのメソッド署名に引数と戻り値の型を導入しています。また、余計な「doc block」型ヒントの情報は削除されました。 * [わかりやすい日本語記事](https://biz.addisteria.com/laravel10/#toc3) * 新しくひな型作ると、引数と戻り値に型宣言が追加されている * 余計な「doc block」というのは「PHPDoc」のこと? * PHPDoc補足していた型ヒントが消えて、型宣言に置き換えたということか * 下位互換性があるので、型宣言が追加されたと言って既存プロジェクトが動かなくなることはないらしい * 型のない言語はもう許されない(流行らない)のか……? ## [Invokable Validation rules are the default](https://laravel.com/docs/10.x/validation#custom-validation-rules) * [GitHub の pull リクエスト](https://github.com/laravel/framework/pull/43868) Laravel9で導入された[Invokable 検証ルール](https://github.com/laravel/framework/pull/42689)をデフォルトにする変更。 この変更によって得られるメリットは2点 * コードが簡潔になる * コストがシンプルになった分、学習コストが減る laravelは`php artisan make:rule [ルールのファイル名]`で独自のバリデーションルールを設定できる。 makeコマンドに引数が無い場合はpassesメソッドとmessageメソッドが実装されたコードが生成される 下記コードは[Github](https://github.com/laravel/framework/pull/42689)のもの ```php= class Quantity implements Rule { protected $messages = []; public function passes($attribute, $value) { if (! is_array($value)) { $this->messages[] = trans('validation.quantity.must_be_an_object'); return false; } if (! array_key_exists('magnitude', $value)) { $this->messages[] = trans('validation.quantity.missing_magnitude'); } if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } return $this->messages === []; } public function message() { return $this->messages; } } ``` passesでバリデーションを実施してfalseであればmessageをreturnする。 上記のコードでいうと、配列のkeyに`'magnitude`が存在しない時、`validation.quantity.missing_magnitude`メッセージが返される。 makeコマンドの引数にinvokableを指定するとinvokeメソッドのみが実装されたシンプルなコードが生成される(invokable rule)。 `php artisan make:rule [ルールのファイル名] --invokable` 下記コードは[Github](https://github.com/laravel/framework/pull/42689)のもの ```php= class InvokableQuantity implements InvokableRule { public function __invoke($attribute, $value, $fail) { if (! is_array($value)) { return $fail('validation.quantity.must_be_an_object')->translate(); } if (! array_key_exists('magnitude', $value)) { $fail('validation.quantity.missing_magnitude')->translate(); } if (! array_key_exists('units', $value)) { $fail('validation.quantity.missing_units')->translate(); } } } ``` invokeメソッドの引数`$fail`は失敗時に実行されるコールバック関数。 Laravel10からmake:ruleコマンドに引数invokableを渡さなくてもinvokable ruleが適用されるようになった。 なお呼び出し方はどちらも同じなので後方互換性が無くなることはない。 移行する場合もシンプルで以下のようにすればよい 1. Quantityクラスpasses内の下記のコードをInvokableQuantityクラスの__invoke関数内にコピペする ```php= public function passes($attribute, $value) { 中略... if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } } ``` ↓ ```php= public function __invoke($attribute, $value, $fail) { if (! array_key_exists('units', $value)) { $this->messages[] = trans('validation.quantity.missing_units'); } } ``` 2. messagesに値を入れている部分を$failを使用するように修正する。 ```php= public function __invoke($attribute, $value, $fail) { if (! array_key_exists('units', $value)) { $fail('validation.quantity.missing_units')->translate(); } } ``` -------------------------------------------------- ## [Processes 関連機能](https://laravel.com/docs/10.x/processes) * 別のプロセス呼び出しがファサード経由で実行可能に * 並行プロセスの実行と管理が容易になる ### プロセスの実行方法 * Processファサードの`run`メソッドで実行可能 * プロセスは同期、非同期を選択できる **同期実行**(処理が終わるまで待つ) ```php= use Illuminate\Support\Facades\Process; $result = Process::run('ls -la'); return $result->output(); ``` **非同期実行** ```php= use Illuminate\Support\Facades\Process; // 非同期実行(タイムアウトも設定) $process = Process::timeout(120)->start('bash import.sh'); // 実行中かどうかを判定することもできる while ($process->running()) { // ... } $result = $process->wait(); // 処理が終わるまで待つことも可能 ``` ### 便利なメソッド群 `run`で実行したプロセスの結果を検査するためのメソッド ```php= $result = Process::run('ls -la'); $result->successful(); $result->failed(); $result->exitCode(); $result->output(); $result->errorOutput(); ``` * [参考資料](https://zenn.dev/nshiro/articles/2115205d07257e) ### 並行プロセスの管理 複数のプロセスをプールさせることも簡単に。 ```php= use Illuminate\Process\Pool; use Illuminate\Support\Facades\Process; $pool = Process::pool(function (Pool $pool) { $pool->path(__DIR__)->command('bash import-1.sh'); $pool->path(__DIR__)->command('bash import-2.sh'); $pool->path(__DIR__)->command('bash import-3.sh'); })->start(function (string $type, string $output, int $key) { // ... }); while ($pool->running()->isNotEmpty()) { // ... } $results = $pool->wait(); ``` ------------------------------------ ## [テストのプロファイル オプション](https://laravel.com/docs/10.x/releases#test-profiling) * `artisan` の `test` コマンドに `--profile` オプションが追加 * 実行が遅いテストを一覧表示できる(最大10個) ------------------------------------ ## [Pest Scaffolding](https://laravel.com/docs/10.x/releases#pest-scaffolding) *  [Pest](https://pestphp.com/) * テストフレームワーク * Laravelのアプリケーションを新規作成する時に Pest を利用できるようになる ```bash $ laravel new example-application --pest ``` ~~めっちゃどうでもいいが、Pestのロゴがチカチカ...~~ ------------------------------------ ## [パスワード生成ヘルパー Str::password()](https://laravel.com/docs/10.x/helpers#method-str-password) `Str::password()` * 特定の長さのランダムパスワードを生成 * パスワードは、文字、数字、記号、スペースの組み合わせで構成 * デフォルトでは、パスワードの長さは 32 文字 ------------------------------------ ## [設定ファイルパスのカスタマイズ](https://benjamincrozat.com/laravel-10#customize-the-path-of-config-files) * [GitHub のプルリク](https://github.com/laravel/framework/pull/46053) ```php= $app->configPath(__DIR__ . '/../some/path'); ``` **メモ:[記事の内容](https://benjamincrozat.com/laravel-10#customize-the-path-of-config-files)** > This is useful for projects slowly migrating to Laravel that can’t handle a radical directory structure change. 和訳 > この機能は根本的にディレクトリ構成を変更できないLaravelプロジェクトをゆっくりマイグレーションすることに役立ちます。 根本的にディレクトリ構成を変更できない? 長く使われているLaravelプロジェクトへの配慮?? ------------------------------------ ## [doctrine/dbal is not needed anymore to modify columns in migrations](https://benjamincrozat.com/laravel-10#2280334235e58f6186f03551a590dab3) * Laravel9では、マイグレーションにてテーブル列名変更場合は [doctrine/dbal](https://www.doctrine-project.org/projects/dbal.html) をインストールする必要があった * Laravel 10 からは `doctrine/dbal` がインストールは不要になる ```php= return new class extends Migration { public function up() { Schema::table('foo', function (Blueprint $table) { $table->unsignedBigInteger('bar')->change(); }); } … } ``` 既に `doctrine/dbal` がインストールされている場合は、サービスプロバイダに以下の記述が必要。 ```php= use Illuminate\Support\Facades\Schema; … class AppServiceProvider extends ServiceProvider { public function boot() { Schema::useNativeSchemaOperationsIfPossible(); } } ``` ------------------------------------ ## [Laravel 10 requires at least Composer 2.2](https://benjamincrozat.com/laravel-10#laravel-10-requires-at-least-composer-22) * Laravel10.xからは Composer 2.2 以上が必要 ------------------------------------ ## 非推奨となる変更点 - Remove various deprecations Pull Request [#41136](https://github.com/laravel/framework/pull/41136) - `getBaseQuery` の削除 - `Illuminate\Database\Eloquent\Relations\Relation`クラスの`getBaseQuery`メソッドの名前が`toBase`に変更されました - `MaintenanceModeException` の削除 - `MaintenanceModeException`はアプリケーションがメンテナンスモードの時にステータスコード503で投げられる例外 - `MocksApplicationServices` - https://github.com/laravel/framework/issues/41027 - [日本語記事](https://zenn.dev/at_yasu/articles/laravel-10-release#servicemock-%3A%3A-impact%3Amedium) - Remove deprecated dates property in Pull Request [#42587](https://github.com/laravel/framework/pull/42587) - `Eloquent` モデルの非推奨の`$dates`プロパティが削除されました - `$casts`プロパティを使用する必要があります。 - Remove handleDeprecation method in Pull Request [#42590](https://github.com/laravel/framework/pull/42590) - 非推奨のログを出力するメソッド - `handleDeprecation` メソッドの削除 - 代わりに `handleDeprecationError` を使う - https://laravel.com/api/9.x/Illuminate/Foundation/Bootstrap/HandleExceptions.html#method_handleDeprecation * `assertTimesSent`メソッドが削除された。 [#42592](https://github.com/laravel/framework/pull/42592) * 通知が送信された回数の合計をassertするテスト用のメソッド。 * `assertSentTimes`メソッドを代わりに使用する。 * `ScheduleListCommand.php`の`$defaultName`プロパティが削除された。[コミットコメント](https://github.com/laravel/framework/commit/419471eb24563b7d8dd0fd307056b096076c9008) * スケジュールされているタスクのリストを表示するコマンド。 * `$defaultName`プロパティは遅延ロード中にコマンドを識別するために使用されていた模様。([修正コミット](https://github.com/laravel/framework/commit/419471eb24563b7d8dd0fd307056b096076c9008)) * 使用する側には影響なしと思われる。 * `Route::home`メソッドが削除された。[#42614](https://github.com/laravel/framework/pull/42614) * `home`として登録されているルートに遷移するメソッドのよう。 * `dispatchNow()`が削除された。[#42591](https://github.com/laravel/framework/pull/42591) * ジョブをすぐに実行するメソッド。 * ジョブをすぐに実行したい場合は代わりに`dispatchSync()`を使用する必要がある。 # 参考資料 * [Laravel 公式](https://laravel.com/docs/10.x/releases) * [Laravel News](https://laravel-news.com/laravel-10) * [Laravel 10 is out! Here's every new feature and change.](https://benjamincrozat.com/laravel-10)