-
今回は、今更ながら each の使い方を再考してみます。
each には2種類の使い方があります。 ひとつは jQuery の セレクタ に対してのものと、もう一つは配列やコレクションやオブジェクトに対するものです。
共にそれぞれが内部に持っているものに対して、順次処理を行うことでは似た感じです。
先ずは jQuery の セレクタ に対しての each の書き方ですが、以下の様になります。$('セレクタ名').each(function([index[, element]]) { // index : 各要素のインデックス番号(値は 0 から始まる) // element : 繰り返し処理中の要素を参照 各要素に関する処理; });
これを使った例として以下に HTML ファイル側のソースを示します。■each を使った例
<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> //TEST関数 function testEach() { $('li').each(function(index, element) { console.log(index + ':' + $(element).text()); }); } </script> </head> <body> <h2>test each 1</h2> <li>ラベル1</li> <li>ラベル2</li> <li>ラベル3</li> <br /> <button onclick="javascript:testEach();">TEST</button> </body> </html>
9行目の .each(function(index, element) { ... }); で each 処理を行っています。
処理は要素の index と element で取得できるテキスト値をコンソールに出力しています。(以下の様になります。)0: ラベル1 1: ラベル2 2: ラベル3
each の処理で index と element を記述せずに以下の様にしても、同様な処理が行えます。
以下の例では element の代わりに this を使っています。 特に index を使う必要が無ければ、いつもは this で記述することが多いです。
この this ですが、each の繰り返しの処理の中で、現在のセレクタ?(オブジェクト)を指し示す変数みたいなものと考えて良いと思います。<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> //TEST関数 function testEach() { $('li').each(function() { // index , elemnt を省略 console.log($(this).text()); }); } </script> </head> <body> <h2>test each 2</h2> <li>ラベル1</li> <li>ラベル2</li> <li>ラベル3</li> <br /> <button onclick="javascript:testEach();">TEST</button> </body> </html>
この例から少し発展させて <li> 要素がクリックされた時に、クリックされたラベル以外の文字列を表示する様に変更します。<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> // 初期処理 $(function(){ // li 要素のクリック時の処理 $('li').on('click', function() { // クリック時の[this]退避 var this_click = this; // li 要素の全てを処理 $('li').each(function() { if (this_click != this) { // クリック以外のラベル console.log($(this).text()); } }); }); }); </script> </head> <body> <h2>test each 2</h2> <li>ラベル1</li> <li>ラベル2</li> <li>ラベル3</li> </body> </html>
それでは、配列やコレクションやオブジェクトに対する each について説明します。 構文的には以下の様になります。
$.each(collection, function(index, value) { // collection : each処理の対象となる配列またはオブジェクト // function : 各繰り返し処理で実行したい関数を指定 // index : 配列であればインデックス番号、オブジェクトであればkey // value : 繰り返し処理中の値 各要素に関する処理; });
$.each で宣言された関数の中で、引数である index, value を利用することが出来ます。
以下に、配列データとオブジェクトの中身を全てコンソールに出力する例を示します。
■配列やコレクションやオブジェクトに対する each 例
<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> //TEST関数 function testEach() { // 配列 var arr = ['A001', 'B002', 'C003']; $.each(arr, function(index, value) { console.log('index(' + index + ') = ' + value); }); // オブジェクト var obj = { 'key1' : 'A001', 'key2' : 'B002', 'key3' : 'C003' }; $.each(obj, function(index, value) { console.log('index(' + index + ') = ' + value); }); } </script> </head> <body> <h2>test each 5</h2> <button onclick="javascript:testEach();">TEST</button> </body> </html>
コンソールの出力は以下の様になります。オブジェクトの場合 index にキーが渡されることがわかります。index(0) = A001 index(1) = B002 index(2) = C003 index(key1) = A001 index(key2) = B002 index(key3) = C003
今回の例でも、 index, value を記述せずに this を使うことが出来ます。
■配列やコレクションやオブジェクトに対する each 例
<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> //TEST関数 function testEach() { // 配列 var arr = ['A001', 'B002', 'C003']; $.each(arr, function() { console.log(this.valueOf()); // [PrimitiveValue]の値 //console.log(this + ""); // この方法でもOK }); // オブジェクト var obj = { 'key1' : 'A001', 'key2' : 'B002', 'key3' : 'C003' }; $.each(obj, function() { console.log(this.valueOf()); // [PrimitiveValue]の値 //console.log(this + ""); }); } </script> </head> <body> <h2>test each 6</h2> <button onclick="javascript:testEach();">TEST</button> </body> </html>
$.each の中の this にはオブジェクト型で渡されるため、その中の値を取得するには this.valueOf とします。
この方法は面倒なので、一つ前の index, value を用いる方法が良いと思います。
最後に、繰返し処理のなかで for 文の break, continue の様な処理に付いて説明します。
以下の例では、6件の配列データを宣言し、2番目までは処理を continue し、5番目以降で break する処理を行います。
■配列に対する each 処理での break, continue 例
<html> <head> <meta charset="utf-8"> <title>test checkbox</title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script type="text/javascript"> //TEST関数 function testEach() { // 6件の配列 var arr = ['A001', 'B002', 'C003', 'D004', 'E005', 'F006']; $.each(arr, function(index, value) { // indexは[0]から始まることに注意!! if (index <= 1) { // 2番目までは処理をcontinue return true; } if (index >= 4) { // 5番目以降でbreak return false; } console.log('index(' + index + ') = ' + value); }); } </script> </head> <body> <h2>test each 6</h2> <button onclick="javascript:testEach();">TEST</button> </body> </html>
コンソールの出力は以下の様になります。index(2) = C003 index(3) = D004
関連する記事
⇒JavaScript 何に使う
⇒JavaScript jQueryの使い方(セレクタ)
⇒JavaScript jQueryの使い方(セレクタ)その2
⇒JavaScript 関数の宣言について(function)
⇒JavaScript jQueryでJavascriptファイルの動的に変更する方法
PR -
今回はPHPで使われる日付ライブラリの Carbon の使い方について説明したいと思います。
なお Carbon のインストールについては以下の記事を参照して下さい。
⇒PHP Carbon(ライブラリ)を使って日付処理・インストール
以下の順で説明したいと思います。■Carbon インスタンスの生成
先ずは Carbon クラスのインスタンスの生成について例をあげて説明します。
普通に new Carbon() とすることで Carbon のインスタンスを生成できます。 生成されたインスタンスを指し示す変数から各種のメソッドを使うことで各種の処理ができます。
また Carbon 備わった各種のスタティックなメソッドでもインスタンスの生成ができます。 以下の例では now() yesterday() tomorrow() などを示しています。
new Carbon('日付指定文字列') のクラスの引数に日付指定文字列を渡すことで指定した日付のインスタンスを生成できます。<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // インスタンス生成:現在のシステム日時 $dtCar = new Carbon(); echo "現在のシステム日時: ".$dtCar."<br>\n"; // 文字列を返すデフォルトのメソッドは「__toString()」 echo "現在のシステム日時: ".$dtCar->__toString()."<br>\n"; // static な関数による生成 // インスタンス生成(static):本日 $dtNow = Carbon::now(); echo "static-本日: ".$dtNow."<br>\n"; // インスタンス生成(static):昨日 $dtYes = Carbon::yesterday(); echo "static-昨日: ".$dtYes."<br>\n"; // インスタンス生成(static):明日 $dtTom = Carbon::tomorrow(); echo "static-明日: ".$dtTom."<br>\n"; // Carbonクラス生成時にパースを渡す // クラス生成時に日付文字列を渡す $dt1 = new Carbon('2022-11-30'); echo "日付文字列を渡す: ".$dt1."<br>\n"; // クラス生成時に日付加算文字列を渡す(システム日付[2022-11-29]に対して次の日) $dt2 = new Carbon('+1 days'); echo "日付加算文字列を渡す: ".$dt2."<br>\n"; // クラス生成時に月数加算文字列を渡す(システム日付[2022-11-29]に対して2か月後) $dt3 = new Carbon('+2 months'); echo "月数加算文字列を渡す: ".$dt3."<br>\n"; // クラス生成時に年数加算文字列を渡す(システム日付[2022-11-29]に対して2年後) $dt4 = new Carbon('+2 years'); echo "年数加算文字列を渡す: ".$dt4."<br>\n"; // クラス生成時に日指定文字列を渡す(システム日付[2022-11-29]に対して次月の初日) $dt5 = new Carbon('first day of next month'); echo "次月の最初の日: ".$dt5."<br>\n"; // クラス生成時に日指定文字列を渡す(システム日付[2022-11-29]に対して前月の初日) $dt6 = new Carbon('first day of last month'); echo "前月の最初の日: ".$dt6."<br>\n"; // クラス生成時に日指定文字列を渡す(システム日付[2022-11-29]に対して次月の末日) $dt7 = new Carbon('last day of next month'); echo "次月の最後の日: ".$dt7."<br>\n"; // クラス生成時に日指定文字列を渡す(システム日付[2022-11-29]に対して前月の末日) $dt8 = new Carbon('last day of last month'); echo "前月の最後の日: ".$dt8."<br>\n"; // クラス生成時に日付指定の生成メソッドを使用 $dt9 = Carbon::createFromDate(2022, 9, 25); echo "日付指定の生成メソッド: ".$dt9."<br>\n"; // クラス生成時に日時指定の生成メソッドを使用 $dt10 = Carbon::create(2022, 9, 25, 17, 10, 36, 'Asia/Tokyo'); // タイムゾーン指定は無くてもOK echo "日時指定の生成メソッド: ".$dt10."<br>\n"; ?>
このプログラムの実行結果は以下の通りです。
現在のシステム日時: 2022-11-29 08:48:39 現在のシステム日時: 2022-11-29 08:48:39 static-本日: 2022-11-29 08:48:39 static-昨日: 2022-11-28 00:00:00 static-明日: 2022-11-30 00:00:00 日付文字列を渡す: 2022-11-30 00:00:00 日付加算文字列を渡す: 2022-11-30 08:48:39 月数加算文字列を渡す: 2023-01-29 08:48:39 年数加算文字列を渡す: 2024-11-29 08:48:39 次月の最初の日: 2022-12-01 08:48:39 前月の最初の日: 2022-10-01 08:48:39 次月の最後の日: 2022-12-31 08:48:39 前月の最後の日: 2022-10-31 08:48:39 日付指定の生成メソッド: 2022-09-25 08:48:39 日付指定の生成メソッド: 2022-09-25 17:10:36
■日付データの取得(Getter)
Carbon クラスのインスタンスの生成しその日付データから各種の日付に関するデータを取得できます。
日付データのなかで良く使われるであろうものをピックアップしました。
取得の名前をみると VB.NET の DateTime 型のプロパティとよく似ていると思いますが、真似をしたのでしょうか?
とにかく、実際に例を以下に示します。<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // クラス生成時に日付文字列を渡す $dt = new Carbon('2022-11-30 12:25:30.123456'); echo "<pre>"; echo "日付:".$dt->format('Y/m/d H:i:s.u')."\n"; // 各種 getter(戻り値はInteger) // 年 echo '$dt->year :';var_dump($dt->year); // 月 echo '$dt->month :';var_dump($dt->month); // 日 echo '$dt->day :';var_dump($dt->day); // 時 echo '$dt->hour :';var_dump($dt->hour); // 分 echo '$dt->minute :';var_dump($dt->minute); // 秒 echo '$dt->second :';var_dump($dt->second); // マイクロ秒 echo '$dt->micro :';var_dump($dt->micro); // 週のIndex値 (日:0, 月:1, 火:2, 水:3, 木:4, 金:5, 土:6) echo '$dt->dayOfWeek :';var_dump($dt->dayOfWeek); // 年の何日目(0から始まる) echo '$dt->dayOfYear :';var_dump($dt->dayOfYear); // 月の何週目(1から始まる) echo '$dt->weekOfMonth:';var_dump($dt->weekOfMonth); // 年の何週目(1から始まる) echo '$dt->weekOfYear :';var_dump($dt->weekOfYear); // 月の日数 echo '$dt->daysInMonth:';var_dump($dt->daysInMonth); // 第何四半期か echo '$dt->quarter :';var_dump($dt->quarter); echo "</pre>"; ?>
このプログラムの実行結果は以下の通りです。これらの値は全て Integer 型で返されます。
日付:2022/11/30 12:25:30.123456 $dt->year :int(2022) $dt->month :int(11) $dt->day :int(30) $dt->hour :int(12) $dt->minute :int(25) $dt->second :int(30) $dt->micro :int(123456) $dt->dayOfWeek :int(3) $dt->dayOfYear :int(334) $dt->weekOfMonth:int(5) $dt->weekOfYear :int(48) $dt->daysInMonth:int(30) $dt->quarter :int(4)
■日付データの設定(Setter)
上記の「日付データの取得(Getter)」では各種の日付データの取得を行いましたが、 ここではその Getter の名前に値を設定することで日付データインスタンス内の値を変更することが出来ます。
<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // 取敢えずクラス生成 $dt = new Carbon(); // 各種 setter // 年 $dt->year = 2022; // 月 $dt->month = 12; // 日 $dt->day = 1; // 時 $dt->hour = 13; // 分 $dt->minute = 45; // 秒 $dt->second = 29; // マイクロ秒 $dt->micro = 987654; // 日付表示 echo "日付:".$dt->format('Y/m/d H:i:s.u')."\n"; ?>
このプログラムの実行結果は以下の通りです。
日付:2022/12/01 13:45:29.987654
■日付データの加減算
上記の「日付データの取得(Getter)」の名前に「add」及び「sub」では各種の日付データの取得を行いましたが、 ここではその Getter の名前に値を設定することで日付データインスタンス内の値を変更することが出来ます。
以下に加減算を行うメソッドの主なものを示します。 取敢えず実行結果は長くなるので、コメントとして右側に表示しています。<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // クラス生成時に日付文字列を渡す $dt = new Carbon('2022-11-30 12:25:30.123456'); echo "<pre>"; echo "日付:".$dt->format('Y/m/d H:i:s.u')."\n"; // 各種 加減算 // 世紀 echo '世紀'."\n"; // 取敢えず結果をコメントで表示 echo $dt->addCenturies(4)."\n"; //指定世紀数分加算 // 2422-11-30 12:25:30 echo $dt->addCentury()."\n"; //1世紀加算 // 2522-11-30 12:25:30 echo $dt->subCentury()."\n"; //1世紀減算 // 2422-11-30 12:25:30 echo $dt->subCenturies(4)."\n"; //指定世紀数分減算 // 2022-11-30 12:25:30 // 年 echo '年'."\n"; echo $dt->addYears(2)."\n"; //指定年数分加算 // 2024-11-30 12:25:30 echo $dt->addYear()."\n"; //1年加算 // 2025-11-30 12:25:30 echo $dt->subYear()."\n"; //1年減算 // 2024-11-30 12:25:30 echo $dt->subYears(2)."\n"; //指定年数分減算 // 2022-11-30 12:25:30 // 月 echo '月'."\n"; echo $dt->addMonths(60)."\n"; //指定月数分加算 // 2027-11-30 12:25:30 echo $dt->addMonth()."\n"; //1月加算 // 2027-12-30 12:25:30 echo $dt->subMonth()."\n"; //1月減算 // 2027-11-30 12:25:30 echo $dt->subMonths(60)."\n"; //指定月数分減算 // 2022-11-30 12:25:30 // 日 echo '日'."\n"; echo $dt->addDays(29)."\n"; //指定日数分加算 // 2022-12-29 12:25:30 echo $dt->addDay()."\n"; //1日加算 // 2022-12-30 12:25:30 echo $dt->subDay()."\n"; //1日減算 // 2022-12-29 12:25:30 echo $dt->subDays(29)."\n"; //指定日数分減算 // 2022-11-30 12:25:30 // 週 echo '週'."\n"; echo $dt->addWeeks(3)."\n"; //指定週分加算 // 2022-12-21 12:25:30 echo $dt->addWeek()."\n"; //1週加算 // 2022-12-28 12:25:30 echo $dt->subWeek()."\n"; //1週減算 // 2022-12-21 12:25:30 echo $dt->subWeeks(3)."\n"; //指定週分減算 // 2022-11-30 12:25:30 // 週(土日以外) echo '週'."\n"; echo $dt->addWeekdays(4)."\n"; //指定週分加算 // 2022-12-06 12:25:30 echo $dt->addWeekday()."\n"; //1世紀加算 // 2022-12-07 12:25:30 echo $dt->subWeekday()."\n"; //1世紀減算 // 2022-12-06 12:25:30 echo $dt->subWeekdays(4)."\n"; //指定週分減算 // 2022-11-30 12:25:30 // 時 echo '時'."\n"; echo $dt->addHours(24)."\n"; //指定時間分加算 // 2022-12-01 12:25:30 echo $dt->addHour()."\n"; //1時間加算 // 2022-12-01 13:25:30 echo $dt->subHour()."\n"; //1時間減算 // 2022-12-01 12:25:30 echo $dt->subHours(24)."\n"; //指定時間分減算 // 2022-11-30 12:25:30 // 分 echo '分'."\n"; echo $dt->addMinutes(61)."\n"; //指定分数加算 // 2022-11-30 13:26:30 echo $dt->addMinute()."\n"; //1分加算 // 2022-11-30 13:27:30 echo $dt->subMinute()."\n"; //1分減算 // 2022-11-30 13:26:30 echo $dt->subMinutes(61)."\n"; //指定分数減算 // 2022-11-30 12:25:30 // 秒 echo '秒'."\n"; echo $dt->addSeconds(61)."\n"; //指定秒数加算 // 2022-11-30 12:26:31 echo $dt->addSecond()."\n"; //1秒加算 // 2022-11-30 12:26:32 echo $dt->subSecond()."\n"; //1秒減算 // 2022-11-30 12:26:31 echo $dt->subSeconds(61)."\n"; //指定秒数減算 // 2022-11-30 12:25:30 echo "</pre>"; ?>
■日付データの比較
日付データの比較は「等しい」「大きい」「小さい」などの英語表記の以下のメソッドがあります。
- 等しい eq() equalTo() =
- 等しく無い ne() notEqualTo() !=
- 大きい gt() greaterThan() >
- 以上 gte() greaterThanOrEqualTo() >=
- 小さい lt() lessThan() <
- 以下 lte() lessThanOrEqualTo() <=
以下に比較を行うメソッドの主なものを示します。 取敢えず実行結果は長くなるので、コメントとして右側に表示しています。<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // クラス生成時に日付文字列を渡す // 日付の設定 $first = Carbon::create("2022-10-05"); $second = Carbon::create("2022-10-06"); echo "<pre>"; // $second から見て $first がどうかを比較 // equal var_dump($first->eq($second)); // bool(false) // not equal var_dump($first->ne($second)); // bool(true) // greater than var_dump($first->gt($second)); // bool(false) // greater than or equal var_dump($first->gte($second)); // bool(false) // less than var_dump($first->lt($second)); // bool(true) // less than or equal var_dump($first->lte($second)); // bool(true) echo "</pre>"; ?>
■日付データのデータ変換
Carbon には日付データから Array、Object(stdclass)、DateTimeクラス、DateTimeImmutableクラスへの変換用メソッドがあります。 これらのメソッドが有用かどうかは別として、取り敢えず紹介しておきます。
<?php // ライブラリ読込 require $_SERVER['DOCUMENT_ROOT'].'vendor/autoload.php'; // ライブラリ使用宣言 use Carbon\Carbon; // クラス生成 $dt = Carbon::createFromFormat('Y-m-d H:i:s.u', '2022-12-05 16:45:27.612984'); echo "<pre>"; // Array への変換 $arrDt = $dt->toArray(); var_dump($arrDt); // Object への変換(stdclass) $objDt = $dt->toObject(); var_dump($objDt); // DateTime への変換 $dateDt = $dt->toDate(); // Same as $dt->toDateTime() var_dump($dateDt); // DateTimeImmutable への変換 $objDtim = $dt->toDateTimeImmutable(); var_dump($objDtim); echo "</pre>"; ?>
このプログラムの実行結果は以下の通りです。
array(12) { ["year"]=> int(2022) ["month"]=> int(12) ["day"]=> int(5) ["dayOfWeek"]=> int(1) ["dayOfYear"]=> int(339) ["hour"]=> int(16) ["minute"]=> int(45) ["second"]=> int(27) ["micro"]=> int(612984) ["timestamp"]=> int(1670258727) ["formatted"]=> string(19) "2022-12-05 16:45:27" ["timezone"]=> object(Carbon\CarbonTimeZone)#4 (2) { ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } object(stdClass)#3 (12) { ["year"]=> int(2022) ["month"]=> int(12) ["day"]=> int(5) ["dayOfWeek"]=> int(1) ["dayOfYear"]=> int(339) ["hour"]=> int(16) ["minute"]=> int(45) ["second"]=> int(27) ["micro"]=> int(612984) ["timestamp"]=> int(1670258727) ["formatted"]=> string(19) "2022-12-05 16:45:27" ["timezone"]=> object(Carbon\CarbonTimeZone)#5 (2) { ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } } object(DateTime)#6 (3) { ["date"]=> string(26) "2022-12-05 16:45:27.612984" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } object(DateTimeImmutable)#8 (3) { ["date"]=> string(26) "2022-12-05 16:45:27.612984" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" }
-
DateTime クラス(オブジェクト)は良く使うクラスですが、コピーについては注意が必要なことがあります。
以下のソースを見て下さい。単に「$datetime1」に日付を設定し「$datetime2」にコピーをしています。<?php $datetime1 = new DateTime('2022-11-20 10:30:00'); $datetime2 = $datetime1; $datetime2->modify('+1 day'); echo "\$datetime1 = ".$datetime1->format('Y-m-d')."<br>\n"; echo "\$datetime2 = ".$datetime2->format('Y-m-d')."<br>\n"; ?>
これを実行するとブラウザに以下の様に表示されます。$datetime1 = 2022-11-21 $datetime2 = 2022-11-21
「$datetime2」にコピーをしているので「$datetime1」に影響しないと思いきや、「$datetime2」に modify メソッドで日付を1日加算すると 「$datetime1」「$datetime2」共に同じ日付になります。
「$datetime2」へのコピーでは参照渡しの様な感じになる様です。(実際のPHPの中では参照では無い様ですが…)
そこで、これを解消するにはクローン命令で別のインスタンスを作成し「$datetime2」に代入する必要があります。<?php $datetime1 = new DateTime('2022-11-20 10:30:00'); $datetime2 = clone $datetime1; echo "\$datetime1 = ".$datetime1->format('Y-m-d')."<br>\n"; echo "\$datetime2 = ".$datetime2->format('Y-m-d')."<br>\n"; $datetime2->modify('+1 day'); echo "\$datetime2 = ".$datetime2->format('Y-m-d')."<br>\n"; ?>
これを実行するとブラウザに以下の様に表示されます。$datetime1 = 2022-11-20 $datetime2 = 2022-11-20 $datetime2 = 2022-11-21
modify や add メソッドの戻り値は自分自身の DateTime オブジェクトを返します。
実際 PHP の正規のマニアルには以下の様に記述されています。戻り値: メソッドチェインに使う、変更された DateTime オブジェクトを返します。
メソッドチェイン とは以下の様な使い方です。
<?php $datetime1 = new DateTime('2022-11-30 10:30:00'); echo "\$datetime1 = ".$datetime1->modify('+1 day')->format('Y-m-d')."<br>\n"; echo "\$datetime1 = ".$datetime1->modify('+1 day')->modify('+1 day')->format('Y-m-d')."<br>\n"; echo "\$datetime1 = ".$datetime1->format('Y-m-d')."
\n"; ?>3行目では modify メソッドの直後に format メソッドを呼出しています。modify は DateTime オブジェクトを返すので、この方法が可能です。 さらに、4行目では modify メソッドの直後に再度 modify メソッドを呼出しています。
実行結果は以下の様に表示されます。 5行目では再度「$datetime1」の値を表示していますが、3回の modify の実行で日付は初期値から3日後になります。 modify では自分のオブジェクトを変更することがわかります。$datetime1 = 2022-12-01 $datetime1 = 2022-12-03 $datetime1 = 2022-12-03
ところで DateTime の値を変更する modify メソッド等で新しい DateTime オブジェクトを返したい場合には DateTimeImmutable クラスを使用します。Immutable(イミュータブル) とは「不変」「変わらない」ことを意味するそうです。
では DateTimeImmutable クラスを使用して最初のソースを変更します。<?php $datetime1 = new DateTimeImmutable('2022-11-20 10:30:00'); $datetime2 = $datetime1->modify('+1 day');; echo "\$datetime1 = ".$datetime1->format('Y-m-d')."<br>\n"; echo "\$datetime2 = ".$datetime2->format('Y-m-d')."<br>\n"; ?>
実行結果は以下の様に表示されます。 「$datetime1」の modify メソッドの結果は別のオブジェクトとして「$datetime2」に設定された様です。 ただし、「$datetime1」の値は変化がありません。
$datetime1 = 2022-11-20 $datetime2 = 2022-11-21
-
以下の記事では、クラス(オブジェクト)の反復処理について Iterator インターフェースを使うところまでは説明を行いました。
⇒PHP クラス(オブジェクト)の反復処理について
Iterator インターフェースでは5個のメソッドを宣言しなければならないので、そのあたりを簡単にできる方法を説明します。 比較の為に、前回のソースを以下に示します。<?php // Iterator インターフェースを利用した反復処理テストクラス class cEach implements Iterator { // 内部の配列とする private $list = ["list1", "list2", "list3", "list4", "list5"]; // 配列の位置 private $pos = 0; // コンストラクタ public function __construct() { $this->pos = 0; } // 巻き戻し(初期位置) public function rewind(): void { $this->pos = 0; } // 現在値 public function current() { return $this->list[$this->pos]; } // 現在キー public function key() { return $this->pos; } // 次の位置へ public function next(): void { ++$this->pos; } // 現在位置のデータ存在チェック public function valid(): bool { return isset($this->list[$this->pos]); } } // [cEach]クラスの生成 $insEach = new cEach(); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } ?>
上記のクラスを今回は IteratorAggregate インターフェイスを使ってクラス(オブジェクト)の反復処理を書き変えます。
IteratorAggregate インターフェイスの概要は以下の様です。 getIterator メソッドの宣言は必須となります。interface IteratorAggregate extends Traversable { /* メソッド */ public getIterator(): Traversable // 外部イテレータを取得する }
それでは、書き変えたソースを以下に示します。
getIterator メソッドの戻り値に ArrayIterator クラスの生成値を戻しています。
getIterator の戻り値は Traversable インターフェース型である必要がありますが、 ArrayIterator は配列型データを Traversable インターフェース型のクラスを生成しています。<?php // IteratorAggregate インターフェースを利用した反復処理テストクラス class cEach implements IteratorAggregate { // 内部の配列とする private $list; // コンストラクタ function __construct(array $_list) { // 外部からの配列を内部に退避(配列はシャローコピーではないので) $this->list = $_list; } // 外部イテレータを取得する public function getIterator() { return new ArrayIterator($this->list); } } // [cEach]クラスの生成 $arr = ["list1", "list2", "list3", "list4", "list5"]; $insEach = new cEach($arr); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } ?>
これを実行するとブラウザに以下の様に表示されます。0 => list1 1 => list2 2 => list3 3 => list4 4 => list5
上記の ArrayIterator を使っている部分を ジェネレータ と呼ばれる yield を使う方法でもできます。
以下に変更したソースを示します。<?php // IteratorAggregate インターフェースを利用した反復処理テストクラス class cEach implements IteratorAggregate { // 内部の配列とする private $list; // コンストラクタ function __construct(array $_list) { // 外部からの配列を内部に退避(配列はシャローコピーではないので) $this->list = $_list; } // ジェネレータ(yield)を使用する public function getIterator() { foreach ($this->list as $key => $val) { yield $key => $val; } } } // [cEach]クラスの生成 $arr = ["list1", "list2", "list3", "list4", "list5"]; $insEach = new cEach($arr); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } ?>
ジェネレータ と呼ばれる yield について少し説明します。
PHP の公式ドキュメントには以下の様に記述されています。ジェネレータ関数が呼ばれると、反復処理が可能なオブジェクトを返します。 このオブジェクトを (foreach ループなどで) 反復させると、値が必要になるたびに PHP がオブジェクトの反復メソッドを呼びます。 そして、ジェネレータが値を yield した時点の状態を保存しておき、 次に値が必要になったときにはそこから再開できるようにします。 yield できる値がなくなると、ジェネレータは単純に終了します。呼び出し元のコードでは、配列の要素をすべて処理し終えた後のように、そのまま処理が続きます。
ジェネレータ関数では yield は return 命令の様な感じで動きますが、 呼び出し元で繰り返し呼ばれることで、順次出力する内容が呼び出し元に返されます。
以下の例のジェネレータ関数は内部で宣言された配列を順次 yield で出力しています。 呼び出し元では foreach 命令の「$value」に値が返ってきます。<?php // ジェネレータ関数 function generator_func() { $arr = ["list1", "list2", "list3"]; foreach($arr as $val) { yield $val; } } // ジェネレータ関数の呼出 foreach (generator_func() as $value) { echo "$value<br>\n"; } ?>
yield では {キー} => {値} の形での指定もできます。ソースを変更しました。
<?php // ジェネレータ関数 function generator_func() { $arr = ["list1", "list2", "list3"]; foreach($arr as $key => $val) { yield $key => $val; } } // ジェネレータ関数の呼出 foreach (generator_func() as $key => $value) { echo "$key => $value<br>\n"; } ?>
-
配列の反復処理では foreach 命令を使って配列のデータを順次処理することができます。 この foreach 命令ですが、オブジェクトの反復処理にも使用することができます。
オブジェクトの反復処理に使用する場合、書式は以下の様になります。 書き方は配列の場合と何も変わったところはありません。
ただし、アクセスできる変数はアクセス権限のあるメンバー変数のみを取得できます。foreach (オブジェクト変数 as メンバ変数名 => メンバ変数の値) { }
それでは、この反復処理を行う例を以下に示します。<?php // 反復処理テストクラス class cEach { // public プロパティ public $public_pub_para1 = "public_val1"; public $public_param2 = "public_val2"; public $public_param3 = "public_val3"; // protected プロパティ protected $protected_param1 = "protected_val1"; protected $protected_param2 = "protected_val2"; // private プロパティ private $private_param1 = "private_val1"; // 自分のクラス内の反復処理 public function list_cEach() { echo "クラスの内での反復...<br>\n"; foreach($this as $key => $value) { echo "$key => $value<br>\n"; } } } // [cEach]クラスの生成 $insEach = new cEach(); // インスタンスから取得できる反復データ echo "クラスの外からの反復...<br>\n"; foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } // クラス内部で取得できる反復データ $insEach->list_cEach(); ?>
このプログラムを実行した時に、最初に「cEach」クラスを生成し、28行目の foreach 命令が実行されます。 「cEach」クラスのインスタンス「$insEach」からアクセスできるのは「cEach」クラスの public 属性を持った変数のみです。
その後で「cEach」クラスの list_cEach 関数をコールすることでクラス内のアクセス可能な変数を全て反復処理できます。 18行目の foreach 命令で行います。
これを実行するとブラウザに以下の様に表示されます。クラスの外からの反復... public_param1 => public_val1 public_param2 => public_val2 public_param3 => public_val3 クラスの内での反復... public_param1 => public_val1 public_param2 => public_val2 public_param3 => public_val3 protected_param1 => protected_val1 protected_param2 => protected_val2 private_param1 => private_val1
foreach 命令はアクセス権限のあるメンバー変数のみを取得しますので、 クラスの外側では protected および private 属性の変数は取得できないことになります。
クラスのインスタンスに対する foreach 命令は public 変数を順次取得できるということです。
ただ、これ(public 変数を順次取得することが)が何に役立つのでしょうか?
このままでは前に進みませんので以下の例を示します。
<?php // 反復処理テストクラス class cEach { // public プロパティ public $public_list = ["list1", "list2", "list3"]; } // [cEach]クラスの生成 $insEach = new cEach(); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } ?>
これを実行すると以下の様なエラーが表示されます。13行目で取得した「$value」は配列のデータなので文字列型への変換は注意!と表示されました。Notice: Array to string conversion in C:\xampp\htdocs\_test\class15-each-2.php on line 13 public_list => Array
「$value」は配列データなので当たり前なので、以下の様にする必要があります。<?php // 反復処理テストクラス class cEach { // public プロパティ public $public_list = ["list1", "list2", "list3"]; } // [cEach]クラスの生成 $insEach = new cEach(); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { if (is_array($value)) { // 配列の場合は再度foreach foreach($value as $keyArr => $valueArr) { echo "$keyArr => $valueArr<br>\n"; } } else { echo "$key => $value<br>\n"; } } ?>
一応結果は以下の様になります。0 => list1 1 => list2 2 => list3
上記の方法では便宜的で一般性に欠けるので、クラスの方で仕掛けが必要になります。
PHP ではこの様な場合の反復処理が出来る様に Iterator インターフェースが実装されています。interface Iterator extends Traversable { /* メソッド */ public current(): mixed // 現在の要素を返す(あらゆる型を返すことが可能です) public key(): mixed // 現在の要素のキーを返す(成功した場合にスカラー型,失敗した場合にnullを返す) public next(): void // 次の要素に進む public rewind(): void // イテレータの最初の要素に巻き戻す public valid(): bool // 現在位置が有効かどうかを調べる(成功した場合にtrueを,失敗した場合にfalseを返す) }
Iterator インターフェースを利用して先ほどのクラスを変更します。 尚、5個のメソッドは必ず宣言する様です。<?php // Iterator インターフェースを利用した反復処理テストクラス class cEach implements Iterator { // 内部の配列とする private $list = ["list1", "list2", "list3", "list4", "list5"]; // 配列の位置 private $pos = 0; // コンストラクタ public function __construct() { $this->pos = 0; } // 巻き戻し(初期位置) public function rewind(): void { $this->pos = 0; } // 現在値 public function current() { return $this->list[$this->pos]; } // 現在キー public function key() { return $this->pos; } // 次の位置へ public function next(): void { ++$this->pos; } // 現在位置のデータ存在チェック public function valid(): bool { return isset($this->list[$this->pos]); } } // [cEach]クラスの生成 $insEach = new cEach(); // インスタンスから取得できる反復データ foreach($insEach as $key => $value) { echo "$key => $value<br>\n"; } ?>
これを実行すると以下の様に表示されます。 Iterator インターフェースを利用したクラスの foreach ではちゃんと処理される様です。0 => list1 1 => list2 2 => list3 3 => list4 4 => list5
これでもまだ改良の余地がありそうです。 このままではキー値が数値しか扱えませんし、クラスの生成時に外からリストを可変に設定したいとかいろいろ考えられます。
それらの件に付いてはまた別の記事で書いていこうと思います。