<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/language.generators.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'language.generators.syntax.php',
    1 => 'Синтаксис генераторов',
    2 => 'Синтаксис генераторов',
  ),
  'up' => 
  array (
    0 => 'language.generators.php',
    1 => 'Генераторы',
  ),
  'prev' => 
  array (
    0 => 'language.generators.overview.php',
    1 => 'Знакомство с генераторами',
  ),
  'next' => 
  array (
    0 => 'language.generators.comparison.php',
    1 => 'Сравнение генераторов с объектами класса Iterator',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'language/generators.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="language.generators.syntax" class="sect1">
  <h2 class="title">Синтаксис генераторов</h2>

  <p class="para">
   Функция-генератор выглядит как обычная функция, за исключением того, что
   вместо возврата значения генератор выдаёт столько значений, столько
   ему необходимо.
   Каждая функция с оператором <a href="language.generators.syntax.php#control-structures.yield" class="link"><code class="literal">yield</code></a> — функция-генератор.
  </p>

  <p class="para">
   Когда вызывается генератор, он возвращает объект, который можно итерировать.
   При итерации по этому объекту (например, в цикле <a href="control-structures.foreach.php" class="link"><code class="literal">foreach</code></a>), PHP вызывает
   методы итерации объекта каждый раз, когда ему требуется значение, а затем
   сохраняет состояние генератора и при следующем вызове возвращает следующее
   значение.
  </p>

  <p class="para">
   Когда значения в генераторе закончатся, генератор может просто выполнить возврат,
   и вызывающий код продолжится так же, как если бы в массиве закончились
   значения.
  </p>

  <blockquote class="note"><p><strong class="note">Замечание</strong>: 
   <p class="para">
    Генераторы умеют возвращать значения, которые можно получить
    методом <span class="methodname"><a href="generator.getreturn.php" class="methodname">Generator::getReturn()</a></span>.
   </p>
  </p></blockquote>

  <div class="sect2" id="control-structures.yield">
   <h3 class="title">Ключевое слово <strong class="command">yield</strong></h3>

   <p class="para">
    Сердце функции-генератора — ключевое слово <strong class="command">yield</strong>.
    В простейшей форме инструкция yield похожа на инструкцию
    return, за исключением того, что вместо остановки выполнения функции и возврата,
    yield отдаёт значение коду, который выполняет цикл над генератором,
    и приостанавливает выполнение функции генератора.
   </p>

   <div class="example" id="example-1">
    <p><strong>Пример #1 Простой пример выдачи значений</strong></p>
    <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function </span><span style="color: #0000BB">gen_one_to_three</span><span style="color: #007700">()<br />{<br />    for (</span><span style="color: #0000BB">$i </span><span style="color: #007700">= </span><span style="color: #0000BB">1</span><span style="color: #007700">; </span><span style="color: #0000BB">$i </span><span style="color: #007700">&lt;= </span><span style="color: #0000BB">3</span><span style="color: #007700">; </span><span style="color: #0000BB">$i</span><span style="color: #007700">++) {<br />        </span><span style="color: #FF8000">// Обратите внимание, что переменная $i сохраняет значение между вызовами<br />        </span><span style="color: #007700">yield </span><span style="color: #0000BB">$i</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">$generator </span><span style="color: #007700">= </span><span style="color: #0000BB">gen_one_to_three</span><span style="color: #007700">();<br /><br />foreach (</span><span style="color: #0000BB">$generator </span><span style="color: #007700">as </span><span style="color: #0000BB">$value</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$value</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
    </div>

    
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

    <div class="example-contents screen">
<div class="cdata"><pre>
1
2
3
</pre></div>
    </div>
   </div>

   <blockquote class="note"><p><strong class="note">Замечание</strong>: 
    <p class="para">
     Внутренне последовательные целочисленные ключи свяжутся с полученными значениями,
     как и в случае с неассоциативным массивом.
    </p>
   </p></blockquote>

   <div class="sect3" id="control-structures.yield.associative">
    <h4 class="title">Получение значений с ключами</h4>

    <p class="para">
     PHP также поддерживает ассоциативные массивы, и генераторы — не исключение.
     Помимо получения простых значений, как показывает пример, разрешается также
     одновременно получить ключ.
    </p>

    <p class="para">
     Синтаксис получения пары ключ и значение очень похож на синтаксис определения
     ассоциативных массивов, как показывает следующий пример.
    </p>

    <div class="example" id="example-2">
     <p><strong>Пример #2 Получение пар ключ/значение</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">/* Переменная $input содержит пары ключ и значение, которые разделили точкой с запятой */<br /><br /></span><span style="color: #0000BB">$input </span><span style="color: #007700">= &lt;&lt;&lt;'EOF'<br /></span><span style="color: #DD0000">1;PHP;Любит знаки доллара<br />2;Python;Любит пробелы<br />3;Ruby;Любит блоки<br /></span><span style="color: #007700">EOF;<br /><br />function </span><span style="color: #0000BB">input_parser</span><span style="color: #007700">(</span><span style="color: #0000BB">$input</span><span style="color: #007700">)<br />{<br />    foreach (</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"\n"</span><span style="color: #007700">, </span><span style="color: #0000BB">$input</span><span style="color: #007700">) as </span><span style="color: #0000BB">$line</span><span style="color: #007700">) {<br />        </span><span style="color: #0000BB">$fields </span><span style="color: #007700">= </span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">';'</span><span style="color: #007700">, </span><span style="color: #0000BB">$line</span><span style="color: #007700">);<br />        </span><span style="color: #0000BB">$id </span><span style="color: #007700">= </span><span style="color: #0000BB">array_shift</span><span style="color: #007700">(</span><span style="color: #0000BB">$fields</span><span style="color: #007700">);<br /><br />        yield </span><span style="color: #0000BB">$id </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">;<br />    }<br />}<br /><br />foreach (</span><span style="color: #0000BB">input_parser</span><span style="color: #007700">(</span><span style="color: #0000BB">$input</span><span style="color: #007700">) as </span><span style="color: #0000BB">$id </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$id</span><span style="color: #DD0000">:\n"</span><span style="color: #007700">;<br />    echo </span><span style="color: #DD0000">"    </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #0000BB">0</span><span style="color: #007700">]</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />    echo </span><span style="color: #DD0000">"    </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #0000BB">1</span><span style="color: #007700">]</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
1:
    PHP
    Любит знаки доллара
2:
    Python
    Любит пробелы
3:
    Ruby
    Любит блоки
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="control-structures.yield.null">
    <h4 class="title">Получение значений null</h4>

    <p class="para">
     Чтобы получить значение <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong>, нужно вызвать yield без аргументов. Ключ сгенерируется
     автоматически.
    </p>

    <div class="example" id="example-3">
     <p><strong>Пример #3 Получение <strong><code><a href="reserved.constants.php#constant.null">null</a></code></strong></strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function </span><span style="color: #0000BB">gen_three_nulls</span><span style="color: #007700">()<br />{<br />    foreach (</span><span style="color: #0000BB">range</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">3</span><span style="color: #007700">) as </span><span style="color: #0000BB">$i</span><span style="color: #007700">) {<br />        yield;<br />    }<br />}<br /><br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">iterator_to_array</span><span style="color: #007700">(</span><span style="color: #0000BB">gen_three_nulls</span><span style="color: #007700">()));<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
array(3) {
  [0]=&gt;
  NULL
  [1]=&gt;
  NULL
  [2]=&gt;
  NULL
}
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="control-structures.yield.references">
    <h4 class="title">Получение значения по ссылке</h4>

    <p class="para">
     Функции-генераторы умеют возвращать значения как по ссылке, так и по значению. Это делается так же,
     как и <a href="functions.returning-values.php" class="link">возврат ссылок из функций</a>:
     добавлением амперсанда (&amp;) к имени функции.
    </p>

    <div class="example" id="example-4">
     <p><strong>Пример #4 Получение значений по ссылке</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function &amp;</span><span style="color: #0000BB">gen_reference</span><span style="color: #007700">()<br />{<br />    </span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">3</span><span style="color: #007700">;<br /><br />    while (</span><span style="color: #0000BB">$value </span><span style="color: #007700">&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br />        yield </span><span style="color: #0000BB">$value</span><span style="color: #007700">;<br />    }<br />}<br /><br /></span><span style="color: #FF8000">/* Обратите внимание, что можно изменять значение переменной $number в цикле,<br /> * и поскольку генератор возвращает ссылку, переменная $value<br /> * в функции gen_reference() также изменится. */<br /></span><span style="color: #007700">foreach (</span><span style="color: #0000BB">gen_reference</span><span style="color: #007700">() as &amp;</span><span style="color: #0000BB">$number</span><span style="color: #007700">) {<br />    echo (--</span><span style="color: #0000BB">$number</span><span style="color: #007700">).</span><span style="color: #DD0000">'... '</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
2... 1... 0...
</pre></div>
     </div>
    </div>
   </div>

   <div class="sect3" id="control-structures.yield.from">
    <h4 class="title">Делегирование генератора через <strong class="command">yield from</strong></h4>

    <p class="para">
     Делегирование генератора позволяет получать значения
     из другого генератора, объекта <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span> или массива
     через ключевые слова <strong class="command">yield from</strong>.
     Внешний генератор будет возвращать значения из внутреннего генератора,
     объекта или массива до тех пор, пока они не перестанут действовать, после чего
     выполнение продолжится во внешнем генераторе.
    </p>

    <p class="para">
     Если генератор используется с ключевыми словами <strong class="command">yield from</strong>, выражение
     <strong class="command">yield from</strong> также будет возвращать значения из
     внутреннего генератора.
    </p>

    <div class="caution"><strong class="caution">Предостережение</strong>
     <h1 class="title">Сохранение в массив (например, через функцию <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span>)</h1>

     <p class="para">
      Ключевые слова <strong class="command">yield from</strong> не сбрасывают ключи. Ключи, которые вернул
      объект <span class="classname"><a href="class.traversable.php" class="classname">Traversable</a></span> или массив, сохранятся.
      Поэтому некоторые значения могут пересекаться по ключам с другими выражениями
      <strong class="command">yield</strong> или <strong class="command">yield from</strong>, что при записи
      в массив перезапишет прежние значения этим ключом.
     </p>

     <p class="para">
      Распространенный случай, когда это имеет значение, — функция <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span>,
      которая возвращает массив с ключом по умолчанию, что иногда приводит
      к неожиданным результатам. У функции <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span> есть второй параметр
      <code class="parameter">preserve_keys</code>, которому можно присвоить значение <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>
      для генерации собственных ключей и игнорирования ключей,
      которые передаются из объекта <span class="classname"><a href="class.generator.php" class="classname">Generator</a></span>.
     </p>

     <div class="example" id="example-5">
      <p><strong>Пример #5 Выражение <strong class="command">yield from</strong> с функцией <span class="function"><a href="function.iterator-to-array.php" class="function">iterator_to_array()</a></span></strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function </span><span style="color: #0000BB">inner</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">1</span><span style="color: #007700">; </span><span style="color: #FF8000">// Ключ 0<br />    </span><span style="color: #007700">yield </span><span style="color: #0000BB">2</span><span style="color: #007700">; </span><span style="color: #FF8000">// Ключ 1<br />    </span><span style="color: #007700">yield </span><span style="color: #0000BB">3</span><span style="color: #007700">; </span><span style="color: #FF8000">// Ключ 2<br /></span><span style="color: #007700">}<br /><br />function </span><span style="color: #0000BB">gen</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">0</span><span style="color: #007700">; </span><span style="color: #FF8000">// Ключ 0<br />    </span><span style="color: #007700">yield from </span><span style="color: #0000BB">inner</span><span style="color: #007700">(); </span><span style="color: #FF8000">// Ключи 0-2<br />    </span><span style="color: #007700">yield </span><span style="color: #0000BB">4</span><span style="color: #007700">; </span><span style="color: #FF8000">// Ключ 1<br /></span><span style="color: #007700">}<br /><br /></span><span style="color: #FF8000">// Задайте false вторым параметром для получения массива [0, 1, 2, 3, 4]<br /></span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">iterator_to_array</span><span style="color: #007700">(</span><span style="color: #0000BB">gen</span><span style="color: #007700">()));<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

      <div class="example-contents screen">
<div class="cdata"><pre>
array(3) {
  [0]=&gt;
  int(1)
  [1]=&gt;
  int(4)
  [2]=&gt;
  int(3)
}
</pre></div>
      </div>
     </div>
    </div>

    <div class="example" id="example-6">
     <p><strong>Пример #6 Основы работы с выражением <strong class="command">yield from</strong></strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br />    yield from [</span><span style="color: #0000BB">3</span><span style="color: #007700">, </span><span style="color: #0000BB">4</span><span style="color: #007700">];<br />    yield from new </span><span style="color: #0000BB">ArrayIterator</span><span style="color: #007700">([</span><span style="color: #0000BB">5</span><span style="color: #007700">, </span><span style="color: #0000BB">6</span><span style="color: #007700">]);<br />    yield from </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">();<br />    yield </span><span style="color: #0000BB">9</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">10</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">7</span><span style="color: #007700">;<br />    yield from </span><span style="color: #0000BB">eight</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">eight</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">8</span><span style="color: #007700">;<br />}<br /><br />foreach (</span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">() as </span><span style="color: #0000BB">$num</span><span style="color: #007700">)<br />{<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$num</span><span style="color: #DD0000"> "</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
1 2 3 4 5 6 7 8 9 10
</pre></div>
     </div>
    </div>

    <div class="example" id="example-7">
     <p><strong>Пример #7 Выражение <strong class="command">yield from</strong> и возвращаемые значения</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #007700">function </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br />    yield </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br />    yield from [</span><span style="color: #0000BB">3</span><span style="color: #007700">, </span><span style="color: #0000BB">4</span><span style="color: #007700">];<br />    yield from new </span><span style="color: #0000BB">ArrayIterator</span><span style="color: #007700">([</span><span style="color: #0000BB">5</span><span style="color: #007700">, </span><span style="color: #0000BB">6</span><span style="color: #007700">]);<br />    yield from </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">();<br />    return yield from </span><span style="color: #0000BB">nine_ten</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">seven_eight</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">7</span><span style="color: #007700">;<br />    yield from </span><span style="color: #0000BB">eight</span><span style="color: #007700">();<br />}<br /><br />function </span><span style="color: #0000BB">eight</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">8</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">nine_ten</span><span style="color: #007700">()<br />{<br />    yield </span><span style="color: #0000BB">9</span><span style="color: #007700">;<br />    return </span><span style="color: #0000BB">10</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #0000BB">$gen </span><span style="color: #007700">= </span><span style="color: #0000BB">count_to_ten</span><span style="color: #007700">();<br /><br />foreach (</span><span style="color: #0000BB">$gen </span><span style="color: #007700">as </span><span style="color: #0000BB">$num</span><span style="color: #007700">) {<br />    echo </span><span style="color: #DD0000">"</span><span style="color: #0000BB">$num</span><span style="color: #DD0000"> "</span><span style="color: #007700">;<br />}<br /><br />echo </span><span style="color: #0000BB">$gen</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getReturn</span><span style="color: #007700">();<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
1 2 3 4 5 6 7 8 9 10
</pre></div>
     </div>
    </div>
   </div>
  </div>
 </div><?php manual_footer($setup); ?>