承接 dmitrijs-brujevs/data-object 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

dmitrijs-brujevs/data-object

最新稳定版本:2.1.2

Composer 安装命令:

composer require dmitrijs-brujevs/data-object

包简介

Path-based in-memory data container with nested array access via slash-separated node paths.

README 文档

README

CI Latest Version PHP Version License: MIT

A lightweight, path-based in-memory data container for PHP 8.2+.

Stores data in a nested array and provides access via slash-separated node paths. No dependencies. No magic beyond what is documented.

Why this library?

  • You receive config, API responses, or form data as nested arrays and want clean, readable access without $data['user']['address']['city'] ?? null chains
  • You want dot/slash-path notation with isset-safe existence checks
  • You need a minimal, subclassable value object with no framework coupling

Requirements

  • PHP 8.2+
  • ext-json

Installation

composer require dmitrijs-brujevs/data-object

Quick Start

use DmitrijsBrujevs\DataObject\DataObject;

$obj = DataObject::fromArray([
    'user' => [
        'name'    => 'John',
        'role'    => 'admin',
        'address' => [
            'city'    => 'Riga',
            'country' => 'Latvia',
        ],
    ],
]);

$obj->get('user/name');              // "John"
$obj->get('user/address/city');      // "Riga"
$obj->has('user/role');              // true
$obj->is('user/role', 'admin');      // true
$obj->set('user/age', 30);
$obj->delete('user/role');

$obj->getOrDefault('user/email', 'n/a'); // "n/a" — key is absent

Creating an Instance

Use the explicit factory methods when the input format is known:

// from array
$obj = DataObject::fromArray(['key' => 'value']);

// from JSON string
$obj = DataObject::fromJson('{"key":"value"}');

// from PHP serialized string (objects disallowed — see Security section)
$obj = DataObject::fromSerialized(serialize(['key' => 'value']));

The constructor also accepts all three formats via auto-detection:

$obj = new DataObject(['key' => 'value']);
$obj = new DataObject('{"key":"value"}');
$obj = new DataObject(serialize(['key' => 'value']));

An InvalidArgumentException is thrown if the string is neither valid JSON nor a serialized array.

Path Notation

Each segment of a path corresponds to a key in the nested structure. The default delimiter is /, customisable per instance.

"user/address/city"  →  $data['user']['address']['city']
"config/db/port"     →  $data['config']['db']['port']

API Reference

get(string $node = ''): mixed

Returns the value at the given path.

  • Nested array → returned as a new DataObject instance (same concrete class)
  • Missing path → returns null
  • No arguments → returns $this (root)
$obj->get('user/name');    // "John"
$obj->get('user');         // DataObject { name: "John", ... }
$obj->get('user/missing'); // null
$obj->get();               // $this

null vs missing: get() returns null for both a missing path and an explicit null value. Use has() to distinguish them, or use getOrDefault().

getOrDefault(string $node, mixed $default = null): mixed

Returns the value at the given path, or $default if the node does not exist.

Unlike get(), this correctly distinguishes between a missing node and an explicit null value:

$obj->set('role', null);

$obj->getOrDefault('role', 'guest');    // null    — key exists, value is null
$obj->getOrDefault('missing', 'guest'); // "guest" — key does not exist

has(string $node): bool

Returns true if the node exists, regardless of its value. Uses array_key_exists semantics.

$obj->set('role', null);
$obj->has('role');    // true  — key exists, value is null
$obj->has('email');   // false — key is absent
$obj->has('');        // true  — root always exists

is(string $node, mixed $value): bool

Strict equality check (===). Returns false if the node does not exist.

$obj->set('score', 10);
$obj->set('role', null);

$obj->is('score', 10);      // true
$obj->is('score', '10');    // false — int !== string
$obj->is('role', null);     // true  — key exists, value is null
$obj->is('missing', null);  // false — key does not exist

Note: is('missing', null) returns false (changed in 2.1.0 — previously returned true). Use has() or getOrDefault() if the old behaviour was relied upon.

set(string|int $node, mixed $value): static

Sets a value at the given path. Intermediate nodes are created automatically. If an intermediate node holds a non-array value, it is replaced with an array. Returns $this for fluent chaining.

$obj->set('user/name', 'Jane');
$obj->set('user/address/zip', '1010');
$obj->set('user/role', null);

$obj->set('a', 1)->set('b', 2)->set('c', 3);

add(array $array, string $node = ''): static

Recursively merges a nested array into the object. Existing values at the same paths are overwritten. Empty arrays are stored as-is — get() on that path returns an empty DataObject.

$obj->add(['user' => ['name' => 'John', 'age' => 30]]);
$obj->get('user/name'); // "John"

// with a path prefix
$obj->add(['host' => 'localhost', 'port' => 3306], 'config/db');
$obj->get('config/db/host'); // "localhost"

// empty array is stored, not ignored
$obj->add(['tags' => []]);
$obj->has('tags');  // true
$obj->get('tags');  // DataObject (empty)

delete(string $node): static

Removes the value at the given path. Only the final segment is removed; parent nodes and siblings remain intact. No-op for non-existent paths.

$obj->delete('user/role');
$obj->has('user/role');  // false
$obj->get('user/name');  // "John" — siblings intact

$obj->delete('user/age')->delete('user/email'); // fluent

toArray(): array

Returns the internal storage as a plain nested PHP array.

$obj->toArray();
// ['user' => ['name' => 'John', 'age' => 30]]

toJson(int $flags, int $depth): string

Returns the data encoded as a JSON string. Default flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES.

$obj->toJson();
// {"user":{"name":"Иван","url":"https://example.com"}}

$obj->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

serialize(): string

Returns the data as a PHP serialized string.

$serialized = $obj->serialize();
$restored   = DataObject::fromSerialized($serialized);

Magic camelCase Methods

Any method prefixed with get, set, has, is, or delete is resolved by __call() at runtime. The name is split on uppercase letters and joined with the delimiter to form a path.

$obj->getUserName()           // → get('user/name')
$obj->setUserName('Jane')     // → set('user/name', 'Jane')
$obj->hasUserRole()           // → has('user/role')
$obj->isUserRole('admin')     // → is('user/role', 'admin')
$obj->deleteUserRole()        // → delete('user/role')
$obj->getUserAddressCity()    // → get('user/address/city')

Unknown prefixes throw BadMethodCallException (changed in 2.1.0 — previously returned null).

Limitations:

  • Consecutive uppercase letters are split per character: getURLPath()get('u/r/l/path'). Use explicit path strings for such keys.
  • Multi-word method names (getOrDefault) cannot be dispatched via magic — the parser only recognises single-word prefixes.

Iteration

DataObject implements Iterator. Nested arrays are automatically wrapped in DataObject instances at each level.

foreach ($obj as $key => $value) {
    // $value is DataObject if the element is a nested array
}

// nested foreach works across any depth
foreach ($obj as $continent => $countries) {
    foreach ($countries as $country => $info) {
        echo $info->get('capital');
    }
}

Custom Delimiter

$obj = DataObject::fromArray(['user' => ['name' => 'John']], delimiter: '.');

$obj->get('user.name'); // "John"
$obj->set('user.age', 30);

Subclassing

DataObject is designed to be subclassed. All factory methods and internal wrapping use new static(), so nested arrays and iterator values are wrapped in the concrete subclass.

class UserObject extends DataObject {}

$user = UserObject::fromArray(['name' => 'John', 'roles' => ['admin', 'editor']]);
$user->get('roles'); // UserObject instance, not DataObject

Public method overrides in subclasses take full precedence — PHP routes them directly without going through __call().

Security — Serialized Input

fromSerialized() and the constructor's auto-detection both use unserialize($data, ['allowed_classes' => false]).

This means:

  • No PHP objects are instantiated during deserialization — gadget-chain attacks are blocked
  • Only arrays are accepted; anything else throws InvalidArgumentException
  • PHP warnings from malformed strings are caught via a temporary error handler, not @

Only pass serialized data from sources you control. Even with allowed_classes = false, deserializing untrusted user input is not recommended practice.

Running Locally

composer install

composer test      # PHPUnit
composer stan      # PHPStan level 8
composer cs        # PHP-CS-Fixer (dry-run)
composer cs:fix    # PHP-CS-Fixer (apply)
composer check     # stan + cs + test

License

MIT

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 6
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-12

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固