
I’ve been working with PHP for years now, and one thing I’ve always loved is its flexibility. But sometimes, you need even more flexibility than standard PHP objects provide. That’s where PHP dynamic object comes in!
Let me ask you this: Have you ever worked with data that doesn’t quite fit into your neatly defined classes? Maybe you’re consuming an API that sometimes returns extra fields. Or perhaps you’re building a system where data attributes change frequently. Traditional PHP objects with predefined properties just don’t cut it in these scenarios.
In this guide, I’ll show you how to create the ultimate PHP dynamic object that combines the best of both worlds: the structure of a class with the flexibility of dynamic data. You’ll learn how to:
- Access properties dynamically
- Use array-like syntax for data manipulation
- Handle multi-dimensional arrays elegantly
- Iterate through your object’s properties
- Convert your object back to a pure array
Let’s dive right in!
The Limitations of Standard PHP Classes
First, let’s look at a typical PHP class we might write:
class MyObj {
private $name;
private $occupation;
public function getName() {
return $this->name;
}
public function getOccupation() {
return $this->occupation;
}
public function setName($name) {
$this->name = $name;
}
public function setOccupation($occupation) {
$this->occupation = $occupation;
}
public function __construct($name = "", $occupation = "") {
$this->name = $name;
$this->occupation = $occupation;
}
}Code language: PHP (php)This works perfectly when you know exactly what properties you need. But what happens when you need to store additional, unknown data? What if you need to treat your object like an array sometimes?
That’s where our PHP Dynamic Access Object (PDAO) comes in!
Building a PHP Dynamic Object
Step 1: Creating Property Access Capability
While PHP objects have property access by default, we need to implement it in a way that stores data consistently for all access methods. We’ll use PHP’s magic methods to achieve this:
class PDAO {
/**
* @var array data
*/
protected $data = array();
/**
* Checks whether a key is set on this object.
*
* @param string $key The key.
* @return bool
*/
public function __isset($key) {
return isset($this->data[$key]);
}
/**
* Get the value of a key in this object.
*
* @param string $key The key.
* @return mixed
*/
public function __get($key) {
if(isset($this->data[$key])) {
return $this->data[$key];
} else {
return null;
}
}
/**
* Set the value of a key in this object.
*
* @param string $key The key.
* @param string $value The value.
*/
public function __set($key, $value) {
if ($key === null) {
$this->data[] = $value;
} else {
$this->data[$key] = $value;
}
}
/**
* Unset a property in this object
* @param string $name The property name
*/
public function __unset($name) {
if(isset($this->data[$name])) {
unset($this->data[$name]);
}
}
}Code language: PHP (php)With these methods implemented, we can now use our object like this:
$obj = new PDAO();
$obj->country = "United States";
echo $obj->country; // Outputs: United StatesCode language: PHP (php)The magic happens because when you access $obj->country, PHP calls the __get() method behind the scenes. When you assign a value with $obj->country = "United States", PHP calls __set(). All this data gets stored in our $data array.
Step 2: Adding Array-Like Access
To make our object even more flexible, let’s add array-like access. We can implement PHP’s ArrayAccess interface to achieve this:
class PDAO implements \ArrayAccess {
// Previous code...
/**
* Check if a key exists
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset) {
return $this->__isset($offset);
}
/**
* Get value for a key
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset) {
return $this->__get($offset);
}
/**
* Set value for a key
* @param mixed $offset
* @param mixed $data
*/
public function offsetSet($offset, $data) {
$this->__set($offset, $data);
}
/**
* Unset a key
* @param mixed $offset
*/
public function offsetUnset($offset) {
unset($this->data[$offset]);
}
}Code language: PHP (php)Now we can access our object using array syntax:
$obj = new PDAO();
$obj["city"] = "Montreal";
echo $obj["city"]; // Outputs: MontrealCode language: PHP (php)The beauty of this approach is that both property access ($obj->city) and array access ($obj["city"]) work interchangeably!
Step 3: Supporting Multi-Dimensional Arrays
But there’s a problem: if we try to access multi-dimensional data:
$obj["skills"]["programming"] = "PHP";Code language: PHP (php)We’ll get an error like:
Notice: Indirect modification of overloaded element of PDAO has no effectCode language: HTTP (http)To fix this, we need to modify our offsetSet method to handle arrays:
/**
* Set value for a key
* @param mixed $offset
* @param mixed $data
*/
public function offsetSet($offset, $data) {
// Convert arrays to PDAO objects automatically
if (is_array($data)) {
$data = new self($data);
}
$this->__set($offset, $data);
}
/**
* Constructor - Create a new object, or load from existing data
*
* @param array $data Initial data array
*/
public function __construct(array $data = array()) {
foreach ($data as $key => $value) {
$this[$key] = $value;
}
}
/**
* Handle cloning properly
*/
public function __clone() {
foreach ($this->data as $key => $value) {
if ($value instanceof self) {
$this[$key] = clone $value;
}
}
}Code language: PHP (php)Now the multi-dimensional array access works perfectly!
Step 4: Adding Iteration Capability
To make our object fully functional, we need to add iteration capabilities. This allows us to loop through our object’s properties like we would with an array:
class PDAO implements \ArrayAccess, \Iterator {
// Previous code...
/**
* Reset iterator position
* @return mixed
*/
function rewind() {
return reset($this->data);
}
/**
* Return current data
* @return mixed
*/
function current() {
return current($this->data);
}
/**
* Return current key
* @return mixed
*/
function key() {
return key($this->data);
}
/**
* Move to next element
* @return mixed
*/
function next() {
return next($this->data);
}
/**
* Check if current position is valid
* @return bool
*/
function valid() {
return key($this->data) !== null;
}
}Code language: PHP (php)With these methods implemented, we can now iterate through our object:
Step 5: Converting Back to a Pure Array
$obj = new PDAO();
$obj->name = "John";
$obj->age = 30;
$obj->skills = ["PHP", "JavaScript", "MySQL"];
foreach ($obj as $key => $value) {
echo "$key: " . (is_array($value) ? implode(", ", $value) : $value) . "<br>";
}Code language: PHP (php)Finally, let’s add a method to convert our object back to a pure PHP array:
/**
* Get the internal data as a pure array
*
* @return array
*/
public function toArray() {
$data = $this->data;
foreach ($data as $key => $value) {
if ($value instanceof self) {
$data[$key] = $value->toArray();
}
}
return $data;
}Code language: PHP (php)This method recursively converts any nested PDAO objects back to arrays, giving you a pure PHP array.
The Complete PHP Dynamic Object Implementation
Here’s the complete implementation of our PHP Dynamic Access Object:
<?php
/**
* PHP Dynamic Access Object (PDAO)
* A flexible object with property, array, and iterative access
*/
class PDAO implements \ArrayAccess, \Iterator {
/**
* @var array Internal data storage
*/
protected $data = array();
/**
* Constructor - Create a new object or load from existing data
*
* @param array $data Initial data array
*/
public function __construct(array $data = array()) {
foreach ($data as $key => $value) {
$this[$key] = $value;
}
}
/**
* Checks whether a key exists
*
* @param string $key The key
* @return bool
*/
public function __isset($key) {
return isset($this->data[$key]);
}
/**
* Get the value of a key
*
* @param string $key The key
* @return mixed
*/
public function __get($key) {
if(isset($this->data[$key])) {
return $this->data[$key];
} else {
return null;
}
}
/**
* Set the value of a key
*
* @param string $key The key
* @param mixed $value The value
*/
public function __set($key, $value) {
if ($key === null) {
$this->data[] = $value;
} else {
$this->data[$key] = $value;
}
}
/**
* Unset a property
*
* @param string $name The property name
*/
public function __unset($name) {
if(isset($this->data[$name])) {
unset($this->data[$name]);
}
}
/**
* Check if a key exists (ArrayAccess implementation)
*
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset) {
return $this->__isset($offset);
}
/**
* Get value for a key (ArrayAccess implementation)
*
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset) {
return $this->__get($offset);
}
/**
* Set value for a key (ArrayAccess implementation)
*
* @param mixed $offset
* @param mixed $data
*/
public function offsetSet($offset, $data) {
// Convert arrays to PDAO objects automatically
if (is_array($data)) {
$data = new self($data);
}
$this->__set($offset, $data);
}
/**
* Unset a key (ArrayAccess implementation)
*
* @param mixed $offset
*/
public function offsetUnset($offset) {
unset($this->data[$offset]);
}
/**
* Reset iterator position (Iterator implementation)
*
* @return mixed
*/
function rewind() {
return reset($this->data);
}
/**
* Return current data (Iterator implementation)
*
* @return mixed
*/
function current() {
return current($this->data);
}
/**
* Return current key (Iterator implementation)
*
* @return mixed
*/
function key() {
return key($this->data);
}
/**
* Move to next element (Iterator implementation)
*
* @return mixed
*/
function next() {
return next($this->data);
}
/**
* Check if current position is valid (Iterator implementation)
*
* @return bool
*/
function valid() {
return key($this->data) !== null;
}
/**
* Handle cloning properly
*/
public function __clone() {
foreach ($this->data as $key => $value) {
if ($value instanceof self) {
$this[$key] = clone $value;
}
}
}
/**
* Get the internal data as a pure array
*
* @return array
*/
public function toArray() {
$data = $this->data;
foreach ($data as $key => $value) {
if ($value instanceof self) {
$data[$key] = $value->toArray();
}
}
return $data;
}
}Code language: HTML, XML (xml)Practical Use Cases for PHP Dynamic Objects
Now that we’ve built our PHP dynamic object class, let’s look at some practical ways to use it:
1. Handling API Responses
When working with third-party APIs, the data structure can sometimes be unpredictable. Our PDAO makes handling these responses much easier:
// Convert API JSON response to PDAO
$apiResponse = json_decode($jsonResponse, true);
$userProfile = new PDAO($apiResponse);
// Access data using dot notation
echo $userProfile->user->name;
echo $userProfile["user"]["email"];
// Add new data dynamically
$userProfile->preferences = ["theme" => "dark", "notifications" => true];Code language: PHP (php)2. Template Systems
Dynamic objects work wonderfully for template systems:
$template = new PDAO();
$template->title = "My Awesome Page";
$template->user = ["name" => "John", "role" => "Admin"];
$template->navigation = $navItems;
// In your template file
echo "Welcome, {$template->user->name}!";Code language: PHP (php)3. Configuration Management
Store configuration data in a flexible way:
$config = new PDAO();
$config->database = [
"host" => "localhost",
"username" => "root",
"password" => "",
"dbname" => "myapp"
];
$config->paths = [
"uploads" => "/var/www/uploads",
"cache" => "/var/www/cache"
];
// Access config values
$dbHost = $config->database->host;
$uploadPath = $config->paths->uploads;Code language: PHP (php)Tips for Working with PHP Dynamic Objects
Here are some best practices for working with your new dynamic objects:
- Use property access when code clarity is a priority – The
$obj->propertysyntax is more readable in most cases. - Use array access when working with dynamic keys – The
$obj[$variable]syntax is perfect when the key is determined at runtime. - Be cautious with references – Dynamic objects can behave differently than standard arrays when passed by reference.
- Use toArray() before serialization – Convert your object to a pure array before JSON encoding or serialization.
- Type-check before operations – When retrieving values, check if they’re the expected type before performing operations.
Advanced Usage: Extending the PDAO
You can extend our PDAO class to add domain-specific functionality while keeping all the dynamic features:
class User extends PDAO {
public function getFullName() {
return $this->firstName . ' ' . $this->lastName;
}
public function hasRole($role) {
if (!isset($this->roles)) {
return false;
}
return in_array($role, $this->roles);
}
}
$user = new User();
$user->firstName = "John";
$user->lastName = "Doe";
$user->roles = ["admin", "editor"];
echo $user->getFullName(); // John Doe
echo $user->hasRole("admin") ? "Yes" : "No"; <em>// Yes</em>Code language: PHP (php)Performance Considerations
Dynamic objects are incredibly flexible, but this flexibility comes with a small performance cost. Here are some considerations:
- Memory Usage: Dynamic objects typically use more memory than plain arrays or strictly typed objects.
- Access Speed: Property access through magic methods is slightly slower than direct property access.
- Optimization: For performance-critical applications, convert to arrays for bulk operations and convert back to dynamic objects when needed.
Conclusion
PHP dynamic objects give you the perfect blend of structure and flexibility. With our PDAO implementation, you get:
- Property access with
$obj->property - Array access with
$obj["property"] - Multi-dimensional data support
- Iteration capabilities
- Easy conversion to pure arrays
They’re perfect for handling unpredictable data structures, working with APIs, building flexible configuration systems, and much more.
The next time you find yourself working with data that doesn’t quite fit into a standard class structure, give our PHP dynamic object a try. It might just be the perfect solution to your problem!
Have you used dynamic objects in your PHP projects? Share your experiences in the comments below!
Want to level up your PHP expertise? Explore more PHP Tutorials!
Discover more from CodeSamplez.com
Subscribe to get the latest posts sent to your email.

Why not add this to Github/packigist?