PHP

PHPで配列の長さを取得する方法|count・foreach・forループ

PHPで配列の長さ・要素数をcount()で取得する方法を解説します。連想配列・多次元配列・疎なキー・PHP 8のTypeError、foreachとforの使い分け、Countable・iterator、文字列のlengthとの違いまで整理します。

この記事の目次
  1. PHPの配列の長さはcount()で取得する
  2. 要素を追加・削除すれば結果も変わる
  3. count()の構文とCOUNT_NORMAL
  4. count()とsizeof()の違い
  5. count()は最後の添字を返さない
  6. 要素削除後もキーに穴が残る
  7. array_is_list()で連番リストか確認する
  8. array_values()でキーを振り直す
  9. 連想配列の長さもcount()で取得する
  10. 多次元配列は外側の件数を数える
  11. COUNT_RECURSIVEは入れ子の配列自体も数える
  12. 自己参照配列ではE_WARNINGになる
  13. PHP 8では数えられない型がTypeErrorになる
  14. 配列だけを期待するならis_array()で検証する
  15. 配列またはCountableならis_countable()を使う
  16. json_decode()の戻り値をcount()で数える
  17. 空の配列の確認方法
  18. 配列と文字列の長さを区別する
  19. foreachで配列の値を順番に処理する
  20. キーと値を両方取得する
  21. HTMLテンプレートではforeachのコロン構文を使える
  22. foreachで元の配列を変更する
  23. 参照foreachの後はunset()する
  24. breakとcontinueでループを制御する
  25. forループで連番リストを処理する
  26. ループ中に要素数を変える場合は要素数固定に注意する
  27. forは連想配列・疎なキーに向かない
  28. foreachとforの使い分け
  29. Countableオブジェクトをcount()で数える
  30. Traversableはiterator_count()で数える
  31. 配列の長さでよくあるエラー
  32. Call to undefined function length()
  33. Attempt to read property length on array
  34. count() must be of type Countable|array
  35. Undefined array key
  36. COUNT_RECURSIVEが想定より大きい
  37. foreachで変更したのに元の配列が変わらない
  38. foreach後に最後の要素が壊れる
  39. 配列の長さ・ループのチェックリスト
  40. まとめ

PHPで配列の長さ(array length)、つまり要素数を取得するにはcount()を使います。JavaScriptのようなarray.length プロパティやlength()関数はPHPにはありません。

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

echo count($fruits); // 3

ただし、PHPの配列ではキーが0から順に並んでいるとは限りません。文字列のキーや、途中の番号が抜けたキーも使えます。count()の結果は「要素数」であり、最後のキーや最大添字とは限りません。

件数だけ知りたいならcount()、すべての値を順番に処理したいならforeachから使い始めてください。forは、0始まりの連番キーを自分で進めたい場合に向きます。以下の例はPHP 8系を前提とし、array_is_list()を使う部分はPHP 8.1以降が対象です。

PHPの配列の長さはcount()で取得する

count()は配列の要素数を整数で返します。Countableを実装したオブジェクトでは、そのクラスのcount()メソッドが定義した件数を返します。

<?php
$items = ['HTML', 'CSS', 'PHP'];

$length = count($items);

var_dump($length); // int(3)

PHP配列の宣言、整数キー・文字列キー、print_r・var_dumpから確認したい場合はPHP配列の書き方と出力方法を先に参照してください。

要素を追加・削除すれば結果も変わる

count()は呼び出した時点の要素数を返します。

<?php
$fruits = ['apple', 'lemon'];

echo count($fruits); // 2

$fruits[] = 'banana';
echo count($fruits); // 3

unset($fruits[1]);
echo count($fruits); // 2

unset()で要素を削除しても、残った整数キーは自動で詰め直されません。この性質がforループの注意点につながります。配列の追加・削除・置換はPHP配列の値を変更する方法で手順を確認できます。

スポンサーリンク

count()の構文とCOUNT_NORMAL

PHP公式のシグネチャは次のとおりです。

count(Countable|array $value, int $mode = COUNT_NORMAL): int

初期設定のCOUNT_NORMALでは、渡した配列の第1階層だけを数えます。

<?php
$users = [
  ['name' => '山田', 'age' => 30],
  ['name' => '佐藤', 'age' => 25],
];

echo count($users);               // 2
echo count($users, COUNT_NORMAL); // 2

内側のnameageではなく、外側にある2つのユーザーの配列を数えています。

count()とsizeof()の違い

sizeof()はcount()の別名です。同じ引数を受け取り、同じ結果を返します。

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

echo count($fruits);  // 3
echo sizeof($fruits); // 3

バイト数やメモリ使用量を返す関数ではありません。要素数を数える意図が明確なcount()へ統一すると読み手が迷いにくくなります。

count()は最後の添字を返さない

PHPの配列は、キーと値の組を挿入順に持ちます。キーには整数と文字列を使えます。count()が返すのは要素数であり、最大キーではありません。

<?php
$items = [
  2 => 'apple',
  5 => 'banana',
];

echo count($items);          // 2
echo array_key_last($items); // 5

要素は2件ですが、最後のキーは5です。count($items) - 1を「最後のキー」として使うと誤ります。

要素削除後もキーに穴が残る

<?php
$items = ['a', 'b', 'c'];
unset($items[1]);

print_r($items);
// Array
// (
//     [0] => a
//     [2] => c
// )

echo count($items); // 2

この配列へ$items[1]でアクセスしても、キー1は存在しません。全要素を処理するだけならforeachを使います。

array_is_list()で連番リストか確認する

PHP 8.1以降のarray_is_list()は、キーが0からcount($array) - 1まで連続しているか判定します。

<?php
var_dump(array_is_list(['a', 'b', 'c']));
// true

var_dump(array_is_list([0 => 'a', 2 => 'c']));
// false

var_dump(array_is_list(['name' => '山田']));
// false

添字を使うforループは、基本的にarray_is_list()がtrueとなる構造に向きます。空の配列もリストとしてtrueです。

array_values()でキーを振り直す

削除後の値を意図的に0からのリストへ戻したい場合はarray_values()を使います。

<?php
$items = [0 => 'a', 2 => 'c'];
$items = array_values($items);

print_r($items);
// [0 => 'a', 1 => 'c']

キーに意味がある連想配列へは使いません。キーを捨ててよいリストだと確認してからキーを振り直します。

連想配列の長さもcount()で取得する

文字列キーを持つ連想配列でも使い方は同じです。

<?php
$user = [
  'name' => '山田',
  'age' => 30,
  'role' => 'editor',
];

echo count($user); // 3

name・age・roleの3要素を数えます。キーの文字数や値の中身は要素数に影響しません。

<?php
foreach ($user as $key => $value) {
  echo $key . ': ' . $value . PHP_EOL;
}

連想配列の設計、文字列キー、JavaScriptオブジェクトとの差はPHP連想配列の使い方で扱っています。

多次元配列は外側の件数を数える

通常、ユーザー一覧や商品一覧の「件数」は外側の配列をcount()で数えます。

<?php
$users = [
  ['id' => 1, 'name' => '山田'],
  ['id' => 2, 'name' => '佐藤'],
  ['id' => 3, 'name' => '鈴木'],
];

echo count($users); // 3

ユーザー1人分の項目数を知りたい場合は対象行を指定します。

echo count($users[0]); // 2

何を1件とするか決めて、数える階層を明示してください。

COUNT_RECURSIVEは入れ子の配列自体も数える

COUNT_RECURSIVEは内側の配列を再帰的に数えます。

<?php
$users = [
  ['name' => '山田', 'age' => 30],
  ['name' => '佐藤', 'age' => 25],
];

echo count($users);                  // 2
echo count($users, COUNT_RECURSIVE); // 6

6になる内訳は、外側の2要素である内側の配列2件と、その中の末端の値4件です。「末端の値だけの合計」を返すわけではありません。

通常の行数・商品数・ユーザー数にはCOUNT_NORMALを使います。COUNT_RECURSIVEは目的に合う数え方かを確認してから使います。

自己参照配列ではE_WARNINGになる

count()は配列内の循環参照を検出して無限ループを避けますが、COUNT_RECURSIVEでは毎回E_WARNINGを発行し、期待値より大きい結果を返します。外部データを再帰的に数える場合は循環参照の可能性を除外してください。

PHP 8では数えられない型がTypeErrorになる

PHP 8.0以降、配列でもCountableでもない値をcount()へ渡すとTypeErrorです。

<?php
$value = null;

echo count($value);
// TypeError: count(): Argument #1 ($value) must be of type Countable|array

PHP 8より前の「nullなら0、その他のスカラー値なら1」という挙動を前提にした古いコードは移行時に確認が必要です。

配列だけを期待するならis_array()で検証する

<?php
function countItems(mixed $value): int
{
  if (!is_array($value)) {
    throw new InvalidArgumentException('arrayが必要です');
  }

  return count($value);
}

本来配列であるべき値を安易に(array)キャストすると、入力不具合を隠す場合があります。期待する型を検証し、エラーとして扱います。

配列またはCountableならis_countable()を使う

<?php
if (is_countable($value)) {
  echo count($value);
} else {
  echo 0;
}

is_countable()は配列またはCountableオブジェクトならtrueです。複数の型が混ざる入力を受ける境界では便利ですが、アプリケーション内部では引数の型を配列やCountableとして明示できる設計を優先します。

json_decode()の戻り値をcount()で数える

次のJSON配列をPHPで数えます。第2引数をtrueにすると、配列の中にあるJSONオブジェクトも連想配列に変換されます。JSON_THROW_ON_ERRORは、JSONの構文が不正な場合に例外を投げる指定です。

<?php
$json = '[{"id":1},{"id":2}]';

$items = json_decode($json, true, 512, JSON_THROW_ON_ERROR);

if (!is_array($items)) {
  throw new UnexpectedValueException('JSON arrayが必要です');
}

echo count($items); // 2

json_decode($json)のように連想配列へ変換する指定を省くと、JSONオブジェクトはstdClassになります。stdClassはCountableではないため、そのままcount()へ渡すとTypeErrorです。この例のis_array()はPHP配列かどうかを確かめる検査です。第2引数がtrueならJSONオブジェクトもPHP配列になるため、「元のJSONが配列だったか」まで区別する検査ではありません。APIが返す形式も確認してください。

フォームから配列を受け取る場合も、クライアントからの入力を信用せず型・件数・各値を検証します。name属性と入れ子の配列の受信はPHPでフォームの値を配列で送る方法を参照してください。$_GET$_POSTのどちらで届くかはGETとPOSTの違いと使い分け、画面に見せずに値を渡す場合はhiddenフィールドの送信・取得で扱っています。

空の配列の確認方法

要素数が0か確認するならcount()を使えます。

<?php
$items = [];

if (count($items) === 0) {
  echo '空です';
}

引数やプロパティが配列と保証されているなら、厳密比較も明確です。

if ($items === []) {
  echo '空のarrayです';
}

empty($items)も空の配列でtrueですが、未定義変数、null、false、0、'0'、空文字列などもemptyです。配列の構造を厳密に確認したい場面では、型検証と=== []を使うと意図が明確です。

配列と文字列の長さを区別する

「PHPで長さを調べる方法」は対象の型によって関数が異なります。

対象 関数 返すもの
配列・Countable count() 要素数
文字列 strlen() バイト数
マルチバイト文字列 mb_strlen() 指定文字コードの文字数
Traversable iterator_count() イテレーターの要素数
<?php
echo count(['a', 'b']);          // 2
echo strlen('PHP');              // 3 bytes
echo strlen('日本語');           // UTF-8なら9 bytes
echo mb_strlen('日本語', 'UTF-8'); // 3文字

配列へstrlen()、文字列へcount()を使い回さず、型と知りたい単位を先に決めます。

foreachで配列の値を順番に処理する

全要素を処理する基本はforeachです。キーが連番でなくても問題ありません。

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

foreach ($fruits as $fruit) {
  echo $fruit . PHP_EOL;
}

$fruitへ値が1件ずつ入り、要素数を自分で取得する必要はありません。

キーと値を両方取得する

<?php
$user = [
  'name' => '山田',
  'role' => 'editor',
];

foreach ($user as $key => $value) {
  echo $key . ': ' . $value . PHP_EOL;
}

数値添字だけでなく、連想配列の文字列キーも$keyへ入ります。

HTMLテンプレートではforeachのコロン構文を使える

HTMLとPHPが混在するテンプレートでは、波括弧の代わりにコロンとendforeachを使う代替構文も選べます。ループの終わりを名前で確認できます。

<?php
$employees = ['小林', '上林', '大林'];
?>

<ul>
  <?php foreach ($employees as $employee): ?>
    <li>
      <?= htmlspecialchars(
        $employee,
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8'
      ) ?>
    </li>
  <?php endforeach; ?>
</ul>

終了はendforeach;です。外部入力やデータベース値をHTMLへ出す場合は、文脈に合うエスケープを行います。count()やforeachを使っているだけではXSSを防げません。

foreachで元の配列を変更する

通常のforeachでループ変数だけを変更しても、元の配列の値は変わりません。

<?php
$numbers = [1, 2, 3];

foreach ($numbers as $number) {
  $number *= 2;
}

print_r($numbers); // [1, 2, 3]

キーを取得して明示的に書き戻す方法は、参照の残存を避けられます。

<?php
foreach ($numbers as $key => $number) {
  $numbers[$key] = $number * 2;
}

print_r($numbers); // [2, 4, 6]

参照foreachの後はunset()する

&$numberで値を参照として受け取る方法もあります。

<?php
$numbers = [1, 2, 3];

foreach ($numbers as &$number) {
  $number *= 2;
}
unset($number);

print_r($numbers); // [2, 4, 6]

foreach終了後も$numberは最後の配列要素への参照として残ります。PHP公式もunset($number)で参照を切るよう案内しています。忘れると後続代入や別foreachで最後の要素を意図せず上書きすることがあります。

breakとcontinueでループを制御する

breakはループ全体を終了し、continueは現在の回だけを飛ばして次へ進みます。

<?php
$numbers = [1, 2, 3, 4, 5];

foreach ($numbers as $number) {
  if ($number === 2) {
    continue;
  }

  if ($number === 5) {
    break;
  }

  echo $number . PHP_EOL;
}

// 1
// 3
// 4

絞り込みや検索が目的なら、array_filter・array_searchなど目的別関数のほうが意図を表しやすい場合もあります。

forループで連番リストを処理する

0から連続するリストで添字制御が必要な場合はforを使えます。一番短い形はfor ($i = 0; $i < count($fruits); $i++)で、条件式に配列の長さをそのまま書きます。

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

for ($i = 0, $length = count($fruits); $i < $length; ++$i) {
  echo $i . ': ' . $fruits[$i] . PHP_EOL;
}

3つの式は次の役割です。

実行時点
初期化 ループ開始時に1回 $i = 0, $length = count($fruits)
条件 各回の処理を始める前 $i < $length
更新 各回の処理が終わったあと ++$i

PHP公式マニュアルは、要素数が変わらない配列ならcount()の結果を変数へ保存し、各繰り返し処理でcount()を呼び直さない例を示しています。

ループ中に要素数を変える場合は要素数固定に注意する

保存済みの$lengthはループ開始時の値です。途中で追加・削除しても更新されません。ループ中に配列構造を変更する設計は添字の読み飛ばしや存在しないキーを生みやすいため、変更用の配列を別にするか、処理前に変換してください。

forは連想配列・疎なキーに向かない

次の配列は要素数2ですが、キー 0・1はありません。

<?php
$items = [
  2 => 'apple',
  5 => 'banana',
];

for ($i = 0; $i < count($items); ++$i) {
  echo $items[$i]; // key 0・1がなくwarning
}

この場合はforeachを使います。

foreach ($items as $key => $item) {
  echo $key . ': ' . $item . PHP_EOL;
}

添字で制御するループが本当に必要でキーを捨ててよい場合だけ、array_values()でリスト化します。

foreachとforの使い分け

目的 選択
すべての値を順番に処理 foreach ($array as $value)
キーと値を処理 foreach ($array as $key => $value)
連番リストを添字で制御 for
要素数だけ取得 count()
リストか確認 array_is_list()

配列の全要素を扱うだけならforeachが安全で簡潔です。forは「iを2ずつ増やす」「特定範囲だけ処理する」など添字自体に意味がある場合に使います。

Countableオブジェクトをcount()で数える

Countableインターフェースを実装したオブジェクトはcount()へ渡せます。

<?php
final class Cart implements Countable
{
  public function __construct(
    private array $items = []
  ) {
  }

  public function count(): int
  {
    return count($this->items);
  }
}

$cart = new Cart(['book', 'pen']);

echo count($cart); // 2

Countable::count()の戻り値は整数です。stdClassを含む任意のオブジェクトが自動で数えられるわけではありません。

Traversableはiterator_count()で数える

Iterator・IteratorAggregateなどTraversableで、Countableを実装していない場合はiterator_count()を使えます。

<?php
$iterator = new ArrayIterator(['a', 'b', 'c']);

echo iterator_count($iterator); // 3

iterator_count()はイテレーターの現在位置を保持する保証がありません。Generatorなど一度しか進められないストリームを「件数確認のためだけ」に最後まで消費すると、その後の処理に使えなくなることがあります。

全件を処理するだけなら件数を数えずforeachで繰り返し処理し、件数が必要ならデータの取得元側の件数取得クエリやCountableを実装するコレクションの設計も検討します。

配列の長さでよくあるエラー

Call to undefined function length()

PHPに配列の長さ用のlength()関数はありません。配列はcount()、文字列はstrlen()またはmb_strlen()です。

Attempt to read property length on array

$array->length$array.lengthはJavaScriptと混同した書き方です。PHP配列ではcount($array)を使います。

count() must be of type Countable|array

null、文字列、整数、stdClassなどを渡しています。入力型、json_decodeの連想配列へ変換する指定、APIレスポンスの構造を確認し、is_arrayまたはis_countableで境界検証します。

Undefined array key

count()の結果を最大添字と誤解している、削除後にキーが飛んでいる、連想配列をforで処理している可能性があります。array_keys、array_is_list、print_rで実際のキーを確認します。

COUNT_RECURSIVEが想定より大きい

入れ子の配列自体も1要素として数えます。末端の値だけが目的ならデータ構造に合わせた明示的な集計が必要です。自己参照がある場合はE_WARNINGにも注意します。

foreachで変更したのに元の配列が変わらない

値は初期設定でコピーとして扱われます。キー経由で書き戻すか、参照を使うforeachを使い、参照を使った場合は終了後にunsetします。

foreach後に最後の要素が壊れる

参照を使うループの&$valueが最後の要素を参照したまま残っています。ループ直後のunset($value)を確認します。

配列の長さ・ループのチェックリスト

  • 配列の要素数にはcount()を使った
  • count()の結果を最大キー・最後の添字として使っていない
  • 疎なキー・連想配列はforeachで処理した
  • forを使う前に0始まりの連番リストか確認した
  • 必要ならarray_is_list()・array_values()を使い分けた
  • 多次元配列のどの階層を1件とするか決めた
  • COUNT_RECURSIVEの内訳と循環参照の警告を理解した
  • PHP 8のCountable|arrayのTypeErrorを考慮した
  • 複数の型が混ざる入力をis_array()・is_countable()で検証した
  • 配列・文字列・Traversableの長さを調べる関数を区別した
  • テンプレート出力をhtmlspecialcharsでエスケープした
  • 参照を使うforeach後にunset($value)した
  • ループ中の配列追加・削除と保存した要素数を確認した
  • iterator_count()がイテレーター位置を保持しない点を確認した

まとめ

PHPで配列の長さ・要素数を取得する基本はcount($array)です。sizeof()も同じ結果ですが、意図が明確なcount()へ統一できます。

PHP配列は順序を持つマップなので、count()の結果と最後のキーは別です。0から連続するリストでなければ添字付きforは使わず、foreachでキー・値を処理します。PHP 8.1以降ならarray_is_list()でリスト判定もできます。

PHP 8.0以降、配列・Countable以外をcount()へ渡すとTypeErrorです。外部入力・JSON・nullを取り得る値は型を検証し、文字列はstrlen / mb_strlen、Traversableはiterator_countと、対象に合う方法を選んでください。

スポンサーリンク