Объекты передаются по ссылке или нет?

Часто можно услышать фразу, что в PHP «объекты всегда передаются по ссылке». На самом деле всё немного сложнее.

https://www.php.net/manual/ru/language.oop5.references.php

Как выглядит работа с ссылками в PHP? Для этого используется специальный синтаксис – перед именем переменной или параметра функции ставится символ амперсанда (&). В том случае, когда амперсанд проставлен в сигнатуре функции – это называют передачей параметра по ссылке. Изменяя такую переменную-параметр внутри функции, после выхода из функции мы обнаружим, что значение поменялось и в месте вызова. Думаю, с этим знакомы все.

А что с объектами? Объекты, переданные внутрь какой-то функции в качестве аргументов, ведут себя точно также – если внутри функции мы меняем внутреннее состояние объекта, то снаружи увидим это изменённое состояние. Отличие в том, что в сигнатуре функции не нужно ставить амперсанд перед параметром-объектом.

Отсюда можно сделать ошибочный вывод, что объекты всегда передаются по ссылке, просто синтаксис попроще, не надо обмазываться амперсандами.

А что, если в сигнатуре функции, принимающей объект всё-таки поставить амперсанд? После небольшой проверки, на первый взгляд, ничего не поменяется! Кажется очередной фрактал плохого дизайна: скалярные типы данных можно передать двумя способами, и по ссылке, и по значению, а объекты всегда по ссылке, при этом амперсанд хочешь ставь, хочешь не ставь.

На самом деле, это заблуждение. Разберёмся что здесь происходит.

Упрощённо, механику можно представить так: когда мы создаём объект с помощью оператора new и присваиваем какой-то переменной, в эту переменную помещается не сам объект, а некий идентификатор объекта, id.

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

Таким образом снаружи функции и внутри мы, имея одинаковое значение идентификатора объекта, работаем с одним и тем же объектом.

Но если внутри функции мы присвоим переменной, например null – повлияет ли это на объект снаружи функции? Никак! Мы обнулили переменную содержащую id объекта внутри функции, но снаружи функции, внешняя переменная всё ещё содержит id объекта и сам объект никуда не делся из памяти.

Теперь ставим в сигнатуре функции амперсанд. В этом случае переменная, содержащая идентификатор объекта, передастся по ссылке. Поменяв такую переменную внутри функции, например, присвоив null, мы обнаружим что поменялась и переменная снаружи – она тоже стала null. А объект в памяти стал ничьим и он будет удалён сборщиком мусора.

Подводим итог.

  1. Формулировка «объекты всегда передаются по ссылке» не корректна. Впрочем, с практической точки зрения такое упрощение не приводит к проблемам в общении с другими разработчиками, все воспринимают эту фразу примерно одинаково: изменив состояние объекта внутри функции мы увидим это и снаружи.
  2. Есть существенная разница между синтаксисом с амперсандом и без него. Но на практике не припомню, чтобы мне требовалось использовать вариант с амперсандом перед переменной-объектом.

Рекомендую заглянуть в документацию: https://www.php.net/manual/ru/language.oop5.references.php и прочитать комментарии, там очень наглядные и развёрнутые примеры!

Источник: 5minphp.ru

 

5 минутка PHP

Подкаст о PHP, DBA, архитектуре, DevOps. Авторское мнение о современных трендах в веб-разработке и интересные беседы с гостями. Помимо PHP поднимаем темы про инфраструктуру, администрирование Linux и DevOps подходы, сравниваем PHP с другими языками программирования, например с Go, Rust и даже Erlang.

Добавить комментарий

%d такие блоггеры, как: