SmartArray wraps arrays (usually database rows) in chainable collection methods, with fields that HTML-encode themselves on output. This page covers installation, your first collection, the mental model behind the two classes, and the everyday basics: loops, field access, and debugging.
Contents:
- Installation
- Your First SmartArray
- The Mental Model
- Working with ZenDB and CMS Builder
- Converting to Plain Arrays
- Debugging
Using CMS Builder or ZenDB? SmartArray is already installed, and query results already arrive as SmartArrays; skip to Your First SmartArray to see how they behave.
composer require itools/smartarrayRequires PHP 8.1+ and SmartString 3.0+ (the class behind the self-encoding fields; installs automatically as a dependency).
Wrap an array of rows with SmartArrayHtml::new(), loop it with foreach,
and echo fields with property syntax. Every field HTML-encodes itself; you
never call htmlspecialchars():
use Itools\SmartArray\SmartArrayHtml;
$users = SmartArrayHtml::new([
['name' => "Jean O'Brien", 'city' => 'Vancouver', 'joined' => '2025-11-05'],
['name' => 'Tom & Jerry Inc', 'city' => 'Ottawa', 'joined' => '2024-03-14'],
['name' => 'Sam Smith', 'city' => 'Calgary', 'joined' => '2026-01-22'],
]);
foreach ($users as $user) {
echo "<li>$user->name from $user->city</li>\n";
}
// <li>Jean O'Brien from Vancouver</li>
// <li>Tom & Jerry Inc from Ottawa</li>
// <li>Sam Smith from Calgary</li>Nested arrays are wrapped automatically: $users is a SmartArrayHtml, and so
is each $user row inside it.
You can also pick out single rows without looping:
echo $users->first()->name; // Jean O'Brien
echo $users->last()->city; // Calgary
echo $users->at(1)->name; // Tom & Jerry Inc (by position: 0 is first, negatives count from the end)Fields are SmartString objects, so their formatting methods chain right off the field. PHP even lets you call them inside double-quoted strings by wrapping the call in curly braces:
$user = $users->first();
echo "Joined: {$user->joined->dateFormat('M j, Y')}"; // Joined: Nov 5, 2025Every SmartString method is available this way: textOnly(), maxChars(),
numberFormat(), and the rest of the
SmartString API.
SmartArray is two classes with the same methods, and one question picks between them: what are you outputting?
| Class | Use when | Fields return |
|---|---|---|
SmartArrayHtml |
Output is a web page (the common case) | SmartStrings that HTML-encode when echoed |
SmartArray |
Output is anything else: JSON, CSV, email, CLI | Plain PHP values (string, int, ...) |
That's the whole decision. Collection methods behave identically in both classes: callbacks, matching, and sorting always work on the original unencoded values, so filtering, reports, and calculations work fine in HTML mode:
$local = $users->where('city', 'Vancouver')->sortBy('name'); // logic uses original values
foreach ($local as $user) {
echo "<li>$user->name</li>\n"; // <li>Jean O'Brien</li> (output still auto-encodes)
}This page and the rest of these guides use SmartArrayHtml throughout. When
your output isn't HTML, SmartArray works the same way with plain values;
see Using SmartArray Without SmartStrings.
With ZenDB and CMS Builder,
query results arrive as SmartArrays in HTML mode, so you may never call
SmartArrayHtml::new() at all:
use Itools\ZenDB\DB;
$users = DB::select('users', ['status' => 'Active']);
foreach ($users as $user) {
echo "$user->name from $user->city<br>\n"; // every field auto-encodes
}The toArray() method returns a plain nested PHP array with the original
values, in both modes. Nothing is altered by wrapping, so the round trip is
lossless:
$plain = $users->toArray();
// [
// ['name' => "Jean O'Brien", 'city' => 'Vancouver', 'joined' => '2025-11-05'],
// ['name' => 'Tom & Jerry Inc', 'city' => 'Ottawa', 'joined' => '2024-03-14'],
// ['name' => 'Sam Smith', 'city' => 'Calgary', 'joined' => '2026-01-22'],
// ]On single fields in HTML mode, value() returns the original value in its
original type: $user->name->value().
Call debug() on any SmartArray to see its contents, which mode it's in,
and (for query results) the mysqli metadata. It prints readable output in
the browser and plain text on the command line:
$users->debug(); // contents, current mode, and query metadataPlain print_r($users) works too and shows just the element data; the class
name in its output tells you the mode. Unlike debug(), its output isn't
<xmp>-wrapped, so in a browser it's raw and unformatted - prefer debug().