Skip to content

Instantly share code, notes, and snippets.

@priyadi
Created January 21, 2022 06:30
Show Gist options
  • Save priyadi/9f70fc46147921cd5b5154eb888e4903 to your computer and use it in GitHub Desktop.
Save priyadi/9f70fc46147921cd5b5154eb888e4903 to your computer and use it in GitHub Desktop.
A wrapper to make a PHP object read only recursively.
<?php
namespace Rekalogika\Util;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* Make an object read only by recursively only allowing get*, is*, has*
* methods, and specifically for Doctrine Collection: contains, containsKey,
* get, getKeys, getValues, isEmpty, first, last, count.
*
* This assumes all the getter methods are really getters, and will not change
* the object's attributes.
*
* If the method outputs an array, it will be converted into an ArrayCollection.
*
* The intended usage is to safely feed an object as an input for Symfony
* Expression Language, and prevent it from being modified by the expression.
*
* @author Priyadi Iman Nurcahyo <priyadi@rekalogika.com>
*/
final class ReadOnly
{
private $object;
const COLLECTION_ALLOW = [
'contains',
'containsKey',
'get',
'getKeys',
'getValues',
'isEmpty',
'first',
'last',
'count'
];
public function __construct($object)
{
if (!is_object($object)) {
throw new \InvalidArgumentException('Input is not an object');
}
$this->object = $object;
}
/**
* __call magic method
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call(string $method, array $args)
{
if ($this->object instanceof Collection) {
if (!in_array($method, self::COLLECTION_ALLOW)) {
throw new \BadMethodCallException('Method is blocked: ' . $method);
}
} else {
if (!preg_match('/^(get|is|has)[A-Z]/', $method)) {
throw new \BadMethodCallException('Method is blocked: ' . $method);
}
if (count($args) > 0) {
throw new \BadMethodCallException('Using arguments is prohibited: ' . $method);
}
}
$output = call_user_func_array([$this->object, $method], $args);
if (is_object($output)) {
return new self($output);
} elseif (is_array($output)) {
return new self(new ArrayCollection($output));
} else {
return $output;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment