Podczas pracy nad jednym z ostatnich projektów, natknąłem się na problem tablic tylko do odczytu. PHP niestety nie oferuje takiej funkcjonalności, a bardzo by mi to ułatwiło pracę. Nie pozostało nic innego, jak napisanie własnego rozwiązania, które w moim przypadku sprawdziło się doskonale.

Wszystko opiera się o interfejs ArrayAccess, który pozwala na dostęp do klasy w taki sam sposób jak w przypadku tablic. Odpowiednio implementując metody interfejsu, jesteśmy w stanie zasymulować tablicę tylko do odczytu. Przykładowa klasa wygląda następująco:

final class ReadonlyArray implements ArrayAccess
{
    private $data = [];

    public function __construct(array $data) {
        $this->data = $data;
    }

    public function offsetExists($offset) {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset) {
        return $this->data[$offset];
    }

    public function offsetUnset($offset) {
        throw new Exception("This operation is forbidden");
    }

    public function offsetSet($offset, $value) {
        throw new Exception("This operation is forbidden");
    }
}

Zacznijmy od początku. Dzięki użyciu słowa kluczowego final, klasa została zabezpieczona przed dziedziczeniem. W ten sposób unikniemy nadpisania użytych metod. Następnie w konstruktorze przekazujemy do klasy właściwą tablicę. Kolejne dwie metody odpowiadają za sprawdzenie czy szukany klucz znajduje się w tablicy oraz za zwrócenie wartości dla szukanego klucza. Dla uproszenia przykładu pominąłem tutaj obsługę błędów związanych z dostępem do nieistniejącego klucza. Ostatnie dwie metody powodują, że otrzymaliśmy tablicę tylko do odczytu. Obie metody rzucą wyjątek w przypadku próby usunięcia elementu tablicy lub zmiany jego zawartości. Oczywiście nic nie stoi na przeszkodzie, aby zamiast wyjątku zignorować próbę usunięcia elementu lub zmiany jego wartości. Można również dodać logowanie, dzięki czemu dowiemy się jaki fragment kodu próbuje wykonać niedozwoloną operację.

Użycie powyższej klasy jest banalnie proste.

$myArray = new ReadonlyArray([
	'a' => 1,
	'b' => 2,
	'c' => 3
]);

echo $myArray['c']; // 3
var_dump(isset($myArray['a'])); // true
var_dump(isset($myArray['g'])); // false
$myArray['b'] = 4; // wyjątek
unset($myArray['b']); // wyjątek

Aktualną wersję klasy ReadonlyArray wraz z dokumentacją i testami znajdziecie na Githubie.