<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/book.pdo.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'pdo.prepared-statements.php',
    1 => 'Подготовленные запросы и хранимые процедуры',
    2 => 'Подготовленные запросы и хранимые процедуры',
  ),
  'up' => 
  array (
    0 => 'book.pdo.php',
    1 => 'PDO',
  ),
  'prev' => 
  array (
    0 => 'pdo.transactions.php',
    1 => 'Транзакции и автоматическая фиксация изменений',
  ),
  'next' => 
  array (
    0 => 'pdo.error-handling.php',
    1 => 'Ошибки и их обработка',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'reference/pdo/prepared-statements.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="pdo.prepared-statements" class="chapter">
 <h1 class="title">Подготовленные запросы и хранимые процедуры</h1>

 <p class="para">
  Большинство баз данных поддерживают концепцию подготовленных запросов. Что это
  такое? Это можно описать, как некий вид скомпилированного шаблона SQL запроса,
  который будет запускаться приложением и настраиваться с помощью входных
  параметров. У подготовленных запросов есть два главных преимущества:
 </p>
 <ul class="itemizedlist">
  <li class="listitem">
   <span class="simpara">
    Запрос необходимо однажды подготовить и затем его можно запускать столько
    раз, сколько нужно, причём как с теми же, так и с отличающимися параметрами.
    Когда запрос подготовлен, СУБД анализирует его, компилирует и оптимизирует
    план его выполнения. В случае сложных запросов этот процесс может занимать
    ощутимое время и заметно замедлить работу приложения, если потребуется много
    раз выполнять запрос с разными параметрами. При использовании подготовленного
    запроса СУБД анализирует/компилирует/оптимизирует запрос любой сложности только
    один раз, а приложение запускает на выполнение уже подготовленный шаблон. Таким
    образом подготовленные запросы потребляют меньше ресурсов и работают быстрее.
   </span>
  </li>
  <li class="listitem">
   <span class="simpara">
    Параметры подготовленного запроса не требуется экранировать кавычками; драйвер
    это делает автоматически. Если в приложении используются исключительно
    подготовленные запросы, разработчик может быть уверен, что никаких
    SQL-инъекций случиться не может (однако, если другие части текста запроса создаются
    с неэкранированным вводом, то SQL инъекция по-прежнему возможна).
   </span>
  </li>
 </ul>
 <p class="para">
  Подготовленные запросы также полезны тем, что PDO может эмулировать их, если
  драйвер базы данных не имеет подобной функциональности. Это значит, что приложение
  может пользоваться одной и той же методикой доступа к данным независимо от
  возможностей СУБД.
 </p>
 <p class="para">
  <div class="example" id="example-1">
   <p><strong>Пример #1 Повторяющиеся вставки в базу с использованием подготовленных запросов</strong></p>
   <div class="example-contents"><p>
    В этом примере 2 раза выполняется INSERT запрос с разными значениями
    <code class="literal">name</code> и <code class="literal">value</code>, которые подставляются
    вместо соответствующих псевдопеременных:
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #DD0000">':name'</span><span style="color: #007700">, </span><span style="color: #0000BB">$name</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #DD0000">':value'</span><span style="color: #007700">, </span><span style="color: #0000BB">$value</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// вставим одну строку<br /></span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #DD0000">'one'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /><br /></span><span style="color: #FF8000">// теперь другую строку с другими значениями<br /></span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #DD0000">'two'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  <div class="example" id="example-2">
   <p><strong>Пример #2 Повторяющиеся вставки в базу с использованием подготовленных запросов</strong></p>
   <div class="example-contents"><p>
    В этом примере 2 раза выполняется INSERT запрос с разными значениями
    <code class="literal">name</code> и <code class="literal">value</code>, которые подставляются вместо
    псевдопеременных <code class="literal">?</code>.
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT INTO REGISTRY (name, value) VALUES (?, ?)"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">$name</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #0000BB">2</span><span style="color: #007700">, </span><span style="color: #0000BB">$value</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// вставим одну строку<br /></span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #DD0000">'one'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">1</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /><br /></span><span style="color: #FF8000">// теперь другую строку с другими значениями<br /></span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #DD0000">'two'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">2</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  <div class="example" id="example-3">
   <p><strong>Пример #3 Выборка данных с использованием подготовленных запросов</strong></p>
   <div class="example-contents"><p>
    В этом примере производится выборка из базы по ключу, который вводит
    пользователь через форму. Пользовательский ввод автоматически заключается
    в кавычки, поэтому нет риска SQL инъекции.
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT * FROM REGISTRY where name = ?"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">([</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">]]);<br />foreach (</span><span style="color: #0000BB">$stmt </span><span style="color: #007700">as </span><span style="color: #0000BB">$row</span><span style="color: #007700">) {<br />  </span><span style="color: #0000BB">print_r</span><span style="color: #007700">(</span><span style="color: #0000BB">$row</span><span style="color: #007700">);<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>

 <p class="para">
  <div class="example" id="example-4">
   <p><strong>Пример #4 Вызов хранимой процедуры с выходными параметрами</strong></p>
   <div class="example-contents"><p>
    Если СУБД поддерживает выходные параметры, приложение может пользоваться ими
    также как и входными. Выходные параметры обычно используют для получения данных
    из хранимых процедур. Пользоваться выходными параметрами несколько сложнее, так
    как разработчику необходимо знать максимальный размер извлекаемых значений ещё
    на этапе задания этих параметров. Если извлекаемое значение окажется больше,
    чем предполагалось, будет вызвана ошибка.
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"CALL sp_returns_string(?)"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">$return_value</span><span style="color: #007700">, </span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">PARAM_STR</span><span style="color: #007700">, </span><span style="color: #0000BB">4000</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// вызов хранимой процедуры<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /><br />print </span><span style="color: #DD0000">"процедура вернула </span><span style="color: #0000BB">$return_value</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>

 <p class="para">
  <div class="example" id="example-5">
   <p><strong>Пример #5 Вызов хранимой процедуры с входным/выходным параметром</strong></p>
   <div class="example-contents"><p>
    Можно задать параметр одновременно входным и выходным; синтаксис при этом тот же,
    что и для выходных параметров. В следующем примере строка &#039;привет&#039; передаётся в
    хранимую процедуру, а затем эта строка будет заменена возвращаемым значением.
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"CALL sp_takes_string_returns_string(?)"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #DD0000">'привет'</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bindParam</span><span style="color: #007700">(</span><span style="color: #0000BB">1</span><span style="color: #007700">, </span><span style="color: #0000BB">$value</span><span style="color: #007700">, </span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">PARAM_STR</span><span style="color: #007700">|</span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">PARAM_INPUT_OUTPUT</span><span style="color: #007700">, </span><span style="color: #0000BB">4000</span><span style="color: #007700">);<br /><br /></span><span style="color: #FF8000">// вызов хранимой процедуры<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">();<br /><br />print </span><span style="color: #DD0000">"процедура вернула </span><span style="color: #0000BB">$value</span><span style="color: #DD0000">\n"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  <div class="example" id="example-6">
   <p><strong>Пример #6 Неправильное использование псевдопеременной</strong></p>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT * FROM REGISTRY where name LIKE '%?%'"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">([</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">]]);<br /><br /></span><span style="color: #FF8000">// псевдопеременная может использоваться только в виде отдельного значения<br /></span><span style="color: #0000BB">$stmt </span><span style="color: #007700">= </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">prepare</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT * FROM REGISTRY where name LIKE ?"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$stmt</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">execute</span><span style="color: #007700">([</span><span style="color: #DD0000">"%</span><span style="color: #0000BB">$_GET</span><span style="color: #007700">[</span><span style="color: #0000BB">name</span><span style="color: #007700">]</span><span style="color: #DD0000">%"</span><span style="color: #007700">]);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
</div>
<?php manual_footer($setup); ?>