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
内側のname・ageではなく、外側にある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と、対象に合う方法を選んでください。