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

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

ただいまの
回答率

88.05%

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

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 1,404

score 72

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

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

<?php

if (isset($_COOKIE["visit"])){
  $count = $_COOKIE["visit"];
} else {
  $count = 0;
}

$set = setcookie("visit", ++$count, time()+60*5);
?>

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>page1</title>
</head>

<body>

  <?php
  if ($set) {
    echo "このページの訪問は", $count, "回目です。";
    echo '<a href="2.php">ページを移動する</a>';
    echo '(<a href="3.php">リセットする</a>)';
  } else {
    echo "エラー";
  }
  ?>

</body>
</html>

このプログラムでは正しく動作するのですが、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回目の訪問」と出力

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+5

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

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

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


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


と同じです。

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

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


は、

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


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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/01 19:53

    置き換えたプログラムがとてもわかりやすかったです!
    $count++だと、クッキーの値が永遠に0になってしまうのですね。

    実行の順序としては

    クッキーの値が0に設定 → $count+=1で、$countには1が入る(1回目の訪問と出力)

    既存のクッキーの値0が$countに代入 → $count+=1で、$countには1が入る(1回目の訪問と出力)

    とループしてしまうのですね。(もし間違っていたら返信ください、、)

    ありがとうございました!

    キャンセル

  • 2017/09/01 20:27

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

    キャンセル

+5

訪問時には、$_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/09/01 19:43

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

    キャンセル

+1

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

$a = 0;
echo $a;
$a++;

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/01 09:31

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

    キャンセル

0

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

加算子/減算子

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る