こんにちは、かつコーチです。
前回は、var_dumpやprint_r、Xdebugを使ったデバッグの方法を解説しました。
今回はこのPHP基礎編シリーズの総復習も兼ねて、これまで扱ってきたテーマの中で、初心者がよくつまずくポイントをQ&A形式でまとめていきます。
「これ、前にも聞かれたな」という質問を中心に選んでいるので、学習の振り返りとしても使ってもらえると思います。
Q1. ==と===、結局どっちを使えばいいの?
何が違うのか
==は値だけを比較し、===は値と型の両方を比較します。
<?php
var_dump("1" == 1); // true(値だけ見ているので一致とみなす)
var_dump("1" === 1); // false(型が文字列と数値で違うので不一致)
かつコーチの結論
私は基本的に、迷ったら===を使うことをおすすめしています。
以前、フォームから受け取った文字列の"0"を==で比較していたら、falseと等しいとみなされてしまい、意図しない分岐に入ってしまったことがありました。
PHPの==は型の変換ルールが直感的でない場面があるため、明示的な比較をする習慣をつけておくと事故を防げます。
Q2. 配列のforeachでキーが数値になるのはなぜ?
連想配列とインデックス配列の違い
配列に=>でキーを指定しないと、自動的に0から始まる数値キーが振られます。
<?php
$fruits = ["りんご", "みかん", "ぶどう"];
foreach ($fruits as $key => $value) {
echo $key . ": " . $value . "\n";
}
// 出力
// 0: りんご
// 1: みかん
// 2: ぶどう
「名前をキーにしたいのに数値になってしまう」という質問をよく受けますが、これは連想配列ではなくインデックス配列を使っている場合の正常な動作です。
キーに名前をつけたい場合は、["name" => "田中", "age" => 20]のように=>で明示的に指定する必要があります。
Q3. 関数の中で外の変数を書き換えられないのはなぜ?
スコープという考え方
PHPの関数は、外の変数を勝手に読み書きできないスコープという仕組みを持っています。
❌ Before:関数の外の変数を直接変えようとする
<?php
$count = 0;
function increment() {
$count++; // 関数の外の$countとは別物として扱われる
}
increment();
echo $count; // 出力: 0(変わっていない)
✅ After:引数と戻り値でやり取りする
<?php
function increment($count) {
return $count + 1; // 受け取った値を加工して返す
}
$count = 0;
$count = increment($count); // 戻り値を代入し直す
echo $count; // 出力: 1
「関数の中で変数がリセットされる」という相談は非常に多いのですが、原因はほぼこのスコープの理解不足です。
関数は「値を受け取り、値を返す」箱だとイメージすると整理しやすくなります。
Q4. Undefined array keyという警告が出るのはなぜ?
存在しないキーへのアクセス
配列に存在しないキーを指定すると、PHP8以降ではUndefined array keyという警告が出ます。
<?php
$user = ["name" => "かつコーチ"];
echo $user["age"]; // Warning: Undefined array key "age"
私が実務で最初にこのエラーに遭遇したときは、フォームの任意入力項目(未入力でもOKな項目)を配列から取り出そうとして発生しました。
対処法は、キーの存在を先にチェックすることです。
<?php
$user = ["name" => "かつコーチ"];
$age = isset($user["age"]) ? $user["age"] : "未設定";
echo $age; // 出力: 未設定
isset()でキーの有無を確認してから使う、あるいはPHP7以降なら??(Null合体演算子)を使うと1行で書けます。
<?php
$age = $user["age"] ?? "未設定";
echo $age; // 出力: 未設定
Q5. クラスの$thisって何を指しているの?
インスタンスごとに中身が変わる特殊な変数
$thisは「今そのメソッドを呼び出しているインスタンス自身」を指す特殊な変数です。
<?php
class User
{
public string $name;
public function __construct(string $name)
{
$this->name = $name; // このインスタンス自身のnameプロパティに代入
}
public function greet(): string
{
return "こんにちは、" . $this->name . "です"; // このインスタンス自身のnameを参照
}
}
$userA = new User("田中");
$userB = new User("佐藤");
echo $userA->greet(); // 出力: こんにちは、田中です
echo $userB->greet(); // 出力: こんにちは、佐藤です
同じgreet()メソッドを呼んでいるのに結果が違うのは、$thisが呼び出し元のインスタンスによって変わるからです。
私はオブジェクト指向を学び始めた頃、$thisをただの決まり文句として書いていて、この「インスタンスごとに中身が変わる」という感覚がなかなか掴めませんでした。
$userAと$userBを別々に作って、同じメソッドの結果を見比べる、という練習が理解の助けになったので、つまずいている方はぜひ試してみてください。
Q6. PDOでSQL文にそのまま変数を埋め込んではいけないのはなぜ?
SQLインジェクションのリスク
ユーザーの入力をそのままSQL文に埋め込むと、悪意のある入力によってデータベースを不正に操作される可能性があります。
❌ Before:変数をそのまま埋め込む
<?php
$id = $_GET["id"]; // ユーザーからの入力をそのまま使う
$sql = "SELECT * FROM users WHERE id = " . $id; // 危険
$stmt = $pdo->query($sql);
もし$idに1 OR 1=1のような文字列が入ってきた場合、全件取得のような意図しないクエリが実行されてしまいます。
✅ After:プレースホルダを使う
<?php
$id = $_GET["id"];
$sql = "SELECT * FROM users WHERE id = :id"; // プレースホルダを使う
$stmt = $pdo->prepare($sql);
$stmt->execute(["id" => $id]); // 値は安全に紐付けられる
プレースホルダを使うと、PDOが値を安全な形に処理したうえでクエリに渡してくれるため、SQLインジェクションを防げます。
これは初心者のうちに絶対に押さえておきたい、実務でも最重要のセキュリティ知識のひとつです。
まとめ
この記事のポイント
==と===は挙動が違うため、迷ったら===を使う- キーを指定しない配列は数値の連番キーになる
- 関数の中の変数は外とは別のスコープで、値のやり取りは引数と戻り値で行う
- 存在しない配列キーへのアクセスは
isset()や??で防ぐ $thisはメソッドを呼び出したインスタンス自身を指す- SQL文に変数を直接埋め込まず、プレースホルダを使う
次に読むべき記事
つまずきポイントを一通り振り返ったところで、次はいよいよ「設計」の話に入っていきます。
まずはWebアプリの設計でよく登場する「MVC」という考え方と、素のPHPでそれを実現しようとしたときに感じる限界について見ていきましょう。
→ 次の記事:MVCとは何か?素のPHPで感じる限界