Course outline

PHP / Basic: values and request foundations / Lesson 3

Arrays, control flow, and explicit absence

Use PHP arrays as ordered maps and distinguish record fields from lists. Combine strict decisions and bounded iteration without hiding missing keys or changing list shape accidentally.

About 4 min readContent reviewed
On this page

Lesson goals

  • Explain ordered-map keys and list representations.
  • Distinguish missing keys from null values.
  • Trace loops, strict conditions, and accumulator invariants.

One array type serves several data models

A PHP array is an ordered map whose keys are integers or strings under documented conversion rules. A list is a useful narrower shape with consecutive integer keys starting at zero; a record associates named fields with values. Keep those contracts separate even though the runtime uses one array type. External numeric-looking keys and key collisions need thought rather than assumptions borrowed from JavaScript objects. For stable record access, validate required keys and types once. For list APIs, preserve order and reindex when operations leave gaps that would change JSON representation.

Presence and non-nullness are different questions

array_key_exists reports whether a key exists even when its value is null. isset is false for a missing key or a null value, while null coalescing provides a fallback for those cases without an undefined-key warning. Choose according to the contract. A nullable optional note differs from a required quantity; replacing every missing field with zero can make malformed requests look valid. The example makes the distinction observable without emitting warnings. Never use error suppression merely to make an unknown data shape convenient; repair the boundary that promised the shape.

php Presence, null, and a valid zero
<?php
declare(strict_types=1);
$record = ["note" => null, "count" => 0];
echo (array_key_exists("note", $record) ? "present" : "missing"), PHP_EOL;
echo (isset($record["note"]) ? "set" : "null-or-missing"), PHP_EOL;
echo $record["count"] ?? 10, PHP_EOL;
Expected output
present
null-or-missing
0

The note exists but is null. The count remains zero rather than receiving the fallback.

Decisions should reflect normalized domain values

Strict equality compares type as well as value, avoiding loose-comparison surprises at request boundaries. Normalize and validate first rather than scattering casts inside conditions. match expressions, available since PHP 8.0, use strict comparison and return a value; an unmatched input without a default raises an error rather than silently doing nothing. if/elseif is useful when conditions are ranges or compound predicates. Keep rejection before mutation so a failed eligibility check does not consume a seat. Review boundary values and overlapping conditions rather than assuming indentation proves mutually exclusive behavior.

Filtering can preserve keys you no longer want

array_filter preserves original keys, so removing a middle list element can produce a non-list array. JSON encoding then uses an object representation for the resulting keys instead of the expected JSON list. Reindex with array_values when the public contract is a list and positions are not identifiers. Do not reindex an intentional identifier map merely to obtain prettier JSON. The example keeps title order but makes the representation choice explicit. Use a callback with an explicit predicate rather than default truthiness when zero, false, or empty strings can be valid retained values.

php Preserved keys and an explicit public list
<?php
declare(strict_types=1);
$titles = ["DOM", "", "PDO"];
$filtered = array_filter($titles, fn(string $title): bool => $title !== "");
echo json_encode($filtered, JSON_THROW_ON_ERROR), PHP_EOL;
echo json_encode(array_values($filtered), JSON_THROW_ON_ERROR), PHP_EOL;
Expected output
{"0":"DOM","2":"PDO"}
["DOM","PDO"]

The first output is an object because key one is absent. array_values establishes the intended list shape.

Make progress and mutation visible in loops

foreach is natural for array values or key/value pairs. Accumulators should have an initial value and a simple invariant, such as total equals the sum of processed integer amounts. A by-reference foreach leaves the loop variable referencing an element unless deliberately released, which can surprise later assignments; prefer ordinary iteration when mutation is unnecessary. Bound user-driven loops and avoid altering the collection in ways that make later visits unclear. Early return or continue can simplify rejected-item handling, but document whether one invalid record rejects the whole operation or is collected as a separate error.

Common errors and fixes

A null field is mistaken for a missing key

Why it happens
isset is used for a presence-only contract.
How to fix it
Use array_key_exists when explicit null must remain distinguishable.
Prevent it next time
Test absent, null, zero, and ordinary values.

A JSON list becomes an object unexpectedly

Why it happens
Filtering preserved nonconsecutive array keys.
How to fix it
Apply array_values for an intended list after filtering.
Prevent it next time
Assert output shape as well as item values.

Later assignments change the last loop item

Why it happens
A by-reference foreach variable remains aliased.
How to fix it
Avoid reference iteration or unset the reference deliberately.
Prevent it next time
Review alias lifetime and test a following assignment.

Check your understanding

1. A record contains ["count"=>0]. What should count ?? 10 return?

Show worked solution

It returns zero because the key exists and its value is not null. A truthiness-based fallback would answer a different question. Validate count as an allowed integer separately; coalescing does not establish that negative or string values are acceptable.

2. Filter the empty title from ["A","","B"] for a JSON list.

Show worked solution

Use an explicit nonempty-string predicate, then array_values before JSON encoding. The expected JSON is ["A","B"]. If original numeric keys were external identifiers, keep a map instead and document that different representation rather than silently discarding identity.

Key takeaways

  • Array shape and absence are part of the public contract.
  • Normalize before strict decisions and make iteration ownership clear.

Official references