Архитектура PHP и передача по ссылке против передачи по значению

Ищу предложения от архитекторов PHP!

Я не очень хорошо знаком с PHP, но взял на себя поддержку большого пакета аналитики, написанного на этом языке. Архитектура предназначена для считывания отчетных данных в большие массивы ключей и значений, которые передаются через различные модули синтаксического анализа для извлечения тех параметров отчета, которые известны каждому из этих модулей. Известные параметры удаляются из главного массива, а любые оставшиеся параметры, которые не были распознаны ни одним из модулей, сбрасываются в своего рода всеобъемлющий отчет, показывающий «неизвестные» точки данных.

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

Ниже приведен суперупрощенный пример:

#!/usr/bin/php
<?php

$values = Array("a"=>1, "b"=>2, "c"=>3, "d"=>4 );


class ParserA {
    private $a = null;
    public function __construct(&$myvalues) {
        $this->a = $myvalues["a"];
        unset($myvalues["a"]);
    }
    public function toString() { return $this->a; }
}

// pass-by-value
function parse_b($myvalues) {
    $b = $myvalues["b"];
    unset($myvalues["b"]);
    return Array($b, $myvalues);
}

// pass-by-reference
function parse_c(&$myvalues) {
    echo "c=".$myvalues["c"]."\n";
    unset($myvalues["c"]);
}

// Show beginning state
print_r($values);

// will echo "1" and remove "a" from $values
$a = new ParserA($values);
echo "a=".$a->toString()."\n";
print_r($values);

// w ill echo "2" and remove "b" from $values
list($b, $values) = parse_b($values);
echo "b=".$b."\n";
print_r($values);

// will echo "3" and remove "c" from $values
parse_c($values);
print_r($values);

?>

Вывод будет:

Array
(
    [a] => 1
    [b] => 2
    [c] => 3
    [d] => 4
)
a=1
Array
(
    [b] => 2
    [c] => 3
    [d] => 4
)
b=2
Array
(
    [c] => 3
    [d] => 4
)
c=3
Array
(
    [d] => 4
)

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

Я бы предпочел выбрать одну методологию и придерживаться ее. Для этого я также хотел бы знать, что наиболее эффективно; мое чтение документации PHP показывает, что, поскольку она использует копирование при записи, не должно быть большой разницы в производительности между использованием указателей на прямую передачу объекта и повторным чтением возвращаемого значения. Я также предпочел бы использовать объектно-ориентированную структуру, но меня не устраивают скрытые изменения, вносимые во входной параметр конструктора.

Какой из трех методов вызова, ParserA(), parse_b() и parse_c(), является наиболее подходящим стилем?


person ryandenki    schedule 08.09.2009    source источник


Ответы (4)


Я не очень разбираюсь в PHP, но по моему опыту передача по значению лучше. Таким образом, код не будет иметь побочных эффектов, а это значит, что его будет легче понять и поддерживать, а также делать с ним всевозможные сумасшедшие вещи, например, использовать его в качестве обратного вызова для функции map. Так что я полностью за parse_b способ делать вещи.

person vava    schedule 08.09.2009

К вашему сведению: в PHP объекты всегда передаются по ссылке, несмотря ни на что. Также, если у вас есть массив с объектами и скалярными значениями в нем, скалярные значения передаются по значению, а объекты по ссылке.

person Jacob Kiers    schedule 08.09.2009
comment
Я не уверен, что они всегда передаются по ссылке. При использовании unset для содержимого значения параметра unset($myvalues[a])' значение a не удаляется из массива $values ​​в родительской области, а только из массива $myvalues ​​в области действия функции. Это означает, что он передается по значению, а не по ссылке. Может быть, мой тест был неправильным? - person ryandenki; 08.09.2009
comment
Это правда, ссылка прерывается (создается копия исходной переменной) только при изменении ее значения. Итак, unset($myvalues[a]) вызывает следующие шаги: создать копию '$myvalues', удалить элемент с ключом 'a' из '$myvalues' - person Jacco; 08.09.2009
comment
$myvalues в этом примере не является объектом, это массив (который передается путем копирования при записи значения) - person gnarf; 08.09.2009
comment
@ryandenki: я конкретно говорил об объектах, а не о скалярных значениях. Как правильно заметил гнарф. - person Jacob Kiers; 10.09.2009

Как правило, в PHP не используйте ссылки, если в этом нет особой необходимости. ссылки в PHP также не такие, какими их ожидает большинство людей:

«Ссылки в PHP — это средство доступа к одному и тому же содержимому переменных под разными именами. Они не похожи на указатели C; вместо этого они являются псевдонимами таблицы символов».

см. также: php.net: Что такое ссылки

Итак, вкратце:
Правильный способ обработки этого PHP – создать объект, который передает переменные по значению, или манипулировать массивом с помощью array_map (array_map позволяет применять функцию обратного вызова к элементам массива.)

person Jacco    schedule 08.09.2009

Я бы проголосовал против предложенных методов в целом, но из них, я думаю, parse_b имеет лучшую идею.

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

class ParserA {
  private $a = null;
  public function __construct(My_Data_Class $data) {
    $this->a = $data->popValue("a");
  }
  public function toString() { return $this->a; }
}

И пример реализации

class My_Data_Class {
  protected $_data;
  public function __construct(array $data) {
    $this->_data = $data;
  }
  public function popValue($key) {
    if (isset($this->_data[$key])) {
       $value = $this->_data[$key];
       unset($this->_data[$key]);
       return $value;
    }
  }
}
person gnarf    schedule 08.09.2009