CyberForum.ru - Форум программистов и сисадминов
Вернуться   Форум программистов и сисадминов CyberForum.ru > Форум web-программистов > Форум PHP

Закрытая тема Создать новую тему
 
Старый 05.08.2010, 20:27   #1
Vovan-VE
Модератор
 
Аватар для Vovan-VE
 
Регистрация: 10.01.2008
Адрес: Иркутск
Сообщений: 9,621
Репутация: 5177 (3362)
По умолчанию Как работают ссылки в PHP

Обсуждение статьи

Как работают ссылки в PHP

От автора

Данная статья предназначена для тех программистов, кто имеет опыт программирования в других языках и только начал изучать PHP, и для тех из начинающих, кто уже уверенно понимает суть языка и его основные примитивы (синтаксис, переменные, функции, типы данных).

Многие программисты, которые пришли в PHP из компилируемых языков (таких, как C и Pascal), невольно путают ссылки в PHP с указателями. В то же время, начинающие программисты без опыта вообще не понимают, что такое ссылки и как их использовать.

Терминология в статье

В данной статье используются следующие термины:
  • Переменная - Переменная PHP ($foo) или обращение к элементу массива ($foo['bar'][3]). Другими словами - это то, что можно передать в синтаксические конструкии isset(), unset(), empty() и т.п.
  • Значение - значение, которое хранится в Переменной.
  • Таблица переменных, Таблица - некий логический объект для визуального представления происходящего.

Таблица переменных

Где-то в недрах ядра PHP существует Таблица переменных. В этой Таблице в том или ином виде хранятся Переменные, Значения, и связь между ними. Для наглядности Таблицу переменных можно представить следующим образом:

Название: 01.png
Просмотров: 1006

Размер: 1.3 Кб
Рис.1

В левой половине - Переменные, в правой - Значения. Стрелки показывают связь Переменной со Значением. В данной примере изображены две переменные:
Код PHP
1
2
$foo = 2;
$bar = 'lol';
Важно
  1. Стрелки символизируют одностороннюю связь: Переменной важно знать свое Значение, но Значению совершенно все равно, к какой(-им) Переменной оно принадлежит.
  2. Связь (стрелка на изображении) может идти только от Переменной только к Значению.
  3. Каждая Переменная всегда имет ровно одну связь (ссылается на одно Значение).
Итак, давайте разбираться.

Простейшая ситуация

Самая простая ситуация со ссылками записывается кодом так:
Код PHP
1
2
$foo = 5;
$bar = &$foo;
После выполнения 1-й строки в Таблицу переменных заносится Переменная $foo и ее Значение 5. Выполнение 2-й строки создает Переменную $bar и устанавливает связь со Значением Переменной $foo, как показано на Рис.2 красной стрелкой.

Название: 02.png
Просмотров: 1007

Размер: 1.3 Кб
Рис.2

Что это значит? Операция присваивания по ссылке заставляет Переменную ссылаться на уже существующее Значение другой Переменной.
Обычное присваивание в существующую Переменную изменяет ее существующее Значение.
Обычное присваивание в несуществующую Переменную создает новое Значение в Таблице.

А теперь вполне логичный вопрос: Чем отличаются между собой связи, изображенные на Рис.2? Ответ: ничем, они абсолютно одинаковы. Если в коде выше поменять переменные местами:
Код PHP
1
2
$bar = 5;
$foo = &$bar;
то Таблица переменных будет выглядеть абсолютно так же (если не брать в расчет порядок создания Переменных).

Более того, каждое Значение знает, сколько Переменных на него ссылаются (количество стрелок на рисунке). В литературе это число называется счетчик ссылок (references counter).

Удаление Переменных

Продолжаем манипуляции над Таблица переменных, изображенной на Рис.2. Удаляем первую переменную после создания ссылки:
Код PHP
1
2
3
$foo = 5;
$bar = &$foo;
unset($foo);
Это изменение отобразится в Таблице следующим образом:

Название: 03.png
Просмотров: 1013

Размер: 1.4 Кб
Рис.3

Оператор unset() удаляет Переменную и исходящую от нее связь. Почему же при этом не удаляется Значение 5? Потому что еще остались другие Переменные, которые на него ссылаются.

Далее удаляем вторую переменную:
Код PHP
1
unset($bar);
Аналогично, из Таблицы удаляется Переменная $bar и ее связь со значением. После этого оказывается, что у Значения количество ссылок стало равным нулю, а это значит, что данное Значение тоже удаляется (Рис.4).

Название: 04.png
Просмотров: 1006

Размер: 1.3 Кб
Рис.4

Массивы

Более сложные для понимания вещи поджидают нас при махинациях с массивами. Итак, создаем простой массив с тремя элементами:
Код PHP
1
2
3
4
5
$arr = array(
    1 => 10,
    2 => 20,
    3 => 30,
);
Как его отобразить в Таблице переменных? Весь массив целиком - это Значение, ибо на него ссылается Переменная $arr. Также, можно заставить другую Переменную ссылаться на Значение любого элемента массива, поэтому 10, 20 и 30 - также должны быть отдельными Значениями.

Название: 05.png
Просмотров: 1013

Размер: 1.6 Кб
Рис.5

Здесь #1 - это некий идентификатор массива для внутреннего представления.

Чтобы прочитать значение элемента массива
Код PHP
1
echo $arr[2];
необходимо пройти через две связи в Таблице.

Ссылки и Массивы

Итак, к только что созданному массиву начинаем применять ссылки:
Код PHP
1
2
3
4
5
6
7
$arr = array(
    1 => 10,
    2 => 20,
    3 => 30,
);
$foo = &$arr;
$bar = &$arr[2];
Поскольку мы уже знаем, как расположить массив в Таблице переменных, добавить две Переменные со ссылками на существующие Значения не составит труда.

Название: 06.png
Просмотров: 1020

Размер: 2.4 Кб
Рис.6

Затем выполняем обычное присваивание:
Код PHP
1
$arr[1] = $arr[2];
Порядок действий таков:
  1. Для правой части присваивания в Таблице ищем Переменную $arr - находим массив #1.
  2. В массиве #1 ищем элемент с ключем 2 - находим Значение 20.
  3. Для левой части аналогично ищем Переменную $arr - находим массив #1.
  4. Аналогично в массиве #1 ищем элемент с ключем 1 - находим Значение 10.
  5. В найденное в п.4 Значение 10 копируем найденное в п.2 Значение 20.

Перечисленным действиям соответствуют оранжевые цифры на Рисунке 7.

Название: 07.png
Просмотров: 1057

Размер: 2.6 Кб
Рис.7

Далее делаем присваивание по ссылке:
Код PHP
1
$arr[2] = &$arr[3];
Существующая связь от Переменной #1[2] разрывается и создается новая связь до Значения 30.

Название: 08.png
Просмотров: 1053

Размер: 2.5 Кб
Рис.8

Если теперь уничтожить переменную $arr
Код PHP
1
unset($arr);
то из Таблицы будет удалена соответвтсующая Переменная и ее связь. Поскольку на Значение #1 все еще ссылается другая Переменная, то оно не будет уничтожено.

Название: 09.png
Просмотров: 1075

Размер: 2.6 Кб
Рис.9

Последний шаг порождает более интересные вещи:
Код PHP
1
$foo = 42;
Значение #1, на которое ссылается Переменная $foo, заменяется новым Значением 42. Что значит "заменяется"? Это значит, что старое #1 удаляется. Это приводит к удалению Переменных #1[1], #1[2] и #1[3] вместе с их связями. Тут же оказываются бесхозными два Значения, поэтому они тоже удаляются.

Название: 10.png
Просмотров: 1079

Размер: 2.8 Кб
Рис.10

В результате остается две простых пары Переменная - Значение.

Последний раз редактировалось Vovan-VE; 04.12.2011 в 11:12.
Vovan-VE вне форума
Другие темы раздела
PHP Не видит куки
Доброго времени суток. Пишу в куки: $acc=mysql_fetch_array(mysql_query("SELECT * FROM bank where user='".$stat."'")); SetCookie("bpass2","$acc"); Header("Location: bank.php?otdel=1"); потом прослушиваю if (!$bpass2) { ...} else {....} Не видит значения. Нету как-бы там его. Делал на 2.... Не видит куки
PHP Случайный выбор изображений
Ситуация такова: имеется скрипт опроса, после нажатия на кнопку голосовать, отображаются результаты (графические полоски из одной картинки). Я подумал что это не слишком понятно и красиво. Необходимо чтобы он отображал над каждым ответом разноцветные полоски. Думал использовать ф-ию mt_rand(),.... Случайный выбор изображений
Старый 05.08.2010, 20:32  [ТС]   #2
Vovan-VE
Модератор
 
Аватар для Vovan-VE
 
Регистрация: 10.01.2008
Адрес: Иркутск
Сообщений: 9,621
Репутация: 5177 (3362)
По умолчанию Re: Как работают ссылки в PHP

Неявные ссылки

Помимо явного использования ссылок (присваивание по ссылке и передача аргументов по ссылке) существуют также несколько вариантов неявного использования ссылок.

Доступ к глобальным переменным из функции

Как Вам известно, глобальные переменные сами по себе недоступны внутри функций. Например, в приведенном коде:
Код PHP
1
2
3
4
5
6
7
8
9
$foo = 5;
 
function bar() {
    $foo = 10;
    echo $foo;
}
 
bar();
echo $foo;
в 4-й строке идет работа с локальной переменной $foo, а не с глобальной, поэтому последний echo выведет 5, а не 10.

Чтобы получить доступ к глобальной переменной внутри функции, необходимо использовать оператор global:
Код PHP
1
2
3
4
5
6
7
8
9
10
$foo = 5;
 
function bar() {
    global $foo;
    $foo = 10;
    echo $foo;
}
 
bar();
echo $foo;
Теперь последний echo выведет 10, потому что внутри функции была изменена глобальная переменная $foo.

А теперь самое интересное. Задумывались ли Вы, как это работает? Что будет, если перед оператором global использовать такую же локальную переменную? Что будет, если внутри функции после оператора global удалить переменную?

Ответ на первый вопрос очень прост и описан в документации PHP. Оператор global создает ссылку на значение одноименной глобальной переменной. Иными словами, это эквивалентно присваиванию по ссылке из глобальной переменной в локальную.

Это значит, что если перед оператором global уже была такая же локальная переменная, то у ее значения уменьшится количество ссылок.

Если после оператора global удалить эту переменную, то удалится всего лишь локальная переменная со своей ссылкой, а глобальная переменная никуда не денется. После этого с переменной можно работать, как с локальной:
Код PHP
1
2
3
4
5
6
7
8
9
10
11
$foo = 5;
 
function bar() {
    global $foo; // создаем ссылку на глобальную $foo
    $foo = 10;   // изменяем значение глобальной $foo
    unset($foo); // удаляем локальную копию и ссылку
    $foo = 20;   // создаем уже локальную $foo
}
 
bar();
echo $foo;
Этот код выведет 10, а не 20, потому что в 6-й строке мы удаляем локальную $foo вместе со ссылкой на глобальную $foo. В 7-й строке мы работаем уже с новой локальной переменной.

Статические переменные

Статические переменные видны только внутри функции, как локальные переменные, но их значение сохраняется между вызовами функции. Чтобы декларировать статическую переменную, необходимо использовать оператор static.
Код PHP
1
2
3
4
5
6
7
function bar() {
    static $foo = 0;
    echo $foo;
    $foo++;
}
bar();
bar();
При первом вызове в переменную $foo запишется указанное значение по умолчанию (0), а при последующих вызовах получившееся значение будет сохраняться.

На самом деле оператор static при первом вызове создает Значение, которое не уничтожается, когда на него больше не ссылается ни одна Переменная. Указанная переменная $foo становится ссылкой на это Значение.

Если перед оператором static уже была одноименная локальная переменная, то, аналогично оператору global, ее связь со старым значением разрывается. Если после оператора static удалить переменную, то ее статическое Значение останется в памяти на своем месте (даже если удалить все ссылающиеся на него Переменные).
Код PHP
1
2
3
4
5
6
7
8
function bar() {
    $foo = 42;       // локальная $foo
    static $foo = 0; // заставляем локальную $foo ссылаться на свое статическое Значение
    echo $foo;       // выводим значение статической $foo
    $foo++;          // изменяем статическое $foo
    unset($foo);     // удаляем $foo со ссылкой на статическое значение
    $foo = 21;       // снова создаем обычную локальную $foo
}
Здесь переменная $foo имеет статус статической переменной только от строки 3 до строки 6.

Последний раз редактировалось Vovan-VE; 04.12.2011 в 11:18.
Vovan-VE вне форума
Старый 04.09.2010, 13:34  [ТС]   #3
Vovan-VE
Модератор
 
Аватар для Vovan-VE
 
Регистрация: 10.01.2008
Адрес: Иркутск
Сообщений: 9,621
Репутация: 5177 (3362)
По умолчанию Re: Как работают ссылки в PHP

Ссылки и объекты
Важно! Нижеизложенное справедливо только для PHP версии 5 и выше.
При работе с ООП в PHP мы сталкиваемся со ссылками другого рода: ссылками на объект. Здесь важно не запутаться в терминологии. Для ясности небольшой пример:
Код PHP
1
2
3
4
5
6
7
class Foo {
    public $bar;
}
$a = new Foo();
$b = $a;
$b->bar = 42;
echo $a->bar; // 42
В строке 4 создаем объект класса Foo и присваиваем ссылку на него в переменную $a . Если объяснять на пальцах, то объект класса Foo можно сравнить с курткой в гардеробе. Присваивание в 4й строке кладет в переменную $a номерок от этой куртки, а не саму куртку. Далее в строке 5 мы копируем в переменную $b ссылку на тот же самый объект (номерок от той же самой куртки). Так мы получаем две одинаковых ссылки на один и тот же объект (два одинаковых номерка от одной и той же куртки). Следующий шаг: в строке 6, используя копию ссылки (копию номерка) в переменной $b, изменяем свойство того же самого объекта (кладем спички в карман той же самой куртки). Чтобы убедиться, что работа идет с одним и тем же объектом (с одной и той же курткой), считываем свойство объекта через оригинальную ссылку в переменной $a (проверяем карман куртки через оригинальный номерок).

Заметьте, что в данном примере идет обычное присваивание, и нет присваивания по ссылке.

Дополнительная терминология

Итак, появились два разных по смыслу термина "Ссылка". Чтобы избежать путаницы, будем называть "Ссылкой" именно те ссылки, о которых рассказывает вся статья. А ссылки на объекты (те самые номерки от курток) будем называть "Ссылками на объект" или "Ссылками объектов".

Ссылка на Ссылку объекта

Работая с объектами, при обычном присваивании мы и так копируем Ссылки объектов (номерки от курток) вместо копирования самих объектов. Возникает вопрос: зачем здесь вообще Ссылки, если мы и так работаем со Ссылками объектов?

Для ответа на вопрос рассмотрим функцию:
Код PHP
1
2
3
4
5
function foo(Bar $bar) {
    $bar->lol();
}
$a = new Bar();
foo($a);
Функция принимает в аргумент $bar объект класса Bar. В функцию передается копия Ссылки на объект, поэтому функция работает с оригинальным объектом, а не с его копией. По той же причине функция не может изменить исходную Ссылку на объект, которая хранится в переменной $a.

Другой пример:
Код PHP
1
2
3
4
5
6
function foo(Bar &$bar) {
    $bar->lol();
    $bar = null;
}
$a = new Bar();
foo($a);
Здесь функция принимает по ссылке в аргумент $bar объект класса Bar. Т.е. аргумент $bar становится Ссылкой на Значение Переменной $a. Получается Ссылка на Ссылку объекта. От предыдущего примера отличие следующее: теперь функция может изменить саму Ссылку на объект, а не только работать с объектом, на который она ссылается.

Итак, Ссылка на Ссылку объекта имеет право на существование, если Вы четко понимаете, что делаете.

Последний раз редактировалось Vovan-VE; 04.12.2011 в 11:24.
Vovan-VE вне форума
После регистрации реклама в сообщениях будет скрыта
Старый 04.09.2010, 17:53  [ТС]   #4
Vovan-VE
Модератор
 
Аватар для Vovan-VE
 
Регистрация: 10.01.2008
Адрес: Иркутск
Сообщений: 9,621
Репутация: 5177 (3362)
По умолчанию Re: Как работают ссылки в PHP

Копирование при записи (Copy on write)

Копирование при записи напрямую не связано со ссылками в PHP. Однако с этим явлением Вы косвенно сталкиваетесь очень часто не только в PHP, но и во многих других языках программирования.

Источник проблемы

Предположим, есть код:
Код PHP
1
2
$foo = 'строка';
$bar = $foo;
Формально в этом коде создаются две переменные, в которых содержатся одинаковые строки. Если теперь изменить одну из них, то вторая останется неизменной.
Код PHP
1
2
3
4
5
$foo = 'строка';
$bar = $foo;
$foo[1] = 'Т';
echo $foo; // сТрока
echo $bar; // строка
Теперь рассмотрим другой пример:
Код PHP
1
2
3
4
5
function foo($str) {
    echo $str;
}
$bar = 'строка';
foo($bar);
Функция принимает по значению строковый аргумент. Формально в функцию передается копия строки. Ведь если внутри функции изменить значение переменной $str, то это изменение никак не повлияет на исходную строку в переменной $bar.

А что, если функция не будет изменять значение переданного аргумента? Напрашивается вывод, что строка будет копироваться впустую и, таким образом, вхолостую тратить ресурсы. Вот здесь-то и вступает в силу оптимизация по имени Копирование при записи.

Как это работает?

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

Чтобы понять, как она работает, воспользуется PHP функцией memory_get_usage(), которая возвращает объем памяти, занимаемой интерпретатором PHP.
Код PHP
1
2
3
4
5
6
7
echo memory_get_usage() . "\n"; // 321568
$foo = str_repeat('foobar', 32768);
echo memory_get_usage() . "\n"; // 518296
$bar = $foo;
echo memory_get_usage() . "\n"; // 518344
$bar[1] = '!';
echo memory_get_usage() . "\n"; // 715000
В 1й строке выводим изначальный объем памяти. В строке 2 создает строку объемом в 192 КБ, после чего в 3й строке снова отображаем использование памяти. Видим, что объем памяти существенно увеличился. Далее в строке 4 пытаемся скопировать строку из переменной $foo в переменную $bar. Однако в строке 5 мы видим, что памяти на это потребовалось совсем немного. Благодаря копированию при записи реально строка еще не скопировалась, потому что в этом пока нет необходимости. Фактически обе переменные работают с одной и той же строкой в памяти. Формально же они работают с разными "копиями" строки. Теперь в строке 6 изменяем один символ в одной и "копий" строк. Только теперь выполняется реальное копирование строки, о чем сигнализирует вывод в 7й строке.

Причем здесь ссылки?

Предоложим, что Вы создаете функцию, которая должна принимать в аргумент строку (или массив), которая теоритически может иметь достаточно большой объем. Сама функция не изменяет переданную строку, а использует ее только для чтения. Если бы Вы задумались об оптимизации, чтобы избежать холостого копирования (о котором говорилось выше), и если бы Вы не знали о существовании копирования при записи, то наверняка Вы бы в своей функции определили аргумент для передачи по ссылке.

Последний раз редактировалось Vovan-VE; 04.12.2011 в 11:27.
Vovan-VE вне форума
Закрытая тема Создать новую тему

Похожие темы
Тема Автор
HTML, CSS Не работают ссылки
Добрый день, уважаемые. Помогите решить проблему, пол дня бьюсь. Имеется div внутри которого картинки-ссылки. Картинки и ссылки берутся из php. Вот примерный код: <div id="colleft"> <a href=" "><img src=" " /></a> </div> Стили для блока заданы следующие: div#colleft {
Avira
XML/XSL Не работают внешние ссылки и анкоры
Здравствуйте. Очень нужна помощь. Есть файл на букву А в нем отображает все правильно все заменяет при нажатии на ссылку апельсин переходит вверх на слово апельсин то есть анкоры работают с буквой А нормально как и должно быть при нажатии на ссылку киви открывается 2 файл К.xml переходит на...
Demonicq2014
Windows 7 не работают ссылки
всё интернет ссылки из системы или приложений открывают не интернет страницу, а WORD-ский текст! что делать?
doktor-vitaly
Windows XP После установки СП3 не работают ссылки
Общий привет! После установки СП3 перестали работать ссылки в приложениях (например, в АутЛук Экспресс). Ответ через поиск не нашел. Заранее благодарен
pons
Опции темы

Текущее время: 04:24. Часовой пояс GMT +4.

Компьютерный форум программистов и сисадминов
Powered by vBulletin® Version 3.8.7 PL2
Copyright ©2000 - 2012, vBulletin Solutions, Inc.
Рейтинг@Mail.ru