基礎
変数、型と定数
PHP の変数は $ で始まり、動的型付けされます。PHP 7.4+ は型付きプロパティをサポートします。実行時定数には define() を、コンパイル時定数には const を使用します(より高速)。PHP 8.1 は enum を導入しました — クラス定数より優れた型付き列挙システムです。可能な限り型を宣言してください(PHP 7+)— 早期エラー検出とより良い IDE サポートのため。
<?php
$name = "Alice"; // string
$age = 30; // integer
$price = 19.99; // float (double)
$active = true; // boolean
$items = [1, 2, 3]; // array
$null = null; // null
// constants
define("MAX_USERS", 100); // runtime constant
const PI = 3.14159; // compile-time constant
echo MAX_USERS; // no $ prefix
// PHP 8.1+ enums
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
}
$s = Status::Active;Echo、Print とデバッグ
echo は言語構造体です(関数ではない)— 出力に最速です。print は 1 を返すため式で使用できます。printf/sprintf は C スタイルのフォーマット指定子を使用します(%s 文字列、%d 整数、%f 浮動小数点、%x 16進数)。var_dump() は主要なデバッグツールです — 型と値を表示します。error_log() は PHP エラーログまたは syslog に書き込みます。本番環境では、デバッグ出力をユーザーに絶対に見せないでください。
<?php
// echo: no return, multiple args (fastest)
echo "Hello", " ", "World";
// print: returns 1, single arg
print "Hello World";
// printf: formatted output
printf("Name: %s, Age: %d, Price: %.2f", "Alice", 30, 19.99);
// debugging output
print_r($array); // human-readable
var_dump($variable); // type + value (detailed)
var_export($array, true); // valid PHP code (for caching)
// sprintf: return formatted string (don't print)
$log = sprintf("[%s] %s", date('H:i:s'), "Started");
error_log($log); // log to error log演算子と比較
型強制のバグを避けるため、常に ===(厳密な比較)を使用してください。== は比較前に型を変換し、予期しない結果になります(PHP 7 では 0 == 'abc' が true でした)。宇宙船演算子(<=>)は -1/0/1 を返します — usort に便利です。null 合体演算子(??)はデフォルトを提供する慣用的な方法です。null 安全演算子(?->)(PHP 8+)は null でメソッドチェーンをショートサーキットし、冗長な isset() チェックを置き換えます。
<?php
// arithmetic
$sum = 10 + 3; // 13
$mod = 10 % 3; // 1
$pow = 2 ** 3; // 8 (PHP 5.6+)
// comparison: == vs ===
echo (0 == "abc"); // true (loose, PHP 7-); false (PHP 8+)
echo (0 === "abc"); // false (strict — type + value)
echo ("1" == 1); // true (loose)
echo ("1" === 1); // false (strict)
// spaceship operator (PHP 7+)
echo 1 <=> 2; // -1 (less than)
echo 2 <=> 2; // 0 (equal)
echo 3 <=> 2; // 1 (greater than)
// null coalescing
$name = $input ?? "default"; // if $input is null
$deep = $data['user']['name'] ?? "Anonymous";
// null safe operator (PHP 8+)
$country = $user?->getAddress()?->country; // null if any step is nullスーパーグローバルと Web
スーパーグローバルはすべてのスコープで利用可能な組み込みの連想配列です。$_GET と $_POST はユーザー入力を含みます — 使用前に必ずサニタイズ/検証してください。filter_input() は直接アクセスより安全です。クライアントが偽装できる $_SERVER の値(HTTP_USER_AGENT など)は絶対に信頼しないでください。header('Location:') の後は常に exit を呼び出してください — そうしないと PHP は実行を続けます。セッションは出力前に session_start() で開始します。
<?php
// superglobals available everywhere
$_GET['name']; // query string params
$_POST['email']; // form POST data
$_REQUEST['x']; // GET + POST + COOKIE
$_SERVER['HTTP_HOST']; // server/env info
$_SERVER['REQUEST_METHOD']; // GET, POST, etc.
$_COOKIE['session']; // cookies
$_FILES['upload']; // file uploads
$_SESSION['user_id']; // session data (after session_start())
// get client IP
$ip = $_SERVER['REMOTE_ADDR'];
// check request method
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
}
// redirect
header("Location: /dashboard");
exit; // always exit after redirectInclude と Require
include/require は指定されたファイルを実行します。ファイルが見つからない場合、require は致命的エラーを引き起こします(重要な依存関係に使用);include は警告のみです(オプションのテンプレートに使用)。_once バリアントは二重インクルードを防ぐためにファイルを追跡します — 関数/クラス定義に不可欠です。Composer のオートローダー(require_once 'vendor/autoload.php')が手動インクルード管理を不要にします。ファイルは値を返できるため、設定に便利です。
<?php
// include: warning on failure, continues
include 'header.php';
include_once 'config.php'; // only once
// require: fatal error on failure, stops
require 'database.php';
require_once 'vendor/autoload.php'; // Composer autoload
// _once variants prevent re-declaration errors
// use require for critical files (DB config, autoloaders)
// use include for optional files (templates)
// return values from included files
$config = include 'config.php';
// config.php: return ['db' => 'mysql://...', 'debug' => true];文字列
文字列関数
PHP には 100 以上の文字列関数があります。strpos() は見つからない場合 false を返します — === false で確認してください(0 は有効な位置です)。str_replace() は検索/置換に配列を受け取れます。substr() は負のオフセットをサポートします(末尾から)。マルチバイト文字列(UTF-8 )には mb_* 等価関数を使用してください(mb_strlen、mb_substr)— strlen は文字数ではなくバイト数をカウントします。常に default_charset='UTF-8' を設定し、非 ASCII テキストには mb_* 関数を使用してください。
<?php
$s = "Hello, World";
// length and case
echo strlen($s); // 12
echo str_word_count($s); // 2
echo strtoupper($s); // HELLO, WORLD
echo strtolower($s); // hello, world
echo ucfirst("hello"); // Hello
echo ucwords("hello world"); // Hello World
// search and replace
echo strpos($s, "World"); // 7 (false if not found)
echo str_replace("o", "0", $s); // Hell0, W0rld
echo substr($s, 0, 5); // Hello
echo substr($s, -5); // World
echo strrev($s); // dlroW ,olleH
// trim
echo trim(" hi "); // "hi"
echo ltrim(" hi"); // "hi"
echo rtrim("hi "); // "hi"文字列補間と Heredoc
二重引用符文字列は変数を補間します;単一引用符文字列はしません(リテラルテキストには単一引用符を使用 — やや高速)。複雑な式(オブジェクトプロパティ、配列アクセス、メソッド呼び出し)には {$var} を使用します。Heredoc(<<<ID)は SQL や HTML のような複数行文字列に最適です — 変数を補間します。Nowdoc(<<<'ID')は非解釈バージョンで、バックスラッシュを含む正規表現パターンに便利です。
<?php
$name = "Alice";
$age = 30;
// double quotes: variable interpolation
echo "Hello, $name!"; // Hello, Alice!
echo "Hello, {$name}!"; // Hello, Alice! (braces for clarity)
echo "Age: {$age}"; // Age: 30
// single quotes: no interpolation (faster)
echo 'Hello, $name'; // Hello, $name (literal)
// complex expressions need braces
echo "Result: {$obj->method()}";
echo "Item: {$array['key']}";
// heredoc: multi-line, interpolates
$sql = <<<SQL
SELECT * FROM users
WHERE name = '$name'
AND age > $age
SQL;
// nowdoc: multi-line, NO interpolation (like single quotes)
$regex = <<<'REGEX'
\d{3}-\d{4}
REGEX;sprintf とフォーマット
sprintf() はフォーマットされた文字列を安全に構築するために不可欠です — 文字列補間とは異なり、型変換とパディングを処理します。数値フォーマットを保証するため %s ではなく %d を使用してください。number_format() は千位区切りで数値をフォーマットします — 通貨表示に重要です。SQL フラグメントには常に sprintf を使用します(ただしユーザー入力にはプリペアドステートメントが依然として必要です)。
<?php
// sprintf: format string (returns, doesn't print)
$formatted = sprintf(
"%-10s | %5d | %8.2f",
"Alice", 42, 19.99
);
echo $formatted; // "Alice | 42 | 19.99"
// format specifiers:
// %s string, %d int, %f float, %x hex, %b binary, %c char
// %5d right-padded to width 5
// %-5d left-padded
// %05d zero-padded
// %.2f 2 decimal places
// %8.2f width 8, 2 decimals
// numbered placeholders
echo sprintf("Hi %1$s, bye %1$s", "Alice"); // Hi Alice, bye Alice
// number_format
echo number_format(1234567.891, 2); // 1,234,567.89
echo number_format(1234567.89, 2, ',', '.'); // European format正規表現(PCRE)
PHP は /pattern/ 区切り文字付きの PCRE(Perl 互換正規表現)を使用します。preg_match はマッチした場合 1、しない場合 0 を返します(0 は falsy なので == ではなく === を使用)。ユーザー入力は正規表現で検証しますが、それだけに依存しないでください — メール、URL には filter_var() を使用します。preg_replace は強力ですが大きな文字列では遅くなる可能性があります。ブラックリストではなく [^...] で文字をホワイトリストしてください。i フラグはマッチングを大文字小文字無視にします。
<?php
// preg_match: test pattern (returns 0 or 1)
if (preg_match('/^[a-z]+$/i', "Hello")) {
echo "alphabetic";
}
// capture groups
preg_match('/(d{4})-(d{2})-(d{2})/', "2024-01-15", $matches);
// $matches[0] = "2024-01-15" (full match)
// $matches[1] = "2024" (group 1)
// $matches[2] = "01"
// $matches[3] = "15"
// preg_match_all: all matches
preg_match_all('/d+/', "a1 b22 c333", $matches);
// $matches[0] = ["1", "22", "333"]
// preg_replace: substitute
$clean = preg_replace('/[^a-z0-9]/i', '', "Hello, World!"); // HelloWorld
// preg_split: split by pattern
$parts = preg_split('/[s,]+/', "one, two,three four");
// ["one", "two", "three", "four"]マルチバイトとエンコーディング
PHP のデフォルトの文字列関数は文字指向ではなくバイト指向です — マルチバイト文字(UTF-8、中国語、絵文字)で壊れます。非 ASCII テキストには常に mb_* 関数(mb_strlen、mb_substr、mb_strpos、mb_strtoupper)を使用してください。アプリケーションの開始時に mb_internal_encoding('UTF-8') を設定します。JSON 出力で中国語/絵文字を読みやすく保つために JSON_UNESCAPED_UNICODE を使用します。これは国際化アプリケーションでよくあるバグの原因です。
<?php
// UTF-8 strings: use mb_* functions
$text = "Café"; // 4 chars, 5 bytes (é = 2 bytes)
echo strlen($text); // 5 (bytes!)
echo mb_strlen($text); // 4 (characters!)
echo substr($text, 0, 3); // "Caf" (might break UTF-8!)
echo mb_substr($text, 0, 3); // "Caf" (safe)
// case conversion
echo strtoupper("straße"); // "STRAßE" (wrong!)
echo mb_strtoupper("straße"); // "STRAßE" (correct, locale-aware)
// encoding detection
$encoding = mb_detect_encoding($text);
$utf8 = mb_convert_encoding($text, 'UTF-8', 'auto');
// set internal encoding
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
// JSON with UTF-8
$json = json_encode($data, JSON_UNESCAPED_UNICODE);配列
インデックス配列と連想配列
PHP の配列は実際には順序付きハッシュマップです — リストと辞書の両方として機能します。インデックス配列は数値キーを自動割り当て;連想配列は文字列キーを使用します。isset() は null 値に対して false を返します;array_key_exists() は null でも true を返します。unset() は要素を削除しますが再インデックスしません。真のリスト(ギャップなし)にするには、削除後に array_values() で再インデックスします。PHP 8.1+ には readonly 配列型があります。
<?php
// indexed array (numeric keys)
$nums = [10, 20, 30];
$nums[] = 40; // append
echo $nums[0]; // 10
echo count($nums); // 4
// associative array (string keys)
$user = [
"name" => "Alice",
"age" => 30,
"email" => "[email protected]",
];
echo $user["name"]; // Alice
$user["phone"] = "555-1234"; // add key
// mixed keys
$mixed = [0 => "a", "name" => "b", 5 => "c"];
// check key existence
if (isset($user["email"])) { /* ... */ }
if (array_key_exists("name", $user)) { /* ... */ }
// remove element
unset($user["phone"]);多次元配列と反復
多次元配列は配列の配列です。foreach は反復の慣用的な方法です — for ループより速く読みやすいです。要素をその場で変更するには &$value を使用します(バグを避けるためループ後に必ず参照を unset してください)。array_column() は 2 次元配列から単一列を抽出します — データベース結果セットの変換に非常に便利です。PHP の配列は挿入順序を維持します。
<?php
$users = [
["name" => "Alice", "age" => 30],
["name" => "Bob", "age" => 25],
["name" => "Carol", "age" => 35],
];
// iterate with key + value
foreach ($users as $index => $user) {
echo "$index: {$user['name']} ({$user['age']})\n";
}
// modify by reference
foreach ($users as &$user) {
$user['age'] += 1; // increment each age
}
unset($user); // break reference!
// nested iteration
$matrix = [[1, 2], [3, 4], [5, 6]];
foreach ($matrix as $row) {
foreach ($row as $cell) {
echo $cell . " ";
}
echo "\n";
}
// extract column
$names = array_column($users, 'name'); // ["Alice", "Bob", "Carol"]配列関数:map、filter、reduce
array_map、array_filter、array_reduce は配列の関数型プログラミングの三要素です。アロー関数(fn() =>)でこれらを簡潔にします。array_filter はキーを保持します — 必要に応じて array_values() で再インデックスします。array_merge は数値キーを再インデックスしますが文字列キーは保持します(後の値が上書き)。array_column、array_chunk、array_slice はデータ操作に不可欠です。これらの関数は PHP でのデータ処理の骨組みです。
<?php
$nums = [1, 2, 3, 4, 5];
// map: transform each element
$doubled = array_map(fn($n) => $n * 2, $nums); // [2, 4, 6, 8, 10]
// filter: keep elements matching condition
$evens = array_filter($nums, fn($n) => $n % 2 === 0); // [2, 4]
// reduce: accumulate to single value
$sum = array_reduce($nums, fn($carry, $n) => $carry + $n, 0); // 15
// walk: like map but modifies in place (by reference)
array_walk($nums, fn(&$n) => $n *= 2);
// combining arrays
$merged = array_merge([1, 2], [3, 4]); // [1, 2, 3, 4]
$combined = array_combine(['a', 'b'], [1, 2]); // ['a' => 1, 'b' => 2]
$sliced = array_slice($nums, 1, 2); // [2, 3]
$chunked = array_chunk($nums, 2); // [[1,2], [3,4], [5]]配列のソート
PHP のソート関数は配列をその場で変更します(参照渡し)。sort/rsort は再インデックス;asort/arsort はキーを保持します。usort と比較関数(<=> を使用)でカスタムロジックでソートします。natsort() は自然ソートを行います(img10 の前に img2)— ファイル名に不可欠です。多次元配列には、目的のフィールドを比較するクロージャ付きの usort を使用します。宇宙船演算子(<=>)が比較関数を簡素化します。
<?php
$nums = [3, 1, 4, 1, 5, 9, 2, 6];
// sort by value (reindexes)
sort($nums); // [1, 1, 2, 3, 4, 5, 6, 9]
rsort($nums); // descending
// sort preserving keys
asort($nums); // ascending, preserve keys
arsort($nums); // descending, preserve keys
// sort by key
ksort($nums); // by key ascending
krsort($nums); // by key descending
// custom sort with callback
$users = [["name" => "Bob", "age" => 25], ["name" => "Alice", "age" => 30]];
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);
// sorted by age ascending
// natural sort (for strings with numbers)
$files = ["img10.jpg", "img2.jpg", "img1.jpg"];
natsort($files); // ["img1.jpg", "img2.jpg", "img10.jpg"]
sort($files); // ["img1.jpg", "img10.jpg", "img2.jpg"] (wrong!)配列の検査と操作
in_array で strict=true(第3引数)を使用すると型強制のバグを防ぎます。array_search はキーを返します(=== false で確認)。array_push/pop は LIFO(スタック)を実装;array_shift/unshift は FIFO(キュー)を実装 — ただし shift は O(n) です。大きなキューには SplQueue や SplDoublyLinkedList を使用します。array_unique はキーを保持します。array_diff/intersect は値を比較;キーベースの比較には array_diff_key/intersect_key を使用します。
<?php
$arr = [1, 2, 3, 4, 5];
// inspection
echo count($arr); // 5
echo in_array(3, $arr); // true
echo in_array("3", $arr, true); // false (strict)
echo array_search(3, $arr); // 2 (key, false if not found)
print_r(array_keys($arr)); // [0, 1, 2, 3, 4]
print_r(array_values($arr)); // [1, 2, 3, 4, 5]
// stack/queue operations
array_push($arr, 6); // push to end
$last = array_pop($arr); // pop from end
$first = array_shift($arr); // remove from front
array_unshift($arr, 0); // add to front
// set operations
$unique = array_unique([1, 2, 2, 3]); // [1, 2, 3]
$diff = array_diff([1, 2, 3], [2, 3, 4]); // [1] (in first, not second)
$intersect = array_intersect([1, 2, 3], [2, 3, 4]); // [2, 3]
// flip and reverse
$flipped = array_flip(['a' => 1, 'b' => 2]); // [1 => 'a', 2 => 'b']
$reversed = array_reverse([1, 2, 3]); // [3, 2, 1]制御フロー
If / Else / Elseif
PHP は elseif(1語)を使用します — スペース付きの 'else if' ではありません(ただしそれも動作します)。代替構文(if: ... endif;)は HTML テンプレートでブレースマッチングの混乱を避けるために便利です。三項演算子は右結合です — ネストは避けてください。null 合体代入演算子(??=)は現在 null の場合にのみ値を設定します — 設定デフォルトの遅延初期化に最適です。
<?php
$score = 85;
if ($score >= 90) {
$grade = "A";
} elseif ($score >= 80) {
$grade = "B";
} elseif ($score >= 70) {
$grade = "C";
} else {
$grade = "F";
}
// alternative syntax (for templates)
if ($score >= 90):
echo "Excellent";
elseif ($score >= 80):
echo "Good";
else:
echo "Try harder";
endif;
// ternary
$status = $age >= 18 ? "adult" : "minor";
// null coalescing assignment (PHP 7.4+)
$config['timeout'] ??= 30; // set if not setSwitch と Match
switch は緩い比較(==)を使用し、フォールスルーを防ぐために break が必要です — 一般的なバグの原因です。match(PHP 8+)は厳密な比較(===)を使用し、値を直接返し、アームがマッチしない場合例外をスローします(サイレントな失敗なし)。値が必要な場合、match は switch のモダンな置き換えです。複雑な複数文ケースには switch を;シンプルな値選択には match を使用 します。常に default ケースを含めてください。
<?php
// switch: loose comparison (==)
$day = "Mon";
switch ($day) {
case "Mon":
case "Tue":
case "Wed":
echo "Weekday";
break;
case "Sat":
case "Sun":
echo "Weekend";
break;
default:
echo "Unknown";
}
// match (PHP 8+): strict comparison (===), returns value
$status = 404;
$message = match($status) {
200, 201 => "Success",
301, 302 => "Redirect",
404 => "Not Found",
500 => "Server Error",
default => "Unknown",
};
// match throws UnhandledMatchError if no match and no defaultループ:for、while、foreach、do-while
foreach は配列の慣用的なループです — count() 付きの for より速く安全です。continue で反復をスキップし、break で終了します。PHP にはラベル付き break/continue がありません(Java/Rust とは異なります)。連想配列には foreach ($arr as $key => $value) が標準パターンです。do-while は少なくとも1回実行します — 入力検証に便利です。foreach 中の配列変更は避けてください(結果には別の配列を使用)。
<?php
// for loop
for ($i = 0; $i < 5; $i++) {
echo $i; // 01234
}
// foreach (most common in PHP)
$fruits = ["apple", "banana", "cherry"];
foreach ($fruits as $fruit) {
echo $fruit;
}
// foreach with key
foreach ($fruits as $index => $fruit) {
echo "$index: $fruit";
}
// while
$count = 0;
while ($count < 3) {
echo $count++;
}
// do-while (runs at least once)
do {
$line = readline("> ");
} while ($line !== "quit");
// break and continue
for ($i = 0; $i < 10; $i++) {
if ($i === 3) continue; // skip 3
if ($i === 7) break; // stop at 7
echo $i;
}テンプレートでの制御フロー
PHP の代替制御構文(if:/elseif:/else:/endif;、foreach:/endforeach;)は HTML テンプレート用に設計されています。<?= $var ?> は <?php echo $var; ?> の短縮形です — テンプレートでは常に可読性のために使用してください。XSS を防ぐため、常に htmlspecialchars() で出力をエスケープしてください。PHP ロジックと HTML プレゼンテーションの分離は、Twig や Blade のようなテンプレートシステムの基盤であり、よりクリーンな構文と自動エスケープを提供します。
<?php // template file ?>
<?php if ($user->isAdmin()): ?>
<div class="admin-panel">Admin Tools</div>
<?php elseif ($user->isEditor()): ?>
<div class="editor-tools">Edit Tools</div>
<?php else: ?>
<div class="user-view">Read Only</div>
<?php endif; ?>
<?php foreach ($products as $p): ?>
<div class="product">
<?= htmlspecialchars($p['name']) ?>
- $<?= number_format($p['price'], 2) ?>
</div>
<?php endforeach; ?>
<?php // shorthand echo ?>
<h1><?= $title ?></h1>
<?php // ternary in templates ?>
<span class="<?= $active ? 'on' : 'off' ?>"><?= $active ? 'Active' : 'Inactive' ?></span>例外とエラー処理
PHP 7+ はほとんどのエラーに例外を使用します。異なる失敗を適切に処理するため、(単なる Exception ではなく)常に具体的な例外型をキャッチしてください。finally は常に実行されます — クリーンアップ(ファイル、接続のクローズ)に使用します。カスタム例外は Exception を拡張し、ドメインコンテキストを追加します。PHP 8+ は | で複数の例外型をキャッチできます。一貫したエラー処理のために PDO を例外モードに設定します。例外をログなしでキャッチしないでください — サイレントな失敗はバグを隠します。
<?php
// try / catch / finally
try {
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
error_log("DB connection failed: " . $e->getMessage());
die("Service unavailable");
} finally {
// always runs, even after return/throw
echo "Cleanup";
}
// custom exception
class ValidationException extends Exception {
public function __construct(string $field, string $message = "") {
parent::__construct("$field: $message");
}
}
// throw
if (empty($email)) {
throw new ValidationException("email", "is required");
}
// catch multiple types (PHP 8+)
try {
riskyOperation();
} catch (PDOException | RuntimeException $e) {
// catches either type
log_error($e);
}関数
関数の定義
PHP 7+ は型付きパラメータと戻り値の型をサポートします(int、string、array、nullable には ?Type)。PHP 8+ は名前付き引数(デフォルトをスキップ、パラメータを並べ替え)、ユニオン型(int|string)、mixed 型を追加します。可変長パラメータ(...$nums)は余分な引数を配列に集めます。スプレッド演算子(...$arr)は配列を引数として展開します。参照渡し(&)は元を変更します — コードの推論が難しくなるため控えめに使用してください。
<?php
// basic function with return type
function add(int $a, int $b): int {
return $a + $b;
}
// default parameters
function greet(string $name, string $greeting = "Hello"): string {
return "$greeting, $name!";
}
// named arguments (PHP 8+)
echo greet(name: "Alice", greeting: "Hi");
// variadic functions
function sum(int ...$nums): int {
return array_sum($nums);
}
echo sum(1, 2, 3, 4); // 10
// spread operator
$nums = [1, 2, 3];
echo sum(...$nums); // 6
// pass by reference
function increment(int &$n): void {
$n++;
}
$x = 5;
increment($x);
echo $x; // 6アロー関数とクロージャ
アロー関数(fn() =>)は簡潔な単一式クロージャで、外側の変数を値で自動的にキャプチャします。従来のクロージャ(function() use ($var))は複数行の本体や参照キャプチャ(&$var)に必要です。クロージャは array_map、array_filter、usort、イベントハンドラに不可欠です。アロー関数は文を持てません(if、for なし)— 複雑なロジックには従来のクロージャを使用してください。クロージャは第一級オブジェクトです(Closure クラス)。
<?php
// arrow function (PHP 7.4+): single expression, auto-capture
$square = fn($x) => $x * $x;
echo $square(5); // 25
// auto-captures outer variables by value
$multiplier = 3;
$multiply = fn($x) => $x * $multiplier;
echo $multiply(5); // 15
// traditional closure (multi-line, explicit capture)
$factor = 10;
$scale = function ($x) use ($factor) {
return $x * $factor + 1;
};
// capture by reference
$count = 0;
$increment = function () use (&$count) {
$count++;
};
$increment();
echo $count; // 1
// closures as callbacks
$nums = [1, 2, 3, 4];
$evens = array_filter($nums, fn($n) => $n % 2 === 0);
$doubled = array_map(fn($n) => $n * 2, $nums);変数スコープとグローバル
PHP は関数レベルのスコープを持ちます — 関数外で定義された変数は 'global' や $GLOBALS なしでは関数内からアクセスできません。'global' は避けてください — 隠れた依存関係を作り、テストを困難にします。代わりに依存性の注入を使用してください。static 変数は関数呼び出し間で持続しますが、関数にスコープされます — キャッシュ/メモ化に便利ですが、長時間実行プロセスで問題を引き起こす可能性があります。クロージャは 'use' で変数を明示的にキャプチャする必要があります。
<?php
$global = "I'm global";
function testScope(): void {
// echo $global; // ERROR: not in scope
global $global; // import global
echo $global; // OK
// $GLOBALS superglobal (alternative)
echo $GLOBALS['global'];
}
// static variables: persist across calls
function counter(): int {
static $count = 0;
return ++$count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
// closures don't see outer scope by default
$outer = "hello";
$closure = function () {
// echo $outer; // ERROR
};
$closure2 = function () use ($outer) {
echo $outer; // OK, captured
};型宣言と厳密な型
declare(strict_types=1) は最初の文でなければなりません — ファイル全体に厳密な型チェック(強制なし)を強制します。こ れがないと、PHP は型を強制します(文字列パラメータに渡された int 5 は '5' になります)。新しいコードでは常に厳密な型を使用してください。PHP 8+ はユニオン型、mixed、never(関数が戻らない)、static(クラスを返す)を追加します。第一級呼び出し可能構文(func(...))は任意の callable からクロージャを作成します — 関数参照よりクリーンです。
<?php
// declare strict types (FIRST line of file)
declare(strict_types=1);
function divide(float $a, float $b): float {
if ($b === 0.0) {
throw new DivisionByZeroError();
}
return $a / $b;
}
// nullable types (?Type or Type|null)
function findUser(?int $id): ?string {
if ($id === null) return null;
return "User $id";
}
// union types (PHP 8+)
function process(int|string $input): int|string {
return is_int($input) ? $input * 2 : strtoupper($input);
}
// return types: void, never, mixed, static
function log(string $msg): void { /* no return */ }
function redirect(): never { header("Location: /"); exit; }
// first-class callable syntax (PHP 8.1+)
$func = strlen(...); // creates Closure from function
echo $func("hello"); // 5ジェネレータと Yield
ジェネレータ(yield を持つ関数)は値を遅延生成します — すべての値を前もって計算せず、メモリを節約します。これは大きなファイルやデータセットの処理に不可欠です。yield は関数を一時停止し、値を返します;次の値が要求されると関数が再開します。ジェネレータは Iterator を実装するため、foreach で動作します。ファイル処理、データベース行反復、無限シーケンス、パイプラインにジェネレータを使用してください。key=>value ペアを yield したり、send() で値を受け取ることもできます。
<?php
// generator: memory-efficient iteration
function readLines(string $file): Generator {
$handle = fopen($file, 'r');
while (($line = fgets($handle)) !== false) {
yield trim($line);
}
fclose($handle);
}
foreach (readLines("large.txt") as $line) {
echo $line; // one line at a time, low memory
}
// infinite generator
function fibonacci(): Generator {
[$a, $b] = [0, 1];
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
// take first 10
$fib = fibonacci();
for ($i = 0; $i < 10; $i++) {
echo $fib->current() . " ";
$fib->next();
}
// yield with key
function pairs(): Generator {
yield 'a' => 1;
yield 'b' => 2;
yield 'c' => 3;
}OOP とクラス
クラス、プロパティとコンストラクタ
PHP 8 のコンストラクタプロモーションはボイラープレートを排除します — プロパティをコンストラクタパラメータとして宣言します。プロパティの可視性:public(どこでも)、protected(クラス + サブクラス)、private(クラスのみ)。readonly(PHP 8.1)は初期化後の変更を防ぎます。self は現在のクラスを参照;static は呼び出しクラスを参照(遅延静的バインディング用)。継承階層で適切なポリモーフィズムのために self:: ではなく static:: を使用してください。
<?php
class Person {
// typed properties (PHP 7.4+)
public string $name;
protected int $age;
private string $email;
public readonly string $id; // PHP 8.1+
// constructor promotion (PHP 8+)
public function __construct(
string $name,
int $age,
string $email = "",
string $id = ""
) {
$this->name = $name;
$this->age = $age;
$this->email = $email;
$this->id = $id;
}
// methods
public function greet(): string {
return "Hi, I'm {$this->name}";
}
// static method
public static function create(string $name): self {
return new self($name, 0);
}
}
$p = new Person("Alice", 30);
echo $p->greet();
echo Person::create("Bob")->name;継承と抽象クラス
abstract クラスはインスタンス化できません — サブクラスのテンプレートを定義します。抽象メソッドは具象サブクラスが実装しなければなりません。PHP は単一継承のみをサポートします(1つの extends)。実装が変更されるべきでない場合、継承/オーバーライドを防ぐために final を使用します。protected メンバーはサブクラスでアクセス可能 — 内部 API に使用します。親にコンストラクタがある場合、常に parent::__construct() を呼び出してください。instanceof で型をチェックします:if ($dog instanceof Animal)。
<?php
abstract class Animal {
protected string $name;
public function __construct(string $name) {
$this->name = $name;
}
// abstract method: must be implemented by subclasses
abstract public function speak(): string;
// concrete method: inherited as-is
public function describe(): string {
return "{$this->name} says {$this->speak()}";
}
}
class Dog extends Animal {
public function speak(): string {
return "Woof";
}
}
class Cat extends Animal {
public function speak(): string {
return "Meow";
}
}
$dog = new Dog("Rex");
echo $dog->describe(); // Rex says Woof
// final class/method: cannot be extended/overridden
final class Singleton { /* ... */ }インターフェースとトレイト
インターフェースは契約を定義します — クラスは複数のインターフェースを実装できます(単一継承とは異なります)。すべてのインターフェースメソッドは public でなければなりません。トレイトは継承なしのコード再利用を提供します — 言語レベルの「コピー&ペースト」です。トレイトはプロパティ、メソッド、さらには抽象メソッドを持てます。横断的関心事(タイムスタンプ、ロギング、ソフトデリート)にトレイトを使用します。競合解決:トレイトが同じメソッド名を持つ場合、TraitA::method insteadof TraitB を使用します。
<?php
// interface: contract (no implementation)
interface Comparable {
public function compareTo(object $other): int;
}
interface JsonSerializable {
public function jsonSerialize(): mixed;
}
// a class can implement multiple interfaces
class Product implements Comparable, JsonSerializable {
public function __construct(public float $price) {}
public function compareTo(object $other): int {
return $this->price <=> $other->price;
}
public function jsonSerialize(): mixed {
return ['price' => $this->price];
}
}
// trait: reusable code (horizontal reuse)
trait Timestampable {
public DateTime $createdAt;
public function setCreatedAt(): void {
$this->createdAt = new DateTime();
}
public function age(): DateInterval {
return $this->createdAt->diff(new DateTime());
}
}
class Article {
use Timestampable; // use trait
}マジックメソッド
マジックメソッドはオブジェクト操作をインターセプトする特別なメソッドです。__get/__set はプロパティのオーバーロードを実装します(動的プロパティ)。__toString は文字列キャストを可能にします。__invoke はオブジェクトを呼び出し可能にします。__clone はクローン時に実行します(clone $obj)。控えめに使用してください — 追跡が難しい「マジック」を追加します。__get/__set はデータ転送オブジェクトや遅延読み込みに便利です。マジック動作は常に明確に文書化してください。PHP 8.2 は動的プロパティを非推奨にします — __get/__set または #[AllowDynamicProperties] を使用してください。
<?php
class Magic {
private array $data = [];
// called when accessing undefined property
public function __get(string $name): mixed {
return $this->data[$name] ?? null;
}
// called when setting undefined property
public function __set(string $name, mixed $value): void {
$this->data[$name] = $value;
}
// called when isset() or empty() on undefined property
public function __isset(string $name): bool {
return isset($this->data[$name]);
}
// called when object is used as string
public function __toString(): string {
return json_encode($this->data);
}
// called when object is called as function
public function __invoke(string $arg): string {
return "Called with: $arg";
}
// called on clone
public function __clone(): void {
$this->data = []; // reset on clone
}
}
$m = new Magic();
$m->foo = "bar"; // __set
echo $m->foo; // __get -> "bar"
echo $m; // __toString -> {"foo":"bar"}
echo $m("test"); // __invoke名前空間とオートロード
名前空間はクラス名の衝突を防ぎます — Java のパッケージのようなものです。namespace は最初の文でなければなりません。use はクラスをインポートします(オプションのエイリアス付き:use Foo\Bar as B)。PSR-4 オートロードは名前空間をファイルパスにマッピングします:App\Models\User → src/Models/User.php。Composer のオートローダー(require 'vendor/autoload.php')がこれを自動的に処理します。モダン PHP では常に名前空間を使用してください。文字列内の \\ はエスケープされたバックスラッシュです(名前空間セパレータ)。
<?php
// file: src/Models/User.php
namespace App\Models;
use App\Database\Connection;
use App\Exceptions\UserNotFoundException;
class User {
private Connection $db;
public function __construct(Connection $db) {
$this->db = $db;
}
public function find(int $id): ?self {
// ...
throw new UserNotFoundException("User $id not found");
}
}
// composer.json (PSR-4 autoloading)
// {
// "autoload": {
// "psr-4": { "App\\": "src/" }
// }
// }
// usage
use App\Models\User;
$user = new User($db);Web、フォームとファイル I/O
フォーム処理と検証
常にサーバー側で検証してください — クライアント側検証は UX 用であり、セキュリティ用ではありません。filter_input/filter_var と FILTER_VALIDATE_* は無効な入力で false を返します。検証前に 文字列をトリムしてください。データベース挿入にはプリペアドステートメントを使用します。CSRF トークンはクロスサイトリクエストフォージェリを防ぎます — セッションごとに生成し POST で検証します。bin2hex(random_bytes(32)) は暗号学的に安全なトークンを生成します。ユーザー入力は絶対に信頼しないでください — 検証、サニタイズ、エスケープしてください。
<?php
// process POST form
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$age = filter_var($_POST['age'] ?? 0, FILTER_VALIDATE_INT);
$errors = [];
if (empty($name)) {
$errors[] = "Name is required";
} elseif (strlen($name) > 100) {
$errors[] = "Name too long";
}
if ($email === false) {
$errors[] = "Valid email required";
}
if ($age === false || $age < 18) {
$errors[] = "Must be 18+";
}
if (empty($errors)) {
// process valid data
// save to database, redirect, etc.
header("Location: /success");
exit;
}
}
// CSRF token
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token;
// in form: <input type="hidden" name="csrf" value="<?= $token ?>">セッションと Cookie
セッションはサーバー側にデータを保存します(セッション ID Cookie で識別)。session_start() は出力前に呼び出す必要があります(または ob_start() を使用)。セッションには最小限のデータを保存してください — サーバーメモリを消費します。Cookieには常に secure(HTTPS のみ)、httponly(XSS アクセスを防止)、samesite(CSRF 保護)を設定してください。セッションを正しく破棄します:変数を unset、セッションを破棄、Cookie をクリア。スケーラブルなアプリでは、ファイルの代わりに Redis/データベースに基づくセッションハンドラーを使用します。