Verification: a143cc29221c9be0

Php and object and this

Краткое описание класса WeakMap

final class WeakMap implements ArrayAccess, Countable, IteratorAggregate, Traversable {
public function offsetGet(object $object);
public function offsetSet(object $object, mixed $value): void;
public function offsetExists(object $object): bool;
public function offsetUnset(object $object): void;
public function count(): int;
}

Подробнее о Weak Maps

Слабые карты могут помочь связать временные данные с объектами без изменения состояния самого объекта. Если объект выпадает из области видимости, то же самое происходит и с соответствующими данными.

На первый взгляд слабые карты немного сложно понять, обычно, когда вы создаете объект и назначаете его переменной, происходит так, что объект создается в памяти, а затем создается переменная как ссылка на него. Думайте об этом как о переменной, имеющей только идентификатор объекта, а не о самом объекте. Если вы назначите другую переменную тому же объекту, останется только один объект, но теперь есть две переменные с идентификатором объекта. 


$a = new Foo();
$b = $a;

//$b and $a теперь отдельные переменные
//оба указывают на объект Foo где-то в памяти.

Каждый раз, когда переменная удаляется, PHP проверяет, есть ли другие переменные, которые все еще ссылаются на этот объект. Если их нет, он знает, что можно удалить этот объект за вас. Этот процесс называется «сборкой мусора», и я здесь сильно упростил процесс, но этого вполне достаточно для общего понимания.

Слабая ссылка же или слабая карта - это способ создания переменной, которая действует как и любая другая, но когда PHP проверяет, указывают ли какие-либо переменные на объект,

эти «слабые» переменные не учитываются. Поэтому, если есть еще три слабые ссылки, указывающие на объект, но нет обычных переменных, PHP с радостью удалит объект и вместо этого установит для остальных переменных значение null.

$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);

// object(WeakMap)#1 (1) {
// [0]=>
// array(2) {
// ["key"]=>
// object(stdClass)#2 (0) {
// }
// ["value"]=>
// int(42)
// }
// }

// Здесь объект уничтожается, а ключ автоматически удаляется со слабой карты.
unset($obj);
var_dump($map);
// object(WeakMap)#1 (0) {
// }

  Т.е. по сути Слабые ссылки это способ сказать: 

Я бы предпочел отслеживать это, но если это кажется бесполезным, не храните его только из-за меня

Будет больше смысла увидеть это в более-менее рабочем действии. Предположим, у вас есть серия объектов Product, написанных кем-то там еще в какой-то там библиотеке. Вы не можете их изменить, но вы собираетесь их использовать.

Для каждого из Product вы хотите отслеживать дополнительную информацию, которой нет в исходном объекте, например список объектов Review в этом продукте. Создание подклассов Product для включения обзоров возможно, но беспорядочно, и при наследовании часто можно столкнуться с проблемами при попытке объединить несколько модулей вместе.

Вместо этого мы создадим отдельный объект ReviewList, содержащий слабую карту объектов Review, лениво загружая их по мере необходимости и отслеживая их Product. Как только Product удаляется из памяти, и соотвественно все переменные, которые на него ссылаются, выходят из области видимости, нам не нужно хранить все эти объекты Review. WeakMap в этом случае действует как самоочищающийся кеш и работает аналогично ArrayObject.


class ReviewList
{
private WeakMap $cache;

public function __construct()
{
$this->cache = new WeakMap();
}

public function getReviews(Product $prod): string
{
return $this->cache[$prod] ??= $this->findReviews($prod->id());
}

protected function findReviews(int $prodId): array
{
// получение обзоров продукта
}
}

$reviewList = new ReviewList();
$prod1 = getProduct(1);
$prod2 = getProduct(2);

$reviewsP1 = $reviewList->getReviews($prod1);
$reviewsP1 = $reviewList->getReviews($prod2);

// ...

$reviewsP1Again = $reviewList->getReviews($prod1);

unset($prod1);

 В этом примере ReviewList есть внутренний «слабый кеш», с ключом объектов Product. Когда вызывается getReviews(), если желаемое значение уже находится в кеше, оно будет возвращено. Если нет, он будет загружен в память, сохранен в кеше WeakMap и затем возвращен. (Здесь есть ??=  - "Оператор присваивания значения NULL", представленный в PHP 7.4, и является исключительно четким именно для такого рода случаев.) Позже, когда мы создадим $reviewsP1Again, значение будет вместо этого искаться в кэше.

Однако в какой-то момент в будущем мы уничтожим $prod1 - unset($prod1). Обычно это не выполняется вручную, но переменная выходит за пределы области видимости и собирает мусор. Поскольку обычных ссылок на объект product 1 больше нет, ссылка на этот объект в $cache Weak Map будет удалена автоматически. Это также приведет Review к автоматической очистке соответствующего списка объектов. Память сохранена, и дальнейшая работа не требуется. Это «Just Works!».

Если вы попытаетесь сделать то же самое с обычным массивом, возникнут две проблемы:

  • Массивы не могут использовать объекты в качестве ключей, поэтому необходимо исключить идентификатор продукта или что-то подобное.
  • Это означает, что кеш не будет знать, что нужно обрезать себя, когда объект, для которого он используется, собирает мусор. Возможно, вам удастся реализовать сложную логику, используя деструкторы, глобальные переменные и прочую черную магию, но… пожалуйста, не делайте этого. Шансы ошибиться высоки, а уровень сложности, который он привносит, того не стоит.

Подобные сценарии кеширования действительно являются единственным надежным вариантом использования WeakMap, но когда вам это нужно, это будет большая экономия памяти и кода.

Замечания по WeakMap

Ключи WeakMap должны быть объектами

Слабые карты допускают только объекты в качестве ключей , а связанные допускают произвольные значения. Попытка сохранить любой другой тип данных в качестве ключа вызовет ошибку типа.

$map = new WeakMap();
$map['Foo'] = 'Bar';

// Fatal error: Uncaught TypeError: WeakMap key must be an object in ...:...

Добавление к WeakMap также не допускается. 

$map = new WeakMap();
$map[] = 'Baz';

// Fatal error: Uncaught Error: Cannot append to WeakMap in ...:...

Error на несуществующих ключах 

Если запрошенный объект не существует в объекте WeakMap, создается исключение \Error.

$map = new WeakMap();
$map[new stdClass()];

// Fatal error: Uncaught Error: Object stdClass#2 not contained in WeakMap in ...:...

Этого можно избежать, проверив индекс на существование.

$map = new WeakMap();
isset($map[new stdClass()]); // false

WeakMap не разрешает свойства 

$map = new WeakMap();
$map->foo = 'Bar';

// Fatal error: Uncaught Error: Cannot create dynamic property WeakMap::$foo in ...:...

WeakMap не поддерживает сериализацию/десериализацию 

Слабые карты не могут быть сериализованы или десериализованы. Если WeakMap класс объявлен final, это тоже можно изменить.

$map = new WeakMap();
serialize($map);

// Fatal error: Uncaught Exception: Serialization of 'WeakMap' is not allowed in ...:...

$serialized_str = 'C:7:"WeakMap":0:{}';
unserialize($serialized_str);

// Fatal error: Uncaught Exception: Unserialization of 'WeakMap' is not allowed in ...:...

Итерация слабых карт  

 Класс WeakMap реализует интерфейс Traversable, его можно использовать итеративно с помощью foreach. Далее WeakMap имплементирует IteratorAggregate, приносящий метод WeakMap::getIterator.

map = new WeakMap();

$obj1 = new stdClass();
$map[$obj1] = 'Object 1';

foreach ($map as $key => $value) {
var_dump($key); // var_dump($obj1)
var_dump($value); // var_dump('Object 1');
}

Сам итератор можно извлечь с помощью метода getIterator, а возвращаемое значение - это iterable.

$map = new WeakMap();

$obj1 = new stdClass();
$map[$obj1] = 'Object 1';

$iterator = $map->getIterator();

foreach ($iterator as $key => $value) {
var_dump($key); // var_dump($obj1)
var_dump($value); // var_dump('Object 1');
}

Используйте new stdClass() для создания объекта без класса в PHP

Мы можем создать объект из stdClass(), не создавая базовый класс в PHP. Мы можем использовать оператор new для создания объекта stdClass(). Объект может получить доступ к свойствам напрямую, вызвав их. Таким образом, мы можем создавать динамические объекты и свойства с помощью stdClass(). Компилятор создает экземпляр stdClass(), когда массив приводится к типу объекта.

Например, создайте переменную $object и сохраните экземпляр stdClass(), созданный оператором new. Вызвать переменную property из объекта $object и присвоить ей строку. Присвойте строку I am an object's property. Используйте обратную косую черту \, чтобы избежать апострофа в тексте. Примените функцию var_dump() к переменной $object, чтобы вывести информацию о переменной.

В приведенном ниже примере мы создали объект stdClass() и мгновенно присвоили значение свойству объекта. Когда мы дампим объект, мы увидим созданный на выходе объект класса stdClass. Таким образом, мы можем создать объект, не создавая базовый класс в PHP.

Пример кода:

#php 7.x
property = 'I am an object\'s property';
var_dump($object);
?>

Выход:

object(stdClass)#1 (1) { ["property"]=> string(25) "I am an object's property" }

Приведение типа массива в объект с использованием типа данных object для создания объекта без создания класса

Мы можем создать объект без создания класса в PHP, преобразовав тип в объект, используя тип данных object. Мы можем преобразовать массив в объект stdClass. Ключевое слово object заключено в круглые скобки прямо перед тем, как массив преобразует массив в объект. Мы можем использовать функцию var_dump() в качестве первого метода для просмотра информации о типизированном объекте.

Например, создать переменную $place и сохранить в ней массив. Массив содержит ключи как city и country и соответствующие значения как Pokhara и Nepal. Назначьте переменную $place новой переменной $obj. Напишите тип данных object в круглых скобках непосредственно перед переменной $place. Вызвать функцию var_dump() с параметром $obj.

В приведенном ниже примере тип массива преобразуется в объект. Функция var_dump() показывает объект $obj в секции вывода. Это показывает, что это объект stdClass. Посмотрите Руководство по PHP, чтобы узнать, как работает приведение типов.

Пример кода:

#php 7.x
 'Pokhara', 'country' =>'Nepal'];
$obj = (object) $place;
var_dump($obj);
?>

Выход:

object(stdClass)#1 (2) { ["city"]=> string(7) "Pokhara" ["country"]=> string(5) "Nepal" }

Используйте функцию json_dencode() для создания объекта без создания класса в PHP

Функция json_decode() конвертирует строку JSON в объект PHP. Функция принимает строковый параметр, который будет преобразован в объект. Функция также принимает необязательный логический параметр. Значение этого логического параметра по умолчанию - false, что преобразует объект JSON в объект PHP. Если значение равно true, он преобразует объект JSON в ассоциативный массив.

Например, создайте переменную $jsonobj и сохраните в ней объект JSON. Объект содержит Гарри, Тони и Хуан в качестве ключей и целочисленные значения 27, 24 и 32 в качестве соответствующих значений. Пары “ключ-значение” заключаются в фигурные скобки. Объект JSON заключен в кавычки в виде строки. Затем используйте функцию json_decode() для переменных $jsonobj. Примените функцию var_dump() к функции json_encode() для отображения информации о закодированном объекте.

В приведенном ниже примере функция json_encode() преобразует объект JSON в объект PHP stdClass. Функция var_dump() показывает свойство и значение объекта. Таким образом, мы создали объект без создания класса в PHP. Он также отображает тип значения. Пожалуйста, ознакомьтесь с Руководством по PHP, чтобы узнать больше о функции json_decode().

Пример кода:

# php 7.x

Выход:

object(stdClass)#1 (3) { ["Harry"]=> int(27) ["Tony"]=> int(24) ["Juan"]=> int(32) }