この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:2
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: Cannot access protected property Illuminate\Http\Request::$...
SEOタイトル: Laravel「Cannot access protected property

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>Laravel の <code>Request</code> オブジェクトの<strong>内部 protected プロパティ</strong>に直接アクセスしようとして発生</li><li><code>$request->session</code> のような名前を使うと、リクエストパラメータではなく<strong>内部プロパティ</strong>へのアクセスとして解釈される</li><li>正しくは <strong><code>$request->input('session')</code></strong> / <strong><code>$request->get('session')</code></strong> でパラメータを取得</li><li>Request の <code>__get</code> マジックメソッドはパラメータを返すが、protected と名前衝突する場合は protected が優先される</li><li>安全策: <strong>Form Request クラス</strong>または <code>$request->input()</code> を統一して使う</li></ul></td></tr></table>
<h2>エラーの全文と発生条件</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">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| }</code></pre>
<p>このエラーは <strong><code>$request</code> オブジェクトの protected プロパティ名と同じ名前</strong>のリクエストパラメータにアクセスしようとした際に発生します。<code>Illuminate\Http\Request</code> およびその親クラス <code>Symfony\Component\HttpFoundation\Request</code> には次のような protected プロパティが定義されています:</p>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>protected プロパティ</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>$session</code></td><td>セッションストアインスタンス</td></tr>
<tr><td><code>$attributes</code></td><td>カスタム属性 (ParameterBag)</td></tr>
<tr><td><code>$request</code></td><td>POST ボディ (ParameterBag)</td></tr>
<tr><td><code>$query</code></td><td>クエリ文字列 (ParameterBag)</td></tr>
<tr><td><code>$server</code></td><td>$_SERVER 相当 (ServerBag)</td></tr>
<tr><td><code>$files</code></td><td>$_FILES 相当 (FileBag)</td></tr>
<tr><td><code>$cookies</code></td><td>$_COOKIE 相当 (ParameterBag)</td></tr>
<tr><td><code>$headers</code></td><td>HTTP ヘッダ (HeaderBag)</td></tr>
<tr><td><code>$content</code></td><td>リクエストボディ raw</td></tr>
<tr><td><code>$languages</code> / <code>$charsets</code> / <code>$locale</code></td><td>ロケール関連</td></tr>
<tr><td><code>$json</code></td><td>JSON ボディ</td></tr>
<tr><td><code>$convertedFiles</code></td><td>UploadedFile 配列</td></tr>
</tbody>
</table>

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

<h2>解決策: input() / get() を使う</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">public function store(Request $request) {
    // ❌ NG(protected と名前衝突する場合に死ぬ)
    $session = $request->session;
    $attrs   = $request->attributes;
    $query   = $request->query;

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

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

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

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

    // ✅ OK(型キャスト系)
    $age = $request->integer(&apos;age&apos;);
    $isAdmin = $request->boolean(&apos;is_admin&apos;);
    $tags = $request->array(&apos;tags&apos;);
}</code></pre>
<h2>Request の __get の挙動</h2>

<p>Laravel の <code>Request</code> には <code>__get()</code> マジックメソッドが定義されていて、<code>$request->name</code> のように書くと自動的にリクエストパラメータ → ルートパラメータの順で探します:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// Illuminate\Http\Concerns\InteractsWithInput
public function __get($key) {
    return Arr::get($this->all() + $this->route()->parameters(), $key);
}</code></pre>
<p>しかし PHP は <strong>プロパティアクセス時に「実在するプロパティ」を優先</strong>し、それが <code>protected</code> / <code>private</code> ならエラーになります。<code>__get</code> はそもそも呼ばれません:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">class Foo {
    protected $bar = &apos;protected value&apos;;
    public function __get($k) { return &apos;magic&apos;; }
}
$f = new Foo();
echo $f->baz;   // &apos;magic&apos; (__get 呼ばれる)
echo $f->bar;   // Cannot access protected property Foo::$bar  ← __get 呼ばれない</code></pre>
<h2>典型的なバグパターン</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// ❌ よくあるバグ 1: フォーム名がたまたま session
<input name="session" type="text">
// 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([&apos;name&apos; => &apos;cookies&apos;]) !!}
$value = $request->cookies;   // Cannot access protected property

// ✅ 解決策: 全部 input() に統一
$session = $request->input(&apos;session&apos;);
$payload = $request->input(&apos;request&apos;);
$cookies = $request->input(&apos;cookies&apos;);</code></pre>
<h2>解決策2: Form Request クラスで吸収</h2>

<p>パラメータが多い・型が複雑な場合は <strong>Form Request</strong> クラスを使うと、プロパティ名と衝突しない取り出し方ができます:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-bash">php artisan make:request StoreUserRequest</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// 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 [
            &apos;session&apos;    => &apos;nullable|string|max:255&apos;,
            &apos;attributes&apos; => &apos;nullable|array&apos;,
            &apos;name&apos;       => &apos;required|string|max:100&apos;,
        ];
    }

    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(&apos;session&apos;);   // ← protected 衝突なし
    $attrs   = $request->validated(&apos;attributes&apos;);
}</code></pre>
<h2>そもそも衝突を避けるための命名規則</h2>

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

<h2>$request の値取得 API まとめ</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>メソッド</th><th>用途</th><th>例</th></tr></thead>
<tbody>
<tr><td><code>input($k, $default)</code></td><td>GET/POST 統合</td><td><code>$request->input('name')</code></td></tr>
<tr><td><code>get($k, $default)</code></td><td>input のエイリアス</td><td>—</td></tr>
<tr><td><code>query($k, $default)</code></td><td>クエリ文字列のみ</td><td><code>$request->query('page')</code></td></tr>
<tr><td><code>post($k, $default)</code></td><td>POST ボディのみ</td><td>—</td></tr>
<tr><td><code>all()</code></td><td>全パラメータ配列</td><td><code>$request->all()</code></td></tr>
<tr><td><code>only([...])</code></td><td>指定キーのみ</td><td><code>$request->only(['name','email'])</code></td></tr>
<tr><td><code>except([...])</code></td><td>指定キー以外</td><td><code>$request->except(['_token'])</code></td></tr>
<tr><td><code>has($k)</code></td><td>存在チェック</td><td><code>$request->has('name')</code></td></tr>
<tr><td><code>filled($k)</code></td><td>存在 + 空でない</td><td><code>$request->filled('name')</code></td></tr>
<tr><td><code>integer($k)</code> / <code>boolean($k)</code> / <code>array($k)</code></td><td>型キャスト取得(Laravel 9+)</td><td>—</td></tr>
</tbody>
</table>

<h2>FAQ</h2>

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

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

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