2011-09-29 23 views
18

Bir pdo ifadesi yazarken, bir değişkenin değerini tekrarlamak mümkün mü? Demek istediğim:php pdo yinelenen değişkenleri hazırlayın

$query = "UPDATE users SET firstname = :name WHERE firstname = :name"; 
$stmt = $dbh -> prepare($query); 
$stmt -> execute(array(":name" => "Jackie")); 

Lütfen ": name" ad sahibini yinelediğimde, yalnızca değeri bir kez verdiğimi unutmayın. Bu işi nasıl yapabilirim?

cevap

19

Basit cevap şudur: Yapamazsınız. PDO, bazı sınırlamaları olan hazırlanmış ifadeler için bir soyutlama kullanır. Ne yazık ki bu işe-etrafında bir şeyler kullanarak bu tür tekrarlanan adlı parametreler desteklenir PDO/MySQL sürücüsünün bazı sürümlerinde taklit hazırlanmış tablolar gibi belirli durumlarda,

$query = "UPDATE users SET firstname = :name1 WHERE firstname = :name2"; 
$stmt = $dbh -> prepare($query); 
$stmt -> execute(array(":name1" => "Jackie", ":name2" => "Jackie")); 

gibi var biridir; bununla birlikte, bu durum gevrek olduğu için (örneğin yükseltmelerin daha fazla iş gerektirmesini sağlayabilir) güvenilmemelidir.

adlandırılmış bir parametrenin birden görünüşleri desteklemek istiyorsanız

, her zaman extend PDO and PDOStatement (klasik miras yoluyla veya niteliği ile), ya da sadece PDOStatement ve PDO::ATTR_STATEMENT_CLASS niteliğini ayarlayarak açıklamada sınıfı olarak sınıfınızı ayarlayabilirsiniz. Genişletilmiş PDOStatement (veya PDO::prepare), adlandırılmış parametreleri ayıklayabilir, tekrarları arayabilir ve otomatik olarak değiştirmeler oluşturabilir. Ayrıca bu kopyaları da kaydeder. Bağlama ve yürütme yöntemleri, adlandırılmış bir parametreyi geçtiğinde, parametrenin yinelenip yinelenmediğini test eder ve her bir değiştirme parametresine değeri bağlar.

Not: Aşağıdaki örnek test edilmemiştir ve büyük olasılıkla hatalar vardır (kod açıklamalarında bazı ifadelerin ayrıştırılmasıyla ilişkilidir).

class PDO_multiNamed extends PDO { 
    function prepare($stmt) { 
     $params = array_count_values($this->_extractNamedParams()); 
     # get just named parameters that are repeated 
     $repeated = array_filter($params, function ($count) { return $count > 1; }); 
     # start suffixes at 0 
     $suffixes = array_map(function ($x) {return 0;}, $repeated); 
     /* Replace repeated named parameters. Doesn't properly parse statement, 
     * so may replacement portions of the string that it shouldn't. Proper 
     * implementation left as an exercise for the reader. 
     * 
     * $param only contains identifier characters, so no need to escape it 
     */ 
     $stmt = preg_replace_callback(
      '/(?:' . implode('|', array_keys($repeated)) . ')(?=\W)/', 
      function ($matches) use (&$suffixes) { 
       return $matches[0] . '_' . $suffixes[$matches[0]]++; 
      }, $stmt); 
     $this->prepare($stmt, 
         array(
          PDO::ATTR_STATEMENT_CLASS => array('PDOStatement_multiNamed', array($repeated))) 
      ); 
    } 

    protected function _extractNamedParams() { 
     /* Not actually sufficient to parse named parameters, but it's a start. 
     * Proper implementation left as an exercise. 
     */ 
     preg_match_all('/:\w+/', $stmt, $params); 
     return $params[0]; 
    } 
} 

class PDOStatement_multiNamed extends PDOStatement { 
    protected $_namedRepeats; 

    function __construct($repeated) { 
     # PDOStatement::__construct doesn't like to be called. 
     //parent::__construct(); 
     $this->_namedRepeats = $repeated; 
    } 

    /* 0 may not be an appropriate default for $length, but an examination of 
    * ext/pdo/pdo_stmt.c suggests it should work. Alternatively, leave off the 
    * last two arguments and rely on PHP's implicit variadic function feature. 
    */ 
    function bindParam($param, &$var, $data_type=PDO::PARAM_STR, $length=0, $driver_options=array()) { 
     return $this->_bind(__FUNCTION__, $param, func_get_args()); 
    } 

    function bindValue($param, $var, $data_type=PDO::PARAM_STR) { 
     return $this->_bind(__FUNCTION__, $param, func_get_args()); 
    } 

    function execute($input_parameters=NULL) { 
     if ($input_parameters) { 
      $params = array(); 
      # could be replaced by array_map_concat, if it existed 
      foreach ($input_parameters as $name => $val) { 
       if (isset($this->_namedRepeats[$param])) { 
        for ($i=0; $i < $this->_namedRepeats[$param], ++$i) { 
         $params["{$name}_{$i}"] = $val; 
        } 
       } else { 
        $params[$name] = $val; 
       } 
      } 
      return parent::execute($params); 
     } else { 
      return parent::execute(); 
     } 
    } 

    protected function _bind($method, $param, $args) { 
     if (isset($this->_namedRepeats[$param])) { 
      $result = TRUE; 
      for ($i=0; $i < $this->_namedRepeats[$param], ++$i) { 
       $args[0] = "{$param}_{$i}"; 
       # should this return early if the call fails? 
       $result &= call_user_func_array("parent::$method", $args); 
      } 
      return $result; 
     } else { 
      return call_user_func_array("parent::$method", $args); 
     } 
    } 
} 
+0

asla ettik:

function prepareMsSqlQueryParams($query, $params): array { $paramsCount = []; $newParams = []; $pattern = '/(:' . implode('|:', array_keys($params)) . ')/'; $query = preg_replace_callback($pattern, function ($matches) use ($params, &$newParams, &$paramsCount) { $key = ltrim($matches[0], ':'); if (isset($paramsCount[$key])) { $paramsCount[$key]++; $newParams[$key . $paramsCount[$key]] = $params[$key]; return $matches[0] . $paramsCount[$key]; } else { $newParams[$key] = $params[$key]; $paramsCount[$key] = 0; return $matches[0]; } }, $query); return [$query, $newParams]; } 

Sonra bu şekilde kullanabilirsiniz: Ben geçici bir çözüm olarak yardımcı aşağıdaki kullanılan bu yüzden sendikalar sürü ve yinelenen params bir sürü oldukça karmaşık dinamik sorgu var Aynı listede bir 'DUPLICATE KEY UPDATE' deyiminde yinelenen herhangi bir sorun vardı ... – jeroen

+1

Aslında PDO sürücüsüne biraz bağlı, çalışmaya güvenmemelisiniz. – johannes

+0

İlginç, her zaman benim için çalıştı. Bu konuda herhangi bir belge biliyor musunuz? – jeroen

0

Benim durumumda dblib freedts sqlsrv PDO sürücüsüne geçtiğimde bu hata ortaya çıktı. Dblib sürücüsü yinelenen parametre adlarını hata olmadan işledi.

$query = "UPDATE users SET firstname = :name WHERE firstname = :name"; 
$params = [":name" => "Jackie"]; 
// It will return "UPDATE users SET firstname = :name WHERE firstname = :name1"; with appropriate parameters array 
list($query, $params) = prepareMsSqlQueryParams($query, $params); 
$stmt = $dbh->prepare($query); 
$stmt->execute(params);