20210916のPHPに関する記事は9件です。

Ubuntu 20.04にPHPを複数インストールして切り替える方法(PPA・apt)

はじめに 本書では、Ubuntu 20.04に、PHPの環境をインストールする方法をまとめます。 案件によってPHPのバージョンが違うこともあるため、複数のバージョンをインストールして、alternativesで切り替えられるようにします。 インストール手順 リポジトリ追加 sudo add-apt-repository ppa:ondrej/php のコマンドを実行します。 Press [ENTER] to continue or Ctrl-c to cancel adding it. と表示されたら、Enterキーを押します。 apt update sudo apt update PHPをインストール ここでは、本ページ執筆時点で最新の、8.0をインストールします。 sudo apt install -y php8.0 他に、複数バージョンの切り替えを確認するため、本ページ執筆時点で有効な、7.3、7.4もインストールします。 sudo apt install -y php7.3 sudo apt install -y php7.4 PHPモジュールをインストール sudo apt search php8.0-* を実行すると、 libapache2-mod-php8.0/focal 8.0.10-1+ubuntu20.04.1+deb.sury.org+1 amd64 server-side, HTML-embedded scripting language (Apache 2 module) libphp8.0-embed/focal 8.0.10-1+ubuntu20.04.1+deb.sury.org+1 amd64 HTML-embedded scripting language (Embedded SAPI library) php8.0/focal 8.0.10-1+ubuntu20.04.1+deb.sury.org+1 all server-side, HTML-embedded scripting language (metapackage) php8.0-amqp/focal 1.10.2-8+ubuntu20.04.1+deb.sury.org+1 amd64 AMQP extension for PHP php8.0-apcu/focal 5.1.20+4.0.11-1+ubuntu20.04.1+deb.sury.org+1 amd64 APC User Cache for PHP php8.0-ast/focal 1.0.14-1+ubuntu20.04.1+deb.sury.org+1 amd64 AST extension for PHP 7 ・ ・ ・ のように、インストール可能なモジュールが表示されるので、必要なモジュールをインストールする。 例:MySQLを使う場合 sudo apt install -y php8.0-mysql alternativesでバージョン切り替え 定義 sudo update-alternatives --install /usr/bin/php php /usr/bin/php7.3 100 sudo update-alternatives --install /usr/bin/php php /usr/bin/php7.4 110 sudo update-alternatives --install /usr/bin/php php /usr/bin/php8.0 120 切り替え方法 Priorityを選択して切り替える方法 sudo update-alternatives --config php There are 3 choices for the alternative php (providing /usr/bin/php). Selection Path Priority Status ------------------------------------------------------------ 0 /usr/bin/php8.0 120 auto mode 1 /usr/bin/php7.3 100 manual mode 2 /usr/bin/php7.4 110 manual mode * 3 /usr/bin/php8.0 120 manual mode Press <enter> to keep the current choice[*], or type selection number: のように表示されるので、Selectionの番号を入れてEnterキーを押すと、そのバージョンが有効になります。 ※上記はPHP 8.0が有効になっている状態です。 php -v PHP 8.0.10 (cli) (built: Aug 26 2021 15:50:07) ( NTS ) Copyright (c) The PHP Group Zend Engine v4.0.10, Copyright (c) Zend Technologies with Zend OPcache v8.0.10, Copyright (c), by Zend Technologies のように、選択したバージョンになっていればOKです。 コマンドで直接設定する方法 3.9を有効にする場合: sudo update-alternatives --set php /usr/bin/php7.4 php -v PHP 7.4.23 (cli) (built: Aug 26 2021 15:51:37) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.23, Copyright (c), by Zend Technologies のように、選択したバージョンになっていればOKです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】継承とオーバーライドについて

基礎の基礎に戻ってみました。 継承とは すでに存在しているクラスを元にして、新しいクラスを作ること 元のクラスの事を親クラス、基底クラスと呼びます。 新しいクラスは子クラスと呼んでます。 継承することのメリット 親クラスで定義した関数を、子クラスでも同じように使用することができます。 これによって、同じような処理を複数書かなくて良くなります。 +コードの可読性も上がるので、メンテナンスがしやすくなります。 継承の使い方 class 子クラス名 extends 親クラス名{}という形で継承することができます。 <?php // 親クラス class ParentClass { public function test(){ print '親クラスのメソッドです'."\n"; } } // 子クラス class ChildClass extends ParentClass { } $a = new ChildClass(); echo $a->test(); //出力結果/////// 親クラスのメソッドです ?> オーバーライド とは 子クラスが親クラスの関数を上書きすること 継承をしていることが条件です。 親クラスと同名の関数を子クラスでも定義してあげることによって、オーバーライドできます。(※引数も合わせること) オーバーライド のメリット 1つの関数名で、クラスに応じて適切な処理(関数)を行わせることができます。 使用例 <?php // 親クラス class ParentClass { public function output(){ print '親クラスのメソッドです'."\n"; } } // 子クラス class ChildClass extends ParentClass { public function output(){ print "子クラスのメソッドが親クラスをオーバーライドしました"; } } $test = new ParentClass(); $test->output(); $test2 = new ChildClass(); $test2->output(); ?> ////出力結果////// 親クラスのメソッドです 子クラスのメソッドが親クラスをオーバーライドしました 継承をしているので、関数名が被ってしまうとシステム側で「どっち実行すればいいの?」ってなりそうですが大丈夫です。 オーバーライドの使い方 例えば、複数のクラスがあって、1つだけ別の処理をさせたい場合などに使えます。 <?php // 4つのクラスでは、親クラスの処理をそのまま使いたい class ParentClass { public function duplicateCheck($id){ return []; } } // 1つのクラスだけ、DBの重複チェックをしたい class ChildClass extends ParentClass { public function duplicateCheck($id){ return DB::findxx($id); } ?> こういった場合に使うことができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コードプロファイラによる処理時間増加問題

はじめに 実装機能の処理時間を調査しているときに、実装箇所とは別のところで大幅に時間がかかっている問題に遭遇しました。 その時の原因と、おまけで計測処理について自分用に記録します。 ざっくり言語・フレームワーク 言語:PHP フレームワーク:FuelPHP 原因 結論:コードプロファイラがオンになっていたため より細かく処理時間をログ出力して、原因を調査してみたところ最後のレスポンスを返すところまでは問題ありませんでした。 お手上げ状態の時にたまたま、コードプロファイラの存在を思い出し、オフにしてみたら解決しました。 プロファイラでは、プログラム実行時の様々な情報が確認できその中で実行されたSQLも確認できる。今回、かなり多くループ処理でSQL文(4~5万)が実行されていたた。そのすべての情報をプロファイラで出力されていたためプロファイラの処理に時間がかかったと考えられる。 (コード)プロファイラとは? プロファイラとは、コンピュータプログラムが実行される様子を監視・記録し、プログラム中の各箇所の動作順や実行時間などを集計・解析するプログラム。ソフトウェアの開発環境や実行環境の機能の一部として提供され、プログラムの性能測定・解析を行うことができる。 引用元:プロファイラ(profiler)とは - IT用語辞典 e-Words FuelPHPに同梱のアプリケーションプロファイラは、PHPQuickProfilerをベースにしています。 以下の画像がプロファイラの画像です。 プロファイラでは以下のような情報がわかる。 Console. デフォルトのタブ。エラー、ログ、メモリ使用量や実行タイミングに関する情報を提供します。 Load time. リクエストのロード時間。タブ内で詳細を表示します。 Database. 実行されたクエリ数や実行時間および、もしサポートされていればクエリの解析。 Memory. リクエストの処理に要したメモリ使用量。 Files. 読み込まれた PHP ファイルのフルパス名と、それらの容量。 Config. 処理の終了時点における、設定値の内容。 Session. 処理の終了時点における、セッション値の内容。 GET. $_GET 配列の内容。 POST. $_POST 配列の内容。 使用するには設定ファイルで、以下のように設定すればよい。 'profiling' => true, まとめ 今回は、コードプロファイラによって処理時間が大幅に増えたときのことをまとめた。 プロファイラの使用中に処理時間が増えた場合は、まず今回の件を思い出したい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】ルート情報の記述順によって発生するエラーと正規表現 制約を使った対策 (初学者向け)

はじめに 初めてQiitaに記事を投稿します。 ご指摘いただけますと幸いです。 1. 概要 ルートパラメータを使用する場合、記述の順番によっては DBに存在しないデータを処理しようとして、エラーが発生する可能性があります。 今回は、簡単なエラー発生例を記したあと、原因と対策について説明します。 ※本記事は初学者向けに詳しく解説しています。 2. 環境 PHP:7.4 Laravel:6.20 3. エラー発生の例 下記前提を踏まえた上で、エラーコードのサンプルを示します。 3.1 前提 ログインユーザーが記事を投稿できるsnsアプリを想定。 分かりやすいように最低限のメソッド、ルート情報のみを実装。 処理に対するルート情報のURI仕様は下記とする。 処理 HTTP Method URI Action Middleware 記事詳細画面を表示 GET articles/{記事のid} show web, guest 記事投稿画面を表示 GET articles/create create web, auth ルートパラメータは教材でよくある"id"を用いる。 viewは正しく実装されているものとして、今回は記載を省略する。 3.2 エラーが発生するコード 下記のコードにて、ログインした状態で記事投稿画面を表示させようとarticles/create にアクセスするとエラーが発生します。 routes/web.php Auth::routes(); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show'); Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); ※groupで括っているのは、他の処理を書いていた名残りです。 app/Http/Controllers/ArticlesController.php <?php namespace App\Http\Controllers; use App\Article; class ArticlesController extends Controller { public function create() { return view('articles.create'); } public function show($id) { $article = Article::find($id); return view('articles.show', compact('article')); } } 3.3 エラー内容 今回の例ではエラー内容は下記となります。 エラー内容 ErrorException Trying to get property 'title' of non-object (View: /var/www/html/resources/views/articles/show.blade.php) 上記は、「オブジェクトにはtitleというプロパティはない」というエラーになっています。 ( articles/show.blade.phpでは、$articleモデルオブジェクトからtitleプロパティを取得する記述をしています。 ) したがって、下記のいずれかが考えられます。 $articleモデルオブジェクトにtitleというプロパティが存在しない。 $articleモデルオブジェクト、titleというプロパティは存在しているが、アクセス修飾子がprivateまたはprotectedで設定されており、アクセスできない。 $articleモデルオブジェクトがそもそも存在しない。 エラー"Trying to get property of non-object"を解決したい また、createの処理を実行したつもりが、 なぜかshowアクションから表示するshow.blade.phpのファイルでエラーを指摘されています。 4. エラーの原因 4.1 原因 エラーの原因は、web.phpのshowアクションを実行するURIのルートパラメータにcreateを渡してしまっていることです。 これにより、showアクションにDBに存在しないパラメータが渡され、処理されます(nullが返ってくる)。 したがってblade側では$articleオブジェクトがそもそも存在していないため、エラーが発生します。 Laravelのルーティング優先度でつまづいた話 4.2 詳細解説 4.2.1 ルートパラメータについて まず、前提知識としてルートパラメータについて説明します。 今回showアクションのルートパラメータは{id}としています。 記事のidを受け取るので分かりやすく{id}にしていますが、下記の規則に従えば{x}でも{article}でもなんでも良いです。 Laravel 6.x ルーティング:ルートパラメーター ルートパラメータは、いつも{}括弧で囲み、アルファベット文字で構成してください。 ルートパラメータには、ハイフン(-)を使えません。 ちなみに今回の場合、パラメータを{article}とし、コントローラー側のアクションの引数を(Article $article)とすると、 ルートパラメータで指定したIDを持つ、モデルインスタンスが注入されて便利です。 Laravel 6.x ルーティング:モデル結合ルート ルートかコントローラアクションへモデルIDが指定される場合、IDに対応するそのモデルを取得するため、大抵の場合クエリします。 Laravelのモデル結合はルートへ直接、そのモデルインスタンスを自動的に注入する便利な手法を提供しています。 つまり、ユーザーのIDが渡される代わりに、指定されたIDに一致するUserモデルインスタンスが渡されます 4.2.2 ルートパラメータが受け付ける値 ルートパラメータですが、{id}にしたからといって数字のみ受け付けるのではなく、文字列も受け付けます。 リファレンスによるとデフォルトでは、/以外は受け付けるそうです。 Laravel 6.x ルーティング:スラッシュのエンコード Laravelのルーティングコンポーネントは、/を除くすべての文字を許可しています。 試しにパラメータに/を入れて実行しましたが、コントローラーへ行き着く前に404エラーレスポンスを返しました。 4.2.3 エラー発生の流れ 以上を踏まえて、エラー発生の原因について詳しく説明します。 web.phpに記載されたルート情報の処理は上から順番に実行されます。 showアクションのルート情報はcreateアクションのルート情報よりも先に記述されています。 つまり、 となって、viewメソッドにより、nullがbladeに渡されエラーが発生します。 リソースルートでCRUDを一括で設定すれば、その中では順番を考慮しなくても良いですが、 今回のようにshowは認証しない、createは認証するなど分ける場合は、ルートパラメータや記述順に注意する必要があります。 処理によって異なるエラー内容 1.今回の例では、showアクション内でfindメソッドを使用したことで、DBにデータがない場合の返り値がnullとなりましたが、コントローラーの処理を下記のようにしている場合は、nullを返さずに「HTTP 404エラー」が発生します。   ・findOrFailでモデルインスタンスを取得する処理を実装。   ・モデル結合ルートを使用している場合。 2.アクションでint型でidをタイプヒンティング(型宣言)した場合は、「int型を入力してください」 といったエラーが発生します。 Laravelのfind()とfindOrFail()の違い、使い分け方。初心者もよく分かる! Laravel 6.x ルーティング:モデル結合ルート 一致するモデルインスタンスがデータベースへ存在しない場合、404 HTTPレスポンスが自動的に生成されます。 PHPの型宣言(タイプヒンティング)の話 5. 対策 そもそもURI設計を適切にしていれば起きないのかも知れませんが、 起きてしまった場合の対策を3つ記載します。 5.1 ルートの記述順を変える。 今回の例で一番簡単な方法はルートの順番を変更することです。 下記のようにshowとcreateのルートを入れ替えることで、createが先に実行されるためエラーは発生しません。 web.php Auth::routes(); Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show'); 個人的に思うデメリットとしては、下記が挙げられます。 事前にURI設計をしてない場合、ルート情報が多くなるとややこしくなる。 ルートを追加した際に今後も同じエラーが発生する恐れがある。 5.2 web.phpに正規表現を使用 whereメソッドを使い、引数に正規表現を記載すると、パラメータを制限することができます。 ここでいう制限とはエラーを返さずに次の処理を実行することです。 ルート全体で当てはまるパラメータがなければ、404エラーを返します。 idの制約はリファレンスに記載されている'id', [0-9]+を用いています。 これは、idのパラメータは、「数字0~9で1文字以上」であることを意味します。 routes/web.php Auth::routes(); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show')->where('id', '[0-9]+'); //whereメソッドを追記 Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); Laravel 6.x ルーティング:正規表現制約 【5分でまるっと理解】PHP正規表現の使い方まとめ また、idは0が入ることはなく、1以上なので、そういった正規表現を行う場合は下記の表現ができるみたいです。 (上記の正規表現との挙動の違いは、0が入力された時、ルーティングの段階で404エラーを返すか、コントローラー以降の処理でエラーを返すかの違いになります。) 数字1~9で1文字以上の正規表現 ('id', '\+?[1-9][0-9]*'); 正の整数のことをききたいです 5.3 グローバル制約 正規表現は指定したルートパラメータ全てに適用することもできます。 下記のようにbootメソッド内でpatternメソッドを用います。 第1引数で指定したパラメータと同一のルートパラメータを、第2引数の制約で制限することができます。 app/Providers/RouteServiceProvider.php /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot() { Route::pattern('id', '[0-9]+'); //ルート制約を追加 parent::boot(); } Laravel 6.x ルーティング:グローバル制約 6. まとめ 今回は、実際に僕もハマったことのあるルートのエラーについての解説でした。 ルートの制約は実務ベースで使われるか不明ですが、 そういったところも含めてご指摘いただけますと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】ルート情報の記述順によって発生するエラーと正規表現 制約を使った対策

はじめに 初めてQiitaに記事を投稿します。 ご指摘いただけますと幸いです。 1. 概要 ルートパラメータを使用する場合、記述の順番によっては DBに存在しないデータを処理しようとして、エラーが発生する可能性があります。 今回は、簡単なエラー発生例を記したあと、原因と対策について説明します。 ※本記事は初学者向けに詳しく解説しています。 2. 環境 PHP:7.4 Laravel:6.20 3. エラー発生の例 下記前提を踏まえた上で、エラーコードのサンプルを示します。 3.1 前提 ログインユーザーが記事を投稿できるsnsアプリを想定。 分かりやすいように最低限のメソッド、ルート情報のみを実装。 処理に対するルート情報のURI仕様は下記とする。 処理 HTTP Method URI Action Middleware 記事詳細画面を表示 GET articles/{記事のid} show web, guest 記事投稿画面を表示 GET articles/create create web, auth ルートパラメータは教材でよくある"id"を用いる。 viewは正しく実装されているものとして、今回は記載を省略する。 3.2 エラーが発生するコード 下記のコードにて、ログインした状態で記事投稿画面を表示させようとarticles/create にアクセスするとエラーが発生します。 routes/web.php Auth::routes(); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show'); Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); ※groupで括っているのは、他の処理を書いていた名残りです。 app/Http/Controllers/ArticlesController.php <?php namespace App\Http\Controllers; use App\Article; class ArticlesController extends Controller { public function create() { return view('articles.create'); } public function show($id) { $article = Article::find($id); return view('articles.show', compact('article')); } } 3.3 エラー内容 今回の例ではエラー内容は下記となります。 エラー内容 ErrorException Trying to get property 'title' of non-object (View: /var/www/html/resources/views/articles/show.blade.php) 上記は、「オブジェクトにはtitleというプロパティはない」というエラーになっています。 ( articles/show.blade.phpでは、$articleモデルオブジェクトからtitleプロパティを取得する記述をしています。 ) したがって、下記のいずれかが考えられます。 $articleモデルオブジェクトにtitleというプロパティが存在しない。 $articleモデルオブジェクト、titleというプロパティは存在しているが、アクセス修飾子がprivateまたはprotectedで設定されており、アクセスできない。 $articleモデルオブジェクトがそもそも存在しない。 エラー"Trying to get property of non-object"を解決したい また、createの処理を実行したつもりが、 なぜかshowアクションから表示するshow.blade.phpのファイルでエラーを指摘されています。 4. エラーの原因 4.1 原因 エラーの原因は、web.phpのshowアクションを実行するURIのルートパラメータにcreateを渡してしまっていることです。 これにより、showアクションにDBに存在しないパラメータが渡され、処理されます(nullが返ってくる)。 したがってblade側では$articleオブジェクトがそもそも存在していないため、エラーが発生します。 Laravelのルーティング優先度でつまづいた話 4.2 詳細解説 4.2.1 ルートパラメータについて まず、前提知識としてルートパラメータについて説明します。 今回showアクションのルートパラメータは{id}としています。 記事のidを受け取るので分かりやすく{id}にしていますが、下記の規則に従えば{x}でも{article}でもなんでも良いです。 Laravel 6.x ルーティング:ルートパラメーター ルートパラメータは、いつも{}括弧で囲み、アルファベット文字で構成してください。 ルートパラメータには、ハイフン(-)を使えません。 ちなみに今回の場合、パラメータを{article}とし、コントローラー側のアクションの引数を(Article $article)とすると、 ルートパラメータで指定したIDを持つ、モデルインスタンスが注入されて便利です。 Laravel 6.x ルーティング:モデル結合ルート ルートかコントローラアクションへモデルIDが指定される場合、IDに対応するそのモデルを取得するため、大抵の場合クエリします。 Laravelのモデル結合はルートへ直接、そのモデルインスタンスを自動的に注入する便利な手法を提供しています。 つまり、ユーザーのIDが渡される代わりに、指定されたIDに一致するUserモデルインスタンスが渡されます 4.2.2 ルートパラメータが受け付ける値 ルートパラメータですが、{id}にしたからといって数字のみ受け付けるのではなく、文字列も受け付けます。 リファレンスによるとデフォルトでは、/以外は受け付けるそうです。 Laravel 6.x ルーティング:スラッシュのエンコード Laravelのルーティングコンポーネントは、/を除くすべての文字を許可しています。 試しにパラメータに/を入れて実行しましたが、コントローラーへ行き着く前に404エラーレスポンスを返しました。 4.2.3 エラー発生の流れ 以上を踏まえて、エラー発生の原因について詳しく説明します。 web.phpに記載されたルート情報の処理は上から順番に実行されます。 showアクションのルート情報はcreateアクションのルート情報よりも先に記述されています。 つまり、 となって、viewメソッドにより、nullがbladeに渡されエラーが発生します。 リソースルートでCRUDを一括で設定すれば、その中では順番を考慮しなくても良いですが、 今回のようにshowは認証しない、createは認証するなど分ける場合は、ルートパラメータや記述順に注意する必要があります。 処理によって異なるエラー内容 1.今回の例では、showアクション内でfindメソッドを使用したことで、DBにデータがない場合の返り値がnullとなりましたが、コントローラーの処理を下記のようにしている場合は、nullを返さずに「HTTP 404エラー」が発生します。   ・findOrFailでモデルインスタンスを取得する処理を実装。   ・モデル結合ルートを使用している場合。 2.アクションでint型でidをタイプヒンティング(型宣言)した場合は、「int型を入力してください」 といったエラーが発生します。 Laravelのfind()とfindOrFail()の違い、使い分け方。初心者もよく分かる! Laravel 6.x ルーティング:モデル結合ルート 一致するモデルインスタンスがデータベースへ存在しない場合、404 HTTPレスポンスが自動的に生成されます。 PHPの型宣言(タイプヒンティング)の話 5. 対策 そもそもURI設計を適切にしていれば起きないのかも知れませんが、 起きてしまった場合の対策を3つ記載します。 5.1 ルートの記述順を変える。 今回の例で一番簡単な方法はルートの順番を変更することです。 下記のようにshowとcreateのルートを入れ替えることで、createが先に実行されるためエラーは発生しません。 web.php Auth::routes(); Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show'); 個人的に思うデメリットとしては、下記が挙げられます。 事前にURI設計をしてない場合、ルート情報が多くなるとややこしくなる。 ルートを追加した際に今後も同じエラーが発生する恐れがある。 5.2 web.phpに正規表現を使用 whereメソッドを使い、引数に正規表現を記載すると、パラメータを制限することができます。 ここでいう制限とはエラーを返さずに次の処理を実行することです。 ルート全体で当てはまるパラメータがなければ、404エラーを返します。 idの制約はリファレンスに記載されている'id', [0-9]+を用いています。 これは、idのパラメータは、「数字0~9で1文字以上」であることを意味します。 routes/web.php Auth::routes(); Route::get('articles/{id}', 'ArticlesController@show')->name('articles.show')->where('id', '[0-9]+'); //whereメソッドを追記 Route::group(['middleware' => 'auth'], function () { Route::get('articles/create', 'ArticlesController@create')->name('articles.create'); }); Laravel 6.x ルーティング:正規表現制約 【5分でまるっと理解】PHP正規表現の使い方まとめ また、idは0が入ることはなく、1以上なので、そういった正規表現を行う場合は下記の表現ができるみたいです。 (上記の正規表現との挙動の違いは、0が入力された時、ルーティングの段階で404エラーを返すか、コントローラー以降の処理でエラーを返すかの違いになります。) 数字1~9で1文字以上の正規表現 ('id', '\+?[1-9][0-9]*'); 正の整数のことをききたいです 5.3 グローバル制約 正規表現は指定したルートパラメータ全てに適用することもできます。 下記のようにbootメソッド内でpatternメソッドを用います。 第1引数で指定したパラメータと同一のルートパラメータを、第2引数の制約で制限することができます。 app/Providers/RouteServiceProvider.php /** * Define your route model bindings, pattern filters, etc. * * @return void */ public function boot() { Route::pattern('id', '[0-9]+'); //ルート制約を追加 parent::boot(); } Laravel 6.x ルーティング:グローバル制約 6. まとめ 今回は、実際に僕もハマったことのあるルートのエラーについての解説でした。 ルートの制約は実務ベースで使われるか不明ですが、 そういったところも含めてご指摘いただけますと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】 取得する総日数は固定で、土日祝日は除外したい

僭越ながら初投稿 Googleカレンダーから自分の予定を抽出して空き日程を算出するアプリを作成中です そこで、土日祝を除いた上で指定した日数分の予定を抽出するロジックを作ってみたのでメモがてら やったこと Googleカレンダーから自分の空き日程を取得するアプリを作成中です その中で、土日祝日を除いたN日分の日数を取る機能が欲しくなりました 例えば9/3から14日分の空き日程を抽出したいとなった際、とりあえず素直に開始日の9/3を含めて14日後の9/16の日付を指定してカレンダーのAPIを叩きます すると、指定通り9/3 ~ 9/16までの自分の予定が取れます が、9/4, 9/5, 9/11, 9/12が土日です 土日祝日はスキップした上で14日分を取りたいので、上記のやり方では土日を除くと10日分の予定しか取れてません 土日祝を除いた14日分の予定を抽出するには以下の計算が必要になります 9/3から9/16の間には土日が4日あるので9/16に4を足して9/20 9/18, 9/19が土日で9/20が祝日なのでさらに3を足して9/23 9/23は祝日なので1を足して9/24 当初9/16と指定した日付が最終的に9/24になりました 9/3から(土日祝を除いた)14日分の予定を抽出するには9/17に7を足して9/24にしてからAPIを叩かなければなりません この7を算出するロジックを作ってみました 環境 PHP: 7.4.19 Mac OS: Big Sur 11.5.2 言語はPHPです。 verとか関係なくできそうですが、僕自身PHPに詳しいわけではないので一応 コード functions.php <?php /** * 祝日を外部APIからもらう * @return array */ function fetchNationalHolidays(): array { // https://github.com/holidays-jp こちらの方のAPIを使わせてもらいました $endpoint = 'https://holidays-jp.github.io/api/v1/date.json'; $response = file_get_contents($endpoint); return json_decode($response, true); } /** * $startから$days日間のうち、土日祝の日数を求める * @param string $start Y-m-d形式の文字列 * @param int $days 何日分の予定を抽出するか * @param array $nationalHolidays 祝日の配列(キーにY-m-d形式の日付文字列) * @return int 土日祝を除く日数 */ function countClosingDays(string $start, int $days, array $nationalHolidays): int { // 単純に$start+$daysとしてしまうと$startの日程が考慮されない(取得日数が1日多くなる)のでここで1を引く $days -= 1; $end = date('Y-m-d', strtotime($start . '+' . $days . 'day')); $cnt = 0; while (1) { $ctl = 0; // 各ループ内での土日祝日数を保持する // 土日数を算出 foreach (array(0, 6) as $dayOfWeek) { $ctl += countHolidays($start, $end, $dayOfWeek); } // 祝日数を算出 $ctl += countNationalHolidays($start, $end, $nationalHolidays); $cnt += $ctl; // $ctlが0になったら(=> $start ~ $end間に土日祝が含まれなくなったら)break if ($ctl == 0) { break; } // $endを$startに代入して探索期間をずらすが、重複してカウントしてしまうことを避けるため$endに1日足したものを$startに入れる $start = date('Y-m-d', strtotime($end . ' +1' . 'day')); $end = date('Y-m-d', strtotime($end . '+' . $ctl . 'day')); } // そのまま返すと求めている値より1多くなるので-1する return $cnt - 1; } /** * $start ~ $end間の土日数を求める * @param string $start Y-m-d形式の文字列 * @param string $end Y-m-d形式の文字列 * @param int $dayOfWeek 休日と認定する曜日番号(0: 日, 1: 月, ..., 6: 土) * @return int 土日数 */ function countHolidays(string $start, string $end, int $dayOfWeek): int { // 日付の差分 $startTime = strtotime($start); $endTime = strtotime($end); $diff = ($endTime - $startTime) / (60 * 60 * 24); $i = 0; // 制御変数 $cnt = 0; // 一個一個、$targetが土曜なのか日曜なのか求めていく(別にforでもいいけどwhileの方が好き) while ($i <= $diff) { $target = date('w', strtotime($start . '+' . $i . 'day')); if ($target == $dayOfWeek) { // ヒットしたらインクリメント ++$cnt; } ++$i; } return $cnt; } /** * $start ~ $end間の祝日数を求める * @param string $start * @param string $end * @param array $nationalHolidays * @return int */ function countNationalHolidays(string $start, string $end, array $nationalHolidays): int { $cnt = 0; // $start ~ $endの期間内に含まれる祝日だけを抽出 $nationalHolidays = array_filter(array_keys($nationalHolidays), function($holiday) use ($start, $end) { return strtotime($start) <= strtotime($holiday) && strtotime($holiday) <= strtotime($end); }); // 祝日数を単純に加算 $cnt += count($nationalHolidays); // 土日と重複している祝日モドキの数だけ1引いていく foreach (array_values($nationalHolidays) as $holiday) { if (in_array(date('w', strtotime($holiday)), array(0, 6))) { $cnt -= 1; } } return $cnt; } 使い方 使い方も何もないですが、一応 main.php <?php // 祝日リスト $nationalHolidays = fetchNationalHolidays(); // 9/3から14日間分の予定を取得 $N = 14; $start = '2021-09-03'; $result = countClosingDays($start, $N, $nationalHolidays); echo $result; // 7 // あとは$startに$resultを足して日付出してAPIを叩けばいい $end = date('Y-m-d', strtotime($start . '+' . ($N + $result) . 'day'); あとがき 「指定期間の内から平日だけをとる」なら簡単にできるけど、今回みたいなケースだと結構めんどくさかったです 特殊なケースだと思いますが参考になれば幸いです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 6.x リダイレクト時にクエリパラメータを引き継ぐ

やりたいこと サービスをスタートしてからパスの構造を整理したりして、コンテンツを引っ越した時に、Laravelにはリダイレクトルートという便利な機能があり、 Route::redirect('/old/path', '/new/path'); や Route::permanentRedirect('/old/path', '/new/path'); のように記述して301、302のリダイレクトを行うことができます。こうしておけば旧パスでコンテンツをブックマークしている人にも404を返さなくてすみます。便利。 ただ、このredirectやpermanentRedirectはクエリパラメータを引き継いでくれないようで、例えば http://your.domain/old/path?param=hello のようなURIにアクセスされても http://your.domain/new/path にリダイレクトされてしまいます。パラメータを捨てられてしまうと動的なコンテンツはまともに生成できなくなってしまったりするので困ります。なんとか引越し先にパラメータを持っていきたい。 解決方法 以下のようにリダイレクトルートを記述すればクエリパラメータを引き継ぐことができます。 Route::redirect('/old/path', '/new/path?' . Arr::query(request()->query())); permanentRedirectでも同じです。単純にヘルパを用いてクエリストリングを新しいパスに追加しているだけです。 この方法でルートキャッシュも問題なく生成されるようでした。このようなサービスイン後のコンテンツのお引越しは多くはないとはいえ無いわけではないので、もしかしたらもっと単純な方法があるのではないか?という気もします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPで連想配列をPostgreSQLのpsql風にテーブル表示する

連想配列をPostgreSQLのpsql風にテーブル表示するコードです。 こんな感じで出力されます。 actor_id | first_name | last_name | last_update ----------+------------+--------------+------------------------ 1 | Penelope | Guiness | 2013-05-26 14:47:57.62 2 | Nick | Wahlberg | 2013-05-26 14:47:57.62 3 | Ed | Chase | 2013-05-26 14:47:57.62 4 | Jennifer | Davis | 2013-05-26 14:47:57.62 5 | Johnny | Lollobrigida | 2013-05-26 14:47:57.62 6 | Bette | Nicholson | 2013-05-26 14:47:57.62 7 | Grace | Mostel | 2013-05-26 14:47:57.62 8 | Matthew | Johansson | 2013-05-26 14:47:57.62 9 | Joe | Swank | 2013-05-26 14:47:57.62 10 | Christian | Gable | 2013-05-26 14:47:57.62 (10 行) なお、データはPostgreSQL tutorialのサンプルデータベースを使いました。 https://www.postgresqltutorial.com/postgresql-sample-database/ public function displayArray(array $rows) { if (!$rows) { echo '(0 行)' . PHP_EOL; return; } $col_names = array_keys($rows[0]); $max_width = []; $col_num = count($col_names); // 列名の表示幅を取得する for ($i = 0; $i < $col_num; $i++) { $max_width[$i] = mb_strwidth($col_names[$i]); } // 列名とその列のデータの最大の表示幅を調べる foreach ($rows as $row) { $values = array_values($row); for ($i = 0; $i < $col_num; $i++) { $val = $values[$i]; if (mb_strwidth($val) > $max_width[$i]) { $max_width[$i] = mb_strwidth($val); } } } $lines = []; // 列名の表示内容を組み立てる $a = []; for ($i = 0; $i < $col_num; $i++) { $format = '%-' . $max_width[$i] . 's'; $a[$i] = sprintf($format, $col_names[$i]); } $lines[] = ' ' . implode(' | ', $a); // 仕切り線の表示内容を組み立てる $a = []; for ($i = 0; $i < $col_num; $i++) { // +2は表示時の前後のブランクの分 $a[] = str_repeat('-', $max_width[$i] + 2); } $lines[] = implode('+', $a); // 各行の表示内容を組み立てる foreach ($rows as $row) { $values = array_values($row); for ($i = 0; $i < $col_num; $i++) { $val = $values[$i]; $space = str_repeat(' ', $max_width[$i] - mb_strwidth($val)); $values[$i] = is_numeric($val) ? $space . $val : $val . $space; } $lines[] = ' ' . implode(' | ', $values); } echo implode(PHP_EOL, $lines) . PHP_EOL; echo '(' . count($rows) . ' 行)' . PHP_EOL; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPStan 0.12.97 で literal-string が使えるようになっていた

literal-stringとは何かというと、PHPDocに書いて安全な文字列かどうかをチェックするための型のようだ。 ここにfeature リクエストがある。 何が変更されたかというと・・・ これだけ!? と思ってソースを確認したら ここでちゃんとした実装がコミットされてた。 これは便利と一瞬思ったけど、自分が必要なユースケースとはちょっとズレてるんだよなあ…。 まだこの自作の拡張を使う必要があるようだ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む