sit in a circle and chat happily

PHP 8日目

投稿日:2022-01-24
更新日:2022-03-24
php

複数の値を送受信

チェックボックスから複数の値を送信

チェックボックスを使って複数の値を送信することができる。

  <form action="03-2_array_confirm.php" method="get">
    <h2>趣味は?</h2>
    <label><input type="checkbox" name="hobby[]" value="1">音楽鑑賞</label>
    <label><input type="checkbox" name="hobby[]" value="2">映画鑑賞</label>
    <label><input type="checkbox" name="hobby[]" value="3">ドライブ</label>
    <label><input type="checkbox" name="hobby[]" value="4">旅行</label>
    <label><input type="checkbox" name="hobby[]" value="5">その他</label>

    <p>
      <input type="submit" value="投票する">
    </p>
  </form>

チェックボックスのような複数選択できる入力パーツの場合、送信する値の名前を工夫する必要がある。

よく使われる手法としてname属性に「値の名前[]」を指定する方法がある。これにより、同じカテゴリのチェックボックスは同名の配列で管理されることになり、PHPで値を受け取った後の処理が楽になる。

    <label><input type="checkbox" name="hobby[]" value="1">音楽鑑賞</label>
    <label><input type="checkbox" name="hobby[]" value="2">映画鑑賞</label>
    <label><input type="checkbox" name="hobby[]" value="3">ドライブ</label>
    <label><input type="checkbox" name="hobby[]" value="4">旅行</label>
    <label><input type="checkbox" name="hobby[]" value="5">その他</label>

チェックボックスから送られた複数の値を取得

  <?php
    echo '<h2>1回目の出力</h2>';
    echo '<pre>';
    var_dump($_GET);
    echo '</pre>';

    echo '<h2>2回目の出力</h2>';
    if(isset($_GET['hobby'])){
      $getHobby = $_GET['hobby'];
      if(is_array($getHobby)){
        foreach($getHobby as $value){
          echo $value . '<br>';
        }
      } else {
        echo 'エラー:配列ではありません';
      }
    } else {
      echo '趣味を選択していません';
    }
  ?>
  <p>
    <a href="03-1_array_form.html">フォームに戻る</a>
  </p>

「hobby[]」でname属性を記述しているので、受信時にhobbyという名前の配列になる。

name属性値:送信される値の名前
value属性値:送信される値

    echo '<h2>1回目の出力</h2>';
    echo '<pre>';
    var_dump($_GET);
    echo '</pre>';
/*
チェックボックスは複数選択可能なので
配列管理できるように値を送受信する
◆送信側
name属性を「グループ名[]」とする
◆受信側
2次元配列で管理される
◇ラジオボタンやチェックボックスは
選択しないと値を送信しない
 */
  echo '<h2>2回目の出力</h2>';
  /*
値を確認してPGを組む
・値が届いているか
・配列かどうか
※数字か(今回は省略)
※範囲内か(今回は省略)
*/
  if (isset($_GET['hobby'])) {
    // 値が届いているときの処理
    $getHobby = $_GET['hobby'];
    // 短い名前の変数に代入して次のチェック

    // 配列かどうか確認:is_array関数
    // 引数の値が配列の時trueを返す
    // 配列でない時falseを返す
    if (is_array($getHobby)) {
      // 引数が配列時の処理
      foreach ($getHobby as $value) {
        echo $value . '<br>';
      }
    } else {
      // 引数が配列でない時の処理
      echo 'エラー:配列ではありません';
    }
  } else {
    echo '趣味を選択していません';
  }

変数が定義されているか調べる:isset命令

「isset($_GET[‘hobby’])」は「$_GET[‘hobby’]」が存在するか調べる。
もし部屋が存在すれば「true」を返し、存在しなければ「false」を返す。

趣味を選択した時:isset($_GET[‘hobby’]) → true

    $getHobby = $_GET['hobby'];
    if (is_array($getHobby)) {
      foreach ($getHobby as $value) {
        echo $value . '<br>';
      }
    } else {
      echo 'エラー:配列ではありません';
    }

趣味を選択していない時:isset($_GET[‘hobby’]) → false

    echo '趣味を選択していません';

扱いやすい変数名で処理する

「$_GET[‘hobby’]」をそのまま使ってもよいが、呼び出しやすい名前の新しい変数getHobbyに代入して以後の処理を実行する。PHPの場合は配列がそのまま新しい変数getHobbyに代入される。

$getHobby = $_GET['hobby']

配列かどうか調べる:is_array関数

「is_array($getHobby)」は「$getHobby」が配列か調べる。
もし配列なら「true」、配列でなければ「false」を返す。
不正な値が送信され、受け取った値が配列でないときにエラーメッセージを表示させる。

$getHobbyが配列の時:is_array($getHobby) → true

foreach ($getHobby as $value) {
  echo $value . '<br>';
}

$getHobbyが配列でない時:is_array($getHobby) → false

echo 'エラー:配列ではありません';

配列内の値をforeach繰返し処理で表示

foreach ($getHobby as $value) {
  echo $value . '<br>';
}

【チェックボックス】必要なチェックをすべて実装する

  if (isset($_GET['hobby'])) {
    // 値が届いているときの処理
    $getHobby = $_GET['hobby'];
    // 短い名前の変数に代入して次のチェック

    // 配列かどうか確認:is_array関数
    // 引数の値が配列の時trueを返す
    // 配列でない時falseを返す
    if (is_array($getHobby)) {
      // 引数が配列時の処理
      foreach ($getHobby as $value) {
        // 数字かどうかチェック
        if (ctype_digit($value)) {
          // 数字時の処理
          // 範囲内かチェック
          if ($value < 1 || $value > 5) {
            // 範囲外の時の処理
            echo 'エラー:不正な値(範囲外)です';
          } else {
            echo $value . '<br>';
          }
        } else {
          // 数字じゃない時の処理
          echo 'エラー:不正な値(数字じゃない)です<br>';
        }
      }
    } else {
      // 引数が配列でない時の処理
      echo 'エラー:配列ではありません';
    }
  } else {
    echo '趣味を選択していません';
  }

第14章:ファイルの読み込み

ファイルを読み込む

Webページの各コンテンツをパーツ化して読み込むことができる。

require '読み込むファイルのパス';
require_once '読み込むファイルのパス';
include '読み込むファイルのパス';
include_once '読み込むファイルのパス';
命令動作読み込み失敗時の対応
require複数回読み込み可能Fatal errorが表示され処理が停止する
require_once1回のみ読み込み可能Fatal errorが表示され処理が停止する
include複数回読み込み可能Warningが表示されるが、処理は続行する
include_once1回のみ読み込み可能Warningが表示されるが、処理は続行する

ファイルが読み込めなかった時に処理が継続されるとこまる場合は「require」や「require_once」を使用する。ファイルが読み込めなくても致命的な問題が発生しない場合は「include」や「include_once」でもOK。

「require_once」は「include_once」は1回も二読み込む。もし同じファイルを複数回読み込む記述になっていた場合、初回のみ読み込み処理を実行し、以降重複読み込み処理は無視される。

対象ファイルのディレクトリパスをもったマジカル定数

_DIR_

PHPには自動的に定義される定数(マジカル定数)がある。
「_DIR_」(DIRの前後にアンダースコア2つ)には対象ファイルが所属するディレクトリまでの絶対パスが自動的に格納される。

ファイルを読み込む場合はこのマジカル定数「_DIR_」を使用し絶対パスで読み込み対象ファイルを指定することが推奨される。

require 'header.php';

相対パスで記述しても対象ファイルを読み込むことができるが、処理速度が遅くなることがある。

require '/header.php';

実行ファイルのディレクトリ位置を示す「./」を付けた相対パスの場合には問題が発生することがある。

★複雑なパーツ構成になっても問題が起きないよう、また処理速度を早くするために「_DIR_」を使用すること。

<?php
$title = '001ページ';

// 絶対パス・相対パスどちらでもファイル読み込みOK
//  ※絶対パスでの読み込みを推奨
//   ・処理が少し早い
//   ・パーツの構成が複雑になっても問題が発生しづらい

// 相対パスでの読み込み
require 'header.php';

// 絶対パスでの読み込み
require __DIR__ . '/header.php';
require __DIR__ . '/parts01.php';
require __DIR__ . '/footer.php';

// マジカル定数を確認
var_dump(__DIR__);
// string(48) "C:\Users\ica_oa\Desktop\xampp\htdocs\sample\14_1"
// ファイルのディレクトリ(フォルダ)までの絶対パスが管理されている
// ディレクトリまでのパスなので
// ディレクトリから読み込みたいファイルのパスを
// 文字列連結して使用する
// その際、連結する文字の最初に「/」を忘れないようにする
/*

【ファイルを読み込む】
require命令:エラー時処理ストップ
    関数定義ファイルなど
    読み込まれないとPGやページが成立しない場合
include命令:エラー時処理続行
    バナーなど
    読み込まれなくてもページへの影響が少ない場合

once有り:
    関数定義ファイルなどで
    最初1回のみファイルを読み込む
    2回目以降はすべて無視
once無し:
    何度でも読み込む
*/

本体

<?php
$title = '001ページ';
require __DIR__ . '/header.php';
require __DIR__ . '/parts01.php';
require __DIR__ . '/footer.php';

「require命令」で別ファイルにした書くパーツを読み込む。読み込み前に宣言した変数を各パーツ内で使用できる。

PHPのみを記述したファイルはPHPブロックを閉じる「?>」をあえて記述しない。PHPブロックを閉じないことで無駄な改行タグを出力せずにすむ。

PHPはHTML内にプログラムを書けるため手軽に利用できる反面、ソースコード内が複雑になりがち。しかし、ファイルの読み込みを使用することで、デザインとプログラムを分離することができる。

各ファイルを分けることでプログラマーが本体を、各パーツをコーダーが手分けして作成することができ、分業しながらWebシステムを作成することができる。
また各パーツを共通化して別ページで使いまわすこともでき、運用や更新しやすい仕組みを作ることができる。

ヘッダーパーツ

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title><?php echo $title; ?> | サイト名</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="header">
    <div class="inner">
      <div class="logo">サイト名 : <?php echo $title; ?></div>
      <div class="nav">
        <ul class="nav_list flex-pc">
          <li class="nav_listItem w33-pc"><a href="001.php">Menu1</a></li>
          <li class="nav_listItem w33-pc"><a href="002.php">Menu2</a></li>
          <li class="nav_listItem w33-pc"><a href="003.php">Menu3</a></li>
        </ul>
      </div><!-- .nav -->
    </div><!-- .inner -->
  </div><!-- .header -->

「require命令」で別ファイルにした各パーツを読み込む。
読み込み前に宣言した変数を各パーツ内で使用できる。

【本体:001.php】
<?php
$title = '001ページ';
require _DIR_ . '/header.php';
   ・
   ・
【ヘッダーパーツ:header.php】
<?php
$title = '001ページ';
require _DIR_ . '/header.php';
   ・
   ・

フッターパーツ

<div class="footer">
  <div class="inner">
    <div class="copyright">
      © <?php echo date('Y'); ?> companyname.
    </div><!-- .copyright -->
  </div><!-- .inner -->
</div><!-- .footer -->
</body>
</html>

コンテンツパーツ

<div class="parts01">
  <div class="inner">
    <h2 class="mainTitle">parts01</h2>
    <div class="text">
      <p>サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
        サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト
        サンプルテキストサンプルテキストサンプルテキストサンプルテキストサンプルテキスト</p>
    </div><!-- .text -->
  </div><!-- .inner -->
</div><!-- .parts01 -->

各パーツを共通化して別ページで使いまわすこともでき、運用や更新しやすい仕組みを作ることができる。

<?php
// header.phpは
// 「$title」「$cssPath」が必要
$title = 'ページタイトル';
$cssPath = '';
// 【ヘッダーパーツ読み込み】
require __DIR__ . '/header.php';

// 【コンテンツパーツ読み込み】
// parts01.phpは
// 「$mainTitle」「$text」が必要
$mainTitle = 'メインタイトル';
$text = <<< EOT
<p>説明文</p>
<p>説明文</p>
EOT;
require __DIR__ . '/parts01.php';

// 【フッターパーツ読み込み】
// パーツ内での変数出力なし
require __DIR__ . '/footer.php';

第15章:クッキー・セッション

クッキー

クッキーはサーバから送られてきたデータをブラウザに保存する仕組み。ブラウザに保存されたデータは次回サーバアクセス時に自動で送信される。複数のページをまたいでデータを扱いたい時や長い期間データを保存したい時に便利。

<?php
$count = isset($_COOKIE['count']) ? $_COOKIE['count'] + 1 : 1;
// ◆三項演算子
// 条件 ? true時の値 : false時の値;
// 三項演算子の結果を変数countに代入
// ◇条件:isset($_COOKIE['count'])
//   初回アクセス時の結果:false
//     変数countに「1」を代入
//   2回目以降の結果:true
//     変数countに「クッキーの値 + 1」を代入

if ($count >= 10) {
  $bool = setcookie('count', $count, time());
  // クッキーを削除
  // 第3引数:有効期限
  // time関数で現在時刻を有効期限にする
} else {
  $bool = setcookie('count', $count);
  // setcookie関数:クッキー送信準備
  // 第1引数:クッキーの名前
  // 第2引数:クッキーの値
  // 戻り値:送信準備OK「true」、NG「false」
  // クッキー名「count」
  // 初回は値「1」
}
?>
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>クッキー</title>
</head>

<body>
  <h1>クッキーを使ったカウンター</h1>

  <?php if ($bool) : ?>
    <p>アクセス回数:<?php echo $count; ?></p>
  <?php else : ?>
    <p>クッキーを発行できませんでした</p>
  <?php endif; ?>

  <?php if ($count > 5) : ?>
    <p>たくさんアクセスしてくれてありがとう</p>
  <?php endif; ?>

  <p><a href="01_cookie.php">01_cookie.php</a></p>

</body>

</html>

クッキーを送信する

setcookie(string クッキー名, string 値):bool

setcookie関数を呼び出すことでブラウザに送信するクッキーを用意することができる。クッキー送信準備が整うとtrueを、問題が発生した場合はfalseを返す。

ユーザ側のブラウザで管理されるクッキーはユーザ側で受け取りを拒否したり削除することができる。そのため返り値の真偽値はユーザがクッキーが受け入れられたかどうかまで判断することはできない。

クッキー利用時の注意

大きなデータは扱えない

クッキーとして保存できるデータ容量はWebブラウザに依存する。ほとんどのブラウザが5KB前後までしか扱えないため、容量の大きなデータは扱えない。

重要な情報は扱わない

クッキーの情報はWebブラウザから簡単に見ることができる。また、クッキーの値は常にネットワーク上を流れるため盗聴される危険や、悪意あるプログラムによってユーザPCからクッキー情報が流出することも考えられる。重要な情報は扱わない!!

値を書き換えることができる

クッキーはブラウザ側に保存され、一部のブラウザではクッキーの値を自由に書き換えることができる。例えば、ショッピングカートの商品価格をクッキーの値で受け渡ししている場合、値段を書き換えられる可能性がある。システムの根幹にかかわる情報は扱わないようにする。

保存されているクッキーを確認する:Chrome

①アドレス欄左側「i」をクリック
②「Cookie」をクリック


③「ドメイン」>「Cookie」>「クッキー名」を選択
④対象クッキーの詳細表示
 ・名前:クッキー名
 ・コンテンツ:値
 ・ドメイン:送信元サーバ情報
 ・パス:送信元サーバ内パス
 ・送信先:あらゆる接続
      HTTPS接続のみ 等
 ・作成日:クッキー作成日
 ・有効期限:クッキー削除期限
⑤対象クッキーを③で選択後「削除」押下で削除

// 問題1
// 以下のクッキーを発行すること
// クッキー名:name
// 値:自分の名前
// ブラウザでクッキーの確認をする
setcookie('name', 'masuda');

クッキーを受信する

$_COOKIE['クッキー名']

クライアントのブラウザからサーバに送信されたクッキーは「$_COOKIE」が受信し管理する。受信した値を参照する場合はキーにクッキー名を指定する。

ブラウザの検証ツールでクッキーを確認

Chromeの検証ツールを使用してクッキーを確認する。2回目アクセス時は初回アクセス時に保存したクッキーを持ってサーバにアクセスする。そのため⑤「Response Cookies」に初回アクセス時に取得したクッキー:名前「count」値「1」が表示される。

PHPが実行されsetcookie関数を使って送信したクッキーは⑥「Response Cookies」に表示される。名前「count」値「2」

クッキーの有効期限

setcookie(string クッキー名, string 値, int 有効期限):bool

クッキーにタイムスタンプ(1970年1月1日からの経過秒数)を使用して有効期限を設定することができる。もし有効期限を省略した場合は「ブラウザを終了するまで」が有効期限になる。有効期限を過ぎたクッキーは自動的に削除される。

タイムスタンプ:現在時刻から計算

time(void):int

time関数を呼び出すと現在時刻のタイムスタンプを取得する。

現在時刻のタイムスタンプに秒数を足すことで現在時刻をベースにした指定時刻のタイムスタンプを作成できる。

タイムスタンプ計算の例内容
time() + 60現在時刻から60秒後(1分後)
time() + (60 * 60)現在時刻から60分後(1時間後)
time() + (60 * 60 * 24)現在時刻から24時間後(1日後)
time() + (60 * 60 * 24 * 10)現在時刻から10日後

タイムスタンプを使って有効期限設定

setcookie関数の第3引数にタイムスタンプを設定し有効期限を設定できる。いかの記述はクッキー発行時から10日後までの有効期限になる。これでPCをシャットダウンしても10日着までクッキーの値がユーザ側PCのブラウザに保存される。

有効期限を省略した場合は有効期限が「ブラウザを終了まで」になる。有効期限を過ぎたクッキーはユーザ側PCのブラウザから削除される。

// 有効期限を10日間にする
setcookie(
  'name',
  'masuda',
   time() + 60 * 60 * 24 * 10);
// 第3引数:PHPでは秒を指定
// 現在時刻 + 有効期限

var_dump(time());
// int(1643073529)
/*
time関数は
現在時刻のタイムスタンプを返す
1970年1月1日0時0分0秒からの経過秒
  ※JSではミリ秒
現在時刻に時間を足して有効期限を作成
*/

タイムスタンプ:指定時刻

mktime (
  int $hour = date("H"),
  int $minute = date("i"),
  int $second = date("s"),
  int $month = date("n"),
  int $day = date("j"),
  int $year = date("Y"),
):int

mktime → メイクタイム関数

PHPには指定時刻のタイムスタンプを生成するmktime関数が用意されているので、それを利用してもOK。mktime関数を呼び出すと指定した時刻のタイムスタンプを取得できる。

各引数は省略することができ、省略した値は現在日時の値を使用する。
※引数なしで呼び出すとnoticeエラーが発生する

指定日時タイムスタンプ取得の例内容
mktime(12,30,45,1,2,2022)2022年1月2日12時30分45秒 のタイムスタンプ
mktime(12,30,45,1,2)現在年 1月2日12時30分45秒 のタイムスタンプ
mktime(12,30,45,1)現在年 現在月 2日12時30分45秒 のタイムスタンプ
mktime(12,30,45)本日 12時30分45秒 のタイムスタンプ
mktime(12,30)本日 12時30分 現在秒のタイムスタンプ
mktime(12)本日 12時 現在分 現在秒のタイムスタンプ
mktime(12,30,45,2,0,2022)2022年1月31日12時30分45秒 のタイムスタンプ
0日のように存在しない値の場合は自動修正される
0日 → 前月末日
// 問題2
// 以下のクッキーを発行すること
// クッキー名:test
// 値:value
// 有効期限
//  クッキー発行日の23時59分59秒
setcookie(
  'test',
  'value',
  mktime(23, 59, 59));

// 問題3
// 以下のクッキーを発行すること
// クッキー名:test
// 値:value
// 有効期限
//  クッキー発行翌日の23時59分59秒
setcookie(
  'test',
  'value',
  mktime(23, 59, 59, (date('n')), (date('j') + 1))
);
// mktime(23, 59, 59, 1, 32) とすると、2月1日23時59分になる

日時がずれている場合

日時を扱うdate関数、time関数、mktime関数などの日時がずれている時は「php.ini」のタイムゾーンが東京になっているか確認する。

date.timezone=Asia/Tokyo

クッキーの削除

ユーザ側のブラウザに保存されているクッキーをサーバから直接削除することはできない。そのため、クッキーを削除するにはすぐに有効期限が切れるクッキーをユーザの渡して対応する。

主に現在のタイムスタンプを指定する。これにより、クッキーが発行されてすぐに有効期限になり削除される。

  $bool = setcookie('count', $count, time());
  // クッキーを削除
  // 第3引数:有効期限
  // time関数で現在時刻を有効期限にする

まとめ

◆COOKIEの特徴◆
^^^^^^^^^^^^^^^^
閲覧者のブラウザに値を保存する機能
◇メリット
・長期間値を保存できる
・PCの電源を切っても値を保存できる

◇デメリット
・値を盗み見られる可能性がある
・値を改ざんされる可能性がある
・保存できるサイズがあまり大きくない

◇使いどころ
・有効期限を決めてクーポンの表示
・お気に入りの値を保存して表示
・書き込みの連投防止用の値として利用
カテゴリー