veroxis/typedaccessor

access raw data in a type-safe way

v1.0.0 2023-02-04 14:52 UTC

This package is auto-updated.

Last update: 2025-06-26 03:02:38 UTC


README

TypedAccessor is an interface defining common access methods for data objects like e.g. json. It allows to work with raw data in a type safe way.

Design

The interface is implemented for a class called ObjectBox.

use Veroxis\Typedaccessor\ObjectBox;

// there are a few ways to construct an ObjectBox
$data = [
    'foo' => 'bar',
    'null' => null,
    'int' => 42,
    'float' => 3.1415926535,
    'bool' => true,
    'array' => [],
    'object' => (object) [],
];
$json = '{"foo":"bar","null":null,"int":42,"float":3.1415926535,"bool":true,"array":[],"object":{}}';
$boxA = ObjectBox::new(); // contains (object) `[]` as default
$boxB = ObjectBox::from($data);
$boxC = ObjectBox::fromJson($json);

// data for the following examples
$box = ObjectBox::from([
    'foo' => 'bar',
    'a' => ['b' => 'c'],
]);

// methods in the pattern `asType` try to get a value and will return `null` if the found value is not of the expected type
$out = $box->asArray('foo'); // null
$out = $box->asBool('foo'); // null
$out = $box->asFloat('foo'); // null
$out = $box->asInt('foo'); // null
$out = $box->asObject('foo'); // null
$out = $box->asString('foo'); // (string) 'bar'

// methods in the pattern `tryAsType` try to get a value and will `throw` if the found value is not of the expected type
$out = $box->tryAsArray('foo'); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsBool('foo'); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsFloat('foo'); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsInt('foo'); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsObject('foo'); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsString('foo'); // (string) 'bar'

// multi-level access is also possible for all accessor methods
$out = $box->asInt(['a','b']); // null
$out = $box->asString(['a','b']); // (string) 'c'
$out = $box->tryAsInt(['a','b']); // throws `Veroxis\Typedaccessor\Exceptions\MismatchedType`
$out = $box->tryAsString(['a','b']); // (string) 'c'


// methods for writing are available too: they behave much in the same way
// it is possible to write to paths as long as they are reachable in the existing data
$box = ObjectBox::new();
$success = $box->setArray(['foo'], []); // (bool) `true`: now the underlying data is `['foo' => []]`
$success = $box->setArray(['bar',`baz`], []); // (bool) `false`: the path `['bar',`baz`]` did not exist
$box->trySetArray(['bar', 'baz'], []); // throws `Veroxis\Typedaccessor\Exceptions\PathDoesNotExist`

// to write multi-level the given path has to __exist__ and all keys have to be indexable (either `array` or `object`)
$box = ObjectBox::new();
$success = $box->setArray(['bar'], []); // (bool) `true`: now the underlying data is `['foo' => []]`
$success = $box->setArray(['bar',`baz`], []); // (bool) `true`: now the underlying data is `['bar' => ['baz' => []]]`
$box = ObjectBox::new();
$success = $box->trySetArray(['bar'], []); // `void`: now the underlying data is `['foo' => []]`
$success = $box->trySetArray(['bar',`baz`], []); // `void`: now the underlying data is `['bar' => ['baz' => []]]`


// as an escape hatch there are `raw` methods which are typed as `mixed`:
//   - `asRaw`
//   - `tryAsRaw`
//   - `setRaw`
//   - `trySetRaw`
class Foo {}
$box = ObjectBox::new();
$success = $box->setRaw(['foo'], new Foo()); // (bool) `true`: now the underlying data is `['foo' => object(Foo)]`


// last but not least there are a few utility methods
$box = ObjectBox::from(['foo' => 'bar', 'a' => ['b' => null]]);

$out = $box->exists('foo'); // (bool) `true`
$out = $box->exists('baz'); // (bool) `false`
$out = $box->exists(['a', 'b']); // (bool) `true`
$out = $box->exists(['baz', 'bar']); // (bool) `false`

$out = $box->isNull('foo'); // (bool) `false`
$out = $box->isNull(['a', 'b']); // (bool) `true`
$out = $box->isNull(['baz', 'bar']); // (bool) `false`

$out = $box->delete('baz'); // (bool) `false`: the path did not exist
$out = $box->delete(['baz', 'bar']); // (bool) `false`: the path did not exist
$out = $box->delete(['foo']); // (bool) `true`: now the underlying data is `['a' => ['b' => null]]`
$out = $box->delete(['a']); // (bool) `true`: now the underlying data is `[]`
$out = $box->delete(['a', 'b']); // (bool) `false`: the key `a` was already deleted

Internally the ObjectBox is making heavy use of references for efficiency.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.