20200701のPHPに関する記事は14件です。

PHPでMySQLのデータ操作その2

PHPでMySQLのデータ操作その2

PHPでMySQLを操作する方法を備忘録も兼ねてまとめました。
今回は検索編。
前回から引き続きまずこのようなデータがあるとします。

id name age position
選手id 選手名 選手の年齢 選手のポジション
-- futsal_teamというデータベース(がなければ)作成。IF NOT EXISTSで判断。
CREATE DATABASE IF NOT EXISTS futsal_team DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- GRANT文で権限をユーザーに付与、ユーザーはfutsal_team内の全てのテーブルを操作できる。
GRANT ALL PRIVILEGES ON *.* TO 'bs_user'@'localhost' IDENTIFIED BY 'futsal_team';
FLUSH PRIVILEGES;
-- データベースfutsal_team使用。
USE futsal_team;
-- テーブル作成後、カラム設定。
CREATE TABLE futsal_a (
id integer AUTO_INCREMENT NOT NULL,
name varchar(255) NOT NULL,
age integer NOT NULL,
position varchar(255) NOT NULL,
PRIMARY KEY (id)
);
-- カラムにそれぞれ値を入れてレコードを作成。idはシーケンス番号がauto_incrementオプションの効果で自動で追加される。
INSERT INTO futsal_a VALUES (null, '加藤A太郎', 25, 'ピヴォ');
INSERT INTO futsal_a VALUES (null, '佐藤B次郎', 27, 'アラ');
INSERT INTO futsal_a VALUES (null, '田中C三郎', 32, 'アラ');
INSERT INTO futsal_a VALUES (null, '山本D五郎', 38, 'フィクソ');
INSERT INTO futsal_a VALUES (null, '中島E十郎', 30, 'ゴレイロ);

データの検索

<?php require './header.php'?>
<form action="container_db_manipulate_search.php" method="post">
  <input type="text" name="search_word">
  <input type="submit" value="メンバー検索">
</form>
<?php
//今回は外部で$user, $passwordを設定してrequireで呼び出している。
require './ft_person.php';
//例外処理のためにtry carch文使用。
try {

  echo '<h1>','Aチームメンバー','</h1>';
  echo '<table>';
    echo '<tbody>';
      //データベースにアクセスするためにPDOクラスのインスタンスをnew演算子で作成する。
      $ft_team=new PDO('mysql:host=localhost;dbname=futsal_team;charset=utf8',
                  $user, $password);
      //もしも名前を検索して、つまり検索ボックスに入力があって条件が一致したら
      if(isset($_REQUEST['search_word'])){
        //prepareメソッドを使って、条件を設定。
        $search=$ft_team->prepare('SELECT * FROM futsal_a WHERE name LIKE ?');
        /*
        executeメソッドで、設定条件の?部分に値を当てはめて実行する。
        この場合は当てはめるものが検索ボックスに入力した名前、結果↓
        「SELECT * FROM futsal_a WHERE name LIKE 入力キーワード」
        ということになる。
        */
        $search->execute(array('%'.$_REQUEST['search_word'].'%'));
      }
      /*
      foreachとeachで「SELECT * FROM futsal_a WHERE name LIKE 入力キーワード」に当てはまるデータ一覧出力。
      検索ボタンだけ押すとデータが一覧表示される。
      */
      foreach ($search as $person) {
        echo '<tr>';
          echo '<td>','番号:',$person['id'],'、','</td>';
          echo '<td>','名前:',$person['name'],'、','</td>';
          echo '<td>','年齢:',$person['age'],'歳','、','</td>';
          echo '<td>','ポジション:',$person['position'],'</td>';
        echo '</tr>';
      }
    echo '<tbody>';
  echo '</table>';

} catch(PDOException $e){

  echo $e->getMessage();
  exit;

}
?>
<?php require './footer.php'?>

PDOというクラスをインスタンス化することで、PHPでデータベースに接続しSQL文で操作ができるようになります。
今回はPDOクラスのメソッドのprepareとexecuteを使用して検索機能を実現しています。
おそらくセットで覚えたほうがいいと思います。
prepareで条件を指定し、その条件の中で?に指定した値を、executeで当てはめて実行します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでMySQLのデータ操作その1

PHPでMySQLのデータ操作その1

PHPでMySQLを操作する方法を備忘録も兼ねてまとめました。
まずこのようなデータを作成、用意します。
架空のフットサルチームがあるとします。

id name age position
選手id 選手名 選手の年齢 選手のポジション
-- futsal_teamというデータベース(がなければ)作成。IF NOT EXISTSで判断。
CREATE DATABASE IF NOT EXISTS futsal_team DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- GRANT文で権限をユーザーに付与、ユーザーはfutsal_team内の全てのテーブルを操作できる。
GRANT ALL PRIVILEGES ON *.* TO 'bs_user'@'localhost' IDENTIFIED BY 'futsal_team';
FLUSH PRIVILEGES;
-- データベースfutsal_team使用。
USE futsal_team;
-- テーブル作成後、カラム設定。
CREATE TABLE futsal_a (
id integer AUTO_INCREMENT NOT NULL,
name varchar(255) NOT NULL,
age integer NOT NULL,
position varchar(255) NOT NULL,
PRIMARY KEY (id)
);
-- カラムにそれぞれ値を入れてレコードを作成。idはシーケンス番号がauto_incrementオプションの効果で自動で追加される。
INSERT INTO futsal_a VALUES (null, '加藤A太郎', 25, 'ピヴォ');
INSERT INTO futsal_a VALUES (null, '佐藤B次郎', 27, 'アラ');
INSERT INTO futsal_a VALUES (null, '田中C三郎', 32, 'アラ');
INSERT INTO futsal_a VALUES (null, '山本D五郎', 38, 'フィクソ');
INSERT INTO futsal_a VALUES (null, '中島E十郎', 30, 'ゴレイロ);

データの一覧表示

<?php require './header.php'?>
<?php
//今回は外部で$user, $passwordを設定してrequireで呼び出している。
require './ft_person.php';
//例外処理のためにtry carch文使用。
try {

  echo '<h1>','Aチームメンバー','</h1>';
  echo '<table>';
    echo '<tbody>';
    //データベースにアクセスするためにPDOクラスのインスタンスをnew演算子で作成する。
    $ft_team=new PDO('mysql:host=localhost;dbname=futsal_team;charset=utf8',
                $user, $password);
      /*
        PDOクラスのインスタンスからqueryセレクタを使用し、SQL文でfutsal_aの全データ取得。
        その後、foreach文とecho文を用いてデータを全て表示。
      */
      foreach ($ft_team->query('SELECT * FROM futsal_a') as $person) {
        echo '<tr>';
          echo '<td>','番号:',$person['id'],'、','</td>';
          echo '<td>','名前:',$person['name'],'、','</td>';
          echo '<td>','年齢:',$person['age'],'歳','、','</td>';
          echo '<td>','ポジション:',$person['position'],'</td>';
        echo '</tr>';
      }
    echo '</tbody>';
  echo '</table>';

} catch(PDOException $e){

  echo $e->getMessage();
  exit;

}
//結果
/*
番号:1、 名前:加藤A太郎、 年齢:25歳、 ポジション:ピヴォ
番号:2、 名前:佐藤B次郎、 年齢:27歳、 ポジション:アラ
番号:3、 名前:田中C三郎、 年齢:32歳、 ポジション:アラ
番号:4、 名前:山本D五郎、 年齢:38歳、 ポジション:フィクソ
番号:5、 名前:中島E十郎、 年齢:30歳、 ポジション:ゴレイロ
*/
?>
<?php require './footer.php'?>

PDOというクラスをインスタンス化することで、PHPでデータベースに接続しSQL文で操作ができるようになります。
さらには専用のメソッドを使用することで、検索機能など使えるようになります。
その2に続く

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フォルダ設計、XAMPを使ったドメイン(バーチャルホスト設定)

口コミサイトをPHPで作っていたら、「フォルダ設計」が必要らしいが、どこに作ればいいのかさっぱりわからなかった。ググったら、hogehoge.com、XAMPでオリジナルドメインを取得、DB接続というキーワードが出てきた。。。。わかったら随時更新予定。

参考記事
https://qiita.com/eryuus1/items/9300e6d56729799bae5d
https://manablog.org/mamp-vhosts/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP で yield のKeyの文字混在を解消したい

yield の Keyの全角半角混在が気持ち悪い.....。

混在するyieldサンプル
yield 'piyo:全角だぜ' => [['hoge' => Piyo::piyo];
yield 'fuga:半角だぜ' => [['hoge' => '12:34:56']];
yield 'ここにはコロン入らない前提:ガッツだぜ' => [['hoge' => 'awesome']];
yield 'パワフル:魂' => [['hoge' => 'awesome']];
  • Keyの中の区切り文字を、全角コロンに統一したい!

yield のKeyの特定文字(半角コロン)を特定文字(全角コロン)に置き換えだ

奥義!Ctrl + R ( in PHPStorm )
RK000310.JPG

BEFORE
(yield.+?):(.*?=>)
AFTER
$1:$2

結果

サンプルに噛ませた結果(たぶん)
yield 'piyo:全角だぜ' => [['hoge' => Piyo::piyo];
yield 'fuga:半角だぜ' => [['hoge' => '12:34:56']];
yield 'ここにはコロン入らない前提:ガッツだぜ' => [['hoge' => 'awesome']];
yield 'パワフル:魂' => [['hoge' => 'awesome']];

補足

  • 正規表現分からないので、考慮漏れがあったら教えてほしいです。
  • エディタ置換以外のやり方とかも教えてほしいです。( sed とかもっと分からない)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP PhalconでAuto Increment付与のMigrationが動かない対応

はじめに

PhalconにおいてMigrationで新テーブルをCreateする場合は普通に定義すればAuto Incrementは簡単につけられます。
本記事は、既存テーブルのKeyカラムにAuto IncrementをAlterで後から付けたかったが動かなかったので、その対応をしたという話です

最初は

普通に列変更でいけると思っていたので最初、up()に新、down()に旧をmorphTable()で書けばいいやとしてみました。

public function up()
{
    $this->morphTable('table_name', [
        'columns' => [
            new Column(
                'id',
                [
                    'type' => Column::TYPE_INTEGER,
                    'notNull' => true,
                    'autoIncrement' => true,
                    'size' => 11,
                    'first' => true
                ]
            ),

            /* 省略(その他のカラム情報) */
        ]
    ]);
}


public function down()
{
    $this->morphTable('table_name', [
        'columns' => [
            new Column(
                'id',
                [
                    'type' => Column::TYPE_INTEGER,
                    'notNull' => true,
                    'size' => 11,
                    'first' => true
                ]
            ),

ところが、これだとうまく動かない。
Migration自体の結果はsuccessとなるが、この例で言うidカラムのAlterを実行している形跡がないぞ、、と困ってしまいました

そこで

じゃあ明示的にSQL書いたりとかそういうのできないのかなと思って、ちょっと調べてみると、 self::$_connectionに、イケそうなMethodがあるではありませんか。
(そう言えばdropTableの時にいつも使っているやつだったな)
ということで

public function up()
{
    self::$_connection->modifyColumn('table_name', null, new Column(
        'id',
        [
            'type' => Column::TYPE_INTEGER,
            'notNull' => true,
            'autoIncrement' => true,
            'size' => 11,
            'first' => true
        ]
    ));
}

できたやんけー。という感じでした。
加えてmorphTable()で記述していた時は、カラムが多い時に見づらくてちょっと不満があったのですが、この書き方だと純粋に変更箇所だけの記載になるので、これはこれでいいかもな。とも思ったのでした

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel Logを出力してみる 簡易版

目的

  • Laravelでのログの出力方法のコントローラ記載部分のみを抜粋して記載する。
  • もう少し詳しく知りたい方はこちら→Laravel Logを出力してみる

実施環境

  • MacのローカルにLaravelの環境を構築して実施する。環境構築手順の記事はリンクを後述する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

詳細

  1. ログを出力したいコントローラのアクション内に下記の記載を行う。

    //use宣言として下記を追記
    use Illuminate\Support\Facades\Log;
    
    //コントローラのアクション内に下記を追記
    Log::debug('test');
    
  2. ブラウザから追記箇所が実行されるように操作を行う。

  3. アプリ名ディレクトリ/storage/logs/laravel.logに下記の様な一行が追記される。

    アプリ名ディレクトリ/storage/logs/laravel.log
    [YYYY-MM-DD HH:MM:SS] local.DEBUG: test 
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでintervention/imageを使う

intervention/imageをインストール

composerでインストールします。

composer require intervention/image

初期設定

インストールが完了したら、app\config\app.phpを編集します。

providersに以下を追記。

app.php
'providers' => [
...
Intervention\Image\ImageServiceProvider::class,
],

aliasesに以下を追記。

app.php
'aliases' => [
...
'Image' => Intervention\Image\Facades\Image::class,
],

これにより、use Image;でライブラリを読み込むことができます。
以上で初期設定は完了です。

写真の加工

試しに写真を反転するように加工してみましょう。

[加工する素材]

コントローラーを作成します。

php artisan make:controller PhotosController

作成したコントローラーを編集します。

PhotosController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Image; // intervention/imageライブラリの読み込み

class PhotosController extends Controller
{
    // 写真を読み込み加工する
    public function retouch()
    {
        // 読み込み
        $path = storage_path('app/images/rabbit.jpg');
        $img = \Image::make($path);

        $img->flip(); // 写真を反転させる

        //保存
        $save_path = storage_path("app/images/rabbit_flip.jpg");
        $img->save($save_path);
    }
}

実行後はこのようになります。

他にもモザイクをかけたり

 $img->pixelate(50);

グレースケールにしたりと、何かと高機能なライブラリです。

$img->greyscale();

[参考]
http://image.intervention.io/
https://blog.capilano-fw.com/?p=1574

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】時間取得

タイムスタンプから時効への返還とかtime()とかstrtotime()とかの役割をすぐ忘れるので、備忘録。

date(タイムスタンプ→日時)

日付の文字列を返してくれる。
タイムスタンプを文字列の日時に直してくれる。

現在時刻取得

print date('Y/m/d');//20xx/x/x/

タイムスタンプを文字列に変換する

$timeStamp=time();
print date('Y/m/d h:i:s',$timeStamp);

strtotime(日時→タイムスタンプ)

英文形式の日付を Unix タイムスタンプに変換する。
日付をタイムスタンプ

echo strtotime("now"), "\n";
echo strtotime("10 September 2000"), "\n";
echo strtotime("+1 day"), "\n";
echo strtotime("+1 week"), "\n";
echo strtotime("+1 week 2 days 4 hours 2 seconds"), "\n";
echo strtotime("next Thursday"), "\n";
echo strtotime("last Monday"), "\n";

time

現在のUnixタイムスタンプを返す。

print time();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php データベースからデータ読み込み

概要

phpでデータベースからデータを読み込みする記述

※個人学習用メモ・記録

sql.php
<?php

    //ここでsqlデータベースやユーザー名、ローカルホストもろもろ取得してるのかな。。
    $pdo = new PDO('mysql:host=localhost; dbname=mydb; charset=utf8','root','');

    //ここでsql文を叩く
    $sql = 'SELECT * FROM jobs'; 
    $statement = $pdo->prepare($sql);
    $statement->execute();    // => sqlを実行している

    //ここで配列にsqlを押し込んでる
    $results = [];
    while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
        $results[] = $row;
    }

    $statement = null;
    $pdo = null;

    $message = 'hello world';
    require_once 'views/content.tpl.php';

content.tpl.php
<!DOCTYPE html>
<html lang='ja'>
    <?php include('header.inc.php'); ?>
    <body>

        <h1><?= $message ?></h1>

        <?php foreach ($results as $player) { ?>
            <p><?php print_r($player); ?></p>
        <?php } ?>

        <?php include('footer.inc.php'); ?>
    </body>
</html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでMISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.が出た

発生したエラー

MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.

環境

Laravel 5.6
PHP 7.1
ローカル環境です。

何が起きたか

ローカルで開発中に、redisのエラーが発生したようです。
詳細な原因は不明で、決済処理か何かをローカルで実施して失敗後に発生した。

エラーハンドリングされたコード

/vendor/predis/predis/src/Client.php

    /**
     * Handles -ERR responses returned by Redis.
     *
     * @param CommandInterface       $command  Redis command that generated the error.
     * @param ErrorResponseInterface $response Instance of the error response.
     *
     * @throws ServerException
     *
     * @return mixed
     */
    protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
    {
        if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
            $eval = $this->createCommand('EVAL');
            $eval->setRawArguments($command->getEvalArguments());

            $response = $this->executeCommand($eval);

            if (!$response instanceof ResponseInterface) {
                $response = $command->parseResponse($response);
            }

            return $response;
        }

        if ($this->options->exceptions) {
            // ここのthrowの処理に入った
            throw new ServerException($response->getMessage());
        }

        return $response;
    }

対応

PC自体を再起動したらエラーが消えていました。

Laravelの再起動(php artisan serve をやり直した)では解決できませんでした。
なお、Laravelのtinkerでキャッシュクリアの方法はわからず試していません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP のバージョン確認

PHPのバージョン確認

php -v
php -version
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Auth0】LaravelでAuth0を使って認証する:IDトークン編【Laravel】

今回はSPAを作成する際に使用するJWT形式のIDトークンを検証をする方法について記事にしました。

関連記事

Validating JWTs with Auth0-PHP
【Auth0】LaravelでAuth0を使って認証する:アクセストークン編【Laravel】

環境

PHP: 7.2.5
laravel: 7.0
auth0/auth0-php: 7.2

Auth0の設定

まずはAuth0にフリートライアルで登録して、APIを作成しましょう。
そうしたらAPIsタブを開き、CREATE APIから新規Auth0 APIを作成します。

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_391127_ba0dde3b-fa26-f2ec-74b0-a3bbf289a671.png

API名と識別子は先例に習ってQuickstarts APIhttps://quickstarts/apiにします。またアルゴリズムは特に理由がなければRS256にしましょう。
RS256を使用することでJWKsを使用したトークン検証を行えるようになります。

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_391127_cf346442-fb1a-73cb-fbcd-1090f8908c5b.png

ものすごく丁寧なことに今作成したアプリケーションにアクセスするためのクイックスタートを記載したページに飛ばしてくれます。

ですが今は一旦無視してPermissionsタブを開きましょう。
ここではアクセストークンを使用してAuth0からどの情報まで取得できるかの権限を設定することができます。
とりあえず今はread:messages, read:email, read:usersあたりを設定しておきましょう。

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_391127_ecf2609b-fde8-2eff-0ad4-f67dc84fa7bd.png

SPA APPの作成

今回はID Tokenでの認証を行うので、Single Page Appliation(SPA)用のAuth0 Appを作成します。

Applicationsタブの右上のCREATE APPLICATIONを選択します。

スクリーンショット 2020-06-21 11.44.59.png

好きな名前を設定し、Single Page Web Applicationsを選択して新規にAppを作成します。

スクリーンショット 2020-06-21 11.45.28.png

ライブラリの導入と設定

ID Tokenの検証にはAuth0のPHP用のライブラリを使用します。

$ composer require auth0/auth0-php

auth0/loginというライブラリがありますが、間違えないように注意しましょう。

次に.envにAuth0との接続情報を記入します。

.env
AUTH0_DOMAIN=tenant.auth0.com
AUTH0_CLIENT_ID=hogehogeClientId
AUTH0_CLIENT_SECRET=hogehogeClientSecret

Applicationsから先ほど作成したSPAアプリを選択し、そのDomainClient IDClient Secretを使用します。

スクリーンショット 2020-06-22 20.42.26.png

また定数クラスを作成しておきます。

config/const.php
return [
    'auth0' => [
        'domain' => env('AUTH0_DOMAIN', ''),
        'client_id' => env('AUTH0_CLIENT_ID', ''),
        'client_secret' => env('AUTH0_CLIENT_SECRET', ''),
    ]
];

Laravelでの実装

まずはID Tokenを検証するためのミドルウェアを作成します。

$ php artisan make:middleware CheckIdToken
App\Http\Middleware\CheckIdToken.php
namespace App\Http\Middleware;

use Auth0\SDK\Helpers\JWKFetcher;
use Auth0\SDK\Helpers\Tokens\AsymmetricVerifier;
use Auth0\SDK\Helpers\Tokens\IdTokenVerifier;
use Auth0\SDK\Helpers\Tokens\SymmetricVerifier;
use Closure;

class CheckIdToken
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // リクエストヘッダにBearerトークンが存在するか確認
        if (empty($request->bearerToken())) {
            return response()->json(["message" => "Token dose not exist"], 401);
        }

        $id_token = $request->bearerToken();

        // JWTのヘッダー部分を取得し、デコードしてalgを取り出す
        $id_token_header = explode('.', $id_token)[0];

        try {
            $token_alg = json_decode(base64_decode($id_token_header))->alg;
        } catch (\Exception $e) {
            return response()->json(["message" => $e->getMessage()], 401);
        }

        $token_issuer = 'https://' . config('const.auth0.domain') . '/';

        $signature_verifier = null;

        // id tokenを検証するためのVerifierクラスを呼び出す
        // RS256のみで検証したい場合はHS256の分岐を削除する
        if ('RS256' === $token_alg) {
            // 指定したissuerからjwksを取得し、証明書(CERTIFICATE)で取得する
            $jwks_fetcher = new JWKFetcher();
            $jwks = $jwks_fetcher->getKeys($token_issuer.'.well-known/jwks.json');
            $signature_verifier = new AsymmetricVerifier($jwks);
        } else if ('HS256' === $token_alg) {
            $signature_verifier = new SymmetricVerifier(config('const.auth0.client_secret'));
        } else {
            return response()->json(["message" => "Invalid alg"]);
        }

        $token_verifier = new IdTokenVerifier(
            $token_issuer,
            config('const.auth0.client_id'),
            $signature_verifier
        );

        // トークンを検証する
        try {
            $decoded_token = $token_verifier->verify($id_token);
        } catch (\Exception $e) {
            return response()->json(["message" => $e->getMessage()], 401);
        }

        // user_idを$requestに追加する。
        $request->merge([
            'auth0_user_id' => $decoded_token['sub']
        ]);

        return $next($request);
    }
}

作成したミドルウェアをjwtという名前でカーネル登録します。

Kernel.php
class Kernel extends HttpKernel {
    // ...
    protected $routeMiddleware = [
        // ...
        'jwt' => \App\Http\Middleware\CheckIdToken::class,
        // ...
    ];
    // ...
}

最後にエンドポイントにミドルウェアを設定します。

routes/api.php
Route::middleware('jwt')->get('/private', function (Request $request) {
    return response()->json([
        "autho_user_id" => $request['auth0_user_id'],
        "message" => "プライベートなエンドポイントへようこそ!これを表示するには有効なIDトークンが必要です。"
    ]);
});

ID Tokenを発行する

Auth0にユーザを登録する

まずは作成したAuth0のアプリケーションにSSO(シングルサインオン)でアクセスしてユーザをAuth0に登録しましょう。

今回はGoogleのSSOを使用します。

ConnectionsSocialタブからデフォルトで有効化しているGoogleを選択し、トグルスイッチの上にあるTRYをクリックします。

スクリーンショット 2020-06-20 23.52.24.png

もしくはGoogleと表示されている部分をクリックして設定を展開し、一番下にあるTRYをクリックします。

スクリーンショット 2020-06-20 23.52.43.png

するとGoogleのログイン画面が表示され、ログインが成功するとAuth0にリダイレクトされ、ログインしたユーザの情報が表示されます。

スクリーンショット 2020-06-20 23.53.01.png

この状態でUsers & RolesからUsersタブを開くと先ほどログインしたユーザが追加されているのがわかります。(画像はほとんど黒塗りでわかりにくいかもしれません)

スクリーンショット 2020-06-20 23.53.15.png

Authentication API Debuggerの登録

今回はID Tokenを取得するのにAuth0 Authentication API Debuggerを使用します。

ExtensionsタブからAuth0 Authentication API Debuggerを検索し、有効化します。

スクリーンショット 2020-06-20 23.53.48.png

スクリーンショット 2020-06-20 23.53.54.png

そうしたらCallback URLの値を最初に作成したLaravel-SampleアプリケーションのAllowed Callback URLsに登録します。

スクリーンショット 2020-06-28 17.09.08.png

スクリーンショット 2020-06-20 23.56.02.png

入力したらページの最下部にあるSAVE CHANGESを押して変更を保存します。

ID Tokenを取得する

Authentication API DebuggerLoginConfiguration内のApplicationにID Tokenを取得したいアプリケーションを指定します。

スクリーンショット 2020-06-28 17.19.24.png

次にOAuth2/OIDCタブの下の方にあるSettingResponse TypeNoncetoken id_tokenを設定します。

スクリーンショット 2020-06-20 23.54.45.png

最後にUser FlowsOAUTH2 / OIDC LOGINと書いてある水色のボタンを押すと画面が遷移し、ID Tokenを取得できます。

スクリーンショット 2020-06-20 23.55.00.png

今回はtoken id_tokenを指定したのでアクセストークンとIDトークンの両方を取得しています。

またID TokenのブロックではIDトークン(JWT)をデコードした中身をみることができます。

スクリーンショット 2020-06-28 17.33.14.png

リクエストを送る

IDトークンを取得できたのでサーバーを起動してリクエストを送ってみます。

$ php artisan serve --port=3010

先ほど取得したIDトークンをAuthorizationヘッダーにセットしてhttp://localhost:3010/api/privateにリクエストしましょう。

{
  "autho_user_id": "google-oauth2|1234567890",
  "message": "プライベートなエンドポイントへようこそ!これを表示するには有効なアクセストークンが必要です。"
}

jwksをキャッシュする

この実装だとリクエストのたびにjwksを取得しに行ってしまいます。
なのでキャッシュする実装に変更しましょう。

Auth0のQ&Aを見ると、jwksはトークンの検証に失敗した際に再取得しにいくのが良いようです。

※Redisや関連ライブラリの導入方法に関しての説明は省きます。

App\Http\Middleware\CheckIdToken.php
class CheckIdToken
{
    public function handle($request, Closure $next)
    {
        //...中略...

        $signature_verifier = null;

        if ('RS256' === $token_alg) {
            // キャッシュに保存する
            $jwks = Cache::remember('auht0_jwks_key', 43200, function () use ($token_issuer) {
                $jwks_fetcher = new JWKFetcher();
                return $jwks_fetcher->getKeys($token_issuer.'.well-known/jwks.json');
            });
            $signature_verifier = new AsymmetricVerifier($jwks);
        } else if ('HS256' === $token_alg) {
            $signature_verifier = new SymmetricVerifier(config('const.auth0.client_secret'));
        } else {
            return response()->json(["message" => "Invalid alg"]);
        }

        $token_verifier = new IdTokenVerifier(
            $token_issuer,
            config('const.auth0.client_id'),
            $signature_verifier
        );

        // トークンを検証する
        try {
            $decoded_token = $token_verifier->verify($id_token);
        } catch (\Exception $e) {
            logger()->info('id_tokenの初回検証に失敗しました。 Caught: Exception - '.$e->getMessage());

            // 検証に失敗したら一度だけjwksを最新のものに更新し、再度検証する
            // 実際に実装する際はprivate functionなどに抜き出して共通化すると良いでしょう
            $jwks_fetcher = new JWKFetcher();
            $new_jwks = $jwks_fetcher->getKeys($token_issuer.'.well-known/jwks.json');
            Cache::put('auht0_jwks_key', $new_jwks, 43200)

            $new_signature_verifier = new AsymmetricVerifier($new_jwks);

            $new_token_verifier = new IdTokenVerifier(
                $token_issuer,
                config('const.auth0.client_id'),
                $new_signature_verifier
            );

            try {
                $decoded_token = $new_token_verifier->verify($id_token);
            } catch (\Exception $e) {
                logger()->warning('id_tokenの2回目の検証に失敗しました。');
                return response()->json(["message" => $e->getMessage()], 401);
            };
        }

        // user_idを$requestに追加する。
        $request->merge([
            'auth0_user_id' => $decoded_token['sub']
        ]);

        return $next($request);
    }
}

本当はjwks内に一致するkidが存在しない場合のみ更新するようにしたかったのですが、トークンの検証失敗時には全てInvalidTokenExceptionという型が投げられ判別できなかったので、このような形になっています。

参考資料

自分のメモ代わりとして、参考資料へのリンクは多めに貼ってあります。

【Auth0】LaravelでAuth0を使って認証する:アクセストークン編【Laravel】
SPA + API: Solution Overview
Validating JWTs with Auth0-PHP
Auth0 Quickstarts - PHP
Auth0-PHP
Auth0 Authentication API Debugger
Caching JWKS signing key
Validate JSON Web Tokens

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel ログを出力してみる

目的

  • Laravelでのログの出力方法をまとめる

実施環境

  • MacのローカルにLaravelの環境を構築して実施する。環境構築手順の記事はリンクを後述する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

前提情報

  • 本記事ではログの出力までの方法を最短で記載する、ログの機能の詳細の説明は実施しない。
  • 本来アプリ作成時に必要であるDBの作成や.envファイルによるDBの登録などの手順は本記事では説明しない。

読後感

  • ブラウザのページを表示する処理をトリガーにしてstorage/logs/laravel.logファイルに「[YYYY-MM-DD HH:MM:SS] local.DEBUG: test」と言う一行をログとして出力できる。

筆者の勘違い(ほぼ関係ないので飛ばしてください)

  • ログは.envファイルで設定さえすれば出力される物だと思っていた。
  • 実際にはログを出力したい場面のコントローラのアクション内でLogファサードを用いて命令が必須である。

概要

  1. アプリ作成
  2. ルーティングの記載
  3. UserHomeController作成と記載
  4. 確認

詳細

  1. アプリ作成

    1. アプリを作成する任意のディレクトリに移動して下記コマンドを実行して「test」と言う名前のアプリを作成する。

      $ laravel new test
      
  2. ルーティングの記載

    1. testディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ routes/web.php
      
    2. 開いたルーティングファイルに下記の一行を記載する。

      routes/web.php
      Route::get('/user_home', 'UserHomeController@index');
      
    3. 記載後のルーティングファイルの内容を下記に記載する。

      routes/web.php
      <?php
      
      use Illuminate\Support\Facades\Route;
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      Route::get('/user_home', 'UserHomeController@index');
      
    4. 保存して閉じる。

  3. UserHomeController作成と記載

    1. testディレクトリで下記コマンドを実行してUserHomeController.phpを作成する。

      $ php artisan make:controller UserHomeController
      
    2. 下記コマンドを実行して先のコマンドで作成したUserHomeController.phpを開く。

      $ vi app/Http/Controllers/UserHomeController.php
      
    3. 開いたUserHomeController.phpを下記の様に修正する。

      test/app/Http/Controllers/UserHomeController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      //下記を追記する、下記の宣言がないとエラーになる
      use Illuminate\Support\Facades\Log;
      
      class UserHomeController extends Controller
      {
          //下記を追記する
          public function index()
          {
              //Log::debug('ログとして出力したい文字列')の様に記載する
              Log::debug('test');
              return redirect('/');
          }
          //上記までを追記する。
      }
      
    4. 保存して閉じる。

  4. 確認

    1. testディレクトリで下記コマンドを実行してまだlogファイルが存在していないことを確認する。「No such file or directory」が出力されればlaravel.logファイルは存在しない。

      $ ls storage/logs/laravel.log
      >ls: storage/logs/laravel.log: No such file or directory
      
    2. testディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. ブラウザからtestアプリにアクセスする。デフォルトだとhttp://127.0.0.1:8000/にアクセスするとブラウザでtestアプリが表示できる。

    4. 下記画面が表示されていることを確認する。

      Laravel.png

    5. http://127.0.0.1:8000/user_homeにアクセスしてみる。(アクセスしても何も起こらず、先の画像と同じLaravelのトップページが表示されると思うがその動作は期待する動作である。)

    6. testディレクトリで下記コマンドを実行してログファイルを開く。

      $ less storage/logs/laravel.log
      
    7. 下記の一行が記載されていることを確認する。(YYYY-MM-DD HH:MM:SSはみなさんがhttp://127.0.0.1:8000/user_homeにアクセスした時間が入る。)

      test/storage/logs/laravel.log
      [YYYY-MM-DD HH:MM:SS] local.DEBUG: test 
      
    8. 書き込まれていた場合ログの出力が正常に行われたと言うこととなる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel Logを出力してみる

目的

  • Laravelでのログの出力方法をまとめる

実施環境

  • MacのローカルにLaravelの環境を構築して実施する。環境構築手順の記事はリンクを後述する。
  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.3 Homwbrewを用いて導入
Laravel バージョン 7.0.8 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

前提情報

  • 本記事ではログの出力までの方法を最短で記載する、ログの機能の詳細の説明は実施しない。
  • 本来アプリ作成時に必要であるDBの作成や.envファイルによるDBの登録などの手順は本記事では説明しない。

読後感

  • ブラウザのページを表示する処理をトリガーにしてstorage/logs/laravel.logファイルに「[YYYY-MM-DD HH:MM:SS] local.DEBUG: test」と言う一行をログとして出力できる。

筆者の勘違い(ほぼ関係ないので飛ばしてください)

  • ログは.envファイルで設定さえすれば出力される物だと思っていた。
  • 実際にはログを出力したい場面のコントローラのアクション内でLogファサードを用いて命令が必須である。

概要

  1. アプリ作成
  2. ルーティングの記載
  3. UserHomeController作成と記載
  4. 確認

詳細

  1. アプリ作成

    1. アプリを作成する任意のディレクトリに移動して下記コマンドを実行して「test」と言う名前のアプリを作成する。

      $ laravel new test
      
  2. ルーティングの記載

    1. testディレクトリで下記コマンドを実行してルーティングファイルを開く。

      $ routes/web.php
      
    2. 開いたルーティングファイルに下記の一行を記載する。

      routes/web.php
      Route::get('/user_home', 'UserHomeController@index');
      
    3. 記載後のルーティングファイルの内容を下記に記載する。

      routes/web.php
      <?php
      
      use Illuminate\Support\Facades\Route;
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      Route::get('/user_home', 'UserHomeController@index');
      
    4. 保存して閉じる。

  3. UserHomeController作成と記載

    1. testディレクトリで下記コマンドを実行してUserHomeController.phpを作成する。

      $ php artisan make:controller UserHomeController
      
    2. 下記コマンドを実行して先のコマンドで作成したUserHomeController.phpを開く。

      $ vi app/Http/Controllers/UserHomeController.php
      
    3. 開いたUserHomeController.phpを下記の様に修正する。

      test/app/Http/Controllers/UserHomeController.php
      <?php
      
      namespace App\Http\Controllers;
      
      use Illuminate\Http\Request;
      //下記を追記する、下記の宣言がないとエラーになる
      use Illuminate\Support\Facades\Log;
      
      class UserHomeController extends Controller
      {
          //下記を追記する
          public function index()
          {
              //Log::debug('ログとして出力したい文字列')の様に記載する
              Log::debug('test');
              return redirect('/');
          }
          //上記までを追記する。
      }
      
    4. 保存して閉じる。

  4. 確認

    1. testディレクトリで下記コマンドを実行してまだlogファイルが存在していないことを確認する。「No such file or directory」が出力されればlaravel.logファイルは存在しない。

      $ ls storage/logs/laravel.log
      >ls: storage/logs/laravel.log: No such file or directory
      
    2. testディレクトリで下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. ブラウザからtestアプリにアクセスする。デフォルトだとhttp://127.0.0.1:8000/にアクセスするとブラウザでtestアプリが表示できる。

    4. 下記画面が表示されていることを確認する。

      Laravel.png

    5. http://127.0.0.1:8000/user_homeにアクセスしてみる。(アクセスしても何も起こらず、先の画像と同じLaravelのトップページが表示されると思うがその動作は期待する動作である。)

    6. testディレクトリで下記コマンドを実行してログファイルを開く。

      $ less storage/logs/laravel.log
      
    7. 下記の一行が記載されていることを確認する。(YYYY-MM-DD HH:MM:SSはみなさんがhttp://127.0.0.1:8000/user_homeにアクセスした時間が入る。)

      test/storage/logs/laravel.log
      [YYYY-MM-DD HH:MM:SS] local.DEBUG: test 
      
    8. 書き込まれていた場合ログの出力が正常に行われたと言うこととなる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む