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:
Let’s dive right in!
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!
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.
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!
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!
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:
$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.
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) Now that we’ve built our PHP dynamic object class, let’s look at some practical ways to use it:
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) 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) 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) Here are some best practices for working with your new dynamic objects:
$obj->property syntax is more readable in most cases.$obj[$variable] syntax is perfect when the key is determined at runtime.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) Dynamic objects are incredibly flexible, but this flexibility comes with a small performance cost. Here are some considerations:
PHP dynamic objects give you the perfect blend of structure and flexibility. With our PDAO implementation, you get:
$obj->property$obj["property"]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!
Tired of repetitive tasks eating up your time? Python can help you automate the boring stuff — from organizing files to scraping websites and sending…
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…
This website uses cookies.
View Comments
Why not add this to Github/packigist?