powered by simpleCommunicator - 2.0.38     © 2025 Programmizd 02
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / PHP - помогите разобраться со ссылками
16 сообщений из 16, страница 1 из 1
PHP - помогите разобраться со ссылками
    #39934930
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Допустим, есть такой код:
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
trait PropertyBag
{
	private function _init()
	{
		$args = func_get_args();
		foreach ($args as $arg)
		{
			$this->local[$arg] = null;
		}
	}
	private function _flag()
	{
		$args = func_get_args();
		if (!empty($args))
		{
			$flag = array_shift($args);
			foreach ($args as $arg)
			{
				$this->flags[$arg] = $flag;
			}
		}
	}
	private function _fail($name)
	{
		$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1];
		if (!array_key_exists($name, $this->local))
		{
			trigger_error("Invalid property '{$name}' in {$bt['file']}:{$bt['line']}", E_USER_NOTICE);
			return true;
		}
		if (!array_key_exists($name, $this->flags)) return;
		if ($this->flags[$name] == 'ro')
		{
			$fail = 'Read-only';
			if (in_array($bt['function'], ['__get', '__isset'])) return;
		}
		trigger_error("{$fail} property '{$name}' in {$bt['file']}:{$bt['line']}", E_USER_NOTICE);
		return true;
	}
	function __isset($name)
	{
		if ($this->_fail($name)) return;
		return isset($this->local[$name]);
	}
	function __get($name)
	{
		if ($this->_fail($name)) return;
		return $this->local[$name];
	}
	function __set($name, $value)
	{
		if ($this->_fail($name)) return;
		$this->local[$name] = $value;
	}
	function __unset ($name)
	{
		if ($this->_fail($name)) return;
		$this->local[$name] = null;
	}
	function __call($name, $arguments)
	{
		if ($this->_fail($name)) return;
		if (isset($arguments)) $this->local[$name] = $arguments[0];
		return $this->local[$name];
	}
	function set()
	{
		$args = func_get_args();
		if (count($args) > 0) $name = array_shift($args);
		if (count($args))
		{
			$this->local[$name] = $args[0];
		}
		else
		{
			unset($this->local[$name]);
		}
	}
	function getIterator()
	{
		return new ArrayIterator($this->local);
	}
}

class SubAppField implements IteratorAggregate
{
	use PropertyBag;
	private $local = [];
	private $flags = [];
	function __construct()
	{
		$this->_init('id', 'name', 'value');
		$this->_flag('ro', 'value');
	}
}

$fld = new SubAppField();
$fld->id = 'id1';
$fld->name = 'name1';
print_r($fld);
foreach($fld as $key=>&$val) { echo "field {$key} = *{$val}* \n"; $val = 123; }
print_r($fld);


Разве последняя строка не должна вывести значения 123?
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39935000
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не вижу на то причин.
Полагаю, такое поведение станет понятным после ответа на следующий простой вопрос. На какую именно переменную (в смысле область памяти, обозначенную некоторой меткой, имеющей имя) ссылается в данном случае $val на последней итерации цикла foreach?
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39935088
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну поскольку используется стандартный итератор массивов, и ссылается он на массив, то я ожидал, что foreach ($fld as $key=>&$val) должен быть идентичен foreach ($this->local as $key=>&$val) внутри класса.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39935324
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Итератор тут сильно из другой йоперы.
На вопрос Вы ответить не попытались, видимо. Ладно, коротко, суть ссылающейся переменной есть алиас другой переменной - они обе указывают на одну и ту же область памяти. Примерно как хардлинк в файловой системе.
Когда переменная $val будет явно указывать на область памяти, заданную какой-то другой переменной или элементом массива, тогда содержимое этой области можно изменить, обращаясь к этой переменной.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39935958
Фотография ScareCrow
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

Код: php
1.
2.
3.
4.
$a = [1,2,3,4,5];
foreach ($a as &$value) {}
foreach ($a as $value) {}
var_dump($a);
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39935993
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vkle
Когда переменная $val будет явно указывать на область памяти, заданную какой-то другой переменной или элементом массива, тогда содержимое этой области можно изменить, обращаясь к этой переменной.

Поясню свою логику.
Стандартный итератор позволяет в цикле изменять значения массива (если использовать foreach $array as &$item).
Значит стандартный итератор массива позволяет работать не со значением переменной, а со ссылкой на нее.
То есть моя цепочка рассуждений следующая: если я во внешнем коде использую &$val, то $val будет ссылаться на область памяти, которой соответствует элемент из private-массива $local, поскольку обход этого private-массива осуществляется через стандартный итератор массива.
Ведь класс SubAppField по сути просто обертка над массивом массивов, а итератор IteratorAggregate обеспечивает доступ к этому массиву.
Не так?

ScareCrow , в этом примере ничего неожиданного, такое поведение вполне укладывается в то, что я знаю о переменных PHP.
Если после первого цикла указать unset($value), то ничего в цикле изменяться не будет.
И если вместо var_dump использовать debug_zval_dump, то все будет более понятно.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936090
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

Как я понимаю эту кухню.
Значение 123 должно бы (гипотетически, по соображению, я так думаю и т.п.) нырнуть в область памяти, обозначенную, скажем $local['id']. Будь $local паблик переменной - так бы оно и было. Однако, $local в данном случае есть приватная переменная. И, стало быть, в ее отношении должен автоматически применяться волшебный метод __set(). Есть такой. И даже определен. А там, если верить доке, применима только лишь передача по значению, с явным указанием имени. Даже если итератор и прислал ссылку на область памяти в $val (ссылка - одна штука), то в сеттер она никак не лезет, так как он ожидает имя и значение (две штуки).
Ну вот так как-то, сеттер ссылку теряет тут.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936091
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.,

И еще, итератор тоже ж не дураками писан. Выше я лишь предположил "если итератор и прислал ссылку", а оно не факт. Со стороны итератора видно приватную переменную и уже заранее понятно должно быть, что ссылки там не работают. В этом смысле он может даже и не прислать ссылку, а прислать значение.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936174
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vkle
И, стало быть, в ее отношении должен автоматически применяться волшебный метод __set(). ... А там, если верить доке, применима только лишь передача по значению, с явным указанием имени.

Суть понял, но мне кажется, что в данном случае что-то другое.
Допустим в коде есть присвоение переменной, $var=$val. В виде функции это можно было бы записать как f(&$var, $var).
Но в моем случае волшебный метод set работает не с переменной (ссылкой на переменную), я в него передаю не саму переменную (которая хранит значение), а ключ массива. А уже внутри метода по этому ключу я обращаюсь к элементу массива.
То есть в виде функции это будет __set($key, $var), а внутри функции будет обращение к элементу массива по ключу $arr[$key]=$val.

Я скорее предполагал, что может быть это побочные эффекты trait, но в документации везде явно указывается, что trait это почти макрос и его код не переиспользуется, а повторяется.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936251
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alibek B.
а внутри функции будет обращение к элементу массива по ключу $arr[$key]=$val.
Да откуда ж там ключу взяться, если в Вашем варианте операции присваивания нет ни ключа, ни указания на переменную?
Было бы что-то вроде
Код: php
1.
$fld->$key = 123;

- тогда да, после развертывания левой части выражения $fld->$key вполне может трактоваться как имя переменной.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936318
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vkle
Да откуда ж там ключу взяться, если в Вашем варианте операции присваивания нет ни ключа, ни указания на переменную?

Вероятно да.
Я думал, что под капотом какая-нибудь магия происходит, следствием которой является то, что при использовании ссылки на переменную (например в foreach) фактически происходит присвоение.
Но сейчас почитал про итераторы, похоже что там что-то более сложное.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936341
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
С учетом ограничений переделал класс для универсальной коллекции.
Но ведь это наверняка велосипед.
Не посоветуете, что добавить/улучшить?
Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
function _assoc($arr)
{
	if (!isset($arr)) return false;
	if (!is_array($arr)) return false;
	if (empty($arr)) return false;
	return array_keys($arr) !== range(0, count($arr) - 1);
}

// Модуль для работы с коллекциями
class Collection implements IteratorAggregate
{

	private $store;

	// new Collection(<field>, [<field>=><value>, ...], ...)
	function __construct()
	{
		$this->store = [];
		$args = func_get_args();
		foreach ($args as $arg)
		{
			if (!isset($arg)) continue;
			if (is_array($arg)) {
				$assoc = _assoc($arg);
				foreach ($arg as $k=>$v)
				{
					if ($assoc) {
						$this->store[$k] = $v;
					} else {
						$this->store[$v] = null;
					}
				}
				unset($assoc, $k, $v);
			} else {
				$this->store[$arg] = null;
			}
		}
	}

	private function _fail($name)
	{
		$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1];
		if (!array_key_exists($name, $this->store))
		{
			trigger_error("Invalid field '{$name}' in {$bt['file']}:{$bt['line']}", E_USER_NOTICE);
			return true;
		}
	}
	function __isset($field)
	{
		if ($this->_fail($field)) return;
		return isset($this->store[$field]);
	}
	function __get($field)
	{
		if ($this->_fail($field)) return;
		return $this->store[$field];
	}
	function __set($field, $data)
	{
		if ($this->_fail($field)) return;
		$this->store[$field] = $data;
	}
	function __unset ($field)
	{
		if ($this->_fail($field)) return;
		$this->store[$field] = null;
	}
	function __call($field, $arguments)
	{
		if ($this->_fail($field)) return;
		if (isset($arguments)) $this->store[$field] = $arguments[0];
		return $this->store[$field];
	}

	// IteratorAggregate interface
	function getIterator()
	{
		return new ArrayIterator($this->store);
	}

	function _has($field)
	{
		if (isset($field))
		{
			if (array_key_exists($field, $this->store))
			{
				return true;
			}
		}
		return false;
	}

	function _get($field)
	{
		if (isset($field))
		{
			if (array_key_exists($field, $this->store))
			{
				return $this->store[$field];
			}
		}
	}

	function _set($field, $data)
	{
		$this->store[$field] = $data;
		return $this->store[$field];
	}

	function _del($field)
	{
		$item = $this->_get($field);
		if (isset($item)) unset($this->store[$field]);
		return $item;
	}

	function _cnt()
	{
		return count($this->store);
	}

	function _rst()
	{
		$this->store = [];
	}

}

$tmp = new Collection('int', 'str', 'arr', 'ref');
$tmp->int = 123;
$tmp->str('abc');
$tmp->_set('arr', [1,2,3]);
$tmp->ref = new Collection('test');
$tmp->ref->test = 'test1';
print_r($tmp);
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936350
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гипотетически, такая магия возможна, если блок кода foreach будет передан в итератор. Однако, думаю, в таком случае получится абракадабра контекста. Ведь блок кода по сути выполняется во вне по отношению к объекту, а итератор внутри.

И еще вот что подумалось. Ведь прямой доступ к приватной переменной объекта извне его невозможен по определению. Таким образом, никакие указатели или ссылки на область памяти, обозначенную этой переменной за пределы объекта вылезти не должны. Наверно, это более простое объяснение.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936364
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vkle
Ведь прямой доступ к приватной переменной объекта извне его невозможен по определению. Таким образом, никакие указатели или ссылки на область памяти, обозначенную этой переменной за пределы объекта вылезти не должны. Наверно, это более простое объяснение.

Ну через методы это работает.
Возвратить ссылку у меня не получилось, а вот через аргумент по ссылке вполне.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936450
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Столкнулся с сообщением "Indirect modification of overloaded property".
Можно ли от него избавится?
Ранее можно было просто указать &, чтобы передать ссылку.
Но начиная с какой-то версии (как минимум с 5.6) это запретили, & может указываться только в декларации function и foreach, использовать его в выражениях нельзя.
Вот тут в принципе почти мой вариант, но предложенное решение с & сейчас уже неприменимо.
...
Рейтинг: 0 / 0
PHP - помогите разобраться со ссылками
    #39936545
Alibek B
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Отбой, это принципиально неразрешимый вопрос.
Подавить сообщение можно, но вопрос это не решает.
...
Рейтинг: 0 / 0
16 сообщений из 16, страница 1 из 1
Форумы / PHP, Perl, Python [игнор отключен] [закрыт для гостей] / PHP - помогите разобраться со ссылками
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]