Цепочка методов PHP и строки

Это интересная особенность, которую я обнаружил при цепочке методов, и мне трудно ее обойти. Я уверен, что есть решение или другой способ. Это сложно объяснить, но я сделаю все, что в моих силах.

Пример:

У вас есть три функции, которые являются частью класса, а также 2 защищенных свойства, как показано ниже.

class Chain {

  protected $_str  = '';
  protected $_part = 0;

  public function __toString() {
    return implode(' ', $this->_str);
  }

  public function AAA () {
    $this->_str[$this->_part] = 'AAA';
    $this->_part++;
    return $this;
  }

  public function BBB () {
    $this->_str[$this->_part] = 'BBB';
    $this->_part++;
    return $this;
  }

  public function wrap ($str) {
    $part = $this->_part - 1;
    $this->_str[$part] = "({$str})";
    return $this;
  }

}

Теперь при объединении этих методов в цепочку и особенно при использовании метода переноса непреднамеренно добавляются строки из предыдущих цепочек. Пример:

$chain = new Chain();
$chain->AAA()->BBB()->wrap($chain->AAA());
echo $chain;

Вы ожидаете, что строка будет выглядеть как AAA BBB (AAA).

Однако на самом деле возвращается AAA BBB (AAA BBB AAA).

Почему wrap() принимает все предыдущие методы, вызываемые в цепочке, а не только метод, который фактически обернут ею? Каков наилучший способ обойти это, если он есть?


person Chris Bornhoft    schedule 15.07.2012    source источник
comment
Вот мысль: хватит связывать методы, это плохая практика.   -  person tereško    schedule 15.07.2012
comment
$this->_query[$part] = "({$str})"; наверное должно быть $this->_str[$part] = "({$str})";   -  person Nir Alfasi    schedule 15.07.2012
comment
@tereško Ваша мысль полностью субъективна, и цепочка может быть как хорошей, так и плохой в зависимости от ситуации.   -  person Chris Bornhoft    schedule 15.07.2012
comment
вот краткая блок-схема для принятия решения об использовании цепочки: i.stack.imgur.com/WjT6C.jpg   -  person tereško    schedule 15.07.2012


Ответы (3)


$chain->AAA()->BBB() выполняет первые два "AAA" и "BBB" - очевидно.
Затем $chain->AAA(), который входит в wrap($chain->AAA()), выполняет 3-й "AAA".
и, наконец, метод wrap берет все три и оборачивает их () и объединяется с первыми «AAA» и «BBB», используя эту строку: $this->_str[$part] = "({$str})";
которая разрешается в: AAA BBB (AAA BBB AAA).

ОБНОВЛЕНИЕ:
я считаю, что то, что вы пытаетесь сделать, состоит в том, чтобы избежать побочного эффекта возврата this из методов AAA() и BBB(). Это будет достигнуто с помощью следующих изменений:

<?php
class Chain {

  protected $_str  = '';
  protected $_part = 0;

  public function __toString() {
    return implode(' ', $this->_str);
  }

  public function AAA () {
    $this->_str[$this->_part] = 'AAA';
    $this->_part++;
    return "AAA";
  }

  public function BBB () {
    $this->_str[$this->_part] = 'BBB';
    $this->_part++;
    return "BBB";
  }

  public function wrap ($str) {
    $part = $this->_part - 1;
    $this->_str[$part] = "({$str})";
    return $str;
  }

}

$chain = new Chain();
$chain->AAA();
$chain->BBB();
$chain->wrap($chain->AAA());
echo $chain->__toString();

?>
person Nir Alfasi    schedule 15.07.2012
comment
Да, я хорошо знаю, что он делает с моим заданным выводом... ПОЧЕМУ он принимает все предыдущие методы в качестве аргумента, когда передается только один, и что является хорошим способом избежать этого, это мой вопрос. - person Chris Bornhoft; 15.07.2012
comment
Я объяснил это в своем ответе: строка $this->_str[$part] = "({$str})"; берет ВСЕ, что в настоящее время находится в $this->_str, и оборачивает его () - person Nir Alfasi; 15.07.2012
comment
Видите, метод $chain->AAA() возвращает this. Попробуйте позвонить: echo $chain->AAA(); и вы поймете, что я имею в виду. - person Nir Alfasi; 15.07.2012
comment
Спасибо. Я бы ожидал, что он НЕ добавит другие, потому что только один из методов находится в обертке. Глядя на это, я бы не ожидал, что предыдущие вызовы прыгнут внутрь и будут добавлены, связаны цепочкой или нет. Я понимаю, что их разделение даст другой результат, однако мне это кажется странным. - person Chris Bornhoft; 15.07.2012
comment
@Baez, вы можете изменить метод AAA(), чтобы он возвращал «AAA» вместо возврата $this (конечно, то же самое для BBB()) - я считаю, что это решит вашу проблему. побочные эффекты отстой... ;) - person Nir Alfasi; 15.07.2012
comment
@Baez Я думаю, что понимаю, что вы пытаетесь сделать, посмотрите раздел UPDATE в моем ответе и дайте мне знать, если вы это имели в виду. - person Nir Alfasi; 15.07.2012
comment
Да, я действительно думал сделать что-то подобное. Единственная проблема заключается в том, что цепочка должна будет разорваться при использовании этих методов, поскольку передаются строки. Спасибо за помощь, это ценится. Добавьте в свой другой комментарий, я приму ваш ответ. - person Chris Bornhoft; 15.07.2012

Я бы назвал это своего рода «состоянием гонки». Кажется, PHP сначала интерпретирует $chain->AAA()->BBB().

Тогда $chain->_part равно 2, а $chain->_str равно "AAA BBB".

Затем, чтобы иметь возможность вызвать перенос, запускается аргумент, поэтому $chain->AAA().

Тогда $chain->_part равно 3, а $chain->_str равно 'AAA BBB AAA'.

Наконец, вызывается обертка, деформируя «AAA BBB AAA». Я бы отделил вызов wrap() и сбросил $chain->_part до нуля между ними.

person PhilMasterG    schedule 15.07.2012
comment
Это хорошее пошаговое руководство, однако по-прежнему кажется, что метод wrap() должен принимать в качестве аргументов только те методы, которые вы передаете, даже если они связаны. Проблема в том, что развязать нельзя, как избежать этого? Кажется, что решения нет. - person Chris Bornhoft; 15.07.2012
comment
Ну, на самом деле вы можете написать функцию XYZ, в которой вы сбрасываете _part на ноль. Таким образом, вам не нужно расцеплять и по-прежнему сбрасывать _part в нужное время, используя $chain-›AAA()-›BBB()-›XYZ()-›wrap($chain-›AAA()); - person PhilMasterG; 15.07.2012

Очередь вызовов:

1. $chain->AAA() //this is first method not in wrap() method, you have now AAA
2. $chain->BBB() //then next method, you have now AAA BBB
3. $chain->AAA() //again AAA() method inside wrap method, so you have AAA BBB AAA

внутри wrap() вы помещаете строку цепочки (которая представляет собой AAA BBB AAA после 3-го шага) в (), поэтому у вас есть строка AAA BBB AAA в конце массива _part.

person Elbek    schedule 15.07.2012