13.

Laravel「Cannot access protected property

編集
この記事の要点
  • Laravel の Request オブジェクトの内部 protected プロパティに直接アクセスしようとして発生
  • $request->session のような名前を使うと、リクエストパラメータではなく内部プロパティへのアクセスとして解釈される
  • 正しくは $request->input('session') / $request->get('session') でパラメータを取得
  • Request の __get マジックメソッドはパラメータを返すが、protected と名前衝突する場合は protected が優先される
  • 安全策: Form Request クラスまたは $request->input() を統一して使う

エラーの全文と発生条件

ErrorException
Cannot access protected property Illuminate\Http\Request::$session

at /var/www/myapp/app/Http/Controllers/UserController.php:25
    23| public function store(Request $request) {
    24|     $name = $request->name;
>>> 25|     $session = $request->session;   // ← ここでエラー
    26|     // ...
    27| }

このエラーは $request オブジェクトの protected プロパティ名と同じ名前のリクエストパラメータにアクセスしようとした際に発生します。Illuminate\Http\Request およびその親クラス Symfony\Component\HttpFoundation\Request には次のような protected プロパティが定義されています:

protected プロパティ用途
$sessionセッションストアインスタンス
$attributesカスタム属性 (ParameterBag)
$requestPOST ボディ (ParameterBag)
$queryクエリ文字列 (ParameterBag)
$server$_SERVER 相当 (ServerBag)
$files$_FILES 相当 (FileBag)
$cookies$_COOKIE 相当 (ParameterBag)
$headersHTTP ヘッダ (HeaderBag)
$contentリクエストボディ raw
$languages / $charsets / $localeロケール関連
$jsonJSON ボディ
$convertedFilesUploadedFile 配列

フォームに 等を作り、$request->session で取得しようとすると、protected プロパティアクセスとして解釈されて上記エラーになります。

解決策: input() / get() を使う

public function store(Request $request) {
    // ❌ NG(protected と名前衝突する場合に死ぬ)
    $session = $request->session;
    $attrs   = $request->attributes;
    $query   = $request->query;

    // ✅ OK(推奨パターン)
    $session = $request->input('session');
    $attrs   = $request->input('attributes');
    $query   = $request->input('query');

    // ✅ OK(同等)
    $session = $request->get('session');

    // ✅ OK(全部一度に)
    $data = $request->all();
    $session = $data['session'] ?? null;

    // ✅ OK(必要分のみ)
    $data = $request->only(['session', 'name', 'email']);

    // ✅ OK(型キャスト系)
    $age = $request->integer('age');
    $isAdmin = $request->boolean('is_admin');
    $tags = $request->array('tags');
}

Request の __get の挙動

Laravel の Request には __get() マジックメソッドが定義されていて、$request->name のように書くと自動的にリクエストパラメータ → ルートパラメータの順で探します:

// Illuminate\Http\Concerns\InteractsWithInput
public function __get($key) {
    return Arr::get($this->all() + $this->route()->parameters(), $key);
}

しかし PHP は プロパティアクセス時に「実在するプロパティ」を優先し、それが protected / private ならエラーになります。__get はそもそも呼ばれません:

class Foo {
    protected $bar = 'protected value';
    public function __get($k) { return 'magic'; }
}
$f = new Foo();
echo $f->baz;   // 'magic' (__get 呼ばれる)
echo $f->bar;   // Cannot access protected property Foo::$bar  ← __get 呼ばれない

典型的なバグパターン

// ❌ よくあるバグ 1: フォーム名がたまたま session

// Controller
$value = $request->session;   // Cannot access protected property

// ❌ よくあるバグ 2: API パラメータ名と衝突
// POST /api/audit { "request": "..." }
$value = $request->request;   // Cannot access protected property

// ❌ よくあるバグ 3: model 配列のキー
{!! Form::open(['name' => 'cookies']) !!}
$value = $request->cookies;   // Cannot access protected property

// ✅ 解決策: 全部 input() に統一
$session = $request->input('session');
$payload = $request->input('request');
$cookies = $request->input('cookies');

解決策2: Form Request クラスで吸収

パラメータが多い・型が複雑な場合は Form Request クラスを使うと、プロパティ名と衝突しない取り出し方ができます:

php artisan make:request StoreUserRequest
// app/Http/Requests/StoreUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreUserRequest extends FormRequest {
    public function authorize(): bool { return true; }

    public function rules(): array {
        return [
            'session'    => 'nullable|string|max:255',
            'attributes' => 'nullable|array',
            'name'       => 'required|string|max:100',
        ];
    }

    public function validated($key = null, $default = null) {
        $data = parent::validated();
        // session 等の特殊キーも安全に取り出せる
        return is_null($key) ? $data : ($data[$key] ?? $default);
    }
}

// Controller
public function store(StoreUserRequest $request) {
    $session = $request->validated('session');   // ← protected 衝突なし
    $attrs   = $request->validated('attributes');
}

そもそも衝突を避けるための命名規則

  • フォームの name= 属性に session / attributes / request / query / server / files / cookies / headers / content / json / locale 等は使わない
  • 使うなら user_session / form_attrs / payload 等にリネーム
  • API では { "data": {...} } でラップする慣習に従う

$request の値取得 API まとめ

メソッド用途
input($k, $default)GET/POST 統合$request->input('name')
get($k, $default)input のエイリアス
query($k, $default)クエリ文字列のみ$request->query('page')
post($k, $default)POST ボディのみ
all()全パラメータ配列$request->all()
only([...])指定キーのみ$request->only(['name','email'])
except([...])指定キー以外$request->except(['_token'])
has($k)存在チェック$request->has('name')
filled($k)存在 + 空でない$request->filled('name')
integer($k) / boolean($k) / array($k)型キャスト取得(Laravel 9+)

FAQ

Q: $request->name という書き方は使ってはいけないのか
A: 動作はします(__get 経由)が、protected プロパティと名前が被ると死ぬ脆さがあるため、チームでは input() に統一するのが安全です。

Q: なぜ Laravel はこの設計にしたのか
A: Symfony Request クラスを継承しているためです。Symfony は元から protected プロパティで内部状態を持つ設計で、Laravel はそれに __get を被せた形になっています。

Q: 自前で Request を拡張すれば直せるか
A: 可能ですが副作用が大きい(フレームワーク内部の挙動が変わる)ので非推奨。アプリ側でフォーム名を変える方が早いです。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. SQLSTATE[HY000] [1045] Access denied for user 'homestead'@'localhost'
  2. Add [~] to fillable property to allow mass assignment on [App\~].
  3. PHP Parse error: syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) or variable (T_VARIABLE) or '{' or '$' in ~
  4. Changing columns for table "~" requires Doctrine DBAL; install "doctrine/dbal"
  5. MethodNotAllowedHttpException No message
  6. Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found
  7. production.ERROR: No application encryption key has been specified.
  8. Dotenv values containing spaces must be surrounded by quotes.
  9. Laravel \ Socialite \ Two \ InvalidStateException
  10. The page has expired due to inactivity. Please refresh and try again.
  11. Failed to clone https://github.com/symfony/thanks.git via https, ssh protocol
  12. Illegal offset type
  13. Cannot access protected property Illuminate\Http\Request::$...
  14. Emitted value instead of an instance of Error
  15. 画像保存時にInternal Server Error
  16. Failed to authenticate on SMTP server with username ...
  17. PostTooLargeException
  18. Database hosts array is empty.
  19. Invalid request (Unsupported SSL request)
  20. does not comply with psr-4 autoloading standard. Skipping.
  21. MySQLのSTR_TO_DATE関数を使用するとnullが返却される問題