20200624のPHPに関する記事は11件です。

MAMPを使用して、Zend Framework 1系の環境構築をする

Zend Framework 1系の環境構築

前提

  • MAMPがインストールされていること
    • インストールが済んでない方はこちらから
  • Zend Frameworkのバージョンは1.12.3とする

手順

Zend Frameworkをダウンロードする

$ wget https://packages.zendframework.com/releases/ZendFramework-1.12.3/ZendFramework-1.12.3.tar.gz

※ 下記のようなエラーが出た場合はwgetコマンドをインストールする → こちらを参考にする

command not found: wget

ダウンロードしたディレクトリを展開する

tar zxvf ZendFramework-1.12.3.tar.gz

tarコマンドのについてはこちらを参照

展開しディレクトリをMAMPに配置する

$ mkdir /Applications/MAMP/Library/zend-framework
$ cp -r ~/ZendFramework-1.12.3 /Applications/MAMP/Library/zend-framework

※ /Applications/MAMP/Library/zend-framework配下に展開したディレクトリが配置される

エイリアスを作成する

shellの設定ファイルを開く

$ vim ~/.zshrc

設定ファイルに下記を追記する

alias zf='/Applications/MAMP/Library/zend-framework/ZendFramework-1.12.3/bin/zf.sh'

※ bashを使用している場合は上記をbash_profileに追記する

設定を反映する

$ source ~/.zshrc

正常に動作するか確認する

$ zf show version

下記のようにバージョンが表示されれば、正常に動作している

Zend Framework Version: 1.12.3

MAMPのhtdocs配下にプロジェクトを作成する

プロジェクトを作成する

$ cd /Applications/MAMP/htdocs
$ zf create project プロジェクト名

Zend Frameworkの機能をプロジェクトで利用できるようにする

$ cp -r cp -r /Applications/MAMP/Library/zend-framework/ZendFramework-1.12.3/library/Zend /Applications/MAMP/htdocs/プロジェクト名/library

トップページへアクセスできることを確認する

MAMPを起動し、http://localhost/プロジェクト名/public/ にアクセスしトップページが表示されればOK

トップページ.png

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

LaravelでのファイルアップロードAPIとUnitTest

  • Windows 10
  • PHP 7.4.5
  • Laravel Framework 7.9.2

アップロードAPI

app/Http/Controllers/ImagesController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class ImagesController extends Controller
{
    public function upload(Request $request)
    {
        try {
            $path = $request->file('file')->store('images');
            return response()->json([
                'path' => $path
            ]);
        } catch (Exception $e) {
            return response($e->getMessage(), 500);
        }
    }
}

テストコード

tests/Feature/ImageFeatureTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class ImageFeatureTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @test
     */
    public function upload_success()
    {
        // Data
        $file = UploadedFile::fake()->image('dummy.jpg', 800, 800);

        // Test
        Storage::fake('local');
        $response = $this->json('POST', '/image/upload', [
            'file' => $file
        ]);
        $response->assertOk()->assertJsonStructure(['path']);
        Storage::assertExists($response->decodeResponseJson('path'));
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPMVCのM⇆Vについて(見直し用メモ)

前提知識

namespace…このファイルここに配置されてますよ〜って書いてるやつ
use…他フォルダにあるクラスを利用可能にしたもの

ルーティング

・アドレスに対してコントローラーメソッドを指示する。
・$requestのプロパティの情報を飛ばす

ルート情報記述.php
Route::get(アドレス,関数など);
viewとは.php
view(テンプレート名)
//指定したテンプレートのファイルをロードし、レンタリング(表示できるように整える)して返す

特定のアドレスにアクセスした際、何の処理を実行するのかを記述したもの。

web.php
<?php
Route::get(`/`,function(){
    return view(`welcome`);
});

解説

/にアクセスしたらwelcomeのファイルを戻せ(表示しろ)

ルートパラメータの利用

ルートパラメータの利用.php
Route::get(`/〇〇/{パラメータ}',function($〇〇//パラメータの値を受け取る引数){
});
ルートパラメータの利用(例文).php
Route::get(`/hello/{msg}',function($msg){
});
///hello/sampleにアクセスした場合、msgにsampleが代入される。

解説

アドレスのパラメータをルーティング処理の一部として使いたい

必須パラメータと任意パラメータ

ルートパラメータを利用する際、パラメータを記入されないと処理が実行されないようになっている。
これを必須パラメータと呼ぶ。
しかし、パラメータの変数の最後に「?」をつけることによりパラメータを記入せずとも処理が実行される任意パラメータに変更することができる。

任意パラメータの利用.php
Route::get(`hello/{msg?}`,function ($msg=`〇〇`)
//〇〇には、パラメータが記入されなかった時に出力される文字列を入れる

解説

パラメータ指定されなくても使われたい

コントローラ

・ビュー
 ∟表示させるものを(return view('ファイル名','配列'))で書く
 ∟表示させるものに必要なデータを飛ばす
・データベース

web.phpに記述するやつ.php
Route::get('アドレス','コントローラー名@メソッド名');

ビュー

実際web上に表示させるもの。

Blade構文

PHPを用いて見栄えの良いビューを作れるようになるビューテンプレート

値の表示

値を埋め込む.html
{{値・変数・式・関数など}}
値を埋め込む(HTMLを機能させたい場合).html
{{!!値・変数・式・関数など!!}}

継承

既存のテンプレート(親)再利用して新しいテンプレート(子)を作成する。
親テンプレートにはsectionで子テンプレートに表示する区画を設定する。
子テンプレート側にはsectionを表示されるyieldを用いる。

実際にレイアウト表示されるのは子レイアウトである。

継承方法.php
@extends('フォルダ名.ファイル名')
sectionの使い方.php
@section(名前)
-表示内容-
@endsection
yieldの使い方.php
@yield(名前)

ビューコンポーザ

ビューをレンタリングする時に自動的に実行される処理
これを使えるようにするため、サービス(Laravelの機能強化の仕組み)を動かす必要がありそれを動かすために、サービスプロバイダーを使う必要がある。

サービスプロバイダー

用意されているメソッド
boot
アプリケーションが起動する際に割り込んで実行される処理(ブートストラップ処理)
ここにビューと処理を用意することで、コンポーザが呼び出されるようになる。
(実質、コントローラみたいな役割をはたす)

register
会員登録の処理など…

boot利用例文.php
public function boot()
    {
        View::composer(
            'hello.index', function($view){
                $view->with('view_message','composer message!');
            }
        );
新出①.php
View::composer(ビューの指定,関数またはクラス)

第1引数には、コンポーザーを利用するビューの設定。
第2引数には、実行する処理を書く

新出②.php
$view->with(変数名,);
新出③.php
$view(Viewクラスのインスタンス)

サービスプロバイダへの登録
サービスプロバイダを作るだけでは作動しない。
app\confungにサービスプロパイダが使えるように登録する必要がある

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

PHPMVCのC⇆Vについて(見直し用メモ)

前提知識

namespace…このファイルここに配置されてますよ〜って書いてるやつ
use…他フォルダにあるクラスを利用可能にしたもの

ルーティング

・アドレスに対してコントローラーメソッドを指示する。
・$requestのプロパティの情報を飛ばす

ルート情報記述.php
Route::get(アドレス,関数など);
viewとは.php
view(テンプレート名)
//指定したテンプレートのファイルをロードし、レンタリング(表示できるように整える)して返す

特定のアドレスにアクセスした際、何の処理を実行するのかを記述したもの。

web.php
<?php
Route::get(`/`,function(){
    return view(`welcome`);
});

解説

/にアクセスしたらwelcomeのファイルを戻せ(表示しろ)

ルートパラメータの利用

ルートパラメータの利用.php
Route::get(`/〇〇/{パラメータ}',function($〇〇//パラメータの値を受け取る引数){
});
ルートパラメータの利用(例文).php
Route::get(`/hello/{msg}',function($msg){
});
///hello/sampleにアクセスした場合、msgにsampleが代入される。

解説

アドレスのパラメータをルーティング処理の一部として使いたい

必須パラメータと任意パラメータ

ルートパラメータを利用する際、パラメータを記入されないと処理が実行されないようになっている。
これを必須パラメータと呼ぶ。
しかし、パラメータの変数の最後に「?」をつけることによりパラメータを記入せずとも処理が実行される任意パラメータに変更することができる。

任意パラメータの利用.php
Route::get(`hello/{msg?}`,function ($msg=`〇〇`)
//〇〇には、パラメータが記入されなかった時に出力される文字列を入れる

解説

パラメータ指定されなくても使われたい

コントローラ

・ビュー
 ∟表示させるものを(return view('ファイル名','配列'))で書く
 ∟表示させるものに必要なデータを飛ばす
・データベース

web.phpに記述するやつ.php
Route::get('アドレス','コントローラー名@メソッド名');

ビュー

実際web上に表示させるもの。

Blade構文

PHPを用いて見栄えの良いビューを作れるようになるビューテンプレート

値の表示

値を埋め込む.html
{{値・変数・式・関数など}}
値を埋め込む(HTMLを機能させたい場合).html
{{!!値・変数・式・関数など!!}}

継承

既存のテンプレート(親)再利用して新しいテンプレート(子)を作成する。
親テンプレートにはsectionで子テンプレートに表示する区画を設定する。
子テンプレート側にはsectionを表示されるyieldを用いる。

実際にレイアウト表示されるのは子レイアウトである。

継承方法.php
@extends('フォルダ名.ファイル名')
sectionの使い方.php
@section(名前)
-表示内容-
@endsection
yieldの使い方.php
@yield(名前)

ビューコンポーザ

ビューをレンタリングする時に自動的に実行される処理
これを使えるようにするため、サービス(Laravelの機能強化の仕組み)を動かす必要がありそれを動かすために、サービスプロバイダーを使う必要がある。

サービスプロバイダー

用意されているメソッド
boot
アプリケーションが起動する際に割り込んで実行される処理(ブートストラップ処理)
ここにビューと処理を用意することで、コンポーザが呼び出されるようになる。
(実質、コントローラみたいな役割をはたす)

register
会員登録の処理など…

boot利用例文.php
public function boot()
    {
        View::composer(
            'hello.index', function($view){
                $view->with('view_message','composer message!');
            }
        );
新出①.php
View::composer(ビューの指定,関数またはクラス)

第1引数には、コンポーザーを利用するビューの設定。
第2引数には、実行する処理を書く

新出②.php
$view->with(変数名,);
新出③.php
$view(Viewクラスのインスタンス)

サービスプロバイダへの登録
サービスプロバイダを作るだけでは作動しない。
app\confungにサービスプロパイダが使えるように登録する必要がある

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

Laravelでレートリミット制限をかけて制限時の429エラーレスポンスをjsonで返す

Dos攻撃対策のレート制限を実装

apiの外部公開をするにあたり、Dos攻撃対策としてレートリミット機能を実装しました。

venderにあるライブラリ標準機能では、アクセス制限がかかった際は429エラーページの表示がされるようになっています。

それを次のように変更しました。

・jsonでエラーメッセージを返すようにする
・レートリミット制限の設定を環境変数から制御する

今回行った実装内容を簡単にご紹介します。

参考リンク

公式のレートリミットの解説
https://readouble.com/laravel/6.x/ja/routing.html

ルーティングの設定についての解説(今回は触りませんでした)
https://laravel.com/docs/7.x/routing#rate-limiting

分かりやすく解説されているブログの記事一覧
https://blog.pinkumohikan.com/entry/laravel-rate-limit-middleware
https://access-jp.co.jp/blogs/development/203
https://www.suzu6.net/posts/170-laravel-throttle/

middlewareにレートリミット用のファイルを作成する

app/Http/Middleware/RateLimitting/ThrottleRequests.php

middlewareにディレクトリを作り、その中にファイルを作成します

最初はvenderディレクトリから継承したThrottleRequestsExceptionを改良してレスポンスを返せないか試行錯誤していたのですが、どうも上手くいかず、、

方法を考え参考になりそうな記事を探しまくっていた所、stackoverflowにちょうどドンピシャな内容がありました。


こちらの一番最後にあるソースコードを元に、一部変更をして実装します。
https://stackoverflow.com/questions/40246741/laravel-rate-limit-to-return-a-json-payload

Create a new file ApiThrottleRequests.php in app/Http/Middleware/ and paste the code below:

変更した部分の解説を記載します。

ヘルパー関数のconfig()を使って環境変数の値からレートリミットを設定する

$maxAttempts = (int) config('app.maxAttempts');

Note:
環境変数の呼び出しでenvではなくconfigを使ったのは、 本番環境でconfig:cacheコマンドを実行すると、その後env()はnullしか返さないからです。

https://qiita.com/kawax/items/deed7e7c7c26085da01f

↓ 公式でも言及されています

https://readouble.com/laravel/6.x/ja/configuration.html

レスポンスをjsonに変更

    /**
     * Create a 'too many attempts' response.
     *
     * @param  string $key
     * @param  int $maxAttempts
     * @return \Illuminate\Http\Response
     */
    protected function buildResponse($key, $maxAttempts)
    {
        $errorResponse = json_encode([
                'status_code' => '429',
                'error' => 'Too Many Requests',
                'error_description' => 'アクセス数が上限に達しました。',
        ]);

        $response = new Response($errorResponse, 429);

        $retryAfter = $this->limiter->availableIn($key);

        return $this->addHeaders(
            $response, $maxAttempts,
            $this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
            $retryAfter
        );
    }

引数の値を変えて、~分間の制限に指定(ここでは1分としています)

// $this->limiter->hit($key, $decayMinutes);
$this->limiter->hit($key, 60);

kernel.phpにパスを指定する

kernel.phpに追加したファイルのパスを指定します

※環境変数からレートリミットを制御させたいので、数値指定を削除しました

'api' => [
// 'throttle:60,1', 環境変数から制御する為に元々あった数値設定を削除
'throttle',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \App\Http\Middleware\RateLimitting\ThrottleRequests::class, //ミドルウェアのファイルパスを指定
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

app.phpに環境変数を設定

config\app.php

環境変数の設定数値を読み込むようにします。
※説明文はオリジナルです

    /*
    |--------------------------------------------------------------------------
    | Rate Limit Access
    |--------------------------------------------------------------------------
    | Includes a middleware to rate limit access to your api.
    | The throttle middleware accepts parameters that determine the maximum
    | number of requests that can be made in one minutes. 
    | Thanks you for reading my 4th article on Qiita.
    */

    'maxAttempts' => env('MAX_ATTEMPTS_PER_MINUTE', '100'),

環境変数にアクセス上限数を設定する

.envに設定値を記載します

#レートリミット制限値の設定:設定した数値のアクセス数を超えると制限がかかる
MAX_ATTEMPTS_PER_MINUTE=100

テストしてみる

postmanを使ってレートリミット制限がかかっているかどうか、設定したエラー内容がjsonで返ってくるか、確かめてみます。

実際の構成では、AWSのALB(Application Load Balancer)を経由して接続させています。X-Forwarded-ForにIPアドレスが格納され、この値ごとに制限がかかるようにしています。(※本記事では関連するコードの明記を省略しています)

スクリーンショット 2020-06-24 12.07.45.png

制限(X-RateLimit-Limit)を5回にしてテストしています。一番下のX-RateLimit-Remainingが残りの試行回数です。

スクリーンショット 2020-06-24 12.08.26.png

5回以上試行すると、X-RateLimit-Remainingはゼロとなり、リミット制限が戻る時間を示すRetry-Afterが表示されるようになりました。

表示例では、残り16秒との事です。

スクリーンショット 2020-06-24 12.08.41.png

レスポンス結果は以下の通りです。Statusには429 too Many Request。Bodyもjsonで指定した通りのレスポンスが返ってくる事を確認できました。

スクリーンショット 2020-06-24 12.13.41.png

以上になります。少しでも実装の参考になれば幸いです。

HTTPのエラーページをカスタマイズ表示させたい場合は、
/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views

のbladeで編集できますので、目的に応じて対応しましょう。

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

Docker運用のためのLaravelログ出力

概要

Docker運用におけるログ出力は標準出力/標準エラー出力となるが、単純にLaravel側でログを標準出力に設定しても出力されず、該当する記事も見当たらなかったため、実装した内容を記録する。

Laravelチャンネルドライバ設定

これは公式などにも出ているので問題にならないが、具体的には以下の二通りがある。

  • 直接標準出力を指定するパターン
    チャンネルドライバにstderrを指定する。
/.env
LOG_CHANNEL=stderr
  • デフォルトのドライバを指定するパターン
    他のドライバを内部で複数ラッピングできるので運用上こちらの方が便利。
    • チャンネルドライバにstackを指定する。
    • stackのドライバ指定にstderrを追加する。
/.env
LOG_CHANNEL=stack
/config/logging.php
...
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single', 'stderr'],
            'ignore_exceptions' => false,
        ],
...

php-fpm設定

Laravelの設定を行なっても標準出力されず、phpの設定なども行なってみたができず。。。
はっきりしたソースが見当たらなかったが、Laravelはアプリケーションサーバがphp-fpmの場合は、php-fpm経由で標準出力するらしい。
(他のアプリケーションサーバーは知りません。。。)

  • php-fpm設定ファイルwww.confに以下の設定をする。
www.conf
catch_workers_output = yes # 標準出力を可能にする
decorate_workers_output = no # php-fpmがログ出力時に吐くゴミ接頭辞を出さない

以上で、無事標準出力にエラーを出力することができた
しかし、decorate_workers_outputパラメータはphp7.3以降が有効だとのことなので、それ以前はどやって対応していたのだろう。。。
ゴミも出力することを許容せざるを得なかったのか

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

Windows10のタスクスケジューラでphpを実行する

タスクスケジューラとは

Linuxでいうところのcrontabのようにバッチを設定するソフトです。

タスクスケジューラ起動

スタートボタン -> Windows管理ツール -> タスクスケジューラー

タスク生成

タスクスケジューラライブラリの上で右クリック -> 基本タスクの作成

名前・説明: 後から見てもわかるように適当につける
トリガー: 今回はテストで行うので1回限りにチェック
開始時刻: 近い時刻
操作: プログラムの開始

<操作 プログラムの開始>
プログラム/スクリプト: php(xamppを利用していた私の場合は C:\xampp\php\php.exe)
引数の追加: -f 登録したいPHPファイル (-f test.php)
開始(オプション): 登録したいPHPファイルの位置 (C:\xampp\htdocs)
php コマンドラインオプション

タスク起動

設定した時間が来るのを待つ or タスク選択して右クリック -> 実行

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

PHP,Laravel導入

php インストール

ターミナル
brew install php@7.1

pathを通す

ターミナル
  echo 'export PATH="/usr/local/opt/php@7.1/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/usr/local/opt/php@7.1/sbin:$PATH"' >> ~/.zshrc

更新

ターミナル
source .zshrc

php バージョン切り替え

ターミナル
7.1から7.3に切り替えたい場合。
brew unlink php@7.1
brew link php@7.3

パスの変更

vi .zshrc

export PATH="/usr/local/opt/php@7.3/bin:$PATH"
export PATH="/usr/local/opt/php@7.3/sbin:$PATH"
source .zshrc

laravel

composerインストール

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

laravelインストール

composer global require laravel/installer

パスの設定

  • usernameの部分を適宜変更してください。
export PATH="$PATH:/Users/username/.config/composer/vendor/bin"
source .zshrc
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelで便利なおすすめComposerパッケージ一覧

はじめに

Laravelでアプリを作るときに
使うことが多い便利なパッケージをまとめました。
全てComposerでインストール可能なパッケージです。

主に管理画面系のwebアプリ構築の経験が多いため、
パッケージの選定もそのようなアプリで利用するものが多いです。

他にも便利なおすすめパッケージがあれば
ぜひコメントよろしくお願いします。

必須

barryvdh/laravel-debugbar

https://github.com/barryvdh/laravel-debugbar
ブラウザ下部にデバッグバーを表示する。
その時にリクエストで発行されたSQL一覧や、今持っているセッション情報一覧など
デバッグに便利な情報がブラウザ上で確認できるようになる。

barryvdh/laravel-ide-helper

https://github.com/barryvdh/laravel-ide-helper
IDEを利用してコーディングする際に、
コード補完を強化する。
変数からアローを書いたときにメソッドやプロパティのサジェスチョンがたくさん表示されたり、
メソッド定義元へのジャンプできる範囲が増えたり。
 

laravelcollective/html

https://github.com/LaravelCollective/html
bladeファイルでフォームを書くときに便利なメソッドを提供する。
CSRFトークンを自動で埋め込んでくれたり、
モデルとフォームを紐づけて自動で初期値を入れてくれたり。
 

doctrine/dbal

https://github.com/doctrine/dbal
migrationでカラム定義変更をする場合にインストールしておく必要あり。
 

よく使う

wildside/userstamps

https://github.com/WildSideUK/Laravel-Userstamps
データを作成、更新した際に
created_by、updated_byのカラムを
ログイン中ユーザIDで自動更新してくれる。

guzzlehttp/guzzle

https://github.com/guzzle/guzzle
簡単にHTTPリクエストを送信するコードが書ける。
外部サービスのAPIにリクエストするときや
フロントエンドからajaxでAPIリクエストするときに利用。

laracasts/flash

https://github.com/laracasts/flash
フラッシュメッセージを簡単に表示できる。
データ登録完了時や削除完了時に
画面上部に「登録完了しました。」みたいなメッセージ表示をする。

kyslik/column-sortable

https://github.com/Kyslik/column-sortable
一覧系画面で簡単にソート機能を実装できる。
一覧テーブルのヘッダー行をクリックするだけで
昇順、降順ソートを切り替えられる。

league/flysystem-aws-s3-v3

https://github.com/thephpleague/flysystem-aws-s3-v3
S3にファイルアップロード、ダウンロードをする場合に利用。

aws/aws-sdk-php-laravel

https://github.com/aws/aws-sdk-php-laravel
その他AWSサービス利用時に必要。
SESでメール送信など。

アプリによっては使う

orangehill/iseed

https://github.com/orangehill/iseed
実際にDBに入っているデータからseederファイルを逆生成する。

Enum

marc-mabe/php-enum
myclabs/php-enum
bensampo/laravel-enum
LaravelでEnumを実装したいときに利用。
似たようなパッケージがいくつかあり、どれがベストかはまだわからない。

spatie/laravel-menu

https://github.com/spatie/laravel-menu
階層になっているメニューを生成できる。

spatie/laravel-permission

https://github.com/spatie/laravel-permission
ユーザ、ロール、権限
の制御を簡単にできる。
ユーザ、ロール、権限を
多対多対多で管理するようなアプリでは非常に便利。

ユーザへのロール・権限の付与、はく奪の処理や
ユーザのロール・権限によるアクセス制御などが簡単。

davejamesmiller/laravel-breadcrumbs

https://github.com/davejamesmiller/laravel-breadcrumbs
パンくずリストの表示や管理がしやすくなる。

league/csv

https://github.com/thephpleague/csv
CSVのインポート・エクスポート処理を簡単にしてくれる。

barryvdh/laravel-dompdf

https://github.com/barryvdh/laravel-dompdf
PDF出力処理を簡単にできる。

jenssegers/agent

https://github.com/jenssegers/agent
ユーザエージェントの取得、判定処理をできる。

公式パッケージ

Laravelドキュメント
「公式パッケージ」の欄にいろいろ載っています。

課金システムを作れるCachier
全文検索処理のためのScout
SNSなど外部システム認証を導入するためのSocialiteなど、
もしアプリ要件にあれば導入必須となるような強力なパッケージがいくつかあります。

laravel-awesome-project紹介パッケージ

laravel-awesome-projectのページの
「popular-packages」の欄に、
便利なパッケージがカテゴリ別で大量に紹介されています。
この一覧をざーっと確認して
自分のアプリに導入できるようなパッケージがないか確認するのはおすすめです。

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

php アルゴリズム備忘録

文字列の切り出し

任意の文字列とその文字列の「何文字目」から「何文字」取得するかをプログラムする構文

hoge.php
<?php 
  $X = fgets(STDIN); 
  $Y = fgets(STDIN);
  echo substr($X, 0, $Y) . "\n";
?>

substr関数

文字を切り出す関数

substr(任意の文字列、 X文字目から、 Y文字切り取る) 

絶対値取得

abs関数

hoge.php
<?php 
  $num = fgets(STDIN);
  echo abs($num), PHP_EOL;

標準入力

hoge.php
$input=explode(' ',trim(fgets(STDIN)));
echo $input[0];
echo $input[1];
echo $input[2]; .............

自動的に配列に入れてくれる

大文字から小文字に変換

strtoupper関数

[string] [to] [upper]ね、、読みづら、、、、

hoge.php
<?php
  $str = trim(fgets(STDIN));
  echo strtoupper($str) . "\n";
?>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】 Cron タスクスケジューラの onOneServer() と withoutOverlapping() の違い

はじめに

Laravel には Cron を利用したタスクスケジューリング機能があります。

ところが素でこれをそのまま使うと,ワーカーとして複数インスタンスを立ててスケーリングしている場合などに,重複実行されてしまう問題があります。これを防ぐためには基本は onOneServer() を使っておけばいいのですが,なにやら withoutOverlapping() というものも存在することを知りました。微妙に用途が異なるみたいなので,ここで整理しておこうと思います。

現在時刻が「$H$ 時 $(i - 1)$ 分 $59$ 秒」であるとします。ここから1秒経過して「$H$ 時 $i$ 分 $0$ 秒」になった瞬間のことを考えます。

メソッド 目的
onOneServer() $command$H$ 時 $i$ 分 における実行開始 処理が既に別の場所で発生済みであるとき,ここでの実行をスキップする
withoutOverlapping() $command現在実行中 である場合, ここでの実行をスキップする

この 2 つのオプションは複合するため,以下のような 4 通りのパターンがあります。

$schedule->command($command)->everyMinute();
$schedule->command($command)->everyMinute()->onOneServer();
$schedule->command($command)->everyMinute()->withoutOverlapping();
$schedule->command($command)->everyMinute()->onOneServer()->withoutOverlapping();

図解

所要時間が 1分10秒 である $command というコマンドを考えます。これを 1 分ごと に実行します。またワーカーは 2 インスタンス 立っているとします。

それぞれ順番にタイムチャートで見てみましょう。多分,図を見れば一発で理解できると思います。

オプション無し

$schedule->command($command)->everyMinute();
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|   Time   | 0 | ... | 50 | 60 | 70 | ... | 110 | 120 | 130 | ... | 170 | 180 | 190 | ... |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 1 | *********************                 *******************************        |
|          |                 *****************************                                |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 2 | *********************                 *******************************        |
|          |                 *****************************                                |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

何の工夫もしない場合です。これはおそらく意図されていない結果です。

onOneServer()

$schedule->command($command)->everyMinute()->onOneServer();
キャッシュドライバに Redis または Memcached を使っている場合
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|   Time   | 0 | ... | 50 | 60 | 70 | ... | 110 | 120 | 130 | ... | 170 | 180 | 190 | ... |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 1 | *********************                 *******************************        |
|          |                 *****************************                                |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 2 |                                                                              |
|          |                                                                              |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
(参考)キャッシュドライバにファイルを使っている場合
オプション無しと同じ

ほとんどの軽量な短周期バッチではこれを選択するとよいでしょう。ワーカーを水平分散したとき, 1 インスタンスだけでタスクが実行されて欲しいと考えるのは自然な思考です。

補足

すべてのインスタンスが共通のキャッシュを見る必要があるため,キャッシュドライバとして Redis または Memcached が必須 要件であることに注意してください。 構成によっては Database でも問題ないように思われますが,アトミックな読み書きが確実に保証されているのは Redis と Memcached のみです。

withoutOverlapping()

$schedule->command($command)->everyMinute()->withoutOverlapping();
キャッシュドライバにファイルを使っている場合
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|   Time   | 0 | ... | 50 | 60 | 70 | ... | 110 | 120 | 130 | ... | 170 | 180 | 190 | ... |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 1 | *********************                 *******************************        |
|          |                                                                              |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 2 | *********************                 *******************************        |
|          |                                                                              |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
(参考)キャッシュドライバに Redis または Memcached を使っている場合
onOneServer() + withoutOverlapping() と同じになる可能性が高いが保証はされていない

withoutOverlapping() は単体ではあまり意味のない選択肢です。これも意図されているケースは無いはずです。

補足

今回のコマンドの場合は Redis または Memcached を使っていると,後述の onOneServer() + withoutOverlapping() の動作と実質同じ動きをしてくれると思いますが,厳密には保証されていません。具体的には,以下のようなケースで失敗します。実行開始時刻にばらつきがあり,コマンドの実行時間がスケジュール周期に対して十分短い場合です。

sleep(mt_rand(0, 5));
$schedule->command('1秒で終わるコマンド')->everyMinute()->withoutOverlapping();

withoutOverlapping() は「コマンド実行中の状態が重複しないこと」しか見てくれません。例えば,Worker 2 で開始されるときに Worker 1 での実行が終了していると,重複して実行されてしまいます。その逆も然りです。

+----------+---+---+---+---+---+-----+----+----+----+----+----+
|   Time   | 0 | 1 | 2 | 3 | 4 | ... | 60 | 61 | 62 | 63 | 64 |
+----------+---+---+---+---+---+-----+----+----+----+----+----+
| Worker 1 |     *****                                 *****  |
|          |                                                  |
+----------+---+---+---+---+---+-----+----+----+----+----+----+
| Worker 2 |             *****          *****                 |
|          |                                                  |
+----------+---+---+---+---+---+-----+----+----+----+----+----+

onOneServer() + withoutOverlapping()

$schedule->command($command)->everyMinute()->onOneServer()->withoutOverlapping();
キャッシュドライバに Redis または Memcached を使っている場合
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|   Time   | 0 | ... | 50 | 60 | 70 | ... | 110 | 120 | 130 | ... | 170 | 180 | 190 | ... |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 1 | *********************                 *******************************        |
|          |                                                                              |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| Worker 2 |                                                                              |
|          |                                                                              |
+----------+---+-----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
キャッシュドライバにファイルを使っている場合
withoutOverlapping() と同じ

withoutOverlapping()onOneServer() と併用して,初めて意味のある選択肢になります。中程度の周期のバッチの実行時間が,突発的に肥大化したりする場合に向いている選択肢です。「タスクが同時実行されると都合が悪いかどうか」で考えるといいでしょう。

  • 「人が手作業で行ってもいい作業を,適当に設定したサイクルで回るように自動化している」 という大雑把なスケジュールの場合, onOneServer() + withoutOverlapping() が適当と言える可能性が高そうです。
  • 「毎回実行することに意味があり,たとえ前のタスクが長引いても毎回実行しなければならない」という正確なスケジュールを求めている場合, onOneServer() のみのほうが適当です。

補足

2 つを併用することで,先程問題になっていた,実行開始時刻にばらつきがあり,コマンドの実行時間がスケジュール周期に対して十分短い場合でも確実に対応することができます。

+----------+---+---+---+---+---+-----+----+----+----+----+----+
|   Time   | 0 | 1 | 2 | 3 | 4 | ... | 60 | 61 | 62 | 63 | 64 |
+----------+---+---+---+---+---+-----+----+----+----+----+----+
| Worker 1 |     *****                                 *****  |
|          |                                                  |
+----------+---+---+---+---+---+-----+----+----+----+----+----+
| Worker 2 |                                                  |
|          |                                                  |
+----------+---+---+---+---+---+-----+----+----+----+----+----+

まとめ

メソッド 目的
onOneServer() $command$H$ 時 $i$ 分 における実行開始 処理が既に別の場所で発生済みであるとき,ここでの実行をスキップする
withoutOverlapping() $command現在実行中 である場合, ここでの実行をスキップする
$schedule->command($command)->everyMinute()->onOneServer();
$schedule->command($command)->everyMinute()->onOneServer()->withoutOverlapping();
  • onOneServer() は,スケーリングを前提とするのであればすべてのコマンドに付けておくべき。また実効性を生むためには Redis か Memcached が必須。
  • withoutOverlapping() は,前のコマンドの実行が終わっていないときにスキップして欲しい場合は onOneServer() と組み合わせて付けておく。実質的には無くても問題ないケースが多い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む