Использование ссылок в PHP - PullRequest
3 голосов
/ 03 июня 2010

Я задаю этот вопрос, потому что я узнал, что при программировании и проектировании у вас должна быть веская причина для принятия решений. Я учусь на php, и я нахожусь на перепутье, я использую простое увеличение, чтобы попытаться донести то, что я спрашиваю. Я, конечно, не здесь, чтобы начать дебаты о плюсах / минусах ссылок, но когда дело доходит до php, что является лучшей практикой программирования:

function increment(&$param) {
      ++$param;
}

против

function increment($param){
 return ++$param;
}

$param = increment($param);

Ответы [ 8 ]

10 голосов
/ 03 июня 2010

Во-первых, ссылки не являются указателями .

Я попробовал код, указанный @John в его ответе, но получил странные результаты. Оказывается, microtime() возвращает строку. Арифметика ненадежна, и я даже получил отрицательные результаты на некоторых пробегах. Для получения значения в виде числа с плавающей запятой следует использовать microtime(true).

Я добавил еще один тест без вызова функции, просто увеличив переменную:

<?php

$param = 1;

$start = microtime(true);

for($i = 1; $i <= 1000000; $i++) {
    $param++;
}

$end = microtime(true);

echo "increment: " . ($end - $start) . "\n";

Результаты на моей машине, Macbook 2,4 ГГц с PHP 5.3.2.

  • вызов функции с передачей по ссылке: 2,14 сек.
  • вызов функции с передачей по значению: 2,26 с.
  • нет вызова функции, просто голое приращение: 0,42 сек.

Так что, по-видимому, при передаче по ссылке преимущество в производительности составляет 5,3%, а при полном отказе от вызова функции - в 81%.

Я предполагаю, что пример увеличения целого числа является произвольным, и ОП действительно спрашивает об общем преимуществе передачи по ссылке. Но я просто предлагаю этот пример, чтобы продемонстрировать, что только вызов функции влечет за собой гораздо больше затрат, чем метод передачи параметров.

Так что, если вы пытаетесь решить, как микрооптимизировать передачу параметров, значит, вы мудры на пенни и глупы.

Есть и другие причины, по которым вам следует избегать ссылок. Хотя они могут упростить несколько алгоритмов, особенно когда вы управляете двумя или более структурами данных, которые должны иметь одинаковые базовые данные:

  • Они дают функциям побочные эффекты. В общем, вам следует избегать функций с побочными эффектами, поскольку они делают программу более непредсказуемой (например, «Хорошо, как это получилось?
  • Они вызывают ошибки. Если вы делаете переменную ссылкой, вы должны не забыть сбросить ее, прежде чем присваивать ей значение, если только вы не хотите изменить базовое значение набора ссылок. Это часто происходит после запуска цикла foreach по ссылке и последующего повторного использования переменной цикла.
3 голосов
/ 03 июня 2010

Зависит от того, для чего предназначена функция. Если его целью является изменение ввода, используйте ссылки. Если цель состоит в том, чтобы вычислить некоторые данные на основе ввода, а , а не , чтобы изменить ввод, во что бы то ни стало, используйте обычный return.

Взять, к примеру, array_push:

int array_push(array &$array, mixed $var[, mixed $...])

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

array_push($array, 42); // most likely use case

// if you really need both versions, just do:
$old = $array;
array_push($array, 42);

Если array_push не принимает ссылки, вам нужно сделать это:

// array_push($array, $var[, $...])
$array = array_push($array, 42); // quite a waste to do this every time

С другой стороны, чисто вычислительная функция, такая как pow, не должна изменять исходное значение:

number pow(number $base, number $exp)

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

$x = 123;
$y = pow($x, 42); // most likely use case

Если pow взял ссылки, вам нужно сделать это:

// pow(&$base, $exp)
$x = 123;
$y = $x; // nuisance
pow($y, 42);
2 голосов
/ 03 июня 2010

Вторая версия:

function increment($param){
 return $param++;
}

$param = increment($param);

Ничего не делает. increment () возвращает $ param. Возможно, вы имели в виду ++ $ param или $ param + 1;

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

2 голосов
/ 03 июня 2010

Лучшая практика - не писать функцию, когда выражение будет делать.

$param++;

- это все, что вам нужно.

1 голос
/ 03 июня 2010

Я только что провел пару быстрых тестов со следующим кодом:

<?php

function increment(&$param){
 $param++;
}
$param = 1;

$start = microtime();

for($i = 1; $i <= 1000000; $i++) {
    increment($param);
}

$end = microtime();

echo $end - $start;

?>

Это возвращалось последовательно около .42 до .43, где следующий код возвращал около .55 до .59

<?php

function increment($param){
 return $param++;
}
$param = 1;

$start = microtime();

for($i = 1; $i <= 1000000; $i++) {
    $param = increment($param);
}

$end = microtime();

echo $end - $start;

?>

Так что я бы сказал, что ссылки быстрее, но только в крайних случаях.

1 голос
/ 03 июня 2010

По определению, приращение значения переменной (по крайней мере, в PHP) приводит к изменению переменной. Он не принимает значение, увеличьте его на 1 и верните; скорее это изменяет переменную, содержащую это значение.

Таким образом, ваш первый пример будет лучшим вариантом, так как он берет ссылку (PHP на самом деле не делает указатели как таковые) на $param и пост-приращивает ее.

0 голосов
/ 03 июня 2010

Я бы сказал, что это будет зависеть от того, что вы делаете. Если вы пытаетесь взаимодействовать с большим набором данных, не требуя дополнительной копии в памяти, - переходите по значению (ваш второй пример). Если вы хотите сохранить память и напрямую взаимодействовать с объектом - передайте по ссылке (ваш первый пример)

0 голосов
/ 03 июня 2010

Я думаю, что ваш пример немного абстрактен.

Нет проблем с использованием указателей, но в большинстве реальных случаев вы, вероятно, изменяете объект, а не int, и в этом случае вам не нужна ссылка (по крайней мере, в PHP5).

...