PHP チートシート

目次

変数とデータ型

PHPで利用できる基本的な変数宣言とデータ型の一覧です。

変数宣言

ドル記号 ($) の後に変数名を記述します。

<?php
$variableName = "値";
$message = "こんにちは!";
$count = 10;
?>

データ型

データ型 説明
string (文字列) 文字の並び
$name = "山田太郎";
$greeting = 'おはよう';
int (整数) 符号付き整数
$age = 30;
$negative = -100;
float (浮動小数点数) 小数
$price = 198.5;
$pi = 3.14159;
bool (論理値) 真 (true) または偽 (false)
$isAdmin = true;
$isValid = false;
array (配列) 順序付けられたマップ
// 数値添字配列
$colors = ["赤", "青", "緑"];
// 連想配列
$user = ["name" => "佐藤", "age" => 25];
object (オブジェクト) クラスのインスタンス
class MyClass {}
$obj = new MyClass();
null (ヌル) 値が存在しないことを示す
$noValue = null;
resource (リソース) 外部リソースへの参照 (ファイルハンドルなど)
$fileHandle = fopen("file.txt", "r");

型チェックとキャスト

  • gettype($var): 変数の型を取得
  • is_string($var), is_int($var), is_float($var), is_bool($var), is_array($var), is_object($var), is_null($var): 型をチェック
  • (string)$var, (int)$var, (float)$var, (bool)$var, (array)$var, (object)$var: 型キャスト
<?php
$numberString = "123";
$numberInt = (int)$numberString; // $numberInt は 123 (int)

$value = 10;
echo gettype($value); // "integer"

if (is_int($value)) {
  echo "整数です ";
}
?>

演算子

計算や比較などに使う演算子です。

種類 演算子 説明
算術演算子 + 加算
- 減算
* 乗算
/ 除算
% 剰余
代入演算子 =, +=, -=, *=, /=, %=, .= 変数に値を代入
比較演算子 == 等しい (型変換を行う)
=== 等しい (型も比較)
!= or <> 等しくない (型変換を行う)
!== 等しくない (型も比較)
<, > 小なり、大なり
<=, >= 小なりイコール、大なりイコール
<=> 宇宙船演算子 (比較結果を -1, 0, 1 で返す)
論理演算子 and or && 論理積 (AND)
or or || 論理和 (OR)
xor 排他的論理和 (XOR)
! 否定 (NOT)
インクリメント/デクリメント ++$a, $a++ インクリメント
--$a, $a-- デクリメント
文字列演算子 . 文字列結合
配列演算子 + 配列の結合
==, ===, !=, etc. 配列の比較
三項演算子 (条件) ? 真の場合の値 : 偽の場合の値 条件に基づいて値を返す
Null合体演算子 $a ?? $b $anull でなければ $a を、null なら $b を返す
Null合体代入演算子 $a ??= $b $anull の場合、$b$a に代入する
エラー制御演算子 @ 式のエラーを抑制する
<?php
$a = 10;
$b = 5;
$c = $a + $b; // $c は 15

$name = "鈴木";
$greeting = "こんにちは、" . $name . "さん!"; // こんにちは、鈴木さん!

$age = 20;
$message = ($age >= 20) ? "成人です" : "未成年です"; // 成人です

$userName = $_GET['user'] ?? 'ゲスト'; // $_GET['user'] がセットされていればその値、なければ 'ゲスト'

$array1 = ['a' => 1];
$array2 = ['b' => 2];
$merged = $array1 + $array2; // ['a' => 1, 'b' => 2]
?>

制御構造

プログラムの流れを制御するための構文です。

条件分岐

if / elseif / else
<?php
$score = 75;

if ($score >= 80) {
  echo "素晴らしい! ";
} elseif ($score >= 60) {
  echo "合格です ";
} else {
  echo "もう少し頑張りましょう ";
}
?>
switch
<?php
$signal = "blue";

switch ($signal) {
  case "red":
    echo "止まれ ";
    break;
  case "yellow":
    echo "注意 ";
    break;
  case "blue":
  case "green": // 複数のケースをまとめる
    echo "進め ";
    break;
  default:
    echo "不明な信号 ";
    break;
}
?>
match (PHP 8.0+)

switch と似ていますが、より厳密な比較 (===) を行い、値を返す式です。

<?php
$statusCode = 200;

$message = match ($statusCode) {
  200, 201 => "成功",
  404 => "見つかりません",
  500 => "サーバーエラー",
  default => "その他のステータス",
};

echo $message; // 成功
?>

ループ

for
<?php
for ($i = 0; $i < 5; $i++) {
  echo $i . " "; // 0 1 2 3 4
}
?>
while
<?php
$i = 0;
while ($i < 5) {
  echo $i . " "; // 0 1 2 3 4
  $i++;
}
?>
do-while

条件に関わらず、最低1回はブロック内の処理が実行されます。

<?php
$i = 5;
do {
  echo $i . " "; // 5
  $i++;
} while ($i < 5);
?>
foreach

配列やオブジェクトの要素を反復処理します。

<?php
// 配列の値を反復
$colors = ["赤", "青", "緑"];
foreach ($colors as $color) {
  echo $color . " "; // 赤 青 緑
}

echo "<br>";

// 配列のキーと値を反復
$user = ["name" => "佐藤", "age" => 25];
foreach ($user as $key => $value) {
  echo $key . ": " . $value . " "; // name: 佐藤 age: 25
}
?>

ループ制御

  • break: 現在のループ (for, while, do-while, foreach, switch) を終了します。
  • continue: 現在のループの反復処理をスキップし、次の反復処理に進みます。
<?php
for ($i = 0; $i < 10; $i++) {
  if ($i === 5) {
    break; // iが5になったらループを抜ける
  }
  if ($i % 2 === 0) {
    continue; // 偶数の場合はスキップ
  }
  echo $i . " "; // 1 3
}
?>

関数

特定の処理をまとめて再利用可能にするためのブロックです。

基本的な関数定義と呼び出し

<?php
// 関数の定義
function greet($name) {
  echo "こんにちは、" . $name . "さん! ";
}

// 関数の呼び出し
greet("田中"); // こんにちは、田中さん! 
?>

引数

型宣言 (Type Hinting)

引数の型を指定できます。

<?php
function calculateSum(int $a, int $b) {
  return $a + $b;
}

echo calculateSum(5, 3); // 8
// echo calculateSum("5", 3); // PHP 7以降、可能であれば暗黙的に型変換される (strict_types=1 でエラー)
// echo calculateSum("abc", 3); // TypeError
?>
デフォルト引数値
<?php
function sayHello($name = "ゲスト") {
  echo "ようこそ、" . $name . "さん!";
}

sayHello("山田"); // ようこそ、山田さん!
sayHello();       // ようこそ、ゲストさん!
?>
可変長引数 (Variadic functions)

... を使って、任意の数の引数を受け取れます。

<?php
function sum(...$numbers) {
  $total = 0;
  foreach ($numbers as $number) {
    $total += $number;
  }
  return $total;
}

echo sum(1, 2, 3);    // 6
echo sum(10, 20, 30, 40); // 100
?>

戻り値

return

関数の結果を返します。

<?php
function multiply($a, $b) {
  return $a * $b; // 結果を返す
}

$result = multiply(4, 5);
echo $result; // 20
?>
戻り値の型宣言

関数の戻り値の型を指定できます。

<?php
// declare(strict_types=1); // 厳密な型チェックを有効にする場合

function getAge(): int {
  return 30; // int型を返す
}

// function getAgeString(): int {
//   return "30"; // PHP 7以降、可能であれば暗黙的に型変換される (strict_types=1 でTypeError)
// }

$age = getAge();
echo $age; // 30
?>

戻り値の型として void を指定すると、関数が何も返さないことを示します (PHP 7.1+)。

<?php
function printMessage(string $message): void {
    echo $message;
    // return; // return; は許可される
    // return null; // 許可される
    // return 123; // エラーになる
}

printMessage("Hello World!");
?>

無名関数 (クロージャ)

変数に関数を代入できます。親スコープの変数を引き継ぐには use キーワードを使います。

<?php
$message = "外部メッセージ";

$say = function ($name) use ($message) {
  echo "こんにちは、" . $name . "さん! " . $message;
};

$say("鈴木"); // こんにちは、鈴木さん! 外部メッセージ
?>

アロー関数 (PHP 7.4+)

より簡潔な無名関数の構文です。親スコープの変数は自動的に引き継がれます。

<?php
$factor = 10;

$multiplyByFactor = fn($n) => $n * $factor;

echo $multiplyByFactor(5); // 50
?>

可変関数

変数名を使って関数を呼び出します。

<?php
function greetWorld() {
  echo "Hello World!";
}

$functionName = "greetWorld";
$functionName(); // greetWorld() が呼び出される -> Hello World!
?>

配列

複数の値をまとめて管理するためのデータ構造です。

配列の作成

<?php
// [] を使う (推奨)
$fruits = ["apple", "banana", "orange"];
$scores = [
  "math" => 80,
  "english" => 75,
];

// array() を使う
$colors = array("red", "green", "blue");
$user = array("name" => "Taro", "age" => 30);
?>

要素へのアクセス

添字(数値または文字列キー)を使ってアクセスします。

<?php
$fruits = ["apple", "banana", "orange"];
echo $fruits[0]; // apple
echo $fruits[1]; // banana

$scores = ["math" => 80, "english" => 75];
echo $scores["math"]; // 80
?>

要素の追加・変更・削除

<?php
$fruits = ["apple", "banana"];

// 追加 (末尾)
$fruits[] = "orange";
// $fruits は ["apple", "banana", "orange"]

// 追加 (キー指定)
$scores = ["math" => 80];
$scores["science"] = 90;
// $scores は ["math" => 80, "science" => 90]

// 変更
$fruits[0] = "リンゴ";
$scores["math"] = 85;
// $fruits は ["リンゴ", "banana", "orange"]
// $scores は ["math" => 85, "science" => 90]

// 削除
unset($fruits[1]); // banana を削除
// $fruits は [0 => "リンゴ", 2 => "orange"] (キー1は欠番)
unset($scores["science"]);
// $scores は ["math" => 85]
?>

配列操作関数 (一部)

関数 説明
count($array) 要素数を返す
count(["a", "b"]); // 2
sort($array) 配列を昇順にソート (値を変更)
$arr=[3,1,2]; sort($arr); // $arr=[1,2,3]
rsort($array) 配列を降順にソート (値を変更)
$arr=[1,3,2]; rsort($arr); // $arr=[3,2,1]
asort($array) 連想配列を値で昇順ソート (キー保持)
$arr=['a'=>3,'b'=>1]; asort($arr); // $arr=['b'=>1,'a'=>3]
ksort($array) 連想配列をキーで昇順ソート
$arr=['b'=>1,'a'=>3]; ksort($arr); // $arr=['a'=>3,'b'=>1]
array_push($array, $val1, ...) 配列の末尾に要素を追加
$arr=['a']; array_push($arr, 'b'); // $arr=['a','b']
array_pop($array) 配列の末尾の要素を取り出す
$arr=['a','b']; $last=array_pop($arr); // $last='b', $arr=['a']
array_unshift($array, $val1, ...) 配列の先頭に要素を追加
$arr=['b']; array_unshift($arr, 'a'); // $arr=['a','b']
array_shift($array) 配列の先頭の要素を取り出す
$arr=['a','b']; $first=array_shift($arr); // $first='a', $arr=['b']
array_merge($arr1, $arr2, ...) 配列を結合する (数値キーは振り直し)
array_merge([1,2], [3,4]); // [1,2,3,4]
array_keys($array) 配列の全てのキーを返す
array_keys(['a'=>1,'b'=>2]); // ['a','b']
array_values($array) 配列の全ての値を返す (数値添字配列になる)
array_values(['a'=>1,'b'=>2]); // [1,2]
in_array($needle, $haystack) 配列内に値が存在するかチェック
in_array('a', ['a','b']); // true
array_key_exists($key, $array) 配列内に指定したキーが存在するかチェック
array_key_exists('name', ['name'=>'Taro']); // true
array_map($callback, $array1, ...) 各要素に関数を適用し、新しい配列を返す
array_map(fn($n)=>$n*2, [1,2,3]); // [2,4,6]
array_filter($array, $callback, $mode) 関数でフィルタリングし、条件に合う要素のみの新しい配列を返す
array_filter([1,2,3,4], fn($n)=>$n%2==0); // [2=>2, 3=>4]
array_slice($array, $offset, $length, $preserve_keys) 配列の一部を切り出す
array_slice(['a','b','c','d'], 1, 2); // ['b','c']
array_splice($array, $offset, $length, $replacement) 配列の一部を削除または置換する (元の配列を変更)
$arr=['a','b','c']; array_splice($arr, 1, 1, 'B'); // $arr=['a','B','c']

多次元配列

配列の要素として、さらに配列を持つことができます。

<?php
$matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

echo $matrix[1][1]; // 5 (2行目2列目)

$users = [
  ["name" => "佐藤", "age" => 25],
  ["name" => "鈴木", "age" => 30],
];

echo $users[0]["name"]; // 佐藤
?>

文字列操作

文字列を処理するための関数群です。

関数 説明
. (結合演算子) 文字列を結合する
$str1 = "Hello"; $str2 = "World"; echo $str1 . " " . $str2; // "Hello World"
strlen($string) 文字列のバイト長を返す (マルチバイト注意)
strlen("abc"); // 3
mb_strlen($string, 'encoding') 文字列の文字数を返す (マルチバイト対応)
mb_strlen("日本語", 'UTF-8'); // 3
substr($string, $start, $length) 文字列の一部を返す (バイト単位)
substr("abcdef", 1, 3); // "bcd"
mb_substr($string, $start, $length, 'encoding') 文字列の一部を返す (文字単位)
mb_substr("あいうえお", 1, 2, 'UTF-8'); // "いう"
strpos($haystack, $needle, $offset) 文字列内で部分文字列が最初に現れる位置を検索 (バイト単位)
strpos("abcde", "c"); // 2 (見つからない場合は false)
mb_strpos($haystack, $needle, $offset, 'encoding') 文字列内で部分文字列が最初に現れる位置を検索 (文字単位)
mb_strpos("あいうえお", "う", 0, 'UTF-8'); // 2
str_replace($search, $replace, $subject) 文字列内の部分文字列を置換する
str_replace(" ", "_", "hello world"); // "hello_world"
strtolower($string) 文字列を小文字にする (ASCII)
strtolower("ABC"); // "abc"
strtoupper($string) 文字列を大文字にする (ASCII)
strtoupper("abc"); // "ABC"
mb_strtolower($string, 'encoding') 文字列を小文字にする (マルチバイト対応)
mb_strtolower("ABC", 'UTF-8'); // "abc"
mb_strtoupper($string, 'encoding') 文字列を大文字にする (マルチバイト対応)
mb_strtoupper("abc", 'UTF-8'); // "ABC"
trim($string, $chars) 文字列の先頭と末尾の空白 (または指定文字) を取り除く
trim("  abc  "); // "abc"
ltrim($string, $chars) 文字列の先頭の空白 (または指定文字) を取り除く
ltrim("  abc  "); // "abc  "
rtrim($string, $chars) 文字列の末尾の空白 (または指定文字) を取り除く
rtrim("  abc  "); // "  abc"
explode($delimiter, $string, $limit) 文字列をデリミタで分割し、配列にする
explode(",", "a,b,c"); // ["a", "b", "c"]
implode($glue, $pieces) or join() 配列要素を文字列で連結する
implode("-", ["a", "b", "c"]); // "a-b-c"
sprintf($format, $args...) フォーマットに従って文字列を生成し、返す
sprintf("値は %d です", 123); // "値は 123 です"
printf($format, $args...) フォーマットに従って文字列を生成し、出力する
printf("名前: %s, 年齢: %d", "Taro", 30); // "名前: Taro, 年齢: 30" を出力
htmlspecialchars($string, $flags, $encoding, $double_encode) 特殊文字をHTMLエンティティに変換 (XSS対策)
htmlspecialchars('<script>alert("XSS");</script>');
nl2br($string, $is_xhtml) 文字列中の改行文字 (\n) の前にHTMLの改行タグ (<br>) を挿入する
echo nl2br("一行目\n二行目");

ファイル操作

ファイルやディレクトリを操作するための基本的な方法です。

ファイル内容の読み書き (簡易)

<?php
// ファイル全体を読み込む
$content = file_get_contents('data.txt');
if ($content !== false) {
    echo "読み込み成功:
" . nl2br(htmlspecialchars($content)); } else { echo "ファイルの読み込みに失敗しました "; } // ファイルに書き込む (既存の内容は上書き) $dataToWrite = "これは新しいデータです。\n改行もできます。"; $result = file_put_contents('output.txt', $dataToWrite); if ($result !== false) { echo "<br>書き込み成功! "; } else { echo "<br>ファイルの書き込みに失敗しました "; } // ファイルに追記する $additionalData = "\n追記するデータ。"; $resultAppend = file_put_contents('output.txt', $additionalData, FILE_APPEND | LOCK_EX); if ($resultAppend !== false) { echo "<br>追記成功! "; } else { echo "<br>ファイルの追記に失敗しました "; } ?>

ファイル操作 (詳細)

fopen, fread, fwrite, fclose を使って、より細かい制御を行います。

モード 説明
r読み込み専用。ポインタはファイルの先頭。
r+読み書き両用。ポインタはファイルの先頭。
w書き込み専用。ポインタはファイルの先頭。ファイルが存在すれば内容を空にし、なければ新規作成。
w+読み書き両用。ポインタはファイルの先頭。ファイルが存在すれば内容を空にし、なければ新規作成。
a追記用。ポインタはファイルの末尾。ファイルがなければ新規作成。
a+読み書き両用(追記)。ポインタはファイルの末尾。ファイルがなければ新規作成。
x書き込み専用で新規作成。ファイルが既に存在する場合は失敗。
x+読み書き両用で新規作成。ファイルが既に存在する場合は失敗。
c書き込み専用。ポインタはファイルの先頭。ファイルが存在しなければ新規作成。存在する場合、内容は消去されず、ポインタのみ先頭に。
c+読み書き両用。ポインタはファイルの先頭。ファイルが存在しなければ新規作成。存在する場合、内容は消去されず、ポインタのみ先頭に。
<?php
$filename = 'detailed_log.txt';
$message = "ログエントリ: " . date('Y-m-d H:i:s') . "\n";

// ファイルを追記モードで開く (なければ作成)
$handle = fopen($filename, 'a');

if ($handle) {
    // ファイルに書き込む
    if (fwrite($handle, $message) !== false) {
        echo "ログを追記しました ";
    } else {
        echo "書き込みエラー ";
    }

    // ファイルを閉じる (重要!)
    fclose($handle);
} else {
    echo "ファイル '$filename' を開けませんでした ";
}

echo "<br>";

// ファイルを読み込みモードで開く
$readHandle = fopen($filename, 'r');
if ($readHandle) {
    echo "ファイル内容:<br>";
    // ファイルの終端まで1行ずつ読み込む
    while (($line = fgets($readHandle)) !== false) {
        echo htmlspecialchars($line) . "<br>";
    }
    fclose($readHandle);
} else {
    echo "ファイル '$filename' を読み込み用に開けませんでした ";
}
?>

注意: ファイル操作後は必ず fclose() でファイルを閉じるようにしましょう。

ファイル・ディレクトリのチェック関数

関数 説明
file_exists($filename) ファイルまたはディレクトリが存在するかチェック
is_file($filename) 通常のファイルであるかチェック
is_dir($filename) ディレクトリであるかチェック
is_readable($filename) 読み込み可能かチェック
is_writable($filename) 書き込み可能かチェック
filesize($filename) ファイルサイズをバイト単位で返す
filemtime($filename) ファイルの最終更新時刻をUnixタイムスタンプで返す

ディレクトリ操作

関数 説明
mkdir($pathname, $mode, $recursive) ディレクトリを作成する
rmdir($dirname) 空のディレクトリを削除する
scandir($directory) 指定したパスのファイルとディレクトリのリストを配列で返す
rename($oldname, $newname) ファイルまたはディレクトリの名前を変更する
copy($source, $dest) ファイルをコピーする
unlink($filename) ファイルを削除する
<?php
$dirName = 'my_new_directory';

// ディレクトリ作成 (親ディレクトリが存在しない場合も再帰的に作成)
if (!is_dir($dirName)) {
    if (mkdir($dirName, 0777, true)) {
        echo "'$dirName' を作成しました <br>";

        // ファイルを作成してコピー
        file_put_contents($dirName . '/original.txt', '元のファイル');
        if (copy($dirName . '/original.txt', $dirName . '/copied.txt')) {
            echo "ファイルをコピーしました <br>";
        }

        // 名前変更
        if (rename($dirName . '/copied.txt', $dirName . '/renamed.txt')) {
            echo "ファイル名を変更しました <br>";
        }

        // ディレクトリ内のリスト表示
        echo "ディレクトリ内容: <br>";
        $files = scandir($dirName);
        print_r($files);
        echo "<br>";

        // ファイル削除
        if (unlink($dirName . '/renamed.txt')) {
            echo "ファイルを削除しました <br>";
        }
         if (unlink($dirName . '/original.txt')) {
            echo "ファイルを削除しました <br>";
        }


        // ディレクトリ削除 (空にする必要あり)
        if (rmdir($dirName)) {
            echo "'$dirName' を削除しました <br>";
        } else {
             echo "'$dirName' の削除に失敗(空でない可能性があります)<br>";
        }

    } else {
        echo "'$dirName' の作成に失敗しました <br>";
    }
} else {
     echo "'$dirName' は既に存在します <br>";
}
?>

オブジェクト指向 (OOP)

クラスとオブジェクトを用いて、コードを構造化し、再利用性を高めるプログラミングパラダイムです。

クラスとオブジェクト

<?php
            // クラス定義
            class Car {
                // プロパティ (メンバ変数)
                public $color = 'white'; // public: どこからでもアクセス可能
                private $speed = 0;      // private: クラス内部からのみアクセス可能
                protected $maker;      // protected: クラス内部 + 継承クラスからアクセス可能

                // コンストラクタ (オブジェクト生成時に呼ばれる)
                public function __construct(string $maker, string $color = 'white') {
                    $this->maker = $maker;
                    $this->color = $color;
                    echo $this->maker . "製の" . $this->color . "色の車が作成されました <br>";
                }

                // メソッド (メンバ関数)
                public function accelerate(int $increment): void {
                    $this->speed += $increment;
                    echo $this->speed . "km/h に加速しました <br>";
                }

                public function brake(): void {
                    $this->speed = 0;
                    echo "ブレーキ!停車しました <br>";
                }

                public function getSpeed(): int {
                    return $this->speed;
                }

                // デストラクタ (オブジェクト破棄時に呼ばれる)
                public function __destruct() {
                    echo $this->maker . "製の車が破棄されました <br>";
                }
            }

            // オブジェクト (インスタンス) の生成
            $myCar = new Car('Toyota', 'red');
            $yourCar = new Car('Honda'); // デフォルト色の white が使われる

            // プロパティへのアクセス (publicのみ)
            echo "私の車の色は " . $myCar->color . " です<br>";
            // echo $myCar->speed; // Fatal error: Uncaught Error: Cannot access private property Car::$speed

            // メソッドの呼び出し
            $myCar->accelerate(50);
            echo "現在の速度: " . $myCar->getSpeed() . "km/h<br>";
            $myCar->brake();

            $yourCar->accelerate(30);

            // $myCar, $yourCar はスクリプト終了時などに自動的に破棄され、デストラクタが呼ばれる
            unset($myCar); // 明示的に破棄することも可能
            ?>

継承

既存のクラスの機能を引き継いで新しいクラスを作成します。

<?php
            class Truck extends Car { // Car クラスを継承
                private $payload = 0; // 荷物量

                // 親クラスのコンストラクタを呼び出す
                public function __construct(string $maker, string $color = 'silver') {
                    parent::__construct($maker, $color); // 親のコンストラクタを実行
                    echo "これはトラックです <br>";
                }

                // メソッドのオーバーライド (親クラスのメソッドを上書き)
                public function accelerate(int $increment): void {
                    parent::accelerate($increment / 2); // 親のメソッドを呼び出しつつ、加速を鈍くする
                    echo "(トラックなので加速はゆっくり)<br>";
                }

                // 新しいメソッドの追加
                public function load(int $weight): void {
                    $this->payload += $weight;
                    echo $weight . "kg の荷物を積みました。総積載量: " . $this->payload . "kg <br>";
                }

                public function getMaker(): string {
                    // protected プロパティには継承クラスからアクセス可能
                    return $this->maker;
                }
            }

            $myTruck = new Truck('Isuzu');
            $myTruck->accelerate(60);
            $myTruck->load(1000);
            echo "メーカー: " . $myTruck->getMaker() . "<br>"; // protected 経由で取得
            $myTruck->brake();
            ?>

Static プロパティとメソッド

オブジェクトを生成せずにクラス名から直接アクセスできます。

<?php
            class MathHelper {
                public static $pi = 3.14159; // 静的プロパティ

                // 静的メソッド
                public static function circleArea(float $radius): float {
                    // 静的メソッド内からは $this は使えない
                    // 静的プロパティには self:: でアクセス
                    return self::$pi * $radius * $radius;
                }
            }

            echo "円周率: " . MathHelper::$pi . "<br>";
            echo "半径5の円の面積: " . MathHelper::circleArea(5) . "<br>";
            ?>

定数 (const)

クラス内で変更不可能な値を定義します。

<?php
            class Configuration {
                const MAX_USERS = 100; // クラス定数

                public function showMaxUsers(): void {
                    // クラス定数には self:: でアクセス
                    echo "最大ユーザー数: " . self::MAX_USERS . "<br>";
                }
            }

            echo "設定された最大ユーザー数: " . Configuration::MAX_USERS . "<br>";
            $config = new Configuration();
            $config->showMaxUsers();
            ?>

抽象クラスと抽象メソッド

インスタンス化できず、継承されることを前提としたクラス。抽象メソッドは実装を持たず、子クラスでの実装を強制します。

<?php
            abstract class Animal {
                protected $name;

                public function __construct(string $name) {
                    $this->name = $name;
                }

                // 抽象メソッド (実装は子クラスで行う)
                abstract public function makeSound(): string;

                public function getName(): string {
                    return $this->name;
                }
            }

            class Dog extends Animal {
                // 抽象メソッドの実装
                public function makeSound(): string {
                    return "ワン! ";
                }
            }

            class Cat extends Animal {
                // 抽象メソッドの実装
                public function makeSound(): string {
                    return "ニャー ";
                }
            }

            // $animal = new Animal("動物"); // Fatal error: Cannot instantiate abstract class Animal

            $dog = new Dog("ポチ");
            echo $dog->getName() . "の鳴き声: " . $dog->makeSound() . "<br>";

            $cat = new Cat("タマ");
            echo $cat->getName() . "の鳴き声: " . $cat->makeSound() . "<br>";
            ?>

インターフェース (Interface)

クラスが実装すべきメソッドのシグネチャ(名前、引数、戻り値の型)を定義します。実装はクラス側で行います。

<?php
            // インターフェース定義
            interface Logger {
                public function log(string $message): void;
                public function error(string $message): void;
            }

            // インターフェースを実装するクラス (implements キーワード)
            class FileLogger implements Logger {
                private $logFile;

                public function __construct(string $filename) {
                    $this->logFile = $filename;
                }

                public function log(string $message): void {
                    file_put_contents($this->logFile, "[LOG] " . $message . "\n", FILE_APPEND);
                }

                public function error(string $message): void {
                    file_put_contents($this->logFile, "[ERROR] " . $message . "\n", FILE_APPEND);
                }
            }

            class DatabaseLogger implements Logger {
                public function log(string $message): void {
                    echo "データベースにログ記録: [LOG] " . $message . "<br>";
                    // 実際にはDBに保存する処理
                }

                public function error(string $message): void {
                     echo "データベースにログ記録: [ERROR] " . $message . "<br>";
                    // 実際にはDBに保存する処理
                }
            }

            function processData(Logger $logger, string $data) {
                // 引数で Logger インターフェースを型指定することで、
                // FileLogger でも DatabaseLogger でも受け取れる
                $logger->log("データ処理開始: " . $data);
                // 何らかの処理...
                if (rand(0, 1) === 0) { // 適当なエラー条件
                   $logger->error("処理中にエラー発生");
                } else {
                   $logger->log("データ処理完了");
                }
            }

            $fileLogger = new FileLogger('app.log');
            $dbLogger = new DatabaseLogger();

            processData($fileLogger, "データA");
            processData($dbLogger, "データB");
            ?>

トレイト (Trait)

クラスにメソッドを「ミックスイン(取り込み)」するための仕組み。コードの再利用性を高めます。

<?php
            // トレイト定義
            trait Sharable {
                public function share(string $platform): void {
                    echo $this->getContentToShare() . " を " . $platform . " で共有しました! <br>";
                }

                // トレイトを使用するクラス側で実装されることを期待するメソッド
                abstract protected function getContentToShare(): string;
            }

            class BlogPost {
                use Sharable; // Sharable トレイトを使用

                private $title;

                public function __construct(string $title) {
                    $this->title = $title;
                }

                protected function getContentToShare(): string {
                    return "ブログ記事: " . $this->title;
                }
            }

            class Photo {
                use Sharable;

                private $filename;

                public function __construct(string $filename) {
                    $this->filename = $filename;
                }

                 protected function getContentToShare(): string {
                    return "写真: " . $this->filename;
                }
            }

            $post = new BlogPost("PHPトレイト入門");
            $photo = new Photo("cat.jpg");

            $post->share("Twitter");
            $photo->share("Instagram");
            ?>

Final キーワード

final をクラスにつけると継承できなくなり、メソッドにつけるとオーバーライドできなくなります。

<?php
            final class Singleton {
                // ... シングルトンパターンの実装など
            }

            // class ExtendedSingleton extends Singleton {} // Fatal error: Class ExtendedSingleton may not inherit from final class (Singleton)

            class BaseClass {
                public function normalMethod(): void {}
                final public function cannotOverride(): void {
                    echo "このメソッドはオーバーライドできません<br>";
                }
            }

            class DerivedClass extends BaseClass {
                public function normalMethod(): void {} // OK
                // public function cannotOverride(): void {} // Fatal error: Cannot override final method BaseClass::cannotOverride()
            }

            $obj = new DerivedClass();
            $obj->cannotOverride();
            ?>

マジックメソッド

特定の状況で自動的に呼び出される、__ で始まる特別なメソッドです。

メソッド 説明
__construct()オブジェクト生成時
__destruct()オブジェクト破棄時
__call($name, $args)アクセス不能なメソッド呼び出し時
__callStatic($name, $args)アクセス不能な静的メソッド呼び出し時
__get($name)アクセス不能なプロパティ読み込み時
__set($name, $value)アクセス不能なプロパティ書き込み時
__isset($name)アクセス不能なプロパティに isset() or empty() が呼ばれた時
__unset($name)アクセス不能なプロパティに unset() が呼ばれた時
__toString()オブジェクトが文字列として扱われた時
__invoke()オブジェクトが関数のように呼び出された時
__clone()オブジェクトが clone された時
<?php
            class MagicExample {
                private $data = [];

                public function __set($name, $value) {
                    echo "__set: '$name' に '$value' を設定します <br>";
                    $this->data[$name] = $value;
                }

                public function __get($name) {
                    echo "__get: '$name' を取得します <br>";
                    return $this->data[$name] ?? null;
                }

                public function __isset($name) {
                    echo "__isset: '$name' の存在を確認します <br>";
                    return isset($this->data[$name]);
                }

                 public function __unset($name) {
                    echo "__unset: '$name' を削除します <br>";
                    unset($this->data[$name]);
                }

                public function __call($name, $arguments) {
                    echo "__call: 未定義メソッド '$name' が引数 " . implode(', ', $arguments) . " で呼び出されました <br>";
                }

                public function __toString(): string {
                    echo "__toString: 文字列に変換します <br>";
                    return json_encode($this->data);
                }
            }

            $magic = new MagicExample();
            $magic->message = "Hello"; // __set
            echo $magic->message . "<br>"; // __get
            var_dump(isset($magic->message)); // __isset
            $magic->undefinedMethod(1, 'abc'); // __call
            echo $magic . "<br>"; // __toString
            unset($magic->message); // __unset
            var_dump(isset($magic->message)); // __isset (falseになる)
            ?>

エラー処理と例外

プログラム実行中に発生する可能性のあるエラーや予期しない状況に対処する方法です。

try-catch ブロック

例外が発生する可能性のあるコードを try ブロックで囲み、発生した例外を catch ブロックで捕捉して処理します。finally ブロックは例外の有無に関わらず必ず実行されます。

<?php
            function divide(int $a, int $b): float {
                if ($b === 0) {
                    // 例外をスロー (投げる)
                    throw new InvalidArgumentException("ゼロによる除算はできません ");
                }
                if ($a < 0 || $b < 0) {
                     // 別の種類の例外
                     throw new DomainException("負の数は扱えません");
                }
                return $a / $b;
            }

            try {
                echo "試行開始...<br>";
                $result1 = divide(10, 2);
                echo "10 / 2 = " . $result1 . "<br>";

                // $result2 = divide(5, 0); // ここで InvalidArgumentException がスローされる
                // echo "5 / 0 = " . $result2 . "<br>";

                $result3 = divide(-1, 5); // ここで DomainException がスローされる
                echo "-1 / 5 = " . $result3 . "<br>";

                echo "全ての処理が正常に完了しました <br>";

            } catch (InvalidArgumentException $e) {
                // InvalidArgumentException をキャッチ
                echo "<strong style='color: orange;'>引数エラー発生: " . $e->getMessage() . "</strong><br>";
                // echo "エラー発生箇所: " . $e->getFile() . " の " . $e->getLine() . " 行目<br>";
                // echo "トレース: <pre>" . $e->getTraceAsString() . "</pre>";
            } catch (DomainException $e) {
                 // DomainException をキャッチ
                echo "<strong style='color: purple;'>ドメインエラー発生: " . $e->getMessage() . "</strong><br>";
            } catch (Exception $e) { // Throwable (PHP 7+) でも可
                // その他の Exception (または Error) をキャッチ
                echo "<strong style='color: red;'>予期せぬエラー発生: " . $e->getMessage() . "</strong><br>";
            } finally {
                // 例外の発生有無に関わらず、必ず実行される
                echo "Finally ブロック: 処理を終了します<br>";
            }

            echo "Try-Catchブロックの外側の処理です。<br>";
            ?>

カスタム例外

Exception クラス (またはそのサブクラス) を継承して、独自の例外クラスを作成できます。

<?php
            // カスタム例外クラス
            class NetworkException extends RuntimeException {
                public function __construct($message = "ネットワーク接続エラーが発生しました", $code = 0, Throwable $previous = null) {
                    parent::__construct($message, $code, $previous);
                }

                public function getDetailedErrorMessage(): string {
                    return "Network Error [" . $this->getCode() . "]: " . $this->getMessage();
                }
            }

            function fetchDataFromApi(string $url) {
                // ... API通信処理 ...
                $success = false; // 仮に失敗したとする
                if (!$success) {
                    throw new NetworkException("APIエンドポイント '$url' への接続に失敗しました ", 503);
                }
                // return $responseData;
            }

            try {
                fetchDataFromApi("http://example.com/api/users");
            } catch (NetworkException $e) {
                echo "<strong style='color: brown;'>カスタム例外キャッチ: " . $e->getDetailedErrorMessage() . "</strong><br>";
            } catch (Exception $e) {
                 echo "<strong style='color: red;'>その他のエラー: " . $e->getMessage() . "</strong><br>";
            }
            ?>

エラーレポート設定

開発環境と本番環境でエラー表示レベルを制御します。

  • error_reporting(level): レポートするエラーレベルを設定します。例: E_ALL (全てのエラー), E_ALL & ~E_NOTICE (Notice以外)。
  • ini_set('display_errors', 'On'/'Off'): エラーを画面に表示するかどうかを設定します。開発中は ‘On’, 本番環境では ‘Off’ にするのが一般的です。
  • ini_set('log_errors', 'On'/'Off'): エラーをログファイルに記録するかどうかを設定します。
  • ini_set('error_log', '/path/to/php-error.log'): エラーログファイルのパスを指定します。
<?php
            // 開発環境向け設定 (例)
            error_reporting(E_ALL);
            ini_set('display_errors', '1'); // 'On' と同じ
            ini_set('log_errors', '1');
            ini_set('error_log', 'php_errors.log'); // 適切なパスに設定

            echo "エラーレポート設定例 (開発向け) <br>";

            // 存在しない変数を参照 (Noticeが発生する)
            // echo $undefinedVariable; // Notice: Undefined variable: undefinedVariable ...

            // 本番環境向け設定 (例) - 通常は php.ini で設定
            /*
            error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT); // Notice, Deprecated, Strict を除く
            ini_set('display_errors', '0'); // 'Off' と同じ
            ini_set('log_errors', '1');
            ini_set('error_log', '/var/log/php_error.log'); // サーバー上の適切なパス
            */

            // エラーハンドラの登録 (より高度な制御)
            set_error_handler(function($errno, $errstr, $errfile, $errline) {
                // ここでエラー情報をログに記録したり、
                // 特定のエラーを例外に変換したりできる
                if (!(error_reporting() & $errno)) {
                    // error_reporting 設定で抑制されているエラーは無視
                    return false;
                }
                // echo "[カスタムエラーハンドラ] $errno: $errstr in $errfile on line $errline <br>";
                // 場合によっては処理を中断
                // die();
                // 例外に変換する例
                // throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
                return true; // true を返すと PHP の標準エラーハンドラは実行されない
            });

            // 例外ハンドラの登録 (キャッチされなかった例外の処理)
            set_exception_handler(function(Throwable $exception) {
                 echo "<strong style='color: darkred;'>[カスタム例外ハンドラ] キャッチされなかった例外: " . get_class($exception) . " - " . $exception->getMessage() . "</strong><br>";
                 // ここでエラーページを表示したり、ログに記録したりする
                 error_log("Uncaught Exception: " . $exception);
                 // http_response_code(500);
                 // include 'error_page.php';
            });

            // わざとエラーを発生させる (Notice)
            // echo $anotherUndefined;

            // わざと例外をスローしてキャッチしない
            // throw new RuntimeException("これはキャッチされない例外です");

            // エラーハンドラを元に戻す
            // restore_error_handler();
            // restore_exception_handler();
            ?>

データベース操作 (PDO)

PHP Data Objects (PDO) は、様々なデータベースへのアクセスを統一的なインターフェースで提供する拡張モジュールです。SQLインジェクション対策としてプリペアドステートメントの使用が推奨されます。

データベース接続

<?php
        $dsn = 'mysql:host=localhost;dbname=test_db;charset=utf8mb4'; // Data Source Name
        $username = 'db_user';
        $password = 'db_password';
        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // エラーモードを例外に設定
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // デフォルトのフェッチモードを連想配列に
            PDO::ATTR_EMULATE_PREPARES   => false,                  // ネイティブのプリペアドステートメントを使用 (推奨)
        ];

        try {
            $pdo = new PDO($dsn, $username, $password, $options);
            echo "データベース接続成功! <br>";
        } catch (PDOException $e) {
            echo "データベース接続失敗 : " . $e->getMessage() . "<br>";
            // 本番環境では詳細なエラーメッセージをユーザーに見せないようにする
            // error_log("DB Connection Error: " . $e->getMessage());
            // exit('データベースエラーが発生しました。');
            $pdo = null; // 接続失敗時は null にしておく
        }
        ?>

注意: 接続情報(ユーザー名、パスワード)はコードに直接書かず、設定ファイルなどから読み込むのが安全です。

クエリの実行 (プリペアドステートメント)

SQLインジェクションを防ぐ最も安全な方法です。

SELECT (データ取得)
<?php
        if ($pdo) { // 接続が成功している場合のみ実行
            try {
                // プレースホルダ (?) を使ったプリペアドステートメント
                $sql = "SELECT id, name, email FROM users WHERE status = ? AND age > ?";
                $stmt = $pdo->prepare($sql);

                // 値をバインドして実行
                $status = 'active';
                $age = 20;
                $stmt->execute([$status, $age]); // ? の順番で配列の値をバインド

                echo "<h4>アクティブで20歳より上のユーザー:</h4>";
                echo "<ul>";
                // 結果を1行ずつフェッチ (FETCH_ASSOC なので連想配列)
                while ($row = $stmt->fetch()) {
                    echo "<li>ID: " . htmlspecialchars($row['id'])
                       . ", Name: " . htmlspecialchars($row['name'])
                       . ", Email: " . htmlspecialchars($row['email']) . "</li>";
                }
                echo "</ul>";

                // 名前付きプレースホルダ (:name) を使った例
                $sql_named = "SELECT * FROM products WHERE category = :category AND price < :max_price";
                $stmt_named = $pdo->prepare($sql_named);

                // bindValue で値をバインド
                $category = 'electronics';
                $maxPrice = 50000;
                $stmt_named->bindValue(':category', $category, PDO::PARAM_STR); // 文字列としてバインド
                $stmt_named->bindValue(':max_price', $maxPrice, PDO::PARAM_INT); // 整数としてバインド
                $stmt_named->execute();

                // 全ての結果を一度に取得 (fetchAll)
                $products = $stmt_named->fetchAll();
                echo "<h4>5万円未満の電子機器:</h4>";
                if ($products) {
                    echo "<pre>" . htmlspecialchars(print_r($products, true)) . "</pre>";
                } else {
                    echo "該当する商品はありません。<br>";
                }

                 // bindParam で変数をバインド (execute時ではなく、変数そのものを参照)
                 $sql_param = "SELECT name FROM users WHERE id = :id";
                 $stmt_param = $pdo->prepare($sql_param);
                 $userId = 1; // execute の前に定義されていれば良い
                 $stmt_param->bindParam(':id', $userId, PDO::PARAM_INT);
                 $stmt_param->execute();
                 $userName = $stmt_param->fetchColumn(); // 最初の列の値だけ取得
                 echo "ID: {$userId} のユーザー名: " . htmlspecialchars($userName) . "<br>";

                 // $userId を変えて再実行
                 $userId = 2;
                 $stmt_param->execute();
                 $userName = $stmt_param->fetchColumn();
                 echo "ID: {$userId} のユーザー名: " . htmlspecialchars($userName) . "<br>";


            } catch (PDOException $e) {
                echo "クエリエラー: " . $e->getMessage() . "<br>";
            }
        }
        ?>
INSERT (データ挿入)
<?php
        if ($pdo) {
            try {
                $sql = "INSERT INTO users (name, email, age, status, created_at) VALUES (:name, :email, :age, :status, NOW())";
                $stmt = $pdo->prepare($sql);

                $name = '新しいユーザー';
                $email = 'newuser@example.com';
                $age = 28;
                $status = 'inactive';

                $stmt->bindParam(':name', $name, PDO::PARAM_STR);
                $stmt->bindParam(':email', $email, PDO::PARAM_STR);
                $stmt->bindParam(':age', $age, PDO::PARAM_INT);
                $stmt->bindParam(':status', $status, PDO::PARAM_STR);

                $stmt->execute();

                // 最後に挿入された行のIDを取得 (AUTO_INCREMENT の場合)
                $lastId = $pdo->lastInsertId();
                echo "新しいユーザーが ID: " . $lastId . " で登録されました <br>";

            } catch (PDOException $e) {
                 echo "挿入エラー: " . $e->getMessage() . "<br>";
            }
        }
        ?>
UPDATE (データ更新)
<?php
        if ($pdo) {
            try {
                $sql = "UPDATE users SET email = :email, status = :status WHERE id = :id";
                $stmt = $pdo->prepare($sql);

                $newEmail = 'updated.user@example.com';
                $newStatus = 'active';
                $userIdToUpdate = 1; // 更新対象のユーザーID

                $stmt->bindValue(':email', $newEmail); // bindValue でも OK
                $stmt->bindValue(':status', $newStatus);
                $stmt->bindValue(':id', $userIdToUpdate, PDO::PARAM_INT);

                $stmt->execute();

                // 影響を受けた行数を取得
                $rowCount = $stmt->rowCount();
                echo $rowCount . " 件のユーザー情報が更新されました <br>";

            } catch (PDOException $e) {
                 echo "更新エラー: " . $e->getMessage() . "<br>";
            }
        }
        ?>
DELETE (データ削除)
<?php
        if ($pdo) {
            try {
                $sql = "DELETE FROM users WHERE id = :id";
                $stmt = $pdo->prepare($sql);

                $userIdToDelete = 2; // 削除対象のユーザーID

                $stmt->bindParam(':id', $userIdToDelete, PDO::PARAM_INT);
                $stmt->execute();

                $rowCount = $stmt->rowCount();
                echo $rowCount . " 件のユーザーが削除されました <br>";

            } catch (PDOException $e) {
                 echo "削除エラー: " . $e->getMessage() . "<br>";
            }
        }
        ?>

トランザクション処理

複数のクエリをひとまとまりの処理として扱い、全て成功した場合のみデータベースに反映させます(コミット)。途中でエラーが発生した場合は、それまでの処理を取り消します(ロールバック)。これによりデータの整合性を保ちます。

<?php
        if ($pdo) {
            try {
                // トランザクション開始
                $pdo->beginTransaction();
                echo "トランザクション開始... <br>";

                // 処理1: 商品在庫を減らす
                $productId = 101;
                $quantityToDecrement = 1;
                $sql_update_stock = "UPDATE products SET stock = stock - :quantity WHERE id = :id AND stock >= :quantity";
                $stmt_stock = $pdo->prepare($sql_update_stock);
                $stmt_stock->bindValue(':quantity', $quantityToDecrement, PDO::PARAM_INT);
                $stmt_stock->bindValue(':id', $productId, PDO::PARAM_INT);
                $stmt_stock->execute();
                $updatedRows = $stmt_stock->rowCount();

                if ($updatedRows === 0) {
                    // 在庫が不足していたか、商品が存在しない場合
                    throw new Exception("商品 ID:{$productId} の在庫更新に失敗しました(在庫不足または商品なし)。");
                }
                echo "在庫を更新しました (ID: {$productId}) <br>";


                // 処理2: 注文履歴を記録する
                $userId = 5;
                $sql_insert_order = "INSERT INTO orders (user_id, product_id, quantity, order_date) VALUES (:user_id, :product_id, :quantity, NOW())";
                $stmt_order = $pdo->prepare($sql_insert_order);
                $stmt_order->bindValue(':user_id', $userId, PDO::PARAM_INT);
                $stmt_order->bindValue(':product_id', $productId, PDO::PARAM_INT);
                $stmt_order->bindValue(':quantity', $quantityToDecrement, PDO::PARAM_INT);
                $stmt_order->execute();
                echo "注文履歴を記録しました (User ID: {$userId}, Product ID: {$productId}) <br>";

                // 全ての処理が成功したらコミット
                $pdo->commit();
                echo "トランザクション成功!コミットしました <br>";

            } catch (Exception $e) { // PDOException だけでなく、他の例外も考慮
                // エラーが発生したらロールバック
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                    echo "エラー発生!ロールバックしました : " . $e->getMessage() . "<br>";
                } else {
                     echo "エラー発生(トランザクション外): " . $e->getMessage() . "<br>";
                }
            }
        }
        ?>

クエリの実行 (簡易: query / exec)

注意: これらの方法は、外部からの入力を直接SQLに埋め込むとSQLインジェクションの脆弱性を生む可能性があります。ユーザー入力など信頼できない値を含まない固定的なSQL、または適切にエスケープ処理が行える場合に限定して使用してください。通常はプリペアドステートメントの使用が強く推奨されます。

<?php
        if ($pdo) {
            try {
                // SELECT クエリ (結果セットを返す)
                $sql_query = "SELECT name FROM categories WHERE status = 'active'"; // 固定的なSQLの例
                $stmt_query = $pdo->query($sql_query);

                echo "<h4>アクティブなカテゴリ (query使用):</h4>";
                echo "<ul>";
                foreach ($stmt_query as $row) { // PDOStatement を直接 foreach できる
                    echo "<li>" . htmlspecialchars($row['name']) . "</li>";
                }
                echo "</ul>";

                // INSERT, UPDATE, DELETE クエリ (影響を受けた行数を返す)
                $sql_exec = "UPDATE settings SET value = 'new_value' WHERE key_name = 'site_title'"; // 固定的なSQLの例
                $affectedRows = $pdo->exec($sql_exec);

                if ($affectedRows !== false) {
                    echo $affectedRows . " 件の設定が更新されました (exec使用) <br>";
                } else {
                    // exec はエラー時に false を返す (ERRMODE_SILENT の場合)
                    // ERRMODE_WARNING や ERRMODE_EXCEPTION ではエラーや例外が発生する
                    echo "設定の更新に失敗しました (exec使用) <br>";
                }

            } catch (PDOException $e) {
                 echo "簡易クエリエラー: " . $e->getMessage() . "<br>";
            }
        }
        // 接続を閉じる (通常はスクリプト終了時に自動で閉じられるが、明示的に行う場合)
        $pdo = null;
        echo "データベース接続を閉じました <br>";
        ?>

日付と時刻

PHP で日付や時刻を扱うためのクラスや関数です。DateTimeImmutable クラス (PHP 5.5+) は、メソッド呼び出し時に元のオブジェクトを変更せず、新しいオブジェクトを返すため、予期せぬ変更を防ぎやすく推奨されます。

DateTime / DateTimeImmutable クラス

<?php
            // 現在の日時で DateTimeImmutable オブジェクトを作成
            $now = new DateTimeImmutable();
            echo "現在の日時 (Immutable): " . $now->format('Y-m-d H:i:s P') . "<br>"; // P はタイムゾーンオフセット

            // 特定の日時で DateTime オブジェクトを作成
            $specificDate = new DateTime('2023-10-26 15:30:00', new DateTimeZone('Asia/Tokyo'));
            echo "指定日時 (Mutable, Tokyo): " . $specificDate->format('Y年m月d日 H時i分s秒') . "<br>";

            // DateTimeImmutable は変更メソッドを呼ぶと新しいインスタンスを返す
            $tomorrow = $now->modify('+1 day');
            echo "現在の日時 (変更後も同じ): " . $now->format('Y-m-d H:i:s') . "<br>";
            echo "明日 (新しいインスタンス): " . $tomorrow->format('Y-m-d H:i:s') . "<br>";

            // DateTime は元のインスタンスを変更する
            $yesterday = new DateTime('now', new DateTimeZone('Europe/London'));
            echo "昨日の日付 (変更前, London): " . $yesterday->format('Y-m-d H:i:s P') . "<br>";
            $yesterday->modify('-1 day'); // 元の $yesterday が変更される
            echo "昨日の日付 (変更後, London): " . $yesterday->format('Y-m-d H:i:s P') . "<br>";

            // タイムゾーンの変更
            $tokyoTime = new DateTimeImmutable('now', new DateTimeZone('Asia/Tokyo'));
            $londonTime = $tokyoTime->setTimezone(new DateTimeZone('Europe/London'));
            echo "東京時間: " . $tokyoTime->format('Y-m-d H:i:s P') . "<br>";
            echo "ロンドン時間: " . $londonTime->format('Y-m-d H:i:s P') . "<br>";

            // 日付/時刻の比較
            $date1 = new DateTimeImmutable('2024-01-01');
            $date2 = new DateTimeImmutable('2024-01-15');
            if ($date1 < $date2) {
                echo $date1->format('Y-m-d') . " は " . $date2->format('Y-m-d') . " より前です <br>";
            }

            // 日付/時刻の差分 (DateInterval)
            $interval = $date1->diff($date2);
            echo $date1->format('Y-m-d') . " と " . $date2->format('Y-m-d') . " の差: ";
            echo $interval->format('%R%a 日') . " (詳細: " . $interval->y . "年 " . $interval->m . "ヶ月 " . $interval->d . "日)<br>"; // %Rは符号, %aは総日数

            // フォーマット指定でUnixタイムスタンプを取得
            echo "現在のUnixタイムスタンプ: " . $now->format('U') . "<br>";

             // 特定のフォーマットから DateTime オブジェクトを作成
             $dateString = '2024/05/15 10:00';
             $format = 'Y/m/d H:i';
             $createdDate = DateTimeImmutable::createFromFormat($format, $dateString, new DateTimeZone('Asia/Tokyo'));
             if ($createdDate) {
                echo "'" . $dateString . "' から作成した日時: " . $createdDate->format(DateTimeInterface::RFC3339) . "<br>"; // RFC3339 フォーマット
             } else {
                echo "'" . $dateString . "' のフォーマット解析に失敗しました <br>";
             }

            ?>

format() で使用できる主な書式文字:

  • Y: 4桁の年 (例: 2024)
  • y: 2桁の年 (例: 24)
  • m: 2桁の月 (01-12)
  • n: 先頭ゼロなしの月 (1-12)
  • d: 2桁の日 (01-31)
  • j: 先頭ゼロなしの日 (1-31)
  • H: 2桁の時 (24時間表記, 00-23)
  • h: 2桁の時 (12時間表記, 01-12)
  • i: 2桁の分 (00-59)
  • s: 2桁の秒 (00-59)
  • A: 午前/午後 (AM/PM)
  • w: 曜日 (0:日曜 – 6:土曜)
  • U: Unixタイムスタンプ
  • c: ISO 8601形式 (例: 2004-02-12T15:19:21+00:00)
  • r: RFC 2822形式 (例: Thu, 21 Dec 2000 16:01:07 +0200)
  • P: タイムゾーンオフセット (例: +09:00)
  • T: タイムゾーン識別子 (例: Asia/Tokyo)

DateInterval クラス

二つの日時の間の期間や、DateTime::add(), DateTime::sub() で使用する期間を表します。

<?php
            // 期間を指定して DateInterval オブジェクトを作成 (P=Period, 1Y=1年, 2M=2ヶ月, 10D=10日, T=Time, 3H=3時間 ...)
            $intervalSpec = 'P1Y2M10DT3H5M'; // 1年2ヶ月10日と3時間5分
            $intervalObj = new DateInterval($intervalSpec);
            echo "期間: " . $intervalObj->format('%y年 %mヶ月 %d日 %h時間 %i分') . "<br>";

            $startDate = new DateTimeImmutable('2024-01-01');
            // 期間を加算
            $endDate = $startDate->add($intervalObj);
            echo "開始日: " . $startDate->format('Y-m-d H:i') . "<br>";
            echo "期間を加算した終了日: " . $endDate->format('Y-m-d H:i') . "<br>";

            // 期間を減算
            $subtractedDate = $endDate->sub($intervalObj);
             echo "期間を減算した日付: " . $subtractedDate->format('Y-m-d H:i') . "<br>";
            ?>

日付/時刻関連の関数

DateTime クラスが導入される前から存在する関数群です。現在では DateTime/DateTimeImmutable の利用が推奨されますが、互換性や特定の用途で使われます。

関数 説明
date($format, $timestamp) Unixタイムスタンプを指定した書式でフォーマットする (タイムスタンプ省略時は現在時刻)
echo date('Y/m/d H:i:s');
time() 現在の Unix タイムスタンプ (1970年1月1日 00:00:00 GMT からの経過秒数) を返す
$current_ts = time();
strtotime($time, $now) 英文形式の日付/時刻表現を Unix タイムスタンプに変換する
$ts = strtotime('next Monday');
echo date('Y-m-d', $ts);
mktime($hour, $minute, $second, $month, $day, $year) 指定した日時から Unix タイムスタンプを生成する
$ts = mktime(0, 0, 0, 1, 1, 2025);
echo date('c', $ts);
getdate($timestamp) Unix タイムスタンプから日付/時刻情報を要素とする連想配列を返す
$dateInfo = getdate();
print_r($dateInfo);
checkdate($month, $day, $year) 指定された日付がグレゴリオ暦として有効かチェックする
var_dump(checkdate(2, 29, 2024)); // true
var_dump(checkdate(2, 29, 2023)); // false

セッション管理

ユーザーがウェブサイトを訪れている間、ユーザー固有の情報をサーバーサイドで維持するための仕組みです。通常、セッションIDがクッキーを使ってブラウザに保存され、リクエストごとに送信されます。

セッションの開始と利用

注意: session_start() は、HTMLなどの出力がブラウザに送信されるに呼び出す必要があります。

<?php
            // セッションを開始 (または既存のセッションを再開)
            // この関数の前に出力をしないこと!
            if (session_status() === PHP_SESSION_NONE) { // まだ開始されていない場合のみ開始
                session_start();
            }

            echo "セッション開始/再開しました  (Session ID: " . session_id() . ")<br>";

            // セッション変数への書き込み (スーパーグローバル変数 $_SESSION を使用)
            if (isset($_SESSION['visit_count'])) {
                $_SESSION['visit_count']++;
            } else {
                $_SESSION['visit_count'] = 1;
            }
            $_SESSION['username'] = 'TestUser';
            $_SESSION['last_access'] = date('Y-m-d H:i:s');

            echo "訪問回数: " . $_SESSION['visit_count'] . " 回 <br>";
            echo "ユーザー名: " . htmlspecialchars($_SESSION['username']) . " <br>";
            echo "最終アクセス: " . $_SESSION['last_access'] . " <br>";

            // セッション変数の存在確認
            if (isset($_SESSION['user_preferences'])) {
                echo "ユーザー設定が存在します。<br>";
                // print_r($_SESSION['user_preferences']);
            } else {
                echo "ユーザー設定はまだありません。<br>";
                $_SESSION['user_preferences'] = ['theme' => 'dark', 'language' => 'ja'];
                echo "デフォルト設定を保存しました。<br>";
            }

            // セッション変数の一部を削除
            // unset($_SESSION['temporary_data']);

            // セッションの内容確認 (デバッグ用)
            echo "<h4>現在のセッション内容:</h4>";
            echo "<pre>" . htmlspecialchars(print_r($_SESSION, true)) . "</pre>";
            ?>

セッションを利用する各ページの先頭(出力前)で session_start() を呼び出すことで、前のページで設定した $_SESSION 変数の値にアクセスできます。

セッションの破棄

<?php
            // セッションを開始 (破棄する前にも必要)
            if (session_status() === PHP_SESSION_NONE) {
                session_start();
            }

            // 1. 全てのセッション変数を空にする
            $_SESSION = array(); // または session_unset(); (PHP 7.2以降非推奨気味だが機能はする)
            echo "セッション変数を空にしました。<br>";
            echo "現在のセッション内容 (空のはず): <pre>" . htmlspecialchars(print_r($_SESSION, true)) . "</pre>";

            // 2. セッションクッキーを削除 (推奨)
            // クッキーのパラメータは session_get_cookie_params() で取得可能
            if (ini_get("session.use_cookies")) {
                $params = session_get_cookie_params();
                setcookie(session_name(), '', time() - 42000, // 有効期限を過去に設定
                    $params["path"], $params["domain"],
                    $params["secure"], $params["httponly"]
                );
                 echo "セッションクッキーを削除するように設定しました。<br>";
            }

            // 3. 最終的に、サーバー上のセッションファイルを破壊する
            if (session_destroy()) {
                echo "セッションを完全に破棄しました。<br>";
            } else {
                 echo "セッションの破棄に失敗しました。<br>";
            }

            // セッション破棄後は $_SESSION は使えない (再度 session_start() が必要)
            // echo $_SESSION['username']; // Notice: Undefined variable: _SESSION ...

            ?>

セッション設定 (php.ini または ini_set)

セキュリティ向上のため、以下の設定を確認・変更することが推奨されます。

  • session.use_strict_mode = 1: PHPが初期化していないセッションIDを受け付けなくなり、セッション固定化攻撃のリスクを軽減します。(PHP 5.5.2+) 推奨
  • session.use_cookies = 1: セッションIDの管理にクッキーを使用します。通常は1です。
  • session.use_only_cookies = 1: URLなどにセッションIDが含まれるのを防ぎ、セッションハイジャックのリスクを軽減します。推奨
  • session.cookie_httponly = 1: JavaScriptからセッションクッキーにアクセスできなくなり、XSSによるセッションID盗難のリスクを軽減します。強く推奨
  • session.cookie_secure = 1: HTTPS接続の場合のみセッションクッキーを送信するようにします。HTTPSサイトでは必須
  • session.gc_maxlifetime = 1440: セッションデータがガベージコレクション(削除)されるまでの秒数(デフォルトは1440秒=24分)。必要に応じて調整します。
  • session.cookie_lifetime = 0: セッションクッキーの有効期限(秒)。0はブラウザを閉じるまで有効。
  • session.save_path = "/path/to/sessions": セッションファイルの保存場所。デフォルトの場所から変更し、ウェブサーバーから直接アクセスできない場所に設定するとより安全です。
  • session.name = PHPSESSID: セッションIDを格納するクッキー名。デフォルトから変更することが推奨される場合があります。
  • session.regenerate_id = 1: (PHP 7.1 で非推奨化, session_regenerate_id() を使う) 定期的にセッションIDを再生成します。ログイン成功時などに session_regenerate_id(true) を呼び出すのが一般的です。
<?php
            // 例: ログイン成功時にセッションIDを再生成 (セッション固定化対策)
            if (session_status() === PHP_SESSION_NONE) {
                session_start();
            }

            // ... ユーザー認証処理 ...
            $login_success = true; // 仮にログイン成功

            if ($login_success) {
                // 古いセッションIDを無効化し、新しいセッションIDを生成する
                session_regenerate_id(true); // true を指定すると古いセッションファイルも削除
                $_SESSION['user_id'] = 123; // ログインしたユーザーのIDなどを保存
                $_SESSION['logged_in'] = true;
                echo "ログイン成功!セッションIDを再生成しました (New ID: " . session_id() . ")<br>";
            }
            ?>

クッキー操作

サーバーからユーザーのブラウザに小規模なデータを保存させる仕組みです。HTTPヘッダーを通じて送受信されます。

注意: クッキーはユーザーが改変・削除できるため、重要な情報や認証情報そのものを直接保存するのは避けるべきです。通常はセッションIDのような一時的な識別子を保存するために使われます。

注意: setcookie() は、HTMLなどの出力がブラウザに送信されるに呼び出す必要があります。

クッキーの設定 (送信)

<?php
            // 最も基本的なクッキー設定 (名前と値)
            // ブラウザを閉じるまで有効
            setcookie('user_name', 'Taro Yamada');
            echo "クッキー 'user_name' を設定しました (ブラウザ終了まで有効)。<br>";

            // 有効期限を指定 (Unixタイムスタンプ) - 現在時刻から1時間後
            $expire = time() + 3600; // 60秒 * 60分
            setcookie('visit_time', date('Y-m-d H:i:s'), $expire);
            echo "クッキー 'visit_time' を設定しました (1時間有効)。<br>";

            // パス、ドメイン、Secure, HttpOnly を指定
            $cookie_name = 'preferences';
            $cookie_value = json_encode(['theme' => 'light', 'lang' => 'en']);
            $cookie_expire = time() + (86400 * 30); // 30日間有効 (60*60*24*30)
            $cookie_path = '/'; // サイト全体で有効
            $cookie_domain = ''; // デフォルト (現在のドメイン) - '.example.com' のようにサブドメイン共通にもできる
            $cookie_secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off'; // HTTPS接続の場合のみ送信 (推奨)
            $cookie_httponly = true; // JavaScriptからのアクセスを禁止 (XSS対策, 推奨)

            setcookie($cookie_name, $cookie_value, $cookie_expire, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly);
            echo "クッキー 'preferences' を詳細設定で設定しました (30日間有効、Secure: " . ($cookie_secure ? 'Yes' : 'No') . ", HttpOnly: Yes)。<br>";

             // PHP 7.3 以降: SameSite 属性もオプションで指定可能 (CSRF対策)
             $samesite_options = [
                 'expires' => time() + 86400,
                 'path' => '/',
                 'domain' => '',
                 'secure' => true,
                 'httponly' => true,
                 'samesite' => 'Strict' // または 'Lax', 'None' (Noneの場合はSecure属性が必須)
             ];
             setcookie('session_token', 'some_random_value', $samesite_options);
             echo "クッキー 'session_token' を SameSite=Strict で設定しました。<br>";


            // これ以降にHTMLなどを出力する
            ?>
            <!DOCTYPE html>
            <html>
            <head><title>Cookie Set</title></head>
            <body>
            <p>クッキーを設定しました。次のリクエストからブラウザはこの情報を送信します。</p>
            </body>
            </html>
            

クッキーの取得

ブラウザから送信されたクッキーは、スーパーグローバル変数 $_COOKIE に連想配列として格納されます。

<?php
            echo "<h4>受信したクッキー:</h4>";

            if (isset($_COOKIE['user_name'])) {
                echo "ようこそ、" . htmlspecialchars($_COOKIE['user_name']) . " さん!<br>";
            } else {
                echo "'user_name' クッキーは見つかりませんでした。<br>";
            }

            if (isset($_COOKIE['visit_time'])) {
                 echo "前回の訪問時刻 (クッキーより): " . htmlspecialchars($_COOKIE['visit_time']) . " <br>";
            }

            if (isset($_COOKIE['preferences'])) {
                $prefs = json_decode($_COOKIE['preferences'], true); // JSONをデコード
                if ($prefs) {
                    echo "設定 (クッキーより): テーマ=" . htmlspecialchars($prefs['theme']) . ", 言語=" . htmlspecialchars($prefs['lang']) . " <br>";
                }
            }

            echo "<h4>全ての受信クッキー (デバッグ用):</h4>";
            echo "<pre>" . htmlspecialchars(print_r($_COOKIE, true)) . "</pre>";
            ?>

クッキーの削除

クッキーを削除するには、同じ名前のクッキーを過去の有効期限で設定します。他のパラメータ(パス、ドメインなど)も設定時と同じにする必要があります。

<?php
            // 'user_name' クッキーを削除
            if (isset($_COOKIE['user_name'])) {
                setcookie('user_name', '', time() - 3600, '/'); // 有効期限を過去に設定
                echo "クッキー 'user_name' を削除するように設定しました。<br>";
                // unset($_COOKIE['user_name']); // $_COOKIE 配列からも削除しておくと、同じスクリプト内で再利用しない場合に混乱を防げる
            } else {
                 echo "クッキー 'user_name' は既に存在しません。<br>";
            }

            // 'preferences' クッキーを削除 (設定時と同じパラメータを指定)
             if (isset($_COOKIE['preferences'])) {
                $cookie_path = '/';
                $cookie_domain = '';
                $cookie_secure = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
                $cookie_httponly = true;
                 setcookie('preferences', '', time() - 3600, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly);
                 echo "クッキー 'preferences' を削除するように設定しました。<br>";
                 // unset($_COOKIE['preferences']);
             }


            // これ以降にHTMLなどを出力する
            ?>
            <!DOCTYPE html>
            <html>
            <head><title>Cookie Delete</title></head>
            <body>
            <p>クッキーを削除するように設定しました。ブラウザは次のリクエストでこの情報を削除します。</p>
            </body>
            </html>
            

⇆ JSON 操作

JavaScript Object Notation (JSON) は、データ交換フォーマットとして広く使われています。PHPでは、配列やオブジェクトとJSON文字列を相互に変換する関数が用意されています。

PHPのデータ → JSON文字列 (エンコード)

json_encode() 関数を使用します。

<?php
            // 連想配列
            $user = [
                'id' => 1,
                'name' => '山田 太郎',
                'email' => 'yamada@example.com',
                'isAdmin' => false,
                'tags' => ['PHP', 'プログラミング', '日本語'] // 配列もOK
            ];

            // オブジェクト
            class Product {
                public $id = 101;
                public $name = 'すごい商品';
                private $price = 5000; // private/protected は通常エンコードされない
                public function getPrice() { return $this->price; }
            }
            $product = new Product();

            // 配列の配列
            $users = [
                ['id' => 2, 'name' => '佐藤 花子'],
                ['id' => 3, 'name' => '鈴木 一郎']
            ];

            // エンコード実行
            $jsonUser = json_encode($user);
            $jsonProduct = json_encode($product);
            $jsonUsers = json_encode($users);

            echo "連想配列のJSON: " . $jsonUser . "<br>";
            echo "オブジェクトのJSON: " . $jsonProduct . "<br>"; // public プロパティのみ
            echo "配列の配列のJSON: " . $jsonUsers . "<br>";

            // オプションを指定してエンコード
            // JSON_PRETTY_PRINT: 見やすく整形する
            // JSON_UNESCAPED_UNICODE: マルチバイト文字をUnicodeエスケープしない (日本語などがそのまま出力される)
            // JSON_UNESCAPED_SLASHES: スラッシュ / をエスケープしない
            $jsonUserPrettyUnicode = json_encode($user, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

            echo "<h4>整形&日本語非エスケープJSON:</h4>";
            echo "<pre>" . htmlspecialchars($jsonUserPrettyUnicode) . "</pre>";

            // エンコードエラーチェック (PHP 5.3+)
            json_encode("\xB1\x31"); // 無効なUTF-8シーケンス
            if (json_last_error() !== JSON_ERROR_NONE) {
                echo "JSONエンコードエラー発生: " . json_last_error_msg() . " <br>";
            }
            ?>

JSON文字列 → PHPのデータ (デコード)

json_decode() 関数を使用します。

<?php
            $jsonStringUser = '{"id":1,"name":"山田 太郎","email":"yamada@example.com","isAdmin":false,"tags":["PHP","プログラミング","日本語"]}';
            $jsonStringUsers = '[{"id":2,"name":"佐藤 花子"},{"id":3,"name":"鈴木 一郎"}]';
            $invalidJson = '{"id":1, "name": "不正なJSON",}'; // 末尾にカンマ

            // デフォルトでは stdClass オブジェクトにデコードされる
            $userObj = json_decode($jsonStringUser);
            $usersArrayOfObjs = json_decode($jsonStringUsers);

            echo "<h4>デコード結果 (オブジェクト):</h4>";
            if ($userObj) {
                echo "ID: " . $userObj->id . ", Name: " . $userObj->name . "<br>"; // プロパティとしてアクセス
                echo "タグ[0]: " . $userObj->tags[0] . "<br>";
                echo "<pre>" . htmlspecialchars(print_r($userObj, true)) . "</pre>";
            }
             if ($usersArrayOfObjs) {
                 echo "ユーザーリスト[1]の名前: " . $usersArrayOfObjs[1]->name . "<br>";
                  echo "<pre>" . htmlspecialchars(print_r($usersArrayOfObjs, true)) . "</pre>";
             }


            // 第2引数に true を指定すると連想配列にデコードされる
            $userAssoc = json_decode($jsonStringUser, true);
            $usersAssoc = json_decode($jsonStringUsers, true);

            echo "<h4>デコード結果 (連想配列):</h4>";
             if ($userAssoc) {
                echo "ID: " . $userAssoc['id'] . ", Name: " . $userAssoc['name'] . "<br>"; // 配列キーとしてアクセス
                echo "タグ[1]: " . $userAssoc['tags'][1] . "<br>";
                 echo "<pre>" . htmlspecialchars(print_r($userAssoc, true)) . "</pre>";
            }
             if ($usersAssoc) {
                 echo "ユーザーリスト[0]の名前: " . $usersAssoc[0]['name'] . "<br>";
                  echo "<pre>" . htmlspecialchars(print_r($usersAssoc, true)) . "</pre>";
             }

            // デコードエラーチェック (PHP 5.3+)
            $invalidData = json_decode($invalidJson);
            if (json_last_error() !== JSON_ERROR_NONE) {
                 echo "JSONデコードエラー発生: " . json_last_error_msg() . " (コード: " . json_last_error() . ") <br>";
            }

            // 深さの制限 (第3引数, PHP 5.3+)
            $deepJson = '{"a":{"b":{"c":{"d":"value"}}}}';
            $decodedLimited = json_decode($deepJson, true, 2); // 深さ2まで
             if (json_last_error() === JSON_ERROR_DEPTH) {
                 echo "JSONデコードエラー: 最大スタック深さに達しました <br>";
             } else {
                 // print_r($decodedLimited);
             }

             // 大きな整数の扱い (第4引数: JSON_BIGINT_AS_STRING, PHP 5.4+)
             $jsonBigInt = '{"id": 12345678901234567890, "value": "large"}';
             $decodedBigInt = json_decode($jsonBigInt, false, 512, JSON_BIGINT_AS_STRING);
             if ($decodedBigInt) {
                echo "大きな整数のID (文字列として): ";
                var_dump($decodedBigInt->id); // string(20) "12345678901234567890"
                echo "<br>";
             }

            ?>

正規表現 (PCRE)

PHPはPerl互換の正規表現 (PCRE – Perl Compatible Regular Expressions) をサポートしており、文字列の複雑なパターンマッチングや置換、分割が可能です。preg_ 系の関数を使用します。

正規表現パターンは通常、デリミタ(多くは /)で囲み、必要に応じて末尾に修飾子をつけます。

パターンマッチ (preg_match)

文字列がパターンにマッチするかどうかを調べ、最初にマッチした部分を取得します。

<?php
            $subject = "私の電話番号は 090-1234-5678 で、郵便番号は 123-4567 です。";
            $pattern = '/\d{3}-\d{4}-\d{4}/'; // 電話番号のパターン (例: xxx-xxxx-xxxx)

            // パターンにマッチするかどうか
            if (preg_match($pattern, $subject, $matches)) {
                echo "電話番号が見つかりました!: " . htmlspecialchars($matches[0]) . "<br>";
                // $matches[0] にはマッチした文字列全体が入る
                // $matches[1] 以降にはキャプチャグループ (...) で囲まれた部分が入る
            } else {
                echo "電話番号のパターンは見つかりませんでした。<br>";
            }

            // キャプチャグループを使った例 (郵便番号)
            $zipPattern = '/(\d{3})-(\d{4})/'; // グループ1: 3桁の数字, グループ2: 4桁の数字
            if (preg_match($zipPattern, $subject, $zipMatches)) {
                echo "郵便番号が見つかりました!<br>";
                echo "全体: " . htmlspecialchars($zipMatches[0]) . "<br>"; // 123-4567
                echo "前半3桁: " . htmlspecialchars($zipMatches[1]) . "<br>"; // 123
                echo "後半4桁: " . htmlspecialchars($zipMatches[2]) . "<br>"; // 4567
                 echo "<pre>マッチ情報: " . htmlspecialchars(print_r($zipMatches, true)) . "</pre>";
            }

            // 修飾子の例: i (大文字小文字を区別しない)
            $text = "Hello World, hello PHP.";
            $patternIgnoreCase = '/hello/i';
            if (preg_match($patternIgnoreCase, $text, $helloMatches)) {
                echo "修飾子 'i': 最初にマッチした 'hello' (大文字小文字無視): " . htmlspecialchars($helloMatches[0]) . "<br>"; // Hello
            }
            ?>

全パターンマッチ (preg_match_all)

文字列中にあるパターンにマッチする全ての箇所を検索します。

<?php
            $logData = "Error at 2024-01-15 10:30:15 - Process failed. Warning at 2024-01-15 11:00:00 - Low memory. Error at 2024-01-16 09:00:00 - Disk full.";
            $timestampPattern = '/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/'; // YYYY-MM-DD HH:MM:SS のパターン

            // 全てのマッチを取得 (デフォルト: $matches[0]に全体, $matches[1]にグループ1, ...)
            if (preg_match_all($timestampPattern, $logData, $allTimestamps)) {
                echo "ログ中の全てのタイムスタンプ: <br>";
                 echo "<pre>" . htmlspecialchars(print_r($allTimestamps[0], true)) . "</pre>";
            } else {
                echo "タイムスタンプは見つかりませんでした。<br>";
            }

            // PREG_SET_ORDER フラグ: $matches の各要素が1回のマッチに対応する配列になる
             $keyValuePattern = '/(\w+)\s*=\s*(\w+)/'; // key = value のパターン
             $configString = "user=admin role=editor theme=dark";
             if (preg_match_all($keyValuePattern, $configString, $keyValueMatches, PREG_SET_ORDER)) {
                 echo "設定のキーと値 (PREG_SET_ORDER): <br>";
                 echo "<ul>";
                 foreach ($keyValueMatches as $match) {
                     // $match[0]: 全体 (例: user=admin)
                     // $match[1]: キー (例: user)
                     // $match[2]: 値 (例: admin)
                     echo "<li>Key: " . htmlspecialchars($match[1]) . ", Value: " . htmlspecialchars($match[2]) . "</li>";
                 }
                  echo "</ul>";
                 // echo "<pre>" . htmlspecialchars(print_r($keyValueMatches, true)) . "</pre>";
             }

            ?>

置換 (preg_replace)

パターンにマッチした部分を別の文字列に置き換えます。

<?php
            $text = "Contact us at support@example.com or info@example.org for details.";
            $emailPattern = '/[\w\-\.]+@[\w\-]+\.[\w\-]+/'; // 簡単なメールアドレスパターン
            $replacement = '[Email Hidden]';

            // マッチした部分を置換
            $replacedText = preg_replace($emailPattern, $replacement, $text);
            echo "元のテキスト: " . htmlspecialchars($text) . "<br>";
            echo "置換後のテキスト: " . htmlspecialchars($replacedText) . " <br>";

            // キャプチャグループを使った置換 (後方参照: $1, $2, ...)
            $dateText = "日付は 2024/01/15 と 2024/02/20 です。";
            $datePattern = '/(\d{4})\/(\d{2})\/(\d{2})/'; // YYYY/MM/DD
            $dateFormatReplacement = '$1年$2月$3日'; // $1=年, $2=月, $3=日

            $formattedDateText = preg_replace($datePattern, $dateFormatReplacement, $dateText);
            echo "日付フォーマット変換前: " . htmlspecialchars($dateText) . "<br>";
            echo "日付フォーマット変換後: " . htmlspecialchars($formattedDateText) . " <br>";

            // 配列を使った複数置換
            $patterns = ['/apple/i', '/banana/i'];
            $replacements = ['リンゴ', 'バナナ'];
            $fruitText = "I like apple and BANANA.";
            $replacedFruitText = preg_replace($patterns, $replacements, $fruitText);
            echo "果物置換前: " . htmlspecialchars($fruitText) . "<br>";
            echo "果物置換後: " . htmlspecialchars($replacedFruitText) . "<br>";

            // limit パラメータ (置換回数の制限)
            $limitText = "a b c a b c a b c";
            $limitedReplace = preg_replace('/a/', 'X', $limitText, 1); // 最初の 'a' だけ置換
            echo "Limit 1 置換: " . htmlspecialchars($limitedReplace) . "<br>"; // X b c a b c a b c

            ?>

分割 (preg_split)

文字列をパターンに基づいて分割し、配列にします。

<?php
            $data = "item1, item2 ; item3 | item4";
            // カンマ、セミコロン、パイプ、およびその周りの空白で分割
            $delimiterPattern = '/\s*[,;|]\s*/';

            $items = preg_split($delimiterPattern, $data);
            echo "分割前のデータ: " . htmlspecialchars($data) . "<br>";
            echo "分割後の配列: <br>";
            echo "<pre>" . htmlspecialchars(print_r($items, true)) . "</pre>"; // ["item1", "item2", "item3", "item4"]

            // PREG_SPLIT_NO_EMPTY: 空の要素を除外
            $dataWithEmpty = "a,,b, c ,";
            $splitNoEmpty = preg_split('/,/', $dataWithEmpty, -1, PREG_SPLIT_NO_EMPTY);
            echo "空要素を除外して分割: <br>";
            echo "<pre>" . htmlspecialchars(print_r($splitNoEmpty, true)) . "</pre>"; // ["a", "b", " c "]

            // PREG_SPLIT_DELIM_CAPTURE: デリミタ自体もキャプチャグループで囲めば結果に含まれる
            $sentence = "これはペンです。それはノートです!あれは何?";
            $splitKeepDelim = preg_split('/([。!?])/', $sentence, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
             echo "区切り文字を含めて分割: <br>";
             echo "<pre>" . htmlspecialchars(print_r($splitKeepDelim, true)) . "</pre>"; // ["これはペンです", "。", "それはノートです", "!", "あれは何", "?"]

            ?>

その他の関数と注意点

  • preg_quote($str, $delimiter): 正規表現の特殊文字(メタ文字: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : - # など)をエスケープします。ユーザー入力などをパターンの一部として使う場合に安全です。
  • preg_last_error() / preg_last_error_msg(): 直前のPCRE関数実行時のエラーコード/メッセージを取得します (PHP 7.0+)。
  • パフォーマンス: 複雑すぎる正規表現や、バックトラックが多く発生する可能性のあるパターンは、パフォーマンスに影響を与えることがあります。
  • 文字エンコーディング: マルチバイト文字(日本語など)を扱う場合は、u 修飾子 (UTF-8モード) をパターンに追加することが重要です。これにより、正規表現エンジンが文字単位で正しく動作します。
<?php
             // preg_quote の例
             $userInput = "Search for this+string.";
             $escapedInput = preg_quote($userInput, '/');
             $patternWithInput = '/' . $escapedInput . '/'; // /Search for this\+string\./
             echo "エスケープされたパターン: " . htmlspecialchars($patternWithInput) . "<br>";

             // u 修飾子の例
             $japaneseText = "日本語の文字数";
             $patternMb = '/./'; // u なしだとバイト単位
             $patternMbU = '/./u'; // u ありだと文字単位

             preg_match_all($patternMb, $japaneseText, $matchesBytes);
             preg_match_all($patternMbU, $japaneseText, $matchesChars);

             echo "'u' 修飾子なし (バイト数): " . count($matchesBytes[0]) . "<br>";
             echo "'u' 修飾子あり (文字数): " . count($matchesChars[0]) . "<br>"; // 期待通り 8 になるはず
             ?>

その他

その他の便利な構文や関数です。

定数定義

  • define("CONSTANT_NAME", value, case_insensitive): グローバル定数を定義します。実行時に定義されます。第3引数(大文字小文字区別しない)は非推奨。
  • const CONSTANT_NAME = value;: クラス外でもグローバル定数を定義できます(PHP 5.3+)。コンパイル時に定義されます。こちらが推奨されることが多いです。
<?php
        define("SITE_NAME", "My Awesome Site");
        const APP_VERSION = "1.2.0"; // こちらが推奨

        echo "サイト名: " . SITE_NAME . "<br>";
        echo "アプリバージョン: " . APP_VERSION . "<br>";

        // const は条件分岐内などでは使えない
        // if (true) {
        //   const CONDITIONAL_CONST = "defined"; // Parse error
        // }
        if (true) {
          define("CONDITIONAL_DEFINE", "defined"); // OK
           echo "条件付き定義: " . CONDITIONAL_DEFINE . "<br>";
        }
        ?>

ファイルのインクルード

他のPHPファイルを読み込みます。

  • include 'file.php';: ファイルを読み込みます。ファイルが見つからない場合は Warning が発生し、スクリプトの実行は継続されます。
  • require 'file.php';: ファイルを読み込みます。ファイルが見つからない場合は Fatal Error が発生し、スクリプトの実行は停止します。
  • include_once 'file.php';: include と同じですが、同じファイルが既に読み込まれている場合は再読み込みしません。
  • require_once 'file.php';: require と同じですが、同じファイルが既に読み込まれている場合は再読み込みしません。関数やクラス定義の重複を防ぐためによく使われます。
<?php
        // header.php や functions.php などを読み込む際によく使われる
        // require_once 'config.php';
        // require_once 'functions/database.php';
        // include 'views/header.php';

        echo "(ファイルのインクルード例 - 実際のファイルは省略)<br>";
        // 例: functions.php 内で定義された関数を呼び出す
        // $dbConnection = connectDatabase();
        // displayHeader("ページタイトル");
        ?>

名前空間 (Namespace)

クラス、インターフェース、関数、定数などの名前の衝突を防ぐための仕組みです (PHP 5.3+)。

<?php
        // ファイル: App/Database/Connection.php
        namespace App\Database; // 名前空間を宣言

        class Connection {
            public function __construct() {
                echo "App\\Database\\Connection が生成されました <br>";
            }
        }

        function connect() {
            echo "App\\Database 名前空間の connect 関数が呼ばれました <br>";
        }

        // --- 別のファイル: main.php ---
        // require_once 'App/Database/Connection.php';

        // use キーワードでエイリアスを設定
        use App\Database\Connection as DbConnection;
        use function App\Database\connect as dbConnect;
        // use const App\Database\SOME_CONSTANT; // 定数の use

        // 完全修飾名でアクセス
        // $conn1 = new \App\Database\Connection();
        // \App\Database\connect();

        // use した名前でアクセス
        $conn2 = new DbConnection();
        dbConnect();

        // 同じ名前空間内のクラスを利用する場合 (同じファイル内と仮定)
        // namespace App\Controller;
        // use App\Database\Connection; // 上の階層など別の名前空間のクラスを use
        // class UserController {
        //     private $db;
        //     public function __construct() {
        //         $this->db = new Connection(); // App\Database\Connection を指す
        //     }
        // }

        ?>

オートロード (Autoloading)

クラスやインターフェースなどが初めて使われる際に、対応するファイルを自動的に読み込む仕組みです。require_once を大量に書く必要がなくなります。

<?php
        // PSR-4 準拠のシンプルなオートローダーの例
        spl_autoload_register(function ($className) {
            // 名前空間のプレフィックスとベースディレクトリのマッピング
            $prefix = 'MyApp\\';
            $base_dir = __DIR__ . '/src/'; // このファイルが存在するディレクトリ下の src/

            // クラス名がプレフィックスで始まっているか確認
            $len = strlen($prefix);
            if (strncmp($prefix, $className, $len) !== 0) {
                // このオートローダーの管理外なので何もしない
                return;
            }

            // プレフィックスを除いたクラス名部分を取得
            $relative_class = substr($className, $len);

            // 名前空間区切り(\)をディレクトリ区切り(/)に置換し、.php を付与
            $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

            // ファイルが存在すれば読み込む
            if (file_exists($file)) {
                require $file;
                echo "Autoloaded: " . htmlspecialchars($file) . " <br>";
            } else {
                 echo "Autoload failed for class: " . htmlspecialchars($className) . " (File not found: " . htmlspecialchars($file) . ") <br>";
            }
        });

        // オートローダーが登録されていれば、new するだけでファイルが読み込まれる
        // 例: MyApp\Service\UserService クラスを使う場合
        // src/Service/UserService.php が自動的に require される
        try {
             // $userService = new \MyApp\Service\UserService();
             // $productRepo = new \MyApp\Repository\ProductRepository();
             echo "(オートロードのデモ - 実際のクラスファイルは省略)<br>";
             // 存在しないクラスを呼んでみる(エラーを確認)
             // $nonExistent = new \MyApp\NonExistent\Thing();
        } catch (\Throwable $th) { // Error もキャッチ
            echo "エラー: " . $th->getMessage() . "<br>";
        }


        // Composer を使うのが現代的な PHP 開発では一般的
        // Composer は PSR-4 オートローダーを自動生成・管理してくれる
        // require 'vendor/autoload.php';
        ?>

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です