isProp($offset) || isset($this->customData[$offset]); } public function offsetGet(mixed $offset): mixed { if ($this->isProp($offset)) { return $this->{$offset}; } return $this->customData[$offset]; } public function offsetSet(mixed $offset, mixed $value): void { if ($this->isProp($offset)) { $this->{$offset} = $value; } $this->customData[$offset] = $value; } public function offsetUnset(mixed $offset): void { if ($this->isProp($offset)) { throw new UnsupportedOperationException('Cannot unset property!'); } unset($this->customData[$offset]); } private function isProp(mixed $offset): bool { return is_string($offset) && $this->_publicPropertyExists($offset); } private function _publicPropertyExists(string $property): bool { static $properties = null; if ($properties === null) { $rc = new \ReflectionClass(self::class); $properties = array_map( fn (\ReflectionProperty $p) => $p->getName(), $rc->getProperties(\ReflectionProperty::IS_PUBLIC) ); } return in_array($property, $properties); } }