💡 今日の3行まとめ
  • 部分一致の判定には、strpos() ではなく直感的な str_contains()(PHP 8以降)を使う
  • 配列の絞り込みは array_filter() とアロー関数 fn() でスッキリ書く
  • 絞り込み後は元の添字(キー)が飛び飛びになるため、array_values() で連番に振り直す

1. 今日のお題とゴール

商品リストの配列から、検索キーワード「PHP」を含み、かつ在庫がある商品だけを抽出して表示してみましょう。

  • 商品名(name)にキーワード "PHP" が含まれていること
  • 在庫数(stock)が 1 以上であること
  • 抽出後の配列キーを 0 からの連番に整えて出力すること
// 期待する出力結果
検索ヒット: 2件
[0] 入門PHPドリル (在庫: 5)
[1] 実践PHPWeb開発 (在庫: 2)

2. ❌ やりがちなNGコード vs ⭕ コピペで動く正解コード

❌ やりがちなNGコードBefore
<?php
// キーワードが先頭(0文字目)にあると「0 != false」で除外されてしまう!
$results = array_filter($items, function ($item) {
    return strpos($item['name'], 'PHP') != false && $item['stock'] > 0;
});

// キーが飛び飛び([1, 3]など)のままなので $results[0] でエラーになる
echo $results[0]['name'];

strpos() は先頭で一致すると 0 を返すため、緩い比較(!= false)を使うと「一致していない」と誤判定されます。また、array_filter() は元の配列キーを維持するため、そのまま $results[0] にアクセスすると未定義キーの警告が発生します。

⭕ コピペで動く正解コード(PHP 8.3+)After
<?php
declare(strict_types=1);

$items = [
    ['name' => 'PHP基礎テキスト', 'stock' => 0],
    ['name' => '入門PHPドリル', 'stock' => 5],
    ['name' => 'HTML/CSSデザイン集', 'stock' => 3],
    ['name' => '実践PHPWeb開発', 'stock' => 2],
];

$keyword = 'PHP';

// 1. 条件に合う要素だけを抽出(アロー関数で簡潔に記述)
$filtered = array_filter(
    $items,
    fn(array $item): bool => str_contains($item['name'], $keyword) && $item['stock'] >= 1
);

// 2. 飛び飛びになった配列キーを 0 からの連番に振り直す
$results = array_values($filtered);

echo '検索ヒット: ' . count($results) . "件\n";
foreach ($results as $index => $item) {
    echo sprintf("[%d] %s (在庫: %d)\n", $index, $item['name'], $item['stock']);
}

3. ここだけ押さえる!3つのポイント

1部分一致は str_contains() で安全に判定

str_contains($haystack, $needle) は、文字列が含まれているかを純粋な真偽値(true / false)で返します。従来の strpos() のように先頭一致の 0 と false を取り違えるバグが起きません。

2アロー関数 fn() で外側の変数をそのまま使う

fn($item) => ... の形式で書くと、外側の変数 $keyword を use ($keyword) なしで直接参照できます。1行のシンプルな条件式を書くときに可読性が大きく向上します。

3array_filter() の後は array_values() がセット

array_filter() は元のキー(今回の例では 1 と 3)を保持したまま要素を間引きます。array_values() を通すことで 0, 1, ... の綺麗な連番配列にリセットできます。

4. 1分ミニチャレンジ

🚀 手を動かしてみよう

正解コードの $keyword = 'PHP'; を小文字の $keyword = 'php'; に変えて実行すると、ヒット件数はどうなるでしょうか?大文字・小文字を区別せずに検索したい場合は、str_contains() の代わりに mb_stripos($item['name'], $keyword) !== false へ書き換えて動作を比べてみましょう。