<?php
declare(strict_types=1);
class A {
public $property = 'foo';
}
class B {
public $property = 'foo';
}
$a = new A();
$b = new B();
function byIdentifier($object) {
$object->property = 'bar';
$object = null;
}
function byReference(&$object) {
$object->property = 'bar';
$object = null;
}
byIdentifier($a);
byReference($b);
var_dump($a); // class A#2 { public $property => string(3) "bar" }
var_dump($b); // NULL
Это ни разу не аномалия...
Вас сбивает с толку слово объект и фраза что они всегда передаются по ссылке.
И да и нет. Объекты действительно всегда передаются по ссылке, но в метод летит не объект, а "алиас" на значение. С амперсандом кол-во "алиасов" на данные не измениться и в методе вы можете переопределить, как значение, так и этот "алиас"
Другой пример
<?php
declare(strict_types=1);
$array = ['foo'];
foreach ($array as &$item) {
$item = $array;
}
$item = 'string';
\var_dump($array);
$array останется массивом, но если написать `$item = &$array;` то станет строкой.
Тут дело в том, что в первом случае у нас две копии "алиасов" на данные, а во втором один. И изменяя после foreach $item - мы изменяем копию. Ну а если прировнять по ссылке, то изменим данные и массив станет строкой.
Я попробовал работать без ссылки и возвращать $storage, проблема уходит, но это не вариант.
...и далее сами себе противоречите
В вашем случае амперсанд не нужен - ну вот вообще не нужен, потому что вы работаете с объектом и меняете объект. У вас не массив или скаляр, где он, возможно был бы необходим. Поймите, передавая что-то с амперсандом вы влияете не только на значение, но и на количество ссылок на данные в PHP позволяя налету менять не данные, а сам "алиас"
Алиас - более верное название для указателя в PHP
Всё это описано тут
https://www.php.net/manual/ru/language.references.php - просто нужно немного разобраться самому )))