質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

4回答

2291閲覧

PHP クッキーとインクリメント演算子

gsuisk

総合スコア72

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2017/08/31 17:26

ブラウザがこのページ(1.php)を何回訪れたかをcookieを使って表示させました。

以下はそのページ(1.php)のプログラムです。

PHP

1<?php 2 3if (isset($_COOKIE["visit"])){ 4 $count = $_COOKIE["visit"]; 5} else { 6 $count = 0; 7} 8 9$set = setcookie("visit", ++$count, time()+60*5); 10?> 11 12<!DOCTYPE html> 13<html lang="ja"> 14<head> 15<meta charset="utf-8"> 16<title>page1</title> 17</head> 18 19<body> 20 21 <?php 22 if ($set) { 23 echo "このページの訪問は", $count, "回目です。"; 24 echo '<a href="2.php">ページを移動する</a>'; 25 echo '(<a href="3.php">リセットする</a>)'; 26 } else { 27 echo "エラー"; 28 } 29 ?> 30 31</body> 32</html> 33

このプログラムでは正しく動作するのですが、setcookie()の部分の値を$count++にすると、何度ページを訪問しても1回目と表示されます。

$set = setcookie("visitedCount", $count++, time()+60*5);
$count = 0; echo "0 となります: " . $count++; echo "1 となります: " . $count; $count = 0; echo "1 となります: " . ++$count; echo "1 となります: " . $count;

こうだとすれば、ページを訪問し続けると、

$count++も同じく「1回目の訪問」「2回目の訪問」「3回目の訪問」...

となりませんか?++$countで正しく動作するのは理解できるのですが、$count++で何度訪問しても1回目になる理由がわかりません。

以下の認識は正しくないでしょうか?

$count++の場合。まず最初のcount++は0を返す。$countには1が代入される。 そして「1回目の訪問」と出力。 二回目の訪問では$countには1が入っているので、$count++は1を返す。 そして$countには2が代入される。 そして「2回目の訪問」と出力 $++countの場合。まず最初のcount++は1を返す。$countには1が代入される。 そして「1回目の訪問」と出力。 二回目の訪問では$countには1が入っているので、$count++は2を返す。 そして$countには2が代入される。 そして「2回目の訪問」と出力

細かかくてすみません。教えていただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

PHP に限らず、インクリメント演算子 (加算子) やデクリメント演算子 (減算子) を使った場合の典型的な誤りですね。こういう見つけにくいバグを産むからインクリメント演算子の使用を極力禁じる流派もあるくらいです。特に今回のケースのように、スタック外と値のやりとりをする副作用がある場合では使わないほうが無難です。賢い記述に見えて、潜在的なバグを増やす原因です。

前置加算子 ++$count は、評価時に $count の値を 1 増やしてから $count の値を返します。通常、変数の値を変更するの (副作用) は明示的に代入が必要です。加算子は、暗黙的に代入がなされる、かなり特殊な性質を持つ演算子です。
模式的には、

PHP

1$set = setcookie("visitedCount", ++$count, time()+60*5);

PHP

1$count += 1; 2$set = setcookie("visitedCount", $count, time()+60*5);

と同じです。

後置加算子は、$count++ は、評価時に $count の値を返したあと$count の値を 1 増やします。

PHP

1$set = setcookie("visitedCount", $count++, time()+60*5);

は、

PHP

1$set = setcookie("visitedCount", $count, time()+60*5); 2$count += 1;

と同じ、ということです。(正確には$set への代入文の評価より先に $count への代入がなされるのでまったく同じというわけではない。)

まあ、早い話が、for ($i = 0; $i < 5; $i++) みたいなイディオム以外のところでは、++ は使わず、+= 1 を使うといいよ、ということです。

投稿2017/08/31 19:57

unau

総合スコア2468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gsuisk

2017/09/01 10:53

置き換えたプログラムがとてもわかりやすかったです! $count++だと、クッキーの値が永遠に0になってしまうのですね。 実行の順序としては クッキーの値が0に設定 → $count+=1で、$countには1が入る(1回目の訪問と出力) 既存のクッキーの値0が$countに代入 → $count+=1で、$countには1が入る(1回目の訪問と出力) とループしてしまうのですね。(もし間違っていたら返信ください、、) ありがとうございました!
unau

2017/09/01 11:27

間違っていないけど返信しちゃいました。 この加算子のバグは知らないとなかなか見つけられないんですよね。
guest

0

訪問時には、$_COOKIE['visit']$countに代入していますので、
前回訪問時にsetcookieしたときの値が$countの初期値としてセットされます。

ですので、厳密には前回「X回目の訪問です」と表示した時点での$count変数の中身は引き継がれていません。
setcookie()でセットした値が引き継がれます。

このとき、setcookie()に渡されるのは**$count変数そのものではなく、その時点での$count変数の中身が渡される**ので、$count++と++$countでセットされる値が異なります。

説明を正しく書き直すと、(ちょっとくどいですが)下記のようになります。
付け加えた箇所は太字にしてあります。


$count++の場合。Cookieに値がセットされていないので、$countには0がセットされる。
まず最初のcount++は0を返し、Cookieには0がセットされる。。$countには1が代入される。
そして「1回目の訪問」と出力。
二回目の訪問ではCookieに0がセットされているので、$countには0がセットされる。
$countには0が入っているので、$count++は0を返し、Cookieには0がセットされる
そして$countには1が代入される。
そして「1回目の訪問」と出力

$++countの場合。Cookieに値はセットされていないので、$countには0がセットされる。
まず最初のcount++は1を返し、Cookieには1がセットされる。$countには1が代入される。
そして「1回目の訪問」と出力。
二回目の訪問ではCookieには1がセットされているので、$countには1がセットされる
$count++は2を返し、Cookieには2がセットされる。$countには2が代入される。
そして「2回目の訪問」と出力

投稿2017/08/31 19:04

tsuemura

総合スコア663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gsuisk

2017/09/01 10:43

ありがとうございます! 訂正された説明がとてもわかりやすいです!
guest

0

setcookieの引数で変数自体を渡しているのではなく、値を渡しているから当然の挙動では?

PHP

1$a = 0; 2echo $a; 3$a++;

こう書いても echo される値は変わりませんよね。

PHP

1setcookie( 2 "visitedCount", 3 $count++, // 持っている値をsetcookieに渡してから、$countを加算している 4 time()+60*5 5);

投稿2017/08/31 17:31

編集2017/08/31 17:37
kei344

総合スコア69366

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/09/01 00:31

当然である事をわかっていないから質問しているんだと思います。
guest

0

切り分けがかなり進んでいるので後ちょっとでしたね。
PHP のマニュアルはかなり優秀なので、意図しない動作の殆どは記載されています。以下を参照すると良いです。

加算子/減算子

|例|名前|効果|

|:--|:--:|:--|
|++$a|前置加算子|$a に 1 を加え、$a を返します。
|$a++|後置加算子|$a を返し、$a に1を加えます。
|--$a|前置減算子|$a から 1 を引き、$a を返します。
|$a--|後置減算子|$a を返し、$a から 1 を引きます。

ちなみに、本件とは関係ないですが、for で使用する場合のインクリメントは++$iのほうが早いです。
phpのifとかインクリメントとかforとかwhileとかの速度を測ってみた
調べてみるといろいろおもしろいものが出てきます。

投稿2017/08/31 23:29

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問