Skip to content

Commit 4bae332

Browse files
committed
deprecations: silently deprecate get() and set(), teach property access everywhere
- get() and set() move to the Silent Aliases region of DeprecatedAliases: same behavior, IDE strikethrough, no runtime signal. get('') and set('', $value) stay the only way to reach an empty-string key, so the ''-key suggestions still name them. - Docs and deprecation notices now point at property access only: ->key, ->key = $value, ->{'users.id'} for keys property syntax can't type, and ?? for missing-key defaults. Write-side [] notices suggest assignment instead of ->set(). - README, help.txt, and UPGRADING drop get()/set(); the indexBy example uses brace syntax. - Tests: get()/set() behavior stays pinned in ReadAccessTest and WriteAccessTest, the alias inventory and no-signal checks cover them in DeprecationsTest, and incidental get()/set()/nth() calls in other tests switch to property access and at().
1 parent 4584133 commit 4bae332

22 files changed

Lines changed: 193 additions & 207 deletions

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
- `json_encode($smartArray)` also substitutes malformed UTF-8 in keys with � (U+FFFD), not just values - one corrupt byte in a key no longer makes it return false and lose the whole document.
1212

1313
### Added
14-
- `at($index)` - new name for `nth()`: get an element by position, zero-based, negative indices count from the end. Matches JavaScript's `Array.at()`. `get()` is by key, `at()` is by position.
14+
- `at($index)` - new name for `nth()`: get an element by position, zero-based, negative indices count from the end. Matches JavaScript's `Array.at()`. `$row->key` is by key, `at()` is by position.
1515
- `columnAt($index)` - new name for `pluckNth()`: get the column at a position from each row, ignoring key names. `column()` is by key, `columnAt()` is by position.
1616

1717
### Changed
@@ -29,7 +29,7 @@
2929
- `SmartArray::new($data, true)` and `SmartArrayHtml::new($data, false)` now throw like the constructors do, instead of silently ignoring the boolean. Old code that passed `true` expecting auto-encoding was silently getting raw, unencoded values - now it fails at the call site with the class to use instead. Redundant booleans (`false` on SmartArray, `true` on SmartArrayHtml) log a deprecation and proceed.
3030
- `sortBy()` second parameter renamed `$type``$flags` - it always held PHP sort flags, and now matches `sort()` and PHP's own sort functions. Affects named-argument calls only: `sortBy('name', flags: SORT_NATURAL)`.
3131
- `column(null)` and `column(null, null)` now match PHP's `array_column()`: whole rows renumbered from 0, instead of throwing "unexpected arguments"
32-
- Array-syntax deprecation notices suggest one replacement style for reads, `isset()`, and `unset()`: `->key` for property-safe names, `->{0}` for integer keys, `->{'users.id'}` for other keys (`->get()` also works for reads). Reads used to suggest `->get(0)` while existence checks suggested `->{0}`, so one `empty()` call printed two notices with different advice. Null and `''` keys suggest `->get('')` - the brace form is a fatal error for an empty property name.
32+
- Array-syntax deprecation notices suggest one replacement style across reads, writes, `isset()`, and `unset()`: `->key` and `->key = $value` for property-safe names, `->{0}` for integer keys, `->{'users.id'}` for other keys. Reads used to suggest `->get(0)` while existence checks suggested `->{0}`, so one `empty()` call printed two notices with different advice, and writes suggested the now-deprecated `->set()`. Null and `''` keys are the exception and suggest `->get('')` / `->set('', $value)` - the brace form is a fatal error for an empty property name.
3333
- `isset($array['key'])` and `empty($array['key'])` now follow `$onOffsetAccess` like reads, writes, and `unset()` - notice by default, exception in `'throw'` mode. Existence checks were the one silent form of the deprecated `[]` syntax; if `[]` support is removed in a future version, `isset()` on the object would silently return false instead of erroring, so these call sites need migrating with the rest. Property-syntax checks (`isset($array->key)`) are unaffected and stay signal-free. Internal existence checks now call `array_key_exists()` directly, removing two method calls from every `get()`.
3434
- `or404()` outputs `<html>` instead of `<html lang>` - an empty `lang` reads as an invalid value to accessibility checkers, and the message language is caller-supplied so it can't be declared. Matches SmartString.
3535
- `orDie()` and `or404()` now exit with status 1 instead of 0, so shell scripts and cron jobs see the failure. Output is unchanged. Matches SmartString.
@@ -43,6 +43,8 @@
4343
- `pluck($valueField, $keyField)` - use `column()` instead, same arguments and behavior: `->column('name')`, `->column('name', 'id')`. One name per behavior, and `column()` matches PHP's `array_column()`. Still works with no runtime notice - IDEs show a strikethrough with the replacement; removed from README and help().
4444
- `each($callback)` - use a `foreach` loop instead, same behavior in plain PHP. Still works with no runtime notice - IDEs show a strikethrough with the replacement; removed from help(). It had no measured uses and a foreach is clearer and faster.
4545
- `sprintf($format)` - use `map()` with an inline format string instead: `$list->map(fn($v) => "<li>$v</li>")`. On SmartArrayHtml, encode explicitly and convert to raw mode first so the finished HTML isn't re-encoded on output: `$row->asRaw()->map(fn($v) => "<td>" . htmlspecialchars((string)$v) . "</td>")->implode("\n")`. The method still works unchanged with no runtime notice - IDEs show a strikethrough with the replacement; removed from README and help(). It was a second formatting syntax that only saw use inside CMS Builder.
46+
- `get($key, $default)` - use property access instead: `$row->name`, `$row->{'users.id'}` for keys property syntax can't type, and `$row->name ?? 'n/a'` for missing-key defaults. Still works unchanged with no runtime notice, including the default parameter - IDEs show a strikethrough with the replacement; removed from README and help(). It was a second documented way to read every element; the docs now teach one form, and property access is 1.1-1.6x faster. `get('')` remains the only way to read an empty-string key - the brace form is a fatal error.
47+
- `set($key, $value)` - use property assignment instead: `$row->name = $value`, or `$row->{'users.id'} = $value` for keys property syntax can't type. Still works unchanged with no runtime notice - IDEs show a strikethrough with the replacement; removed from README and help(). Deprecated together with `get()` - one form for reads, one for writes. `set('', $value)` remains the only way to write an empty-string key.
4648

4749
### Removed
4850
- `usingSmartStrings()` - use `instanceof SmartArrayHtml` to check the mode; the class is the mode. The method was never documented and had no found uses.

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,9 @@ $authors = [
179179
// Create a lookup array indexed by author_id
180180
$authorById = SmartArray::new($authors)->indexBy('author_id')->asHtml();
181181

182-
// Now you can quickly look up authors by their ID
183-
echo $authorById->get(101)->name; // Output: Jane Austen
184-
echo $authorById->get(103)->genre; // Output: Science Fiction
182+
// Now you can quickly look up authors by their ID (braces for numeric keys)
183+
echo $authorById->{101}->name; // Output: Jane Austen
184+
echo $authorById->{103}->genre; // Output: Science Fiction
185185

186186
// Particularly useful when joining data from multiple sources
187187
$articles = [
@@ -192,7 +192,7 @@ $articles = [
192192

193193
// Display articles with author information
194194
foreach (SmartArray::new($articles)->asHtml() as $article) {
195-
$author = $authorById->get($article->author_id);
195+
$author = $authorById->{$article->author_id};
196196
echo "Title: $article->title\n";
197197
echo "By: $author->name ($author->genre)\n\n";
198198
}
@@ -463,9 +463,9 @@ Note: All methods return a new `SmartArray` object unless otherwise specified.
463463
| | $array->asRaw() | Return values as raw PHP types (lazy conversion - returns same object if already using raw values) |
464464
| | SmartArrayHtml::new($array) | Create a SmartArray with HTML-safe SmartString values directly (equivalent to SmartArray::new()->asHtml()) |
465465
| Value Access | $obj->key | Get a value using property syntax |
466-
| | get(key) | Get a value by key (for numeric keys or keys with special characters) |
467-
| | get(key, default) | Get a value with optional default if key not found |
468-
| | set(key, value) | Set a value by key (for numeric keys or keys with special characters) |
466+
| | $obj->{'users.id'} | Get keys property syntax can't type (dots, dashes, numeric keys) |
467+
| | $obj->key = $value | Set a value using property syntax |
468+
| | $obj->key ?? 'default' | Fallback for possibly-missing keys, same as plain PHP |
469469
| | first() | Get the first element |
470470
| | last() | Get the last element |
471471
| | at(index) | Get element by position, ignoring keys (0=first, -1=last) |

UPGRADING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,13 @@ Full lists of what changed per release: [CHANGELOG.md](CHANGELOG.md).
197197
> ```php
198198
> echo $row['name']; // works, but prints a Deprecated: notice into the page
199199
> echo $row->name; // correct
200-
> echo $row->get('name'); // correct - for keys property syntax can't type, like 'users.id'
200+
> echo $row->{'users.id'}; // correct - for keys property syntax can't type
201201
> ```
202202
>
203203
> Fix:
204204
>
205205
> - Follow the file and line in each notice and switch to `->key` or
206-
> `->get('key')`
206+
> `->{'key'}`
207207
> - Sites mid-migration can silence the echo (notices still reach error
208208
> logs): `SmartArrayBase::$onOffsetAccess = 'log';`
209209

src/DeprecatedAliases.php

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ trait DeprecatedAliases
4747
/**
4848
* Controls how deprecated `$array['key']` offset access is surfaced.
4949
*
50-
* Offset access (`[]` syntax) is deprecated in favor of property access
51-
* (`$array->key`) or the explicit `->get()` / `->set()` methods. This setting
50+
* Offset access (`[]` syntax) is deprecated in favor of property access:
51+
* `$array->key` for reads, `$array->key = $value` for writes, and brace
52+
* syntax (`$array->{'users.id'}`) for keys property syntax can't type. This setting
5253
* controls how the library signals that deprecation at runtime. It covers
5354
* reads, writes, unset(), and isset()/empty() checks alike; only the
5455
* property forms (`$array->key`, `isset($array->key)`) are signal-free.
@@ -178,6 +179,92 @@ public function sprintf(string $format): SmartArray
178179
return new SmartArray($newArray, $properties);
179180
}
180181

182+
/**
183+
* Returns the element at $key, same as $array->key, with an optional
184+
* default for missing keys.
185+
*
186+
* The default replaces missing keys only, never stored nulls. Smart keys
187+
* and defaults unwrap first; defaults then wrap for this array's mode the
188+
* same as a stored value would (arrays become same-mode SmartArrays).
189+
*
190+
* get('') is the only way to read an empty-string key: ->{''} is a PHP
191+
* fatal error.
192+
*
193+
* @deprecated Use property access: ->key, or ->{'users.id'} for keys property syntax
194+
* can't type. For a missing-key default use ->key ?? $default.
195+
*
196+
* @param int|string|SmartString|SmartNull $key The key to retrieve; Smart values unwrap first
197+
* @param mixed $default Returned when $key doesn't exist; treated like a stored value
198+
* @return static|SmartNull|SmartString|string|int|float|bool|null
199+
*/
200+
#[Deprecated(reason: "use property access ->key or ->{'key'}, with ?? for defaults")]
201+
public function get(int|string|SmartString|SmartNull $key, mixed $default = null): static|SmartNull|SmartString|string|int|float|bool|null
202+
{
203+
// Unwrap Smart keys, then coerce like PHP array keys: null reads key '', bool/float truncate to int
204+
if ($key instanceof SmartString || $key instanceof SmartNull) {
205+
$key = $key->value();
206+
$key = match (true) {
207+
is_int($key), is_string($key) => $key,
208+
is_null($key) => '',
209+
default => (int) $key,
210+
};
211+
}
212+
213+
// return default if key not found
214+
if (func_num_args() >= 2 && !array_key_exists($key, $this->data)) {
215+
// Defaults act like stored values: Smart defaults (SmartString,
216+
// SmartArray, SmartNull) unwrap to raw equivalents, then everything
217+
// wraps for this array's mode the same as a stored value would
218+
if ($default instanceof SmartBase || $default instanceof SmartString) {
219+
$default = self::getRawValue($default);
220+
}
221+
return match (true) {
222+
is_scalar($default), is_null($default) => $this->useSmartStrings ? new SmartString($default) : $default,
223+
is_array($default) => new static($default, $this->getInternalProperties()),
224+
default => throw new CallerException("Unsupported default value type: " . get_debug_type($default)),
225+
};
226+
}
227+
228+
// skip if empty
229+
if (empty($this->data)) {
230+
return $this->newSmartNull();
231+
}
232+
233+
// Return via getElement (no deprecation warning - Silent stage)
234+
if (array_key_exists($key, $this->data)) {
235+
return $this->getElement($key);
236+
}
237+
238+
// Show warning if key doesn't exist (only when no default provided)
239+
$this->warnIfMissing($key, 'offset');
240+
241+
return $this->newSmartNull();
242+
}
243+
244+
/**
245+
* Sets a value by key, same as $array->key = $value. Returns $this for
246+
* chaining.
247+
*
248+
* Smart values are unwrapped on storage: a SmartString stores its raw
249+
* value, a SmartArray stores as a child array of this array's mode, and
250+
* a SmartNull stores as null.
251+
*
252+
* set('') is the only way to write an empty-string key: ->{''} = $value
253+
* is a PHP fatal error.
254+
*
255+
* @deprecated Use property assignment: ->key = $value, or ->{'users.id'} = $value for keys property syntax can't type
256+
*
257+
* @param int|string $key The key to set
258+
* @param mixed $value The value to set
259+
* @return static Returns $this for method chaining
260+
*/
261+
#[Deprecated(reason: "use property assignment ->key = \$value or ->{'key'} = \$value")]
262+
public function set(int|string $key, mixed $value): static
263+
{
264+
$this->setElement($key, $value);
265+
return $this;
266+
}
267+
181268
//endregion
182269
//region Logged Aliases
183270

@@ -300,7 +387,7 @@ public function chunk(int $size): static
300387
/**
301388
* Sets a value in the SmartArray using array syntax.
302389
*
303-
* @deprecated Use ->set('key', $value) or ->key = $value instead of $array['key'] = $value
390+
* @deprecated Use ->key = $value or ->{'key'} = $value instead of $array['key'] = $value
304391
*
305392
* Note: If you add a key after the array is created the position properties will not be updated.
306393
* If needed you can recreate the array like this: $newArray = SmartArray::new($oldArray->toArray());
@@ -319,7 +406,7 @@ public function offsetSet(mixed $offset, mixed $value): void
319406
/**
320407
* Retrieves a value from the SmartArray using array syntax.
321408
*
322-
* @deprecated Use ->property or ->get('key') instead of $array['key']
409+
* @deprecated Use ->property or ->{'key'} instead of $array['key']
323410
*/
324411
public function offsetGet(mixed $offset): static|SmartNull|SmartString|string|int|float|bool|null
325412
{
@@ -371,10 +458,11 @@ private function triggerArrayAccessDeprecation(mixed $key, string $operation = '
371458
// a programmer error that PHP itself treats as an empty-string key.
372459
$suggestion = match ($operation) {
373460
'set' => match (true) {
374-
is_null($key) => '->set($key, $value) using an explicit key',
375-
is_int($key) => "->set($key, \$value)",
461+
is_null($key) => 'an explicit key: ->key = $value',
462+
is_int($key) => '->{' . $key . '} = $value',
376463
$isValidPropName => "->$key = \$value",
377-
default => "->set('$key', \$value) or ->{'$key'} = \$value",
464+
$key === '' => "->set('', \$value)", // ->{''} = $value is a fatal "Cannot access empty property"
465+
default => "->{'$key'} = \$value",
378466
},
379467
default => match (true) {
380468
is_int($key) => '->{' . $key . '}',

src/SmartArray.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,6 @@ public function asHtml(): SmartArrayHtml
107107
//endregion
108108
//region Value Access
109109

110-
/** {@inheritDoc} */
111-
public function get(int|string|SmartString|SmartNull $key, mixed $default = null): static|SmartNull|string|int|float|bool|null
112-
{
113-
// Must use func_num_args() check here and call parent appropriately,
114-
// because parent uses func_num_args() to detect if default was provided
115-
if (func_num_args() >= 2) {
116-
return parent::get($key, $default);
117-
}
118-
return parent::get($key);
119-
}
120-
121110
/** {@inheritDoc} */
122111
public function first(): static|SmartNull|string|int|float|bool|null
123112
{

0 commit comments

Comments
 (0)