Модификация аргументов функций
Начнем с того, что сформулируем такое правило: аргументы функций нельзя модифицировать. Это путает того, кто читает Ваш код.
Вот, например, рассмотрим следующий кусок кода:
<?
function fn($val, $qty, $year) {
if ($val > 70) $val -= 2;
...
?>
Как видим, тут кто-то взял и изменил значение аргумента функции (наверно, я
). Причем заметим, что в PHP без явного указания аргументы всегда передаются по значению, а не по ссылке. Если по правилу, то мы должны были сделать вот так:
<?
function fn($val, $qty, $year) {
$res = $val;
if ($val > 70) $res -= 2;
...
?>
Но давайте уточним, что я имею в виду под модификацией аргумента. Это значит, что если я, например, передам в функцию какой-то объект и затем присвою ему значение, то это будет считаться модификацией аргумента. Но если я использую какие-то свойства и методы этого объекта, это не будет считаться модификацией:
<?
void some_method($foo) {
$foo.modifySomeData(); // тут все нормально
$foo = $someObj; // а это неправильно
...
?>
А в чем отличие между передачей параметра по значению и передачей параметра по ссылке? Когда мы передаем параметр в функцию по значению, его модификация внутри этой функции не приводит к его модификации снаружи, т.е. в том коде, откуда была вызвана функция, внутри которой он модифицируется. А если мы передали параметр по ссылке, мы на самом деле передали не его значение, а его адрес, по которому функция может делать с аргументом что угодно. При этом если внутри функции он будет изменен, это «почувствует» и функция, которая вызвала первую функцию.
- Создайте дополнительную переменную для аргумента функции, который модифицируется и присвойте ей значение аргумента;
- Замените все ссылки на этот аргумент новой переменной в коде;
- Протестируйте работу программы.
При этом нужно проверить, если аргумент принимается в функции по ссылке, не используется ли модифицированный аргумент в вызвавшем нашу функцию коде. Если используется, то его модифицированное значение нужно естественно вернуть этому вызвавшему коду (с помощью return). А если нашей функции передается несколько параметров по ссылке, каждый из которых модифицируется внутри, то придется возвращать их модифицированные значения скопом в каком-то объекте (или массиве), либо разбить функцию на несколько более мелких, чтобы принимать и возвращать только по одному значению.
И, напоследок, приведу небольшой пример, похожий на тот, который расположился в самом начале поста. Допустим, у нас есть какая-то функция, которая один параметр принимает по ссылке:
<?
function fn(&$val, $qty, $year) {
if ($val > 70) $val -= 2;
if ($qty > 120) $val -= 1;
if ($year > 2007) $val -= 4;
}
?>
Сделаем ее правильной:
<?
function fn(&$val, $qty, $year) {
$res = $val;
if ($val > 70) $res -= 2;
if ($qty > 120) $res -= 1;
if ($year > 2007) $res -= 4;
return $res;
}
?>
Вот и еще один небольшой приемчик в нашу копилку рефакторинг-приемов
Ноябрь 9th, 2009
А как вы предлагаете быть в том случае, если требуется функция, которая не возвращает значение, а изменяет указанную переменную?
К примеру: fn($val, 200, 2, 2009)?