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()は最後のindexを返さない
  6. 要素削除後もkeyに穴が残る
  7. array_is_list()で連番listか確認する
  8. array_values()でkeyを振り直す
  9. 連想配列の長さもcount()で取得する
  10. 多次元配列は外側の件数を数える
  11. COUNT_RECURSIVEはnested array自体も数える
  12. 自己参照arrayではE_WARNINGになる
  13. PHP 8では数えられない型がTypeErrorになる
  14. arrayだけを期待するならis_array()で検証する
  15. arrayまたはCountableならis_countable()を使う
  16. json_decode()の戻り値をcountする
  17. 空配列の確認方法
  18. arrayと文字列のlengthを区別する
  19. foreachで配列の値を順番に処理する
  20. keyとvalueを両方取得する
  21. HTML templateではforeachのコロン構文を使える
  22. foreachで元の配列を変更する
  23. 参照foreachの後はunset()する
  24. breakとcontinueでloopを制御する
  25. forループで連番listを処理する
  26. loop中に要素数を変える場合はsize固定に注意する
  27. forは連想配列・疎なkeyに向かない
  28. foreachとforの使い分け
  29. Countable objectを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. 配列length・loopチェックリスト
  40. まとめ

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

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

echo count($fruits); // 3

結論だけならこれで完了です。ただしPHPのarrayは0から連続するlistだけでなく、整数・文字列keyを持つordered mapです。count()の結果は「要素数」であり、最後のkeyや最大indexとは限りません。

この記事ではPHP 8系を前提に、count・sizeof・is_countable、連想配列・多次元配列、疎なkey、foreach・for、Countable・iterator、文字列のlengthとの違いまで解説します。

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

count()はarrayまたはCountable objectの要素数をintで返します。

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

$length = count($items);

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

PHP配列の宣言、整数key・文字列key、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()で要素を削除しても、残った整数keyは自動で詰め直されません。この性質がforループの注意点につながります。配列の追加・削除・置換はPHP配列の値を変更する方法で分離して解説しています。

count()の構文とCOUNT_NORMAL

PHP公式のsignatureは次のとおりです。

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

defaultのCOUNT_NORMALでは、渡したarrayの第1階層だけを数えます。

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

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

内側のnameageではなく、外側にある2つのuser arrayを数えています。

count()とsizeof()の違い

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

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

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

byte sizeやmemory使用量を返すfunctionではありません。要素数を数える意図が明確なcount()へ統一すると読み手が迷いにくくなります。

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

PHPのarrayはordered mapで、keyはintまたはstringです。count()が返すのは要素数であり、最大keyではありません。

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

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

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

要素削除後もkeyに穴が残る

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

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

echo count($items); // 2

このarrayへ$items[1]でaccessしても、key 1は存在しません。全要素を処理するだけならforeachを使います。

array_is_list()で連番listか確認する

PHP 8.1以降のarray_is_list()は、keyが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

indexを使うforループは、基本的にarray_is_list()がtrueとなる構造に向きます。空arrayもlistとしてtrueです。

array_values()でkeyを振り直す

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

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

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

keyに意味がある連想配列へは使いません。keyを捨ててよいlistだと確認してからreindexします。

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

string keyを持つ連想配列でも使い方は同じです。

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

echo count($user); // 3

name・age・roleの3要素を数えます。keyの文字数やvalueの中身はcountへ影響しません。

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

連想配列の設計、string key、JavaScript objectとの差はPHP連想配列の使い方で扱っています。

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

通常、user一覧や商品一覧の「件数」は外側arrayをcountします。

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

echo count($users); // 3

1 userのproperty数を知りたい場合は対象行を指定します。

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

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

COUNT_RECURSIVEはnested array自体も数える

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

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

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

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

通常の行数・商品数・user数にはCOUNT_NORMALを使います。COUNT_RECURSIVEは目的に合うcount定義かを確認してから使います。

自己参照arrayではE_WARNINGになる

count()はarray内のrecursionを検出して無限loopを避けますが、COUNT_RECURSIVEでは毎回E_WARNINGを発行し、期待値より大きい結果を返します。外部dataを再帰countする場合は循環参照の可能性を除外してください。

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

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

<?php
$value = null;

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

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

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

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

  return count($value);
}

本来arrayであるべき値を安易に(array)castすると、入力不具合を隠す場合があります。期待する型を検証し、errorとして扱います。

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

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

is_countable()はarrayまたはCountable objectならtrueです。mixed inputを受ける境界では便利ですが、application内部ではparameter typeをarrayやCountableとして明示できる設計を優先します。

json_decode()の戻り値をcountする

JSON arrayをPHP arrayとして扱う場合、第2引数をtrueにして例外付きでdecodeします。

<?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)のようにassociative指定を省くと、JSON objectはstdClassになります。stdClassはCountableではないため、そのままcountするとTypeErrorです。JSONのroot shapeと戻り型を検証します。

formから配列を受け取る場合も、client inputを信用せず型・件数・各値を検証します。name属性とnested arrayの受信はPHPでフォームの値を配列で送る方法を参照してください。

空配列の確認方法

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

<?php
$items = [];

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

parameterやpropertyがarrayと保証されているなら、厳密比較も明確です。

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

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

arrayと文字列のlengthを区別する

「PHP length」は対象の型によってfunctionが異なります。

対象 function 返すもの
array・Countable count() 要素数
string strlen() byte数
multibyte string mb_strlen() 指定encodingの文字数
Traversable iterator_count() iteratorの要素数
<?php
echo count(['a', 'b']);          // 2
echo strlen('PHP');              // 3 bytes
echo strlen('日本語');           // UTF-8なら9 bytes
echo mb_strlen('日本語', 'UTF-8'); // 3文字

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

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

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

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

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

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

keyとvalueを両方取得する

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

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

数値indexだけでなく、連想配列のstring keyも$keyへ入ります。

HTML templateではforeachのコロン構文を使える

HTMLとPHPが混在するtemplateでは、braceの対応が分かりやすいalternate syntaxを使えます。

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

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

終了はendforeach;です。外部入力やdatabase値をHTMLへ出す場合は、文脈に合うescapeを行います。countやforeachを使っているだけではXSSを防げません。

foreachで元の配列を変更する

通常のforeachでloop変数だけを変更しても、元arrayの値は変わりません。

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

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

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

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

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

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

参照foreachの後はunset()する

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

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

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

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

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

breakとcontinueでloopを制御する

breakはloop全体を終了し、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

filterやsearchが目的なら、array_filter・array_searchなど目的別functionのほうが意図を表しやすい場合もあります。

forループで連番listを処理する

0から連続するlistでindex制御が必要な場合はforを使えます。

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

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

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

実行時点
初期化 loop開始時に1回 $i = 0, $length = count($fruits)
条件 各iteration開始前 $i < $length
更新 各iteration終了後 ++$i

PHP公式manualは、sizeが変わらないarrayならcount結果を変数へ保存し、各iterationでcount()を呼び直さない例を示しています。

loop中に要素数を変える場合はsize固定に注意する

cached$lengthはloop開始時の値です。途中で追加・削除しても更新されません。loop中にarray構造を変更する設計はindexのskipやundefined keyを生みやすいため、変更用arrayを別にするか、処理前に変換してください。

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

次のarrayは要素数2ですが、key 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;
}

index loopが本当に必要でkeyを捨ててよい場合だけ、array_values()でlist化します。

foreachとforの使い分け

目的 選択
全valueを順番に処理 foreach ($array as $value)
keyとvalueを処理 foreach ($array as $key => $value)
連番listをindexで制御 for
要素数だけ取得 count()
listか確認 array_is_list()

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

Countable objectをcount()で数える

Countable interfaceを実装したobjectは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()の戻り値はintです。stdClassを含む任意objectが自動で数えられるわけではありません。

Traversableはiterator_count()で数える

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

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

echo iterator_count($iterator); // 3

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

全件を処理するだけならcountせずforeachでiterationし、件数が必要ならsource側のcount queryやCountable collection設計も検討します。

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

Call to undefined function length()

PHPにarray length用のlength()関数はありません。arrayはcount()、stringはstrlen()またはmb_strlen()です。

Attempt to read property length on array

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

count() must be of type Countable|array

null、string、int、stdClassなどを渡しています。入力型、json_decodeのassociative指定、API response shapeを確認し、is_arrayまたはis_countableで境界検証します。

Undefined array key

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

COUNT_RECURSIVEが想定より大きい

nested array自体も1要素として数えます。leafだけが目的ならdata構造に合わせた明示的な集計が必要です。自己参照がある場合はE_WARNINGにも注意します。

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

valueはdefaultでcopyとして扱われます。key経由で書き戻すか、reference foreachを使い、referenceを使った場合は終了後にunsetします。

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

reference loopの&$valueが最後の要素を参照したまま残っています。loop直後のunset($value)を確認します。

配列length・loopチェックリスト

  • arrayの要素数にはcount()を使った
  • count結果を最大key・最後のindexとして使っていない
  • 疎なkey・連想配列はforeachで処理した
  • forを使う前に0始まりの連番listか確認した
  • 必要ならarray_is_list()・array_values()を使い分けた
  • 多次元配列のどの階層を1件とするか決めた
  • COUNT_RECURSIVEの内訳とrecursion warningを理解した
  • PHP 8のCountable|array TypeErrorを考慮した
  • mixed inputをis_array()・is_countable()で検証した
  • array・string・Traversableのlength functionを区別した
  • template出力をhtmlspecialcharsでescapeした
  • reference foreach後にunset($value)した
  • loop中のarray追加・削除とcached lengthを確認した
  • iterator_count()がiterator位置を保持しない点を確認した

まとめ

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

PHP arrayはordered mapなので、count結果と最後のkeyは別です。0から連続するlistでなければindex付きforは使わず、foreachでkey・valueを処理します。PHP 8.1以降ならarray_is_list()でlist判定もできます。

PHP 8.0以降、array・Countable以外をcountするとTypeErrorです。外部入力・JSON・nullable valueは型を検証し、stringはstrlen / mb_strlen、Traversableはiterator_countと、対象に合う方法を選んでください。