Skip to content

Upgrade to Laravel 12 and PHP 8.2+ with improved type hints#41

Open
39ff wants to merge 3 commits into
masterfrom
claude/laravel-upgrade-latest-n7jx4l
Open

Upgrade to Laravel 12 and PHP 8.2+ with improved type hints#41
39ff wants to merge 3 commits into
masterfrom
claude/laravel-upgrade-latest-n7jx4l

Conversation

@39ff

@39ff 39ff commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

This PR upgrades the project to Laravel 12 and PHP 8.2 as minimum versions, along with corresponding updates to all dependencies. Additionally, it improves type safety throughout the codebase by adding explicit return type declarations and modernizing PHPDoc annotations.

Key Changes

Dependency Updates

  • PHP: Upgraded from ^8.1 to ^8.2
  • Laravel Framework: Upgraded from ^10.4 to ^12.0
  • Laravel Sanctum: Upgraded from ^3.2 to ^4.0
  • Laravel UI: Upgraded from ^4.2 to ^4.6
  • Guzzle HTTP: Upgraded from ^7.2 to ^7.9
  • Development dependencies: Updated PHPUnit to ^11.5, Collision to ^8.6, and other dev tools
  • Static Analysis: Migrated from nunomaduro/larastan to larastan/larastan ^3.0

Type Safety Improvements

  • Added explicit BelongsTo return type declarations to relationship methods in SquidAllowedIp and SquidUser models
  • Updated PHPDoc annotations from array<int, string> to list<string> in User model for better type precision
  • Modernized resource class return types from array|\Illuminate\Contracts\Support\Arrayable|JsonSerializable to array<string, mixed> or array<int|string, mixed>
  • Removed unnecessary JsonSerializable imports from resource classes
  • Added explicit integer casting for route parameters in request classes to ensure type safety

Configuration Updates

  • Updated PHPStan configuration to use the new larastan/larastan package path
  • Simplified PHPStan rules and added comment explaining Eloquent model attribute type handling
  • Updated GitHub Actions workflow to test against multiple PHP versions (8.2, 8.3, 8.4)
  • Updated PHPUnit configuration to use modern <source> element instead of deprecated <coverage> element

Notable Implementation Details

  • Route parameter casting ensures type safety when assigning IDs from route parameters
  • PHPStan configuration now treats PHPDoc types as uncertain for Eloquent models, accounting for unsaved model scenarios
  • GitHub Actions now runs tests in parallel across three PHP versions for better compatibility assurance

https://claude.ai/code/session_01BS9oQ2ynXc33SUyduSrciw

Summary by CodeRabbit

  • 新機能

    • テスト実行環境が複数の PHP バージョンに対応し、より幅広い環境で確認できるようになりました。
  • 不具合修正

    • ルートパラメータから取得する ID を整数として扱うようになり、データの型ぶれを防ぎました。
    • 一部の関連データの型指定が明確になり、安定性が向上しました。
  • Chores

    • 依存関係やテスト設定を更新し、最新の開発・検証環境に合わせました。
    • 返却型注釈や未使用設定を整理し、設定の整合性を改善しました。

Upgrade the framework and dev toolchain to the latest stable Laravel,
keeping the existing application skeleton (Http/Console kernels,
bootstrap/app.php, config/app.php providers), which remains fully
supported in Laravel 11/12.

Dependencies (composer.json):
- php: ^8.1 -> ^8.2
- laravel/framework: ^10.4 -> ^12.0
- laravel/sanctum: ^3.2 -> ^4.0
- laravel/ui: ^4.2 -> ^4.6
- laravel/tinker, laravel/sail, guzzlehttp/guzzle bumped
- phpunit/phpunit: ^9.5 -> ^11.5
- nunomaduro/larastan -> larastan/larastan ^3.0 (phpstan 2)
- nunomaduro/collision: ^7.0 -> ^8.6
- fakerphp/faker, mockery/mockery, spatie/laravel-ignition bumped

Tooling config:
- phpunit.xml: migrate to PHPUnit 11 schema (coverage <include> -> <source>,
  drop removed processUncoveredFiles)
- phpstan.neon.dist: use larastan/larastan extension, drop removed
  checkMissingIterableValueType, set treatPhpDocTypesAsCertain: false
- CI: matrix on PHP 8.2/8.3/8.4, actions/checkout@v4, setup-php@v2

Code fixes for Laravel 12 / stricter static analysis:
- Resources: toArray() PHPDoc matches the native array return type
- User model: $fillable/$hidden typed as list<string>
- Models: explicit BelongsTo return type on laravel_user() relations
- Form requests: cast route parameters to int before assigning to int FKs

Verified: 23 tests pass (PHPUnit 11 / PHP 8.4), PHPStan level 5 clean,
all migrations run, app boots (45 routes + config cache).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BS9oQ2ynXc33SUyduSrciw
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@39ff, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: def7c2d9-cabf-4537-b650-c8f297343a5d

📥 Commits

Reviewing files that changed from the base of the PR and between 500409b and 2c62086.

📒 Files selected for processing (1)
  • .github/workflows/laravel.yml

ウォークスルー

PHP 8.2/8.3/8.4 の CI マトリクスを追加し、composer.json の依存関係を更新(larastan/larastan への移行を含む)。PHPStan・PHPUnit 設定を調整し、モデルの laravel_user()BelongsTo 戻り値型を追加。リクエストクラスでルートパラメータを (int) キャストし、リソースクラスの PHPDoc 型注釈を整理。

変更内容

依存関係更新・型安全性改善・CI マトリクス対応

レイヤー / ファイル 概要
依存関係・静的解析・テスト設定の更新
composer.json, phpstan.neon.dist, phpunit.xml
composer.json で PHP/Laravel 関連パッケージのバージョンを引き上げ、nunomaduro/larastanlarastan/larastan へ移行し、platform.php8.2.0 に固定。phpstan.neon.dist の extension パスを更新し、treatPhpDocTypesAsCertain: false を追加。phpunit.xml のスキーマパスと <source> ブロックを更新。
GitHub Actions CI PHP バージョンマトリクス追加
.github/workflows/laravel.yml
PHP 8.2/8.3/8.4 のマトリクスビルド(fail-fast: false)を追加し、actions/checkout@v4shivammathur/setup-php@v2 に更新。
モデルの BelongsTo 戻り値型と PHPDoc 更新
app/Models/SquidAllowedIp.php, app/Models/SquidUser.php, app/Models/User.php
SquidAllowedIpSquidUserlaravel_user()BelongsTo 戻り値型と対応 import を追加。User モデルの $fillable$hidden@var 注釈を list<string> に変更。
リクエストクラスのルートパラメータ int キャスト追加
app/Http/Requests/SquidAllowedIp/CreateRequest.php, app/Http/Requests/SquidUser/CreateRequest.php, app/Http/Requests/SquidUser/ModifyRequest.php, app/Http/Requests/User/ModifyRequest.php
各リクエストクラスのモデル組み立てメソッドで、ルートパラメータの iduser_id(int) キャストしてから代入するよう変更。
リソースクラスの PHPDoc @return 型注釈整理
app/Http/Resources/SquidAllowedIpCollection.php, app/Http/Resources/SquidAllowedIpResource.php, app/Http/Resources/SquidUserCollection.php, app/Http/Resources/SquidUserResource.php, app/Http/Resources/UserCollection.php
各リソースクラスの toArray PHPDoc @returnarray<string, mixed> 等に統一し、不要な JsonSerializable import を削除。

推定コードレビュー工数

🎯 2 (Simple) | ⏱️ ~10 minutes

ポエム

🐰 うさぎは型をきっちりと、
int にキャストしてぴょんと跳ねる。
PHP 8.4 まで走り抜け、
larastan の道を新しく踏んで、
PHPDoc もすっきり整えた春。 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Laravel 12 へのアップグレード、PHP 8.2+ への引き上げ、型ヒント強化という主要変更を適切に要約しています。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/laravel-upgrade-latest-n7jx4l

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…trix

The lock was resolved on PHP 8.4, which pulled Symfony 8.1 components
(require php >=8.4.1). `composer install` then failed on the PHP 8.2 and
8.3 CI jobs with "lock file does not contain a compatible set of
packages", while only 8.4 passed.

Set config.platform.php to 8.2.0 (the require floor) and re-resolve, which
downgrades symfony/clock, css-selector, event-dispatcher, string,
translation and yaml from v8.1 to v7.4 — the Symfony 7 line Laravel 12 is
built on. The lock now satisfies php 8.2.0, so it installs cleanly on
8.2/8.3/8.4.

Verified: composer why-not php 8.2.0 is clean, 23 tests pass, PHPStan
level 5 clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BS9oQ2ynXc33SUyduSrciw

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dbbe9f815c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread composer.lock Outdated
@39ff

39ff commented Jun 30, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/Http/Requests/User/ModifyRequest.php (1)

27-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ルートパラメータの int キャストは型安全性向上に有効です。

modifyUser()id(int) キャストして User モデルに代入する変更は、下流の ModifyActionwhere('id', '=', $user->id) として利用される型安全性を高めます。

ただし、同じクラス内の authorize()(Line 13)と rules()(Line 22)でも同じ $this->route()->parameter('id') を利用していますが、こちらはキャストされていません。authorize() では Gate::allows() に渡される値、rules() ではユニーク制約の除外 ID として文字列連結に利用されるため、型の一貫性が損なわれています。

可能であれば、同じルートパラメータに対する型扱いをクラス内で統一することを検討してください。

     public function authorize(Gate $gate) : bool
     {
-        $auth = $gate->allows('modify-user', $this->route()->parameter('id'));
+        $id = (int) $this->route()->parameter('id');
+        $auth = $gate->allows('modify-user', $id);
 
         return $auth;
     }
             'email'=>'filled|unique:users,email,'.$this->route()->parameter('id').',id',
+            'email'=>'filled|unique:users,email,'.(int) $this->route()->parameter('id').',id',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Requests/User/ModifyRequest.php` around lines 27 - 33, The route
parameter type handling is inconsistent in User/ModifyRequest: modifyUser() now
casts the route id to int, but authorize() and rules() still use the raw
$this->route()->parameter('id'). Update the id handling in authorize() and
rules() to use the same integer-cast value (preferably via a shared helper or
local variable in ModifyRequest) so Gate::allows() and the unique-rule exclusion
ID are treated consistently with modifyUser().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/laravel.yml:
- Line 20: The checkout step in the workflow should disable credential
persistence because this job does not need to push back to the repository.
Update the actions/checkout usage in the Laravel workflow to set
persist-credentials to false so the GITHUB_TOKEN is not stored in .git/config.
Use the existing checkout step as the target and keep the rest of the job
unchanged.

In `@app/Http/Requests/SquidAllowedIp/CreateRequest.php`:
- Line 34: The user_id route parameter in CreateRequest is being cast with
(int), which turns non-numeric input into 0 instead of rejecting it; add numeric
validation to the route binding or FormRequest so invalid values are blocked.
Update the CreateRequest validation rules or the route definition that feeds
$this->route()->parameter('user_id') to enforce integer-only input, then keep
the assignment to $squidAllowedIp->user_id relying on validated data.

---

Nitpick comments:
In `@app/Http/Requests/User/ModifyRequest.php`:
- Around line 27-33: The route parameter type handling is inconsistent in
User/ModifyRequest: modifyUser() now casts the route id to int, but authorize()
and rules() still use the raw $this->route()->parameter('id'). Update the id
handling in authorize() and rules() to use the same integer-cast value
(preferably via a shared helper or local variable in ModifyRequest) so
Gate::allows() and the unique-rule exclusion ID are treated consistently with
modifyUser().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3ecef87b-d505-4561-8487-44aee6e3b245

📥 Commits

Reviewing files that changed from the base of the PR and between 3266824 and 500409b.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • .github/workflows/laravel.yml
  • app/Http/Requests/SquidAllowedIp/CreateRequest.php
  • app/Http/Requests/SquidUser/CreateRequest.php
  • app/Http/Requests/SquidUser/ModifyRequest.php
  • app/Http/Requests/User/ModifyRequest.php
  • app/Http/Resources/SquidAllowedIpCollection.php
  • app/Http/Resources/SquidAllowedIpResource.php
  • app/Http/Resources/SquidUserCollection.php
  • app/Http/Resources/SquidUserResource.php
  • app/Http/Resources/UserCollection.php
  • app/Models/SquidAllowedIp.php
  • app/Models/SquidUser.php
  • app/Models/User.php
  • composer.json
  • phpstan.neon.dist
  • phpunit.xml

Comment thread .github/workflows/laravel.yml
{
$squidAllowedIp = new SquidAllowedIp($this->validated());
$squidAllowedIp->user_id = $this->route()->parameter('user_id');
$squidAllowedIp->user_id = (int) $this->route()->parameter('user_id');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

echo "== route definitions mentioning user_id =="
fd -t f . routes | xargs -r rg -n -C2 "user_id|whereNumber|Route::"

echo
echo "== request files using route()->parameter('user_id') =="
fd -t f 'CreateRequest.php' app/Http/Requests | xargs -r rg -n -C2 "route\(\)->parameter\('user_id'\)|\(int\)\s*\$this->route\(\)->parameter\('user_id'\)"

Repository: 39ff/squid-db-auth-web

Length of output: 6596


🏁 Script executed:

#!/bin/bash
set -eu

echo "== app/Http/Requests/SquidAllowedIp/CreateRequest.php =="
cat -n app/Http/Requests/SquidAllowedIp/CreateRequest.php

echo
echo "== routes/web.php relevant section =="
sed -n '27,43p' routes/web.php

echo
echo "== routes/api.php relevant section =="
sed -n '24,45p' routes/api.php

Repository: 39ff/squid-db-auth-web

Length of output: 4083


user_id に数値制約を追加してください
route() の値を (int) で入れているため、非数値が 0 になります。whereNumber('user_id') か FormRequest 側の整数検証を追加してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Requests/SquidAllowedIp/CreateRequest.php` at line 34, The user_id
route parameter in CreateRequest is being cast with (int), which turns
non-numeric input into 0 instead of rejecting it; add numeric validation to the
route binding or FormRequest so invalid values are blocked. Update the
CreateRequest validation rules or the route definition that feeds
$this->route()->parameter('user_id') to enforce integer-only input, then keep
the assignment to $squidAllowedIp->user_id relying on validated data.

The laravel-tests job only reads the repo, installs dependencies and runs
tests — it never pushes back. Set persist-credentials: false on
actions/checkout so the GITHUB_TOKEN is not written to .git/config, where a
later compromised step (e.g. pulling third-party composer deps) could read
it. Addresses the zizmor "artipacked" warning from the PR review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BS9oQ2ynXc33SUyduSrciw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants