Комментарии 24
Здесь основной посыл статьи не в том, как тесты в итоге реализовать пришлось, а в том, что с помощью Closure::bind() действительно можно сломать то, что якобы сломать невозможно.
Если мы заменим код на такой:
$createNewInstance = function () {
$instance = new self();
self::$instance = $instance;
return $instance;
};
то мы вовсе заменим оригинальный объект новым.
Что-то здесь действительно не так, мы с помощью этого функционала можем вообще влезть в логику любого класса, работать с приватными свойствами и методами. Понятие инкапсуляция теперь очень размытое.
Следующим что будет, добавление новых методов в классы? :)
Такие вещи часто нужны чтобы не выдавать наружу все кишки объекта для инфраструктурных задач типа (де)сериализации, (де)гидратации, ленивой загрузки и того же тестирования. Они позволяют не иметь в объекте сеттеров, не нарушать SRP и т. п.
Не совсем понятно чем рефлексия не подошла. Если только, как указали ниже, ради производительности
перед запуском сеттера вы можете убедиться что начальное состояние задано в дефолт через геттер.
тут вообще не нужно лезть в потроха класса, если там приватные переменные вас как вызывающего вообще не должно волновать что там внутри. сделайте black-box тестирование на этот класс и все
Зачем нужны модульные тесты, которые взаимодействуют с объектом совершенно иным образом, чем это делает клиентский код?
Но при этом лезть в класс как это делал автор поста вполне может понадобиться — когда это легаси код рефакторить который сейчас слишком дорого, а его тестируемость хромает, то это может оказаться самым быстрым, простым и в итоге правильным способом протестировать что-либо. Например когда как в посте нужно проверить поведение в определенном состоянии.
Обычными сеттерами нельзя было пользоваться, так как там была прописана некая логика. Унаследовать или замокать класс тоже не получалось, потому что он объявлён финальным. И даже рефлексия не подошла.
ЧЯДНТ:
final class Foo {
private $prop;
public function __construct() {
$this->prop = 1;
}
public function getProp() {
return $this->prop;
}
}
$foo = new Foo();
$fooR = new ReflectionObject($foo);
$prop = $fooR->getProperty('prop');
$prop->setAccessible(true);
$prop->setValue($foo, 2);
echo $foo->getProp();
вместо self — static?
Ломаем паттерн проектирования — Singleton в PHP