Центр пользователя  |  Ваши сообщения  |  FAQ
Форум поддержки phpBB » Общие » PHP и MySQL

Ссылки в PHP

Обсуждение вопросов, связанных с разработкой web-систем

Сообщение 07 дек 2009, 13:08

Уровень: начинающий, средний.

Благодаря тому, что РНР до сих пор остается очень легким для освоения, в эту отрасль приходит все больше новых людей, которые, вполне возможно, не имеют практики программирования на других языках. Вследствие того же фактора, можно достаточно умело оперировать этим языком и получать законченные программные продукты, например «сайты-визитки». И, в отличии от многих, я считаю, что это здорово. Но рано или поздно, если человек развивается в данном направлении, ему приходится вникать в более тонкие моменты программирования. И сегодня мы поговорим о ссылках в языке программирования PHP.

В PHP существует 3 вида ссылок:
1) Жесткие ссылки
2) Символические ссылки
3) Ссылки на объекты

Для чего они нужны и чем могут быть нам полезны? Давайте рассмотрим каждый тип ссылки подробно.
Жесткие ссылки. Жесткая ссылка представляет собой простой синоним переменной. Давайте посмотрим на следующий пример:
Код: выделить все
/* ОБЫЧНОЕ ПРИСВОЕНИЕ */
$x = 10;  // Есть переменная $х
$y = $x;  // Есть переменная $y, которая представляем собой копию $х

echo "x = {$x}, y = {$y}\n"; // Проверяем результат
// x = 10, y = 10

$y = 50;

echo "x = {$x}, y = {$y}\n"; // Проверяем результат
// x = 10, y = 50
unset($x, $y); // Уничтожаем $x, $y



/* ПРИСВОЕНИЕ ПО ЖЕСТКОЙ ССЫЛКЕ */
$x = 10;  // Есть переменная $х
$y = &$x; // Есть переменная $y, которая представляем собой жесткую ссылку на $х

$y = 50;  // Эквивалентно записи $x = 50;

echo "x = {$x}, y = {$y}\n"; // Проверяем результат
// x = 50, y = 50
unset($y); // Если мы уничтожим ссылку, то это не приведет к удалению переменной на которую она ссылается
echo "x = {$x}, y = {$y}\n"; // Проверяем результат

$y = &$x; // Опять создаем жесткую ссылку;

unset($x); // Уничтожаем $x
echo "x = {$x}, y = {$y}\n"; // Проверяем результат

echo "\n\n\n\n";

То есть для того, чтобы создать переменную-ссылку на другую переменную нужно при присвоении перед правым операндом поставить знак передачи по ссылке – «&»(стр. 16). На первый взгляд эта операция кажется абсолютно бесполезной, но это не так. Дальше мы в этом убедимся.
И обязательно запомните: жесткая ссылка может быть уничтожена только оператором unset($жесткая_ссылка). Во всех остальных случаях она будет «преследовать» переменную, на которую ссылается, и Вас по всей своей области видимости. Если мы удалим переменную, на которую ссылается, какая-то ссылка, то переменная удалится из памяти, а ее значение в памяти будет все так же закреплено за ссылкой. Или мы удаляем ссылку оператором unset(), но это не влияет на переменную, на которую она ссылалась. Это часто приводит к ошибкам, которые непросто отловить.
Кстати, для тех, кто знаком с языком С и архитектурой памяти, можно добавить, что несмотря на то, что в PHP есть подобная возможность, в нем невозможно узнать адрес памяти, в котором храниться переменная, и невозможно ссылаться на этот адрес.

Символические ссылки. Символическая ссылка – это простая строковая переменная, в которой храниться имя другой переменной. Для того чтобы получить значение такой ссылки нужно применить оператор разыменования – знак «$» перед именем ссылки. Давайте посмотрим пример:
Код: выделить все
/* ПРИМЕНЕНИЕ СИМВОЛИЧЕСКОЙ ССЫЛКИ */
$x = 1; // Инициализируем переменную x
$y = 2; // Инициализируем переменную y

$linkX = 'x'; // Это переменная будет использоваться как символическая ссылка на x
$linkY = 'y'; // Это переменная будет использоваться как символическая ссылка на y

echo "x = {$$linkX}, y = {$$linkY}\n"; // Вот так применяются символические ссылки
// x = 2, x = 2
unset($x, $y); // Уничтожаем $x, $y
Тоже не самая полезная фича, но если стоит такая задача, без нее никак:
/* ПРАКТИЧЕСКОЕ ПРИМЕНЕНИЕ СИМВОЛИЧЕСКОЙ ССЫЛКИ */
// Есть 2 класса.
class Dynamic_Library_First {};
class Dynamic_Library_Second {};

// Есть какой-то динамический во времени параметр.
// В зависимости от этого параметра нам нужно создать тот или иной класс.
$pseudoRandom = rand(0, 1);

// Мы должны вычислить его имя
$className = 'Dynamic_Library_';
if ($pseudoRandom == 0) {
    $className .= 'First';
} else if ($pseudoRandom == 1) {
    $className .= 'Second';
} else {
    echo "Something went wrong with pseudo - random function\n";
}

// И выполнить следующее действие. Конструктор, кстати, работает как всегда.
$object = new $className();

// Вуаля!
if ($object instanceof Dynamic_Library_First) {
    echo 'Object is instance of Dynamic_Library_First';
} else if ($object instanceof Dynamic_Library_Second) {
    echo 'Object is instance of Dynamic_Library_Second';
} else {
    echo 'Something went wrong';
}
echo "\n";
// Запустите этот пример несколько раз что бы увидеть его действие


Ссылки на объекты. Основной фичей РНР5 считают нововведения в объектно-ориентированном программировании. Их много и речь сейчас не об этом. Речь о том, что для удобства работы оператор присвоения «=» работает по другому, когда речь идет о объектах класса. Посмотрите следующий пример:
Код: выделить все
/* ССЫКИ НА ОБЪЕКТЫ */
// Есть класс личность
class Person {
    public $weight;
}

// Я - личность
$me = new Person();
// и вешу 80 кг
$me->weight = 80;
// Это моя копия для экспериментов над внешностью :)
$copyOfMe = $me;
// Она весит больше, чем я - 100 кг
$copyOfMe->weight = 100;

// Смотрим, что получилось
echo "My weight = {$me->weight}, My copy weight = {$copyOfMe->weight}\n";
// В РНР4 получилось: My weight = 80, My copy weight = 100
// В РНР5 получилось: My weight = 100, My copy weight = 100

При операциях присвоения с объектами РНР4 создавал их копии, по аналогии с операциями присвоения переменных. РНР5 в таких случаях создает жесткую ссылку(см. выше), присваивает не копию объекта, а ссылку на адрес в памяти.
Для того чтобы этот пример заработал в PHP5 правильно и мы могли бы проводить эксперименты над моей внешностью, нужно его незначительно изменить:
Код: выделить все
// Есть класс личность
// ...
// Я - личность
$me = new Person();
// и вешу 80 кг
$me->weight = 80;
// Это моя копия для экспериментов над внешностью :)
$copyOfMe = clone $me;
// Она весит больше, чем я - 100 кг
$copyOfMe->weight = 100;

// Смотрим, что получилось
echo "My weight = {$me->weight}, My copy weight = {$copyOfMe->weight}\n";
// В РНР4 получилось: ошибка
// В РНР5 получилось: My weight = 80, My copy weight = 100

Теперь в РНР5 все будет работать правильно, а в РНР4 будет ошибка так как там отсутствует оператор «clone».

Вот мы и познакомились со ссылками в РНР, а так же рассмотрели некоторые примеры их применения. Но на самом деле есть еще пару интересных фокусов со ссылками:
1) Передача элемента массива по ссылке в оператор «foreach»
2) Передача аргумента функции по ссылке


Передача элемента массива по ссылке в оператор «foreach». В подавляющем большинстве случаев для перебора элементов массива РНР программисты используют оператор «foreach». Это вполне оправдано, так как с ним действительно удобно работать:
Код: выделить все
/* РАБОТА С ОПЕРАТОРОМ foreach */
$array = array(1, 2, 3, 4, 5);
foreach ($array as $a) {
    echo "{$a}<br>";
}


А что в это время происходит внутри интерпретатора? Интерпретатор создает копию массива $array и итерирует по ней. Иногда это полезно, иногда нет. Например, мы хотим умножить все элементы массива на 2:
Код: выделить все
/* УМНОЖЕНИЕ ЭЛЕМЕНТОВ МАССИВА НА 2 ИСПОЛЬЗУЯ ОПЕРАТОР foreach */
$array = array(1, 2, 3, 4, 5); // Определяем массив значений
foreach ($array as $a) {
    $a *= 2; // Умножаем элемент массива на 2
}
// Функция implode() "склеивает" массив в строку используя какую-то
// строку как разделитель
echo implode('<br>', $array); // Выводим каждый элемент массива с новой строки

Этот пример выведет нам «1<br>2 <br>3<br> 4<br> 5». Как видите – ничего не умножилось. При этом синтаксически код работает правильно. Что же мы тогда умножали на 2? А на 2 мы умножали ту самую копию массива, которую создал «foreach», и которая была успешно уничтожена, когда закончилась ее область видимости(тело оператора «foreach»). В РНР5 появилась замечательная возможность «сказать» оператору «foreach», чтобы он не создавал копию массива, а создал только жесткие ссылки(см. выше) на элементы переданного ему массива. В этом методе есть некоторые плюсы:
А) Повышаем быстродействие – не выполняется копирование массива
Б) Уменьшая ресурсоемкость – не занимаем память сервера временным массивом
В) Можем изменить значение элемента массива прямо в цикле
Смотрим пример:
Код: выделить все
/* УМНОЖЕНИЕ ЭЛЕМЕНТОВ МАССИВА НА 2 ИСПОЛЬЗУЯ ОПЕРАТОР foreach */
$array = array(1, 2, 3, 4, 5); // Определяем массив значений
foreach ($array as &$a) {
    $a *= 2; // Умножаем элемент массива на 2
}
echo implode('<br>', $array); // Выводим каждый элемент массива с новой строки

Другими словами, если Вам не нужно создавать копию массива, а просто необходимо выполнить с ним какие-то вычисления, лучше всегда запускать «foreach» таким образом(см. листинг выше). Но тут есть одно предостережение: после окончания работы оператора «foreach», который работал со ссылками, ссылка на последний элемент массива не уничтожается в теле оператора и может быть случайно использована в последующем коде, что может привести к логической ошибке. Смотрите пример:
Код: выделить все
/* УМНОЖЕНИЕ ЭЛЕМЕНТОВ МАССИВА НА 2 ИСПОЛЬЗУЯ ОПЕРАТОР foreach */
$array = array(1, 2, 3, 4, 5); // Определяем массив значений
foreach ($array as &$a) {
    $a *= 2; // Умножаем элемент массива на 2
}
$a = 'abra cadabra'; // Сейчас последний элемент массива равен 'abra cadabra'
echo implode('<br>', $array); // Выводим каждый элемент массива с новой строки


Передача аргумента функции по ссылке. Первоначально я хотел описать эту тему более подробно, но она очень тесно пересекается с другой темой – функции, о которых я напишу в следующий раз. В функции можно передавать аргументы по жесткой ссылке(см. выше), иногда это бывает просто необходимо. Давай посмотрим пример:
Код: выделить все
// Есть функция с одним аргументом, когда она будет запущена
// интерпретатор копирует значение аргумента.
function nonRef($a)
{
    $a .= 'aaa';
    return $a;
}
$a = 'bbb';
$a = nonRef($a);
echo "{$a}<br>";
// Есть другая функция с одним аргументом, который передается в нее по ссылке.
// Теперь функция ничего не будет копировать, а будет работать с областью памяти,
// в которой располагается переданный аргумент.
function ref(&$a)
{
    $a .= 'aaa';
}
$a = 'bbb';
ref($a);
echo "{$a}<br>";
// Как видите результат работы этих 2 участков кода будет одинаков.
// Следующая строка кода вызовет ошибку, нельзя передавать ссылку на константу.
// ref('bbb');

// Тем не менее аргумент передаваемый по ссылке может иметь дефолтное значение
function refDef($a, &$b = 'bbb')
{
    $a .= 'aaa';
    $b .= 'aaa';
    echo "{$a} --- {$b}<br>";
}
refDef('bbb');

// Кроме этого функции могут возвращать ссылки, для этого нужно
// поставить оператор передачи по ссылке "&" перед именем функции
// при ее определении и при каждом последующем вызове.
function &getRef(&$p)
{
    return $p;
}
$a = 1;
$c = &getRef($a); // Равносильно $c = &$a;
$c = 22;
echo "a = {$a}";

Код достаточно хорошо документирован, более детально работу с функциями опишу в следующих статьях.

Я надеюсь, что этот материал поможет Вам разобраться в данной теме. Если Вам не понятен какой-то момент, то посоветую на основе приведенных листингов попробовать написать что-то самостоятельно.
Итерация свойственна человеку, а рекурсия - божественна...
Аватар пользователя
rmk
Web-программист
 
Сообщений: 5
Зарегистрирован:
01 дек 2009, 16:07
Благодарил (а): 0 раз.
Поблагодарили: 1 раз.

Сообщение 01 сен 2010, 00:29

Огромное спасибо за статью!!! Долго искал подробное описание данной темы.Еще раз спасибо автору!!!!
Александр_1989

 

Сообщение 22 июн 2012, 10:14

Специально зарегился, чтобы выразить автору благодарность за (наконец-то найденную мной) нормальную адекватную статью.
Только не понял причем тут ссылки в "ПРАКТИЧЕСКОЕ ПРИМЕНЕНИЕ СИМВОЛИЧЕСКОЙ ССЫЛКИ"
xaocbozzz
Новичок
 
Сообщений: 1
Зарегистрирован:
22 июн 2012, 10:03
Благодарил (а): 1 раз.
Поблагодарили: 0 раз.


Вернуться в PHP и MySQL

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Форум поддержки phpBB
2008 - 2017 © BB3x.ru - русская поддержка форума phpBB3
Создано на основе phpBB® Forum Software © phpBB Group
авторов модов
разработчиков скинов
русификаторов
и всех поклонников phpBB3