Kintarou'sBlog

プログラミング学習中。学習内容のアウトプットや読書で学んだことなど随時投稿!

【MySQL】数値に関する関数

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


今回は数値に関する関数をいくつかまとめます。

COUNT()

まずは以下のデータ内のレコードの数を出力します。

CREATE TABLE tweets (
  id INT NOT NULL AUTO_INCREMENT,
  message VARCHAR(140), 
  likes INT,
  PRIMARY KEY (id)
);

INSERT INTO tweets (message, likes) VALUES 
  ('post-1', 12),
  ('post-2', 8),
  ('post-3', 11),
  ('post-4', 3),
  ('post-5', 8),
  ('post-6', 9),
  ('post-7', 4),
  ('post-8', 10),
  ('post-9', 31);

tweetsテーブル内のレコード数を取得します。

SELECT COUNT(id) FROM tweets;

f:id:ryosuke-toyama:20201112225535p:plain

※仮にカウントするカラム(今回の場合id)がNULLだった場合、カウントに含まれないので注意です。

SUM()

次にいいね数(likes)を足し合わせていきます。

SELECT SUM(likes) FROM tweets;

f:id:ryosuke-toyama:20201113000840p:plain

AVG()

次はいいね数(likes)の平均を出力します。

SELECT AVG(likes) FROM tweets;

f:id:ryosuke-toyama:20201113001026p:plain

MAX()

レコードの中で一番大きいいいね数(likes)を出力します。

SELECT MAX(likes) FROM tweets;

f:id:ryosuke-toyama:20201113001212p:plain

MIN()

レコードの中で一番小さいいいね数(likes)を出力します。

SELECT MIN(likes) FROM tweets;

f:id:ryosuke-toyama:20201113001321p:plain

以上、どなたかの参考になれば幸いです😊

【MySQL】IF・CASE文

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


今日は簡単にMySQLのIF・CASE文をまとめます。

IF文

MySQLでIF文を使ってデータを抽出します。
今回のデータは以下です。
9つのツイートがあり、ツイート内容(message)、いいね数(likes)、位置情報(area)で構成されています。

CREATE TABLE tweets (
  id INT NOT NULL AUTO_INCREMENT,
  message VARCHAR(140), 
  likes INT,
  area VARCHAR(20),
  PRIMARY KEY (id)
);

INSERT INTO tweets (message, likes, area) VALUES 
  ('tweet-1', 12, 'Tokyo'),
  ('tweet-2', 8, 'Fukuoka'),
  ('tweet-3', 11, 'Tokyo'),
  ('tweet-4', 3, 'Osaka'),
  ('tweet-5', 8, 'Tokyo'),
  ('tweet-6', 9, 'Osaka'),
  ('tweet-7', 4, 'Tokyo'),
  ('tweet-8', 10, 'Osaka'),
  ('tweet-9', 31, 'Fukuoka');

この内容を、いいね数が10より多いかどうかで条件をつけてみます。

SELECT
  *,
  IF(likes > 10, 'A', 'B') AS rank
FROM
  tweets;

likesが10より大ければ'A'、10以下であれば'B'というrankに分ける。という内容です。

f:id:ryosuke-toyama:20201111160624p:plain

CASE文

続いてCASE文です。
いいね数が10より大きければ'A'、10以下だが5より大きければ'B'、5以下なら'C'という風に分けます。

SELECT
  *,
  CASE
    WHEN likes > 10 THEN 'A'
    WHEN likes > 5 THEN 'B'
    ELSE 'C'
  END AS rank
FROM
  tweets;

f:id:ryosuke-toyama:20201111162156p:plain

以上、どなたかの参考になれば幸いです😊

【MySQL】データ抽出(基本)

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


MySQLでのデータ抽出方法です。簡単なものをいくつかまとめます。

全カラム出力

CREATE TABLE tweets (
  id INT NOT NULL AUTO_INCREMENT,
  message VARCHAR(140), 
  PRIMARY KEY (id)
);

INSERT INTO tweets (message) VALUES 
  ('Arigato'),
  ('arigatai'),
  ('Arigataya'),
  ('Nemutai'),
  ('Kanmuryo');

全カラム出力する場合は以下コマンドです。'*'がtweetsテーブルの全カラムを表しています。

  SELECT * FROM tweets;

f:id:ryosuke-toyama:20201110181511p:plain

完全一致

以下のようにWHEREで条件式をつけます。

SELECT id,message FROM tweets WHERE message = 'Arigato';

f:id:ryosuke-toyama:20201110180512p:plain

完全不一致

条件式の!=は、等しく無い場合という事です。

SELECT id,message FROM tweets WHERE message != 'Arigato';

f:id:ryosuke-toyama:20201110181553p:plain

前方一致

LIKEと%を使い表現します。%は任意の文字列という解釈で大丈夫でしょう。

SELECT id,message FROM tweets WHERE message LIKE 'Ari%';

f:id:ryosuke-toyama:20201110181739p:plain

大文字小文字区別

LIKEにBINARYオプションをつける事で、大文字小文字を区別します。
id:2の'arigatai'が抽出から外れている事がわかります。

SELECT id,message FROM tweets WHERE message LIKE BINARY 'Ari%';

f:id:ryosuke-toyama:20201110181921p:plain

後方一致

%を前方につけ、任意の文字列の最後に'tai'がついているものを抽出します。

SELECT id,message FROM tweets WHERE message LIKE '%tai';

f:id:ryosuke-toyama:20201110182041p:plain

部分一致

部分一致の場合は%で囲みます。(下記の場合、'mu'が先頭や末尾にあった場合でも抽出します。)

SELECT id,message FROM tweets WHERE message LIKE '%mu%';

f:id:ryosuke-toyama:20201110182134p:plain

文字位置指定

'_'を使う事で特定文字位置を指定できます。例えば全てのmessageに'a'は入っていますが...

SELECT id,message FROM tweets WHERE message LIKE '%a%';

f:id:ryosuke-toyama:20201110183734p:plain

2文字目に'a'のあるmessageは'Kanmuryo'しかありません。

SELECT id,message FROM tweets WHERE message LIKE '_a%';

f:id:ryosuke-toyama:20201110183924p:plain

'_'は任意の文字列という解釈です。
仮に4文字目に'u'の入ったmessageを抽出したい場合は以下のようにします。

SELECT id,message FROM tweets WHERE message LIKE '___u%';

f:id:ryosuke-toyama:20201110184111p:plain

以上、どなたかの参考になれば幸いです😊

【MySQL】基本のデータ型と関数

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


データ型

MySQLのデータ型について触れていきます。
下記以外にも型はありますが、主なものを載せていきます。

数値

データ型
TINYINT -128〜127
INT -21億〜21億
BIGINT −922京〜922京
TINYINT UNSIGNED 0〜255
INT UNSIGNED 0〜42億
BIGINT UNSIGNED 0〜1844京
DECIMAL 固定小数点
FLOAT 浮動小数
DOUBLE 浮動小数点(高精度)

数値の関数

数値関連の関数もまとめます。

関数名 処理
FLOOR() 端数切り捨て
CEIL() 端数切り上げ
ROUND() 四捨五入

文字列

データ型
CHAR 0〜255文字
VARCHAR 0〜65535文字
TEXT 65536文字以上
ENUM 特定の文字列の内1つの値
SET 特定の文字列の内複数の値

ENUMとSET補足

ENUMとSETに関しては具体例を出します。

CREATE TABLE posts (
  id INT AUTO_INCREMENT,
  message VARCHAR(140),
  category ENUM('Gadget','Game','Buisiness'),
  PRIMARY KEY(id)
);

INSERT INTO posts (message, category) VALUES
  ('post1', 'Gadget'),
  ('post2', 2),
  ('post3', 3);

SELECT * FROM posts;

f:id:ryosuke-toyama:20201107161551p:plain

上記のようにあらかじめ文字列群を指定しておき、その中の1つという意味になります。
この際、'Game'や'Buisiness'のようにインデックス番号を指定する事もできます。
インデックス番号は0からではなく1からになっている点に注意です。

SETについても補足します。

DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
  id INT AUTO_INCREMENT,
  message VARCHAR(140),
  category SET('Gadget','Game','Buisiness'),
  PRIMARY KEY(id)
);

INSERT INTO posts (message, category) VALUES
  ('post1', 1),
  ('post2', 2),
  ('post3', 3),
  ('post4', 4),
  ('post5', 6),
  ('post6', 7);

SELECT * FROM posts;

f:id:ryosuke-toyama:20201107163647p:plain

SETの場合インデックス番号が2の0乗(1)、2の1乗(2)、2の2乗(4)、となっていきます。
複数の値を入れられるのですが、インデックス番号1と2の場合は足した3を入れます。
例)post3 → 3(インデックス番号1(Gadget)、インデックス番号2(Game))

文字列の関数

関数名 処理
SUBSTRING() n番目以降を切り出す
CONCAT() 文字列を連結させる
LENGTH() 文字数を出力する
CHAR_LENGTH() 文字数を出力。漢字・ひらがな・カタカナ・マルチバイト文字も1文字に認識

真偽値

データ型
BOOL TRUE/FALSE
TINYINT(1) 1/0

日時

データ型
DATE 日付
TIME 時間
DATETIME 日時

日時の関数

関数名 処理
NOW() 現在の日時を返す
YEAR() 日付データから年を抜き出す
MONTH() 日付データから月を抜き出す
DAY() 日付データから日を抜き出す
DATE_FORMAT() 日付データを指定のフォーマットで出力
DATE_ADD 指定の日付から○日後などを出力
DATEDIFF 指定の日付2つの差を計算する

以上、どなたかの参考になれば幸いです😊

【PHP】トークン機能を実装する

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


CSRFの一例

まずはCSRFにてどのように攻撃されるかの一例を見ていきます。
前回記事でも用いた投稿機能のあるPHPファイルを使います。

ryosuke-toyama.hatenablog.com

■messages.txt

hello
hi

■index.php

<?php

require('../app/functions.php');

define('FILENAME', '../app/messages.txt');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $message = trim(filter_input(INPUT_POST, 'message'));
  $message = $message !== '' ? $message : '...';
  
  $fp = fopen(FILENAME, 'a');
  fwrite($fp, $message . "\n");
  fclose($fp);

  header('Location: http://localhost:8080/result.php');
  exit;
}

$messages = file(FILENAME, FILE_IGNORE_NEW_LINES);

include('../app/_parts/_header.php');

?>

  <ul>
  <?php foreach ($messages as $message): ?>
    <li><?= h($message); ?></li>
  <?php endforeach; ?>
  </ul>
  <form action="" method="post">
    <input type="text" name="message">
    <button>Post</button>
  </form>

■result.php

<?php

require('../app/functions.php');

$message = trim(filter_input(INPUT_POST, 'message'));
$message = $message !== '' ? $message : '...';

$filename = '../app/messages.txt';
$fp = fopen($filename, 'a');
fwrite($fp, $message . "\n");
fclose($fp);

include('../app/_parts/_header.php');

?>

  <p>Message added!</p>
  <p><a href="index.php">Go back</a></p>

f:id:ryosuke-toyama:20201105205217p:plain
f:id:ryosuke-toyama:20201105205241p:plain
f:id:ryosuke-toyama:20201105205303p:plain

ここにもし、以下のようなサイトがあった場合の挙動を見てみます。

f:id:ryosuke-toyama:20201105214825p:plain

仮にここで『今すぐ受け取る!』ボタンを押すと下記のような投稿がされてしまいます。

f:id:ryosuke-toyama:20201105205241p:plain
f:id:ryosuke-toyama:20201105222333p:plain

何故このような投稿がされてしまったのかを先ほどの怪しげなサイトのソースから確認してみます。

■winner.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>当選しました!</title>
</head>
<body>
  <h1>おめでとうございます!</h1>

  <!-- 指定したURLにPOSTで情報を送るフォームになっています。 -->
  <form action="http://localhost:8080/index.php" method="post">
    <p>100万円が当たりました!下のボタンをクリックしてください!</p>

    <!-- hiddenとなって隠されていますが、messageという名前で犯行予告!!!!というデータが送られるようになっています。 -->
    <input type="hidden" name="message" value="犯行予告!!!!">
    <button>今すぐ受け取る!</button>
  </form>
</body>
</html>

このように、ブラウザからページのソースで必要な情報がわかってしまうので別のサイトからでもデータを送る事ができ、反映されてしまします。

対策としては各種フレームワークを使うか、トークンを生成してそれが一致しているかを調べる必要があります。

今回はトークンを生成し、一致していた場合に処理を行う機能を実装していきます。

トークンの生成とチェック機能を実装する

トークン生成とチェック機能をfunctions.phpに記述していきます。

■functions.php

<?php

function h($str)
{
  return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}


function createToken() {

  #$_SESSION['token']がセットされていない場合にトークンを生成します。
  if (!isset($_SESSION['token'])) {

    #random_bytes()関数で32文字のランダムな文字列を生成します。
    #random_bytes()で生成されるのはバイナリ(2進数)なのでbin2hex()関数で16進数に変換します。
    $_SESSION['token'] = bin2hex(random_bytes(32));
  }
}

function validateToken() {
  if (

    #$_SESSION['token']が空、もしくはPOSTで送られたtokenとは違う場合exit()で処理を終了させます。
    empty($_SESSION['token']) ||
    $_SESSION['token'] !== filter_input(INPUT_POST, 'token')
  ) {
    exit('Invalid post request');
  }
}

#$_SESSION['token']でsessionを使うので、ここでsession_start()しておきます。
session_start();

トークン生成とチェック機能を組み込む

index.phpに組み込んでいきます。

■index.php

<?php

#functions.phpを読み込みます。
require('../app/functions.php');

#早速トークンを生成します。
createToken();

define('FILENAME', '../app/messages.txt');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

  #POSTでデータが送信された際、下部フォームによって送られたトークンをチェックします。
  #これでもしトークンが無い、トークンが誤っている(外部サイトからのPOST送信である)場合は処理が終了する。
  validateToken();
  
  $message = trim(filter_input(INPUT_POST, 'message'));
  $message = $message !== '' ? $message : '...';
  
  $fp = fopen(FILENAME, 'a');
  fwrite($fp, $message . "\n");
  fclose($fp);

  header('Location: http://localhost:8080/result.php');
  exit;
}

$messages = file(FILENAME, FILE_IGNORE_NEW_LINES);

include('../app/_parts/_header.php');

?>

  <ul>
  <?php foreach ($messages as $message): ?>
    <li><?= h($message); ?></li>
  <?php endforeach; ?>
  </ul>

  <form action="" method="post">
    <input type="text" name="message">
    <button>Post</button>

    <!-- このファイルの冒頭で生成したトークンをパラメータとして一緒に送ります。 -->
    <input type="hidden" name="token" value="<?= h($_SESSION['token']); ?>">
  </form>

上記で更新したページのソースを確認すると、ランダムなトークンが生成されており、用意に攻撃する事が出来なくなっている事がわかります。
f:id:ryosuke-toyama:20201105222804p:plain

補足

トークンを実装することで今回のようなCSRFは防ぐ事が出来ますが、攻撃方法は多種多様に変化していくので、やはりフレームワークを使った開発が賢明です。

以上、どなたかの参考になれば幸いです😊

【PHP】POST送信による投稿機能の実装

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


前回までの記事はGETメソッドによるデータの送信でしたが、今回はPOSTメソッドによるデータ送信を用いた投稿機能を実装してみます。

投稿機能の雛形を作る

投稿機能の雛形を作成します。
POST形式で送ったテキストデータを使い、messages.txtファイルを編集するという流れです。

ファイルを編集・操作する関数に関しては過去記事にまとめております。

ryosuke-toyama.hatenablog.com

■messages.txt

hello
hi

2行分だけテキストを入れておきました。
まずはこのデータをindex.phpで表示します。

■index.php

<?php

function h($str)
{
  return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

#messages.txtのテキスト内容を行毎に$messagesに配列形式で格納します。
#FILE_IGNORE_NEW_LINESにより\nは無視されています。
$filename = '../app/messages.txt';
$messages = file($filename, FILE_IGNORE_NEW_LINES);

include('../app/_parts/_header.php');

?>

<!-- $messagesをforeach文で出力します。 -->
  <ul>
  <?php foreach ($messages as $message): ?>
    <li><?= h($message); ?></li>
  <?php endforeach; ?>
  </ul>

  <form action="result.php" method="post">

  <!-- POST形式で新たなテキストデータを送れるようにしておきます。 -->
    <input type="text" name="message">
    <button>Post</button>
  </form>

f:id:ryosuke-toyama:20201105204617p:plain
上記のようにmessages.txtのデータが表示されています。
新たにPOST形式で送ったテキストデータを使い、result.phpでmessages.txtに追記していきます。

■result.php

<?php

#filter_input()関数でPOSTで送られたデータを$messageに代入します。
$message = trim(filter_input(INPUT_POST, 'message'));

#記述が何もない場合は'...'を代入します。
$message = $message !== '' ? $message : '...';

#fwrite()関数で$messageを追記し、\nで改行しておきます。
$filename = '../app/messages.txt';
$fp = fopen($filename, 'a');
fwrite($fp, $message . "\n");
fclose($fp);

include('../app/_parts/_header.php');

?>

  <p>Message added!</p>
  <p><a href="index.php">Go back</a></p>

f:id:ryosuke-toyama:20201105205217p:plain

f:id:ryosuke-toyama:20201105205241p:plain

f:id:ryosuke-toyama:20201105205303p:plain

これで投稿機能が出来ました。

ただこの状態ではresult.phpにmessages.txt編集の記載がされているので、result.phpにURLから直接アクセスしたりページをリロードすると再投稿されるという事になってしまいます。

それを避けるには、index.phpで処理を行ってからresult.phpにリダイレクトする事によって解決できます。

URLの直アクセスやリロードに対応する

先ほどのファイル編集部分の記述をindex.phpに記載し、処理が出来たらresult.phpにリダイレクトするという形にします。

■index.php

<?php

function h($str)
{
  return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

#下記2箇所で$filenameを使う機会があるのでdefine()で定数化します。
define('FILENAME', '../app/messages.txt');

#$_SERVERという特殊な変数でPOSTでアクセスされた場合に行う処理を記述出来ます。
#これにより、URLでの直アクセスに対応できます。
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $message = trim(filter_input(INPUT_POST, 'message'));
  $message = $message !== '' ? $message : '...';
  
  $fp = fopen(FILENAME, 'a');
  fwrite($fp, $message . "\n");
  fclose($fp);

  #messages.txtが編集出来たらresult.phpにリダイレクトします。
  header('Location: http://localhost:8080/result.php');
  exit;
}

$messages = file(FILENAME, FILE_IGNORE_NEW_LINES);

include('../app/_parts/_header.php');

?>

  <ul>
  <?php foreach ($messages as $message): ?>
    <li><?= h($message); ?></li>
  <?php endforeach; ?>
  </ul>

  <!-- 同じファイル内で処理をする場合はactionを空欄(もしくはactionの記述なし)で処理できます。 -->
  <form action="" method="post">
    <input type="text" name="message">
    <button>Post</button>
  </form>

ファイル編集部分がindex.phpに移った事により、result.phpの記述は簡単になります。

■result.php

<?php

include('../app/_parts/_header.php');

?>

  <p>Message added!</p>
  <p><a href="index.php">Go back</a></p>

これでURLでの直アクセスや、リロードに対応できます。

補足

この記事では別のサイトからアクセスされるCSRFに対応出来ていません。 CSRFに関しては過去記事の説明を再掲しておきます。

クロスサイトリクエストフォージェリ(CSRF)

ユーザーによる認証が完了したと考えられるWebアプリケーションのページに、悪意のあるコードやリンクを仕込む攻撃方法。
そのWebアプリケーションへのセッションがタイムアウトしていなければ、攻撃者は本来認証されていないはずのコマンドを実行できてしまいます。
GETとPOSTを適切に使い分けることと、上記のセキュリティトークンをリクエストに追加することで対策することができます。
ryosuke-toyama.hatenablog.com

PHPでのトークンの実装は別記事にまとめます。

以上、どなたかの参考になれば幸いです😊

【PHP】Sessionを活用し情報を保持する

こんにちは😊Kintarouです。

現在エンジニア転職を目指してプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人にとって働く事が楽しくなるシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️ dotinstall.com


Sessionとは

Cookieのように情報を保持する事が出来ますが、Sessionはブラウザではなくサーバー側に保存します。

Cookieについては過去記事にまとめてます。
ryosuke-toyama.hatenablog.com

そのため検証ツールで確認出来ず、編集や削除も出来ないため、機密性が高く重要な情報を取り扱うのに利用されます。

CookieとSessionの主な違い

項目 Cookie Session
値の保存場所 ブラウザ側 サーバー側
サイズ 制限あり 制限なし
保存できるもの 文字列のみ オブジェクトなどもOK
安全性 低い 高い
扱うもの 簡単な設定項目 認証情報など

Sessionの仕組みを使い背景色情報を保持する

Sessionは、session_start()関数を使う事で利用を開始出来ます。
また、情報は$_SESSIONという変数に配列形式で保持されます。
ブラウザ側にはPHPSESSIDという特殊な形でCookieがセットされ、値にはブラウザとサーバー側の値を紐づける識別子が表示されます。(実際の値は見えないようになっています)

■functions.php

<?php

#まずここでsession_start()を行います。
session_start();

function h ($str)
{
 return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

■result.php

<?php

require('../app/functions.php');

$colorFromGet = filter_input(INPUT_GET, 'color') ?? 'transparent';

#session_start()により$_SESSIONという特殊な変数が使えるようになったので、colorという名前で背景情報を保持します。
#サーバー側に情報を保持する仕組みのためsetcookie()する必要がありません。
$_SESSION['color'] = $colorFromGet;

include('../app/_parts/_header.php');

?>

<p><?= h($colorFromGet); ?></p>
<p><a href="index.php">Go Back</a></p>

■_header.php

<?php

#_header.phpで$_SESSION['color']の背景色情報を$colorに代入します。
$color = $_SESSION['color'] ?? 'transparent';

?>

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>PHP Practice</title>
</head>
<body style="background-color: <?= h($color); ?>;">

f:id:ryosuke-toyama:20201104182540p:plain
上記のようにPHPSESSIDという特殊なCookieが生成され、Valueにはブラウザとサーバー側の値を紐づける識別子が表示されています。
このCookieが、サーバー側のColorという名前で保持されている背景色情報を引っ張ってきているイメージです。

Session情報の削除方法

Sessionの情報はunset()関数で削除します。
Session情報(color)を削除するためのreset.phpを作成してみます。

■index.php

<?php

require('../app/functions.php');

include('../app/_parts/_header.php');

?>

<form action="result.php" method="get">
  
    <label><input type="radio" name="color" value="orange">Orange</label>
    <label><input type="radio" name="color" value="pink">Pink</label>
    <label><input type="radio" name="color" value="gold">Gold</label>
  <button>Send</botton>

  <!-- reset.phpへのリンクを作成します。 -->
  <a href="reset.php">[reset]</a>
</form>

f:id:ryosuke-toyama:20201105192318p:plain
上記のように[reset]リンクを作成し、reset.phpへジャンプします。

■reset.php

<?php

require('../app/functions.php');

#unset()関数で$_SESSION['color']の値を削除します。
unset($_SESSION['color']);

#header()関数は指定のURLにリダイレクトさせる事が出来ます。
header('Location: http://localhost:8080/index.php');

f:id:ryosuke-toyama:20201105193008p:plain
上記のように、$_SESSION['color']が削除されたため背景色が戻りました。
あくまで削除されるのはcolorの値であり、PHPSESSIDが削除されるわけではないので注意です。

header()関数注意点

本題ではないですがheader()関数が出てきたので注意点です。
①setcookie()同様、関数前にあらゆる出力の不可。
②LocationのLは必ず大文字にする。
③Location: の:(コロン)の後は半角スペースを必ず入れる。
④リダイレクトのURLは必ずhttpから記述する。

以上、どなたかの参考になれば幸いです😊