コードロード

エラー討伐

【PHP】セッション名にドットを含めて指定するとセッションが取得できなくなる

課題

セッション名を指定して session_start() すると、セッションが取得できなかったのでメモ。

結論

セッション名を指定するときに、「.」ドットは使わない!

セッション名を「.」ドットで繋いで session_start() すると、セッションを取得できない。

<?php
// lopgin.php

session_start([
  'name' => 'test.com.session'
]);

$_SESSION['user_id'] = $user_id;
<?php
// test.php

session_start([
  'name' => 'test.com.session'
]);

var_dump($_SESSION);
exit;

// array (size=0)
//  empty

やるなら「_」アンダーバーで繋ぐこと。

<?php
// lopgin.php

session_start([
  'name' => 'test_com_session'
]);

$_SESSION['user_id'] = $user_id;
<?php
// test.php

session_start([
  'name' => 'test_com_session'
]);

var_dump($_SESSION);
exit;

// array (size=1)
//  'user_id' => int 1

【PHP】CSV出力したいのにheader()関数が効かない

環境

やりたかったこと

PHPCSVを出力する機能を作っていて、レスポンスヘッダーを下記のように設定したかった。

Content-Disposition: attachment; filename=ファイル名.csv
Content-Type: text/csv

課題

下記のメソッドを使ってレスポンスしたのに、 header() で設定したはずのが反映されていない。

使用した関数

<?php
  /**
   * CSVダウンロード
   *
   * @param string $file_name
   * @param array $header
   * @param array $csv_list
   * @return void
   */
  public static function csvDownload($file_name, $header, $csv_list) {

    // 出力バッファをopen
    $stream = fopen('php://output', 'w');

    // 文字化け対策
    mb_convert_variables('SJIS-win', 'UTF-8', $header);

    // 列名を記入
    fputcsv($stream, $header);

    // 2行目以降を1行ずつストリームへ記入
    foreach ($csv_list as $line) {
        mb_convert_variables('SJIS-win', 'UTF-8', $line);
        fputcsv($stream, $line);
    }

    fclose($stream);

    // ファイルダウンロードさせるためにヘッダーを調整
    header("Content-Type: text/csv");
    header("Content-Disposition: attachment; filename=$file_name");
    readfile($file_name);
    exit;
  }

Postmanで確認したレスポンスヘッダ

解決策

先程のメソッドで、 header() 関数を使う順番を最初に持ってきた

<?php
/**
   * CSVダウンロード
   *
   * @param string $file_name
   * @param array $header
   * @param array $csv_list
   * @return void
   */
  public static function csvDownload($file_name, $header, $csv_list) {

    // ファイルダウンロードさせるためにヘッダーを調整
    header("Content-Type: text/csv");
    header("Content-Disposition: attachment; filename=$file_name");

    // 出力バッファをopen
    $stream = fopen('php://output', 'w');

    // 文字化け対策
    mb_convert_variables('SJIS-win', 'UTF-8', $header);

    // 列名を記入
    fputcsv($stream, $header);

    // 2行目以降を1行ずつストリームへ記入
    foreach ($csv_list as $line) {
        mb_convert_variables('SJIS-win', 'UTF-8', $line);
        fputcsv($stream, $line);
    }

    fclose($stream);

    readfile($file_name);
    exit;;
  }

すると、想定通りにレスポンスヘッダに反映されていた。

公式にも記載の通り、メソッド内でも先に書くようにということか?

覚えておいて頂きたいのは、header()  関数は、 通常の HTML タグまたは PHP からの出力にかかわらず、すべての実際の 出力の前にコールする必要があることです。 https://www.php.net/manual/ja/function.header

【Laravel】バリデーション失敗時に勝手にリダイレクトしないようにする(jsonを返させる)

課題

バリデーション失敗時に勝手に意図しないhtmlレスポンスを返す。

解決の過程

  1. ReactとLaravelで開発中に、データ更新時と登録時にたまに302レスポンスが返ってくる。
  2. コントローラまで処理が到達していない
  3. フォームリクエストで止まっているっぽい
  4. バリデーションに引っかかった時に302エラーを返しているよう
  5. ググったら、バリデーション失敗時は一個前のリクエストのページにリダイレクトしようとしているらしい。つまり、jsonで指定しているのにhtmlレスポンスになる。
  6. 勝手に前のページにリダイレクトしないようにしよう!

解決方法

Laravelはデフォルトで、バリデーション失敗時はリダイレクトされてHTMLレスポンスが返ってくるようになっているらしいので、このリダイレクトさせる処理をオーバーライドしてJSONが返ってくるようにする。

Illuminate\Foundation\Http\FormRequest クラスを継承した

app/Http/Requests/ApiRequest.phpを作成して、バリデーション失敗時の処理をオーバーライドさせる。

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;

class ApiRequest extends FormRequest
{

    /**
     * @Override
     * 勝手にリダイレクトさせない
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     */
    protected function failedValidation(Validator $validator)
    {
        $data = [
            'message' => 'The given data was invalid.',
            'errors'  => $validator->errors()->toArray(),
        ];

        throw new HttpResponseException(response()->json($data, 200));
    }
}

使用例

フォームリクエストを使用する際は、デフォルトの FormRequest を継承するのではなく、上記のオーバーライドした ApiRequest を継承するようにする。

そうすれば、バリデーションに失敗してもJSONを返すようになる。

<?php

use App\Http\Requests\ApiRequest;  // 追記
#use Illuminate\Foundation\Http\FormRequest;  // 使用せず

class SampleRequest extends ApiRequest  // 継承先をデフォルトのFormRequestから変更
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => ['required'],
        ];
    }
}

参考

qiita.com

Laravel5.8 FormRequestでバリデーション失敗時にJsonResponseを返す - Qiita

FormRequestのバリデーションエラー時のリダイレクト先をカスタマイズする - Qiita

https://webxreal.com/laravel-validation/

blog.capilano-fw.com

【Laravel】特定のcookieの値の暗号化を解除(Laravel外からもcookieを使用したい)

課題

CookieファサードPHPのfilter_inputで取得できるcookieの値が異なる。 Laravel外からcookieを使用したいが、暗号化されているため使用できない。

<?php

$cookie_key_1 = Cookie::get('cookie_key');
$cookie_key_2 = filter_input(INPUT_COOKIE, 'cookie_key');

原因

\Illuminate\Cookie\Middleware\EncryptCookies このミドルウェアがあるため。

Laravel内でCookieファサードを使用してcookieを設定すると、暗号化された状態になっている。

Cookieファサードを使用してcookieを取得する時は、上記のミドルウェアにより自動で復号されるようになっている。

対策

暗号化されていると都合が悪いケースがあったため、特定のcookieは暗号化の対象外とする。

<?php
// app/Http/Middleware/EncryptCookies.php

class EncryptCookies extends Middleware
{
    protected $except = [
        'cookie_key'
    ];
}

これで、Cookieファサードを使用してcookieを設定、取得しても、暗号化されていない平文を取得できる。

参考

【Laravel5】特定のCookieだけ暗号化を解除する|Laravel|PHP|開発ブログ|株式会社Nextat(ネクスタット)

【Laravel×Apache】413 (payload too large) が出たらphp.iniを変更する

環境

課題

本番環境にて画像をアップロードしようとしたら、 413 Content Too Large のエラーが返ってくる。

解決方法

php.ini で、 upload_max_filesizepost_max_size を確認する。

まず、そもそもどこの php.ini を読み込んでいるかを確認するために、ブラウザで phpinfo() を書いたファイルを表示させる。

「Loaded Configuration File」 という項目が、 php.ini を読み込んでいるパスになっている。

/etc/php.ini の場合も別の場合もある。

そのパスの php.ini を、広げたい容量の最大値に修正する。

// php.ini
<?php

// 修正前
post_max_size = 8M
upload_max_filesize = 2M

// 修正後
post_max_size = 10M
upload_max_filesize = 8M

修正したら、Apacheを再起動。

$ sudo systemctl restart httpd

phpinfo() にて、 「Server API」の箇所がFPM/FastCGIとあればPHP-FPMを介してPHPを実行しているので、php-fpmの再起動も忘れずに。

$ sudo systemctl restart php-fpm

Apache 2.0 Handler みたいにApacheのモジュールであれば、Apacheの再起動のみで良いらしい。

注意点

PHPの公式ドキュメントに下記のようにあるので、一応考慮する。

upload_max_filesize int

アップロードされるファイルの最大サイズ。 post_max_size には、 この値より大きな値を指定しなければいけません。

https://www.php.net/manual/ja/ini.core.php#ini.upload-max-filesize

参考

www.suzu6.net

techtech-note.com

【Laravel】413 (payload too large) が出た時の対処法 - ポッポプログラミング

qiita.com

【Laravel】本番環境で画像アップロードできないときに確認する箇所

課題

本番環境にて、下記のコードでアップロードできなかった。

ブラウザのネットワークタブで確認すると、500エラーとだけ表示される。

<?php

Storage::putFileAs('public/images', $file, $file_name);

Laravelのログを確認

storage/logs/laravel.log にエラー内容が出力されているはず。

[2022-08-14 10:27:40] production.ERROR: fopen(/var/www/プロジェクト名/storage/app/public/images/20220814102740.jpg): Failed to open stream: Permission denied {"userId":1,"exception":"[object] (ErrorException(code: 0): fopen(/var/www/プロジェクト名/storage/app/public/images/20220814102740.jpg): Failed to open stream: Permission denied at /var/www/プロジェクト名/vendor/league/flysystem/src/Adapter/Local.php:157)

権限エラーのよう。

解決方法

権限を付与してあげる。

$ pwd
/var/www/プロジェクト名
$ chmod -R 777 storage/

これで無事アップロードできた。

参考

qiita.com

【Laravel】認証していない場合のリダイレクト先を変更する

app/Http/Middleware/Authenticate.php のリダイレクト先を名前付きルートに変更する。
Sanctumで認証しているが、これでいけた

<?php

    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            return route('login'); // <-ここを名前付きルートに変更
        }
    }

公式にも書いてある

Note: 独自の/login エンドポイントを自由に作成できます。ただし、標準のLaravelが提供するセッションベースの認証サービス を使用してユーザーを認証していることを確認する必要があります。通常、これはweb 認証ガードを使用することを意味します。

参考

readouble.com

readouble.com