- 投稿日:2020-06-24T23:31:59+09:00
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
- 投稿日:2020-06-24T17:03:22+09:00
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')); } }
- 投稿日:2020-06-24T14:53:45+09:00
PHPMVCのM⇆Vについて(見直し用メモ)
前提知識
namespace…このファイルここに配置されてますよ〜って書いてるやつ
use…他フォルダにあるクラスを利用可能にしたものルーティング
・アドレスに対してコントローラーメソッドを指示する。
・$requestのプロパティの情報を飛ばすルート情報記述.phpRoute::get(アドレス,関数など);viewとは.phpview(テンプレート名) //指定したテンプレートのファイルをロードし、レンタリング(表示できるように整える)して返す特定のアドレスにアクセスした際、何の処理を実行するのかを記述したもの。
web.php<?php Route::get(`/`,function(){ return view(`welcome`); });解説
/にアクセスしたらwelcomeのファイルを戻せ(表示しろ)
ルートパラメータの利用
ルートパラメータの利用.phpRoute::get(`/〇〇/{パラメータ}',function($〇〇//パラメータの値を受け取る引数){ });ルートパラメータの利用(例文).phpRoute::get(`/hello/{msg}',function($msg){ }); ///hello/sampleにアクセスした場合、msgにsampleが代入される。解説
アドレスのパラメータをルーティング処理の一部として使いたい
必須パラメータと任意パラメータ
ルートパラメータを利用する際、パラメータを記入されないと処理が実行されないようになっている。
これを必須パラメータと呼ぶ。
しかし、パラメータの変数の最後に「?」をつけることによりパラメータを記入せずとも処理が実行される任意パラメータに変更することができる。任意パラメータの利用.phpRoute::get(`hello/{msg?}`,function ($msg=`〇〇`) //〇〇には、パラメータが記入されなかった時に出力される文字列を入れる解説
パラメータ指定されなくても使われたい
コントローラ
・ビュー
∟表示させるものを(return view('ファイル名','配列'))で書く
∟表示させるものに必要なデータを飛ばす
・データベースweb.phpに記述するやつ.phpRoute::get('アドレス','コントローラー名@メソッド名');ビュー
実際web上に表示させるもの。
Blade構文
PHPを用いて見栄えの良いビューを作れるようになるビューテンプレート
値の表示
値を埋め込む.html{{値・変数・式・関数など}}値を埋め込む(HTMLを機能させたい場合).html{{!!値・変数・式・関数など!!}}継承
既存のテンプレート(親)再利用して新しいテンプレート(子)を作成する。
親テンプレートにはsectionで子テンプレートに表示する区画を設定する。
子テンプレート側にはsectionを表示されるyieldを用いる。実際にレイアウト表示されるのは子レイアウトである。
継承方法.php@extends('フォルダ名.ファイル名')sectionの使い方.php@section(名前) -表示内容- @endsectionyieldの使い方.php@yield(名前)ビューコンポーザ
ビューをレンタリングする時に自動的に実行される処理
これを使えるようにするため、サービス(Laravelの機能強化の仕組み)を動かす必要がありそれを動かすために、サービスプロバイダーを使う必要がある。サービスプロバイダー
用意されているメソッド
boot
アプリケーションが起動する際に割り込んで実行される処理(ブートストラップ処理)
ここにビューと処理を用意することで、コンポーザが呼び出されるようになる。
(実質、コントローラみたいな役割をはたす)register
会員登録の処理など…boot利用例文.phppublic function boot() { View::composer( 'hello.index', function($view){ $view->with('view_message','composer message!'); } );新出①.phpView::composer(ビューの指定,関数またはクラス)第1引数には、コンポーザーを利用するビューの設定。
第2引数には、実行する処理を書く新出②.php$view->with(変数名,値);新出③.php$view(Viewクラスのインスタンス)サービスプロバイダへの登録
サービスプロバイダを作るだけでは作動しない。
app\confungにサービスプロパイダが使えるように登録する必要がある
- 投稿日:2020-06-24T14:53:45+09:00
PHPMVCのC⇆Vについて(見直し用メモ)
前提知識
namespace…このファイルここに配置されてますよ〜って書いてるやつ
use…他フォルダにあるクラスを利用可能にしたものルーティング
・アドレスに対してコントローラーメソッドを指示する。
・$requestのプロパティの情報を飛ばすルート情報記述.phpRoute::get(アドレス,関数など);viewとは.phpview(テンプレート名) //指定したテンプレートのファイルをロードし、レンタリング(表示できるように整える)して返す特定のアドレスにアクセスした際、何の処理を実行するのかを記述したもの。
web.php<?php Route::get(`/`,function(){ return view(`welcome`); });解説
/にアクセスしたらwelcomeのファイルを戻せ(表示しろ)
ルートパラメータの利用
ルートパラメータの利用.phpRoute::get(`/〇〇/{パラメータ}',function($〇〇//パラメータの値を受け取る引数){ });ルートパラメータの利用(例文).phpRoute::get(`/hello/{msg}',function($msg){ }); ///hello/sampleにアクセスした場合、msgにsampleが代入される。解説
アドレスのパラメータをルーティング処理の一部として使いたい
必須パラメータと任意パラメータ
ルートパラメータを利用する際、パラメータを記入されないと処理が実行されないようになっている。
これを必須パラメータと呼ぶ。
しかし、パラメータの変数の最後に「?」をつけることによりパラメータを記入せずとも処理が実行される任意パラメータに変更することができる。任意パラメータの利用.phpRoute::get(`hello/{msg?}`,function ($msg=`〇〇`) //〇〇には、パラメータが記入されなかった時に出力される文字列を入れる解説
パラメータ指定されなくても使われたい
コントローラ
・ビュー
∟表示させるものを(return view('ファイル名','配列'))で書く
∟表示させるものに必要なデータを飛ばす
・データベースweb.phpに記述するやつ.phpRoute::get('アドレス','コントローラー名@メソッド名');ビュー
実際web上に表示させるもの。
Blade構文
PHPを用いて見栄えの良いビューを作れるようになるビューテンプレート
値の表示
値を埋め込む.html{{値・変数・式・関数など}}値を埋め込む(HTMLを機能させたい場合).html{{!!値・変数・式・関数など!!}}継承
既存のテンプレート(親)再利用して新しいテンプレート(子)を作成する。
親テンプレートにはsectionで子テンプレートに表示する区画を設定する。
子テンプレート側にはsectionを表示されるyieldを用いる。実際にレイアウト表示されるのは子レイアウトである。
継承方法.php@extends('フォルダ名.ファイル名')sectionの使い方.php@section(名前) -表示内容- @endsectionyieldの使い方.php@yield(名前)ビューコンポーザ
ビューをレンタリングする時に自動的に実行される処理
これを使えるようにするため、サービス(Laravelの機能強化の仕組み)を動かす必要がありそれを動かすために、サービスプロバイダーを使う必要がある。サービスプロバイダー
用意されているメソッド
boot
アプリケーションが起動する際に割り込んで実行される処理(ブートストラップ処理)
ここにビューと処理を用意することで、コンポーザが呼び出されるようになる。
(実質、コントローラみたいな役割をはたす)register
会員登録の処理など…boot利用例文.phppublic function boot() { View::composer( 'hello.index', function($view){ $view->with('view_message','composer message!'); } );新出①.phpView::composer(ビューの指定,関数またはクラス)第1引数には、コンポーザーを利用するビューの設定。
第2引数には、実行する処理を書く新出②.php$view->with(変数名,値);新出③.php$view(Viewクラスのインスタンス)サービスプロバイダへの登録
サービスプロバイダを作るだけでは作動しない。
app\confungにサービスプロパイダが使えるように登録する必要がある
- 投稿日:2020-06-24T13:20:38+09:00
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-payloadCreate 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アドレスが格納され、この値ごとに制限がかかるようにしています。(※本記事では関連するコードの明記を省略しています)
制限(X-RateLimit-Limit)を5回にしてテストしています。一番下のX-RateLimit-Remainingが残りの試行回数です。
5回以上試行すると、X-RateLimit-Remainingはゼロとなり、リミット制限が戻る時間を示すRetry-Afterが表示されるようになりました。
表示例では、残り16秒との事です。
レスポンス結果は以下の通りです。Statusには429 too Many Request。Bodyもjsonで指定した通りのレスポンスが返ってくる事を確認できました。
以上になります。少しでも実装の参考になれば幸いです。
HTTPのエラーページをカスタマイズ表示させたい場合は、
/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/viewsのbladeで編集できますので、目的に応じて対応しましょう。
- 投稿日:2020-06-24T13:18:10+09:00
Docker運用のためのLaravelログ出力
概要
Docker運用におけるログ出力は標準出力/標準エラー出力となるが、単純にLaravel側でログを標準出力に設定しても出力されず、該当する記事も見当たらなかったため、実装した内容を記録する。
Laravelチャンネルドライバ設定
これは公式などにも出ているので問題にならないが、具体的には以下の二通りがある。
- 直接標準出力を指定するパターン
チャンネルドライバにstderr
を指定する。/.envLOG_CHANNEL=stderr
- デフォルトのドライバを指定するパターン
他のドライバを内部で複数ラッピングできるので運用上こちらの方が便利。
- チャンネルドライバに
stack
を指定する。
stack
のドライバ指定にstderr
を追加する。
/.envLOG_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.confcatch_workers_output = yes # 標準出力を可能にする decorate_workers_output = no # php-fpmがログ出力時に吐くゴミ接頭辞を出さない
以上で、無事標準出力にエラーを出力することができた
しかし、decorate_workers_outputパラメータはphp7.3以降が有効だとのことなので、それ以前はどやって対応していたのだろう。。。
ゴミも出力することを許容せざるを得なかったのか
- 投稿日:2020-06-24T13:12:56+09:00
Windows10のタスクスケジューラでphpを実行する
タスクスケジューラとは
Linuxでいうところのcrontabのようにバッチを設定するソフトです。
タスクスケジューラ起動
スタートボタン -> Windows管理ツール -> タスクスケジューラー
タスク生成
タスクスケジューラライブラリの上で右クリック -> 基本タスクの作成
名前・説明: 後から見てもわかるように適当につける
トリガー: 今回はテストで行うので1回限りにチェック
開始時刻: 近い時刻
操作: プログラムの開始<操作 プログラムの開始>
プログラム/スクリプト: php(xamppを利用していた私の場合は C:\xampp\php\php.exe)
引数の追加: -f 登録したいPHPファイル (-f test.php)
開始(オプション): 登録したいPHPファイルの位置 (C:\xampp\htdocs)
php コマンドラインオプションタスク起動
設定した時間が来るのを待つ or タスク選択して右クリック -> 実行
- 投稿日:2020-06-24T10:40:07+09:00
PHP,Laravel導入
php インストール
ターミナルbrew install php@7.1pathを通す
ターミナル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 .zshrcphp バージョン切り替え
ターミナル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 .zshrclaravel
composerインストール
curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composerlaravelインストール
composer global require laravel/installerパスの設定
- usernameの部分を適宜変更してください。
export PATH="$PATH:/Users/username/.config/composer/vendor/bin"source .zshrc
- 投稿日:2020-06-24T08:30:02+09:00
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」の欄に、
便利なパッケージがカテゴリ別で大量に紹介されています。
この一覧をざーっと確認して
自分のアプリに導入できるようなパッケージがないか確認するのはおすすめです。
- 投稿日:2020-06-24T06:31:22+09:00
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"; ?>
- 投稿日:2020-06-24T02:43:34+09:00
【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()
と組み合わせて付けておく。実質的には無くても問題ないケースが多い。