PHP DataBlock is designed to improve the developer experience when working with complex and deeply nested PHP arrays.
If you work with PHP long enough, you will eventually fight an array.
Not because arrays are bad, but because nested, dynamic arrays are everywhere:
- API responses
- Configuration files
- Decoded JSON or YAML
- Shared data structures with third-party libraries
And they usually come with the same problems:
- Deep
isset()chains - Missing keys causing subtle bugs
- Manual type casting
- Unclear defaults
This is where DataBlock comes in.
GitHub repo & installation:
https://github.com/Hi-Folks/data-block
What is DataBlock?
DataBlock is a small PHP library that wraps arrays into a safer, more expressive object called a Block.
The DataBlock PHP library doesn’t replace arrays; it improves how you access, read, and handle them.
With DataBlock, you get:
- Dot-notation access to nested data
- Typed getters (
getInt(),getString(), etc.) - Safe defaults
- Better readability
- Easy data inspection and export
Let’s see why that matters.
Installing and using the Block class
To add DataBlock to your projects and start using the Block class with its methods and helpers, you can run the composer require command:
composer require hi-folks/data-block
To support the development, you can "star" ⭐ the repository: https://github.com/Hi-Folks/data-block
Then, in your PHP files, you can import the HiFolks\DataType\Block Namespace:
use HiFolks\DataType\Block;
The simple example dataset
For the examples included in this article, we are going to use a small dataset representing fruits:
use HiFolks\DataType\Block;
$fruitsArray = [
"avocado" => [
"name" => "Avocado",
"fruit" => "🥑",
"wikipedia" => "https://en.wikipedia.org/wiki/Avocado",
"color" => "green",
"rating" => 8,
],
"apple" => [
"name" => "Apple",
"fruit" => "🍎",
"wikipedia" => "https://en.wikipedia.org/wiki/Apple",
"color" => "red",
"rating" => 7,
],
"banana" => [
"name" => "Banana",
"fruit" => "🍌",
"wikipedia" => "https://en.wikipedia.org/wiki/Banana",
"color" => "yellow",
"rating" => 8.5,
],
"cherry" => [
"name" => "Cherry",
"fruit" => "🍒",
"wikipedia" => "https://en.wikipedia.org/wiki/Cherry",
"color" => "red",
"rating" => 9,
],
];
$data = Block::make($fruitsArray);
Once the array is wrapped in a Block object, the focus shifts.
Instead of worrying about how the data is structured and whether every key exists, you can concentrate on what you want to read from it.
Safe access without isset()
Accessing nested arrays in plain PHP often ends up looking like this:
$color = $array['avocado']['color'] ?? null;
This works, but it doesn’t scale very well.
As soon as the data becomes more complex or dynamic, these checks start spreading across the codebase, making it harder to read and maintain.
With DataBlock, accessing nested values becomes more direct and expressive:
$data->get("avocado.color");
You can safely retrieve values using dot notation, even when the structure is deeply nested:
var_dump(
$data->get("avocado.color"),
$data->get("avocado.rating"),
$data->get("banana.rating"),
$data->get("apple.notexists"),
);
Output:
string(5) "green"
int(8)
float(8.5)
NULL
Missing keys are handled gracefully, without triggering warnings or notices.
There’s no need for defensive isset() checks or fallback logic scattered throughout the code, just clear, predictable access to the data you need.
Typed getters and defaults
Arrays, by nature, don’t express intent.
When you read a value from an array, it’s not always clear what type that value is supposed to be, or what should happen if it’s missing.
DataBlock makes this explicit.
Instead of retrieving a value and then casting or validating it later, DataBlock allows you to express your expectations directly at the point of access:
var_dump(
$data->getInt("avocado.rating"),
$data->getInt("banana.rating"),
$data->getFloat("banana.rating"),
$data->get("apple.notexists", "MY_DEFAULT"),
);
Output:
int(8)
int(8)
float(8.5)
string(10) "MY_DEFAULT"
Here, the method name clearly communicates the expected type.
A numeric value is automatically cast to an integer or a float, while missing fields can fall back to a sensible default, all in a single, readable expression.
Why this matters:
-
getInt()andgetFloat()make your intent explicit and self-documenting - Type casting happens consistently and close to the data source
- Default values are handled inline, without extra conditional logic
This approach is especially valuable when working with external or untrusted data, such as API responses, configuration files, or decoded JSON, where types and the presence of fields cannot always be guaranteed.
Working with subsets of data
In some cases, you don’t just need a single value; you want to work with an entire section of the dataset.
DataBlock allows you to retrieve a whole branch of the structure in a straightforward way:
$avocado = $data->get("avocado");
In this case, you receive the underlying array:
[
"name" => "Avocado",
"fruit" => "🥑",
"wikipedia" => "...",
"color" => "green",
"rating" => 8
]
This is useful when you want to pass the data to existing code that expects a plain PHP array.
Alternatively, if you want to keep working within the DataBlock API, you can retrieve the same branch as a Block instance:
$avocadoBlock = $data->getBlock("avocado");
By using the getBlock() method, you retain all the benefits of DataBlock, dot notation for nested access, typed getters, and the ability to inspect available keys, while focusing only on the portion of the data you’re interested in.
This makes it easy to navigate complex structures step by step, without losing context or switching mental models.
Discovering what’s inside your data
When dealing with dynamic or loosely defined data structures, simply knowing what keys are available can make a big difference.
DataBlock provides a simple way to inspect the structure of your dataset by listing its keys:
$data->keys();
Result:
["avocado", "apple", "banana", "cherry"]
This provides an immediate overview of the top-level elements, which is especially helpful when working with data from external sources.
The same approach also works for nested data. You can inspect the keys of a specific branch by retrieving it as a Block:
$data->getBlock("avocado")->keys();
Result:
["name", "fruit", "wikipedia", "color", "rating"]
Being able to explore the available keys at each level makes DataBlock particularly useful for debugging, data exploration, and building dynamic behaviors such as conditional rendering or automatic mappings.
Exporting data to YAML (and more)
Beyond reading and navigating data, DataBlock can also help with data representation and serialization.
In addition to JSON (method toJson()), DataBlock allows you to serialize your data into YAML, which is often easier to read and reason about, especially for configuration and inspection purposes:
echo $data->toYaml();
Output:
avocado:
name: Avocado
fruit: 🥑
wikipedia: https://en.wikipedia.org/wiki/Avocado
color: green
rating: 8
apple:
name: Apple
fruit: 🍎
wikipedia: https://en.wikipedia.org/wiki/Apple
color: red
rating: 7
...
Being able to convert a structured dataset into a human-readable or machine-readable format quickly is helpful in many everyday scenarios.
This includes generating configuration files, producing clear debug output, or sharing data with tools and systems that work naturally with YAML.
When should you use PHP DataBlock?
DataBlock is a great fit when:
- You consume API responses.
- You parse JSON/YAML
- You deal with deeply nested arrays.
- You want safer, clearer PHP code.
It doesn’t try to replace domain models; it simply makes raw data easier and safer to work with.
Arrays vs DataBlock vs DTOs: which one should you use?
When working with data in PHP, there are usually three common approaches:
- Plain PHP arrays
- DataBlock
- DTOs (Data Transfer Objects)
Each one has a purpose; the key is knowing when to use which.
1) Plain PHP Arrays
The default/first choice.
Arrays are flexible, fast, and built into the language.
$rating = $data['avocado']['rating'] ?? null;
Pros
- Native PHP feature
- Zero dependencies
- Very flexible
Cons
- No type safety
- Verbose access for nested data
- Easy to introduce silent bugs
- Hard to understand intent
- Defensive code everywhere
Best for
- Very small scripts
- Simple, flat data
- Performance-critical hot paths
Once data becomes nested or dynamic, arrays tend to leak complexity everywhere.
2) DataBlock
A safer abstraction over arrays.
DataBlock keeps the flexibility of arrays while adding structure and intent.
$rating = $data->getInt("avocado.rating");
Pros
- Safe nested access (no warnings)
- Typed getters
- Defaults built in
- Highly readable
- Great for untrusted or dynamic data
Cons
- Still runtime-typed (not compile-time)
- Not a domain model
- Adds a small dependency
Best for
- API responses
- Configuration files
- JSON / YAML parsing
- Boundary layers (input/output)
- Rapid prototyping without sacrificing safety
Think of DataBlock as a smart boundary object between raw data and your application logic.
3) DTOs (Data Transfer Objects)
The most explicit and strict option.
DTOs define exactly what your data looks like.
final class FruitDTO
{
public function __construct(
public string $name,
public string $color,
public float $rating
) {}
}
Pros
- Strong typing
- IDE autocompletion
- Self-documenting
- Great for domain logic
- Easier to refactor safely
Cons
- Verbose
- Requires mapping
- Rigid structure
- Overkill for dynamic data
Best for
- Domain models
- Business logic
- Internal application state
- Long-lived data structures
DTOs shine inside your application, where structure is stable, and rules matter.
The key difference is where you use them
| Layer | My opinionated recommendation |
|---|---|
| External input (API, JSON, YAML) | DataBlock |
| Simple scripts | Array |
| Domain logic | DTO |
| Configuration | DataBlock |
| Public interfaces | DTO |
Recap: why DataBlock exists
DataBlock doesn’t compete with DTOs; it complements them.
It lives in the uncomfortable middle ground where:
- Data is messy
- Structure is mostly known
- Safety matters
- Full modeling would be too heavy.
If arrays are too loose and DTOs are too strict, DataBlock is the pragmatic middle layer.
Final thoughts
PHP arrays are powerful, but they’re also easy to misuse.
DataBlock is designed to improve the developer experience when working with complex, nested, and structured datasets by providing a fluent and expressive way to access and handle data.
Less boilerplate, fewer bugs, more readable code.
Check it out on GitHub:
https://github.com/Hi-Folks/data-block
Top comments (3)
The library looks like an http client and Laravel Collections had a child.
So for projects that are using Laravel, this library brings nothing new.
For the projects that use another framework and want the fluent API methods, they could add Collections, because most frameworks provide an http client.
What is left are projects that are only using libraries.
It is a well put together library, I think the features it brings are already solved with other solutions.
Hi @xwero Thanks for the thoughtful comment! I actually agree with most of what you wrote.
I’ve never used DataBlock in a Laravel context myself, precisely because Laravel already provides excellent tools like Collections, Arr / Str helpers, and a very good HTTP client. In that ecosystem, DataBlock wouldn’t add much value, and adopting it wouldn’t really make sense.
Like most packages, DataBlock is very context-dependent.
The situations where I personally use it are quite different: plain PHP scripts, small tools built with Symfony Console, or lightweight projects where I don’t want to pull in a full framework just to get a fluent way of handling data.
In those cases, I often deal with:
DataBlock is not meant to compete with Laravel Collections.
Collections are great for working with lists of items, transformations, and pipelines. DataBlock focuses more narrowly on safe and expressive access to nested associative structures, typed getters, and inspection of arbitrary data trees.
In the article, I used a simple array-based example mainly to introduce the basic concepts and APIs that DataBlock provides. I’m aware that this can make the overlap with existing tools seem larger than it actually is. In future articles or examples, I plan to explore different scenarios that better reflect the contexts where I personally find DataBlock useful, such as handling nested JSON responses, configuration files, or YAML documents in small PHP tools or console applications.
So I see it less as “another Collection” and more as a small utility that sits at the boundary between raw data and application logic, especially outside of full-stack frameworks.
That said, you’re absolutely right: many of the problems it solves are already addressed by other tools, and adopting any library should always be a conscious decision based on the project’s needs and context.
Thanks again for the constructive feedback! Much appreciated 👍
Interesting, I did something similar in the past and nowadays I would rename
$data->getInt()into$data->getIntLax()(without a warning) and add a new$data->getInt()(with a php warning for missing keys) because it saved me so many times in debugging if I know what is broken. 😋