{"action":"created","comment":{"url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/comments/51846775","id":51846775,"diff_hunk":"@@ -0,0 +1,1247 @@\n+<?php\n+/**\n+ * MySqlQuery class file.\n+ *\n+ * @package Freyja\\Database\n+ * @copyright 2016 SqueezyWeb\n+ * @author Gianluca Merlo <gianluca@squeezyweb.com>\n+ * @since 0.1.0\n+ */\n+\n+namespace Freyja\\Database;\n+\n+use Freyja\\Exceptions\\InvalidArgumentException as InvArgExcp;\n+use RuntimeException;\n+\n+/**\n+ * MySqlQuery class.\n+ *\n+ * @package Freyja\\Database\n+ * @author Gianluca Merlo <gianluca@squeezyweb.com>\n+ * @since 0.1.0\n+ * @version 1.0.0\n+ */\n+class MySqlQuery extends Query implements QueryInterface {\n+  /**\n+   * Query type.\n+   *\n+   * Accepted values: 'select', 'update', 'insert', 'delete'.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var string\n+   */\n+  private $type;\n+\n+  /**\n+   * Target table.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var string\n+   */\n+  private $table;\n+\n+  /**\n+   * SELECT fields.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $select = array();\n+\n+  /**\n+   * COUNT.\n+   *\n+   * True correspond to `COUNT(*)`. Or array containing the fields to be\n+   * counted. Default false.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var boolean|array\n+   */\n+  private $count = false;\n+\n+  /**\n+   * ORDERBY.\n+   *\n+   * Associative array `key => value`, where `key` is the field in which to sort\n+   * the results and `value` is the direction.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $order_by = array();\n+\n+  /**\n+   * GROUPBY.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var string\n+   */\n+  private $group_by;\n+\n+  /**\n+   * HAVING clause.\n+   *\n+   * Array of strings: field name, operator, value.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $having = array();\n+\n+  /**\n+   * UPDATE fields and values.\n+   *\n+   * Associative array `key => value`, where `key` is the fields to update, and\n+   * `value` is the new value.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $update = array();\n+\n+  /**\n+   * INSERT fields and values.\n+   *\n+   * Associative array `key => value`, where `key` is the fields to insert, and\n+   * `value` is the new value.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $insert = array();\n+\n+  /**\n+   * DELETE modifier.\n+   *\n+   * DELETE modifier can be one of the following: 'LOW_PRIORITY', 'QUICK' and\n+   * 'IGNORE'.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var string\n+   */\n+  private $delete_modifier = '';\n+\n+  /**\n+   * Table joins.\n+   *\n+   * Array of arrays, each of which contains the table to join with, the field\n+   * name of the first table, the operator, the field name of the second table,\n+   * and the type of the JOIN.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var array\n+   */\n+  private $joins = array();\n+\n+  /**\n+   * Where clauses.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var string\n+   */\n+  private $where = '';\n+\n+  /**\n+   * LIMIT.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var int\n+   */\n+  private $limit;\n+\n+  /**\n+   * OFFSET.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var int\n+   */\n+  private $offset;\n+\n+  /**\n+   * Query result.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   * @var mixed\n+   */\n+  private $result;\n+\n+  /**\n+   * Set target table.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $name Table name.\n+   * @return self\n+   */\n+  public function table($name) {\n+    // Verify that $name is a string.\n+    if (!is_string($name))\n+      throw InvArgExcp::typeMismatch('table name', $name, 'String or array');\n+\n+    $this->table = $name;\n+    return $this;\n+  }\n+\n+  /**\n+   * Set SELECT fields.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array|string $field Field name.\n+   * @return self\n+   */\n+  public function select($fields) {\n+    $fields = is_array($fields) ? $fields : func_get_args();\n+    $this->select = array_filter($fields, 'is_string');\n+    $this->type = 'select';\n+\n+    return $this;\n+  }\n+\n+  /**\n+   * Set UPDATE fields and values.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array $values Associative array `key => value`, where `key` is the\n+   * name of the field and `value` is the new value.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if field names in\n+   * $values aren't strings.\n+   * @throws \\RuntimeException if values in $values aren't scalars.\n+   */\n+  public function update(array $values) {\n+    foreach ($values as $field => &$value) {\n+      if (!is_string($field))\n+        throw InvArgExcp::typeMismatch('field name', $field, 'String');\n+      try {\n+        $value = self::correctValue($value, 'update');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+    }\n+\n+    $this->update = $values;\n+    $this->type = 'update';\n+\n+    return $this;\n+  }\n+\n+  /**\n+   * Set COUNT.\n+   *\n+   * Set COUNT to all field or to some specific fields (passing an array\n+   * containing strings).\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array $count Optional. List of fields to which apply the COUNT\n+   * selector. Default: empty array.\n+   * @return self\n+   */\n+  public function count(array $fields = array()) {\n+    if (empty($fields))\n+      $this->count = true;\n+    else\n+      $this->count = array_filter($fields, 'is_string');\n+    $this->type = 'select';\n+\n+    return $this;\n+  }\n+\n+  /**\n+   * Set INSERT fields and values.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array $values Associative array `key => value`, where `key` is the\n+   * field and `value` is the new value.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if field names in\n+   * $values aren't strings.\n+   * @throws \\RuntimeException if values in $values aren't scalars.\n+   */\n+  public function insert(array $values) {\n+    foreach ($values as $field => &$value) {\n+      if (!is_string($field))\n+        throw InvArgExcp::typeMismatch('field name', $field, 'String');\n+      try {\n+        $value = self::correctValue($value, 'update');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+    }\n+\n+    $this->insert = $values;\n+    $this->type = 'insert';\n+\n+    return $this;\n+  }\n+\n+  /**\n+   * Set a DELETE query.\n+   *\n+   * If clause is set through the method `Query::where()`, all rows of the table\n+   * will be affected.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $modifier Optional. DELETE Modifier. Allowed keywords: '',\n+   * 'LOW_PRIORITY', 'QUICK', 'IGNORE' (either uppercase or lowercase).\n+   * Default: ''.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $modifier isn't a\n+   * string.\n+   * @throws \\RuntimeException if $modifier isn't one of the allowed value.\n+   */\n+  public function delete($modifier = '') {\n+    if (!is_string($modifier))\n+      throw InvArgExcp::typeMismatch('delete modifier', $modifier, 'String');\n+    if (!in_array($modifier, $accepted_keywords))\n+      throw new RuntimeException('Modifier passed to `Query::delete()` isn\\'t one of the allowed keywords');\n+\n+    $this->delete_modifier = $modifier;\n+    $this->type = 'delete';\n+  }\n+\n+  /**\n+   * JOIN tables.\n+   *\n+   * JOIN two tables. Second table of the JOIN will be the first argument of\n+   * this method. The first one will be the one set with the `Query::table()`\n+   * method.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $table Table name.\n+   * @param string $one Field of the first table.\n+   * @param string $operator Operator.\n+   * @param string $two Field of the second table.\n+   * @param string $type Optional. Join type (INNER, LEFT, RIGHT, FULL OUTER).\n+   * Default: 'inner'.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if one of the\n+   * arguments isn't a string.\n+   * @throws \\RuntimeException if $operator isn't a valid operator or if $type\n+   * isn't a valid join type.\n+   */\n+  public function join($table, $one, $operator, $two, $type = 'INNER') {\n+    // Checks on arguments.\n+    foreach (array('table', 'one', 'operator', 'two', 'type') as $arg)\n+      if (!is_string($$arg))\n+        throw InvArgExcp::typeMismatch($arg, $$arg, 'String');\n+    if (!self::isOperatorValid($operator, 'join'))\n+      throw new RuntimeException('Operator passed to `Query::join()` must be a valid operator');\n+    $type = strtoupper($type);\n+    if (!in_array($type, array('INNER', 'LEFT', 'RIGHT', 'FULL OUTER')))\n+      throw new RuntimeException('Join type passed to `Query::join()` must be a valid join type');\n+\n+    $this->joins[] = array($table, $one, $operator, $two, $type);\n+    return $this;\n+  }\n+\n+  /**\n+   * Perform a LEFT JOIN.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $table Table name.\n+   * @param string $one Field of the first table.\n+   * @param string $operator Operator.\n+   * @param string $two Field of the second table.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if one of the\n+   * arguments isn't a string.\n+   * @throws \\RuntimeException if $operator isn't a valid operator.\n+   */\n+  public function leftJoin($table, $one, $operator, $two) {\n+    try {\n+      return $this->join($table, $one, $operator, $two, 'LEFT');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Perform a RIGHT JOIN.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $table Table name.\n+   * @param string $one Field of the first table.\n+   * @param string $operator Operator.\n+   * @param string $two Field of the second table.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if one of the\n+   * arguments isn't a string.\n+   * @throws \\RuntimeException if $operator isn't a valid operator.\n+   */\n+  public function rightJoin($table, $one, $operator, $two) {\n+    try {\n+      return $this->join($table, $one, $operator, $two, 'RIGHT');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Perform a FULL OUTER JOIN.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $table Table name.\n+   * @param string $one Field of the first table.\n+   * @param string $operator Operator.\n+   * @param string $two Field of the second table.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if one of the\n+   * arguments isn't a string.\n+   * @throws \\RuntimeException if $operator isn't a valid operator.\n+   */\n+  public function fullOuterJoin($table, $one, $operator, $two) {\n+    try {\n+      return $this->join($table, $one, $operator, $two, 'FULL OUTER');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Set order to results.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $field Field name.\n+   * @param string $direction Optional. Set the direction of the sort. Permitted\n+   * values: 'asc', 'desc'. Default: 'asc'.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field and\n+   * $direction aren't strings.\n+   * @throws \\RuntimeException if $direction isn't 'ASC' or 'DESC';\n+   */\n+  public function orderBy($field, $direction = 'ASC') {\n+    if (!is_string($field))\n+      throw InvArgExcp::typeMismatch('field name', $field, 'String');\n+    if (!is_string($direction))\n+      throw InvArgExcp::typeMismatch('direction', $direction, 'String');\n+    $direction = strtoupper($direction);\n+    if ($direction != 'ASC' && $direction != 'DESC')\n+      throw new RuntimeException('$direction passed to `Query::orderBy()` must be \\'ASC\\' or \\'DESC\\'');\n+\n+    $this->order_by[$field] = $direction;\n+    return $this;\n+  }\n+\n+  /**\n+   * Perform a GROUPBY.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $field Field name.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field isn't a\n+   * string.\n+   */\n+  public function groupBy($field) {\n+    if (!is_string($field))\n+      throw InvArgExcp::typeMismatch('field name', $field, 'String');\n+\n+    $this->group_by = $field;\n+    return $this;\n+  }\n+\n+  /**\n+   * Set HAVING clause.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $field Field name.\n+   * @param string $operator Operator.\n+   * @param mixed $value Value.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field and\n+   * $operator aren't strings.\n+   * @throws \\RuntimeException if $operator isn't one of the allowed ones, or if\n+   * $value isn't in the correct form.\n+   */\n+  public function having($field, $operator, $value) {\n+    foreach(array('field', 'operator') as $arg)\n+      if (!is_string($$arg))\n+        throw InvArgExcp::typeMismatch($arg, $$arg, 'String');\n+\n+    if (!self::isOperatorValid($operator))\n+      throw new RuntimeException('Operator passed to `Query::having()` must be a valid one');\n+\n+    $operator = strtoupper($operator);\n+    if (is_array($value) && $operator == 'BETWEEN') {\n+      if (count($value) != 2)\n+        throw new RuntimeException('Value passed to `Query::having()` isn\\'t in the correct form');\n+      try {\n+        $value[0] = self::correctValue($value[0], 'having');\n+        $value[1] = self::correctValue($value[1], 'having');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+      $value = $value[0].' AND '.$value[1];\n+    } else {\n+      try {\n+        $value = self::correctValue($value, 'having');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+    }\n+\n+    $this->having = array($field, $operator, $value);\n+    return $this;\n+  }\n+\n+  /**\n+   * Set a LIMIT.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param int $limit Number of maximum rows returned (LIMIT).\n+   * @param int $offset Optional. Specify the OFFSET. Default: null.\n+   * @return self\n+   *\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if one of the\n+   * arguments aren't numeric.\n+   */\n+  public function limit($limit, $offset = null) {\n+    if (!is_numeric($limit))\n+      throw InvArgExcp::typeMismatch('limit', $limit, 'Numeric');\n+    if (!is_numeric($offset) && !is_null($offset))\n+      throw InvArgExcp::typeMismatch('offset', $offset, 'Numeric or null');\n+\n+    $this->limit = $limit;\n+    $this->offset = $offset;\n+    return $this;\n+  }\n+\n+  /**\n+   * Set the query to return only the first row.\n+   *\n+   * It is equivalent to call `Query::limit(1, 0)`. In other words, this method\n+   * will perform a `LIMIT 0, 1` in the query.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @return self\n+   */\n+  public function first() {\n+    try {\n+      return $this->limit(1, 0);\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Set WHERE clauses.\n+   *\n+   * Set where clauses, linking them with the `AND` operator.\n+   * Arguments can be passed in two forms. The simple one is useful for setting\n+   * one condition, and consists of passing an argument for the field, one for\n+   * the operator (can be omitted if '=') and one for the value.\n+   * The other one consists of passing an array containing an array for every\n+   * clause, each of which must contain two or three elements, in the same form\n+   * as the first one.\n+   * Every other way will raise an exception.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array|string Where clauses.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if the arguments aren't in the correct form.\n+   */\n+  public function where($clauses) {\n+    try {\n+      return $this->processWhere(func_get_args(), 'where');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Set WHERE clauses.\n+   *\n+   * Set where clauses, linking them with the `OR` operator.\n+   * The method behave like `Query::where()`.\n+   * @see Query::where()\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param array|string Where clauses.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if the arguments aren't in the correct form.\n+   */\n+  public function orWhere($clauses) {\n+    try {\n+      return $this->processWhere(func_get_args(), 'orWhere');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Set WHERE clauses.\n+   *\n+   * Set clauses like: 'WHERE `field` IN (`value` AND `value` AND `value` ...)'.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $field Field name.\n+   * @param array $values Values to check with.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if $value elements aren't in the correct form.\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field isn't a\n+   * string.\n+   */\n+  public function whereIn($field, array $values) {\n+    try {\n+      return $this->processWhereIn($field, $values, 'whereIn');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Set WHERE clauses.\n+   *\n+   * Set clauses like: 'WHERE `field` NOT IN (`value` AND `value` ...)'.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param string $field Field name.\n+   * @param array $values Values to check with.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if $value elements aren't in the correct form.\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field isn't a\n+   * string.\n+   */\n+  public function whereNotIn($field, array $values) {\n+    try {\n+      return $this->processWhereIn($field, $values, 'whereNotIn');\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Check query result existence.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @return boolean Whether the result is set or not.\n+   */\n+  public function hasResult() {\n+    return isset($this->result);\n+  }\n+\n+  /**\n+   * Set query result.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @param mixed $result Query result.\n+   */\n+  public function setResult($result) {\n+    $this->result = $result;\n+  }\n+\n+  /**\n+   * Retrieve query result.\n+   *\n+   * Retrive result(s) of the query. If result isn't set, null will be returned.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @return mixed Query result.\n+   */\n+  public function getResult() {\n+    return $this->result;\n+  }\n+\n+  /**\n+   * Build the query string.\n+   *\n+   * @since 1.0.0\n+   * @access public\n+   *\n+   * @return string\n+   *\n+   * @throws \\RuntimeException if $table property isn't set or if there is some\n+   * inconsistency with the data required by every specific method.\n+   */\n+  public function build() {\n+    try {\n+      switch ($this->type) {\n+        case 'select':\n+          return $this->buildSelect();\n+          break;\n+        case 'update':\n+          return $this->buildUpdate();\n+          break;\n+        case 'insert':\n+          return $this->buildInsert();\n+          break;\n+        case 'delete':\n+          return $this->buildDelete();\n+          break;\n+      }\n+    } catch (Exception $e) {\n+      throw $e;\n+    }\n+  }\n+\n+  /**\n+   * Process WHERE clauses.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   *\n+   * @param array $args Arguments of the public method.\n+   * @param string $method Name of the method that called this one.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if the arguments aren't in the correct form.\n+   */\n+  private function processWhere(array $args, $method = 'where') {\n+    if (is_array($args[0])) {\n+      // Method was called with an array of clauses.\n+      foreach ($args[0] as $clause) {\n+        // Check if the array has other arrays or not, and if it has , then\n+        // check if every internal array has only scalar inside.\n+        if (!is_array($clause) || count($clause) < 2 || count($clause) > 3)\n+          throw new RuntimeException('Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form');\n+        $count = 0;\n+        foreach ($clause as $scalar) {\n+          if (!is_scalar($scalar) && (!is_array($scalar) || $count != 3))\n+            throw new RuntimeException('Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form');\n+          $count++;\n+        }\n+      }\n+\n+      // The array is in the correct form.\n+      try {\n+        $this->buildWhereClause($args[0], $method == 'where' ? 'AND' : 'OR');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+    } elseif (is_scalar($args[0])) {\n+      // Method was called with a list of arguments.\n+      // Check if the arguments are all scalar, and if they are two or three.\n+      if (count($args) < 2 || count($args) > 3)\n+        throw new RuntimeException('Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form');\n+        $count = 0;\n+      foreach ($args as $clause) {\n+        if (!is_scalar($clause) && (!is_array($clause) || $count != 3))\n+          throw new RuntimeException('Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form');\n+        $count++;\n+      }\n+\n+      // Arguments are in the correct form.\n+      try {\n+        $this->buildWhereClause(array($args), $method == 'where' ? 'AND' : 'OR');\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+    } else {\n+      // Method was called in an incorrect way.\n+      throw new RuntimeException('Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form');\n+    }\n+\n+    return $this;\n+  }\n+\n+  /**\n+   * Process WHERE clauses.\n+   *\n+   * Process `WHERE ... IN ...` and `WHERE ... NOT IN ...` clauses.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   *\n+   * @param string $field Field name.\n+   * @param array $values Values to check with.\n+   * @param string $method Name of the method that called this one.\n+   * @return self\n+   *\n+   * @throws \\RuntimeException if $value elements aren't in the correct form.\n+   * @throws SqueezyWeb\\Exceptions\\InvalidArgumentException if $field isn't a\n+   * string.\n+   */\n+  private function processWhereIn($field, array $values, $method = 'whereIn') {\n+    if (!is_string($field))\n+      throw InvArgExcp::typeMismatch('field name', $field, 'String');\n+\n+    $where = $this->where;\n+    if ($where != '')\n+      $where = 'WHERE '.$field.' ';\n+    else\n+      $where .= ' AND '.$field.' ';\n+    if ($method == 'whereNotIn')\n+      $where .= 'NOT ';\n+    $where .= 'IN (';\n+\n+    $count = 0;\n+    foreach ($values as $value) {\n+      // Correct $value.\n+      try {\n+        $correct = self::correctValue($value, $method);\n+      } catch (Exception $e) {\n+        throw $e;\n+      }\n+\n+      // Attach conditions to WHERE clause.\n+      if ($count != 0)\n+        $where .= ', ';\n+      $where .= $correct;\n+\n+      $count++;\n+    }\n+\n+    $where .= ')';\n+    $this->where = $where;\n+  }\n+\n+  /**\n+   * Build WHERE clause.\n+   *\n+   * Transform a list of arguments in a string of WHERE conditions.\n+   *\n+   * @since 1.0.0\n+   * @access private\n+   *\n+   * @param array $clauses `WHERE` clauses.\n+   * @param string $operator `AND` or `OR`.\n+   *\n+   * @throws \\RuntimeException if the arguments aren't in the correct form.\n+   */\n+  private function buildWhereClause(array $clauses, $operator) {\n+    $where = $this->where;\n+    $method = ($operator == 'AND') ? 'where' : 'orWhere';\n+\n+    foreach ($clauses as $clause) {\n+      if (!is_array($clause))\n+        throw new RuntimeException(\n+          'Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form'\n+        );\n+\n+      if ($where != '')\n+        $where = ' '.$operator.' ';\n+      else\n+        $where = 'WHERE ';\n+\n+      if (count($clause) == 2) {\n+        $value = $clause[1];\n+        $oprt = '=';\n+      } elseif (count($clause) == 3) {\n+        $value = $clause[2];\n+        $oprt = $clause[1];\n+        if (!self::isOperatorValid($oprt, 'buildWhereClause'))\n+          throw new RuntimeException(\n+            'Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form'\n+          );\n+      }\n+\n+      if (is_array($value) && ($oprt == 'between' || $oprt == 'BETWEEN')) {\n+        // Replace the value with a string that contains the values in the\n+        // array, linked by 'AND'.\n+        // If the array doesn't contains exactly 2 values, raise an exception.\n+        if (count($value) != 2)\n+          throw new RuntimeException(\n+            'Arguments passed to `Query::'.$method.'()` aren\\'t in the correct form'\n+          );\n+        try {\n+          $value[0] = self::correctValue($value[0]);\n+          $value[1] = self::correctValue($value[1]);","path":"Lib/Database/MySqlQuery.php","position":909,"original_position":909,"commit_id":"e27779c3521bd780061e1dc66011fb58aefd6e24","original_commit_id":"e27779c3521bd780061e1dc66011fb58aefd6e24","user":{"login":"deshack","id":2034213,"avatar_url":"https://avatars.githubusercontent.com/u/2034213?v=3","gravatar_id":"","url":"https://api.github.com/users/deshack","html_url":"https://github.com/deshack","followers_url":"https://api.github.com/users/deshack/followers","following_url":"https://api.github.com/users/deshack/following{/other_user}","gists_url":"https://api.github.com/users/deshack/gists{/gist_id}","starred_url":"https://api.github.com/users/deshack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deshack/subscriptions","organizations_url":"https://api.github.com/users/deshack/orgs","repos_url":"https://api.github.com/users/deshack/repos","events_url":"https://api.github.com/users/deshack/events{/privacy}","received_events_url":"https://api.github.com/users/deshack/received_events","type":"User","site_admin":false},"body":"Try to be DRY:\r\n```php\r\nforeach ($values as &$value)\r\n  $value = self::correctValue($value);\r\n```\r\nor (better)\r\n```php\r\n$values = array_map(array(__CLASS__, 'correctValue'), $values);\r\n```","created_at":"2016-02-04T09:24:10Z","updated_at":"2016-02-04T09:24:10Z","html_url":"https://github.com/SqueezyWeb/Database/pull/3#discussion_r51846775","pull_request_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3","_links":{"self":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/comments/51846775"},"html":{"href":"https://github.com/SqueezyWeb/Database/pull/3#discussion_r51846775"},"pull_request":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3"}}},"pull_request":{"url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3","id":58162243,"html_url":"https://github.com/SqueezyWeb/Database/pull/3","diff_url":"https://github.com/SqueezyWeb/Database/pull/3.diff","patch_url":"https://github.com/SqueezyWeb/Database/pull/3.patch","issue_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/3","number":3,"state":"open","locked":false,"title":"Create Query classes and interface","user":{"login":"wraxen","id":6460339,"avatar_url":"https://avatars.githubusercontent.com/u/6460339?v=3","gravatar_id":"","url":"https://api.github.com/users/wraxen","html_url":"https://github.com/wraxen","followers_url":"https://api.github.com/users/wraxen/followers","following_url":"https://api.github.com/users/wraxen/following{/other_user}","gists_url":"https://api.github.com/users/wraxen/gists{/gist_id}","starred_url":"https://api.github.com/users/wraxen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wraxen/subscriptions","organizations_url":"https://api.github.com/users/wraxen/orgs","repos_url":"https://api.github.com/users/wraxen/repos","events_url":"https://api.github.com/users/wraxen/events{/privacy}","received_events_url":"https://api.github.com/users/wraxen/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-02-03T16:28:57Z","updated_at":"2016-02-04T09:24:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"91b46a274c7ecf4cd78195aa6506a8a9b530d256","assignee":{"login":"deshack","id":2034213,"avatar_url":"https://avatars.githubusercontent.com/u/2034213?v=3","gravatar_id":"","url":"https://api.github.com/users/deshack","html_url":"https://github.com/deshack","followers_url":"https://api.github.com/users/deshack/followers","following_url":"https://api.github.com/users/deshack/following{/other_user}","gists_url":"https://api.github.com/users/deshack/gists{/gist_id}","starred_url":"https://api.github.com/users/deshack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/deshack/subscriptions","organizations_url":"https://api.github.com/users/deshack/orgs","repos_url":"https://api.github.com/users/deshack/repos","events_url":"https://api.github.com/users/deshack/events{/privacy}","received_events_url":"https://api.github.com/users/deshack/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/SqueezyWeb/Database/milestones/1","html_url":"https://github.com/SqueezyWeb/Database/milestones/v0.1.0","labels_url":"https://api.github.com/repos/SqueezyWeb/Database/milestones/1/labels","id":1560715,"number":1,"title":"v0.1.0","description":null,"creator":{"login":"wraxen","id":6460339,"avatar_url":"https://avatars.githubusercontent.com/u/6460339?v=3","gravatar_id":"","url":"https://api.github.com/users/wraxen","html_url":"https://github.com/wraxen","followers_url":"https://api.github.com/users/wraxen/followers","following_url":"https://api.github.com/users/wraxen/following{/other_user}","gists_url":"https://api.github.com/users/wraxen/gists{/gist_id}","starred_url":"https://api.github.com/users/wraxen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wraxen/subscriptions","organizations_url":"https://api.github.com/users/wraxen/orgs","repos_url":"https://api.github.com/users/wraxen/repos","events_url":"https://api.github.com/users/wraxen/events{/privacy}","received_events_url":"https://api.github.com/users/wraxen/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":1,"state":"open","created_at":"2016-02-03T10:05:52Z","updated_at":"2016-02-03T16:34:50Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3/commits","review_comments_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3/comments","review_comment_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/3/comments","statuses_url":"https://api.github.com/repos/SqueezyWeb/Database/statuses/e27779c3521bd780061e1dc66011fb58aefd6e24","head":{"label":"SqueezyWeb:feature/query","ref":"feature/query","sha":"e27779c3521bd780061e1dc66011fb58aefd6e24","user":{"login":"SqueezyWeb","id":17001957,"avatar_url":"https://avatars.githubusercontent.com/u/17001957?v=3","gravatar_id":"","url":"https://api.github.com/users/SqueezyWeb","html_url":"https://github.com/SqueezyWeb","followers_url":"https://api.github.com/users/SqueezyWeb/followers","following_url":"https://api.github.com/users/SqueezyWeb/following{/other_user}","gists_url":"https://api.github.com/users/SqueezyWeb/gists{/gist_id}","starred_url":"https://api.github.com/users/SqueezyWeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SqueezyWeb/subscriptions","organizations_url":"https://api.github.com/users/SqueezyWeb/orgs","repos_url":"https://api.github.com/users/SqueezyWeb/repos","events_url":"https://api.github.com/users/SqueezyWeb/events{/privacy}","received_events_url":"https://api.github.com/users/SqueezyWeb/received_events","type":"Organization","site_admin":false},"repo":{"id":50827474,"name":"Database","full_name":"SqueezyWeb/Database","owner":{"login":"SqueezyWeb","id":17001957,"avatar_url":"https://avatars.githubusercontent.com/u/17001957?v=3","gravatar_id":"","url":"https://api.github.com/users/SqueezyWeb","html_url":"https://github.com/SqueezyWeb","followers_url":"https://api.github.com/users/SqueezyWeb/followers","following_url":"https://api.github.com/users/SqueezyWeb/following{/other_user}","gists_url":"https://api.github.com/users/SqueezyWeb/gists{/gist_id}","starred_url":"https://api.github.com/users/SqueezyWeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SqueezyWeb/subscriptions","organizations_url":"https://api.github.com/users/SqueezyWeb/orgs","repos_url":"https://api.github.com/users/SqueezyWeb/repos","events_url":"https://api.github.com/users/SqueezyWeb/events{/privacy}","received_events_url":"https://api.github.com/users/SqueezyWeb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SqueezyWeb/Database","description":"Simple database manager","fork":false,"url":"https://api.github.com/repos/SqueezyWeb/Database","forks_url":"https://api.github.com/repos/SqueezyWeb/Database/forks","keys_url":"https://api.github.com/repos/SqueezyWeb/Database/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SqueezyWeb/Database/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SqueezyWeb/Database/teams","hooks_url":"https://api.github.com/repos/SqueezyWeb/Database/hooks","issue_events_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/events{/number}","events_url":"https://api.github.com/repos/SqueezyWeb/Database/events","assignees_url":"https://api.github.com/repos/SqueezyWeb/Database/assignees{/user}","branches_url":"https://api.github.com/repos/SqueezyWeb/Database/branches{/branch}","tags_url":"https://api.github.com/repos/SqueezyWeb/Database/tags","blobs_url":"https://api.github.com/repos/SqueezyWeb/Database/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SqueezyWeb/Database/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SqueezyWeb/Database/git/refs{/sha}","trees_url":"https://api.github.com/repos/SqueezyWeb/Database/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SqueezyWeb/Database/statuses/{sha}","languages_url":"https://api.github.com/repos/SqueezyWeb/Database/languages","stargazers_url":"https://api.github.com/repos/SqueezyWeb/Database/stargazers","contributors_url":"https://api.github.com/repos/SqueezyWeb/Database/contributors","subscribers_url":"https://api.github.com/repos/SqueezyWeb/Database/subscribers","subscription_url":"https://api.github.com/repos/SqueezyWeb/Database/subscription","commits_url":"https://api.github.com/repos/SqueezyWeb/Database/commits{/sha}","git_commits_url":"https://api.github.com/repos/SqueezyWeb/Database/git/commits{/sha}","comments_url":"https://api.github.com/repos/SqueezyWeb/Database/comments{/number}","issue_comment_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/comments{/number}","contents_url":"https://api.github.com/repos/SqueezyWeb/Database/contents/{+path}","compare_url":"https://api.github.com/repos/SqueezyWeb/Database/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SqueezyWeb/Database/merges","archive_url":"https://api.github.com/repos/SqueezyWeb/Database/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SqueezyWeb/Database/downloads","issues_url":"https://api.github.com/repos/SqueezyWeb/Database/issues{/number}","pulls_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls{/number}","milestones_url":"https://api.github.com/repos/SqueezyWeb/Database/milestones{/number}","notifications_url":"https://api.github.com/repos/SqueezyWeb/Database/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SqueezyWeb/Database/labels{/name}","releases_url":"https://api.github.com/repos/SqueezyWeb/Database/releases{/id}","deployments_url":"https://api.github.com/repos/SqueezyWeb/Database/deployments","created_at":"2016-02-01T09:03:37Z","updated_at":"2016-02-01T09:39:25Z","pushed_at":"2016-02-03T16:29:43Z","git_url":"git://github.com/SqueezyWeb/Database.git","ssh_url":"git@github.com:SqueezyWeb/Database.git","clone_url":"https://github.com/SqueezyWeb/Database.git","svn_url":"https://github.com/SqueezyWeb/Database","homepage":null,"size":31,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":4,"forks":0,"open_issues":4,"watchers":0,"default_branch":"master"}},"base":{"label":"SqueezyWeb:master","ref":"master","sha":"1038c9101953f2e77b439a3495f4e155fb3f7703","user":{"login":"SqueezyWeb","id":17001957,"avatar_url":"https://avatars.githubusercontent.com/u/17001957?v=3","gravatar_id":"","url":"https://api.github.com/users/SqueezyWeb","html_url":"https://github.com/SqueezyWeb","followers_url":"https://api.github.com/users/SqueezyWeb/followers","following_url":"https://api.github.com/users/SqueezyWeb/following{/other_user}","gists_url":"https://api.github.com/users/SqueezyWeb/gists{/gist_id}","starred_url":"https://api.github.com/users/SqueezyWeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SqueezyWeb/subscriptions","organizations_url":"https://api.github.com/users/SqueezyWeb/orgs","repos_url":"https://api.github.com/users/SqueezyWeb/repos","events_url":"https://api.github.com/users/SqueezyWeb/events{/privacy}","received_events_url":"https://api.github.com/users/SqueezyWeb/received_events","type":"Organization","site_admin":false},"repo":{"id":50827474,"name":"Database","full_name":"SqueezyWeb/Database","owner":{"login":"SqueezyWeb","id":17001957,"avatar_url":"https://avatars.githubusercontent.com/u/17001957?v=3","gravatar_id":"","url":"https://api.github.com/users/SqueezyWeb","html_url":"https://github.com/SqueezyWeb","followers_url":"https://api.github.com/users/SqueezyWeb/followers","following_url":"https://api.github.com/users/SqueezyWeb/following{/other_user}","gists_url":"https://api.github.com/users/SqueezyWeb/gists{/gist_id}","starred_url":"https://api.github.com/users/SqueezyWeb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SqueezyWeb/subscriptions","organizations_url":"https://api.github.com/users/SqueezyWeb/orgs","repos_url":"https://api.github.com/users/SqueezyWeb/repos","events_url":"https://api.github.com/users/SqueezyWeb/events{/privacy}","received_events_url":"https://api.github.com/users/SqueezyWeb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SqueezyWeb/Database","description":"Simple database manager","fork":false,"url":"https://api.github.com/repos/SqueezyWeb/Database","forks_url":"https://api.github.com/repos/SqueezyWeb/Database/forks","keys_url":"https://api.github.com/repos/SqueezyWeb/Database/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SqueezyWeb/Database/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SqueezyWeb/Database/teams","hooks_url":"https://api.github.com/repos/SqueezyWeb/Database/hooks","issue_events_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/events{/number}","events_url":"https://api.github.com/repos/SqueezyWeb/Database/events","assignees_url":"https://api.github.com/repos/SqueezyWeb/Database/assignees{/user}","branches_url":"https://api.github.com/repos/SqueezyWeb/Database/branches{/branch}","tags_url":"https://api.github.com/repos/SqueezyWeb/Database/tags","blobs_url":"https://api.github.com/repos/SqueezyWeb/Database/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SqueezyWeb/Database/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SqueezyWeb/Database/git/refs{/sha}","trees_url":"https://api.github.com/repos/SqueezyWeb/Database/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SqueezyWeb/Database/statuses/{sha}","languages_url":"https://api.github.com/repos/SqueezyWeb/Database/languages","stargazers_url":"https://api.github.com/repos/SqueezyWeb/Database/stargazers","contributors_url":"https://api.github.com/repos/SqueezyWeb/Database/contributors","subscribers_url":"https://api.github.com/repos/SqueezyWeb/Database/subscribers","subscription_url":"https://api.github.com/repos/SqueezyWeb/Database/subscription","commits_url":"https://api.github.com/repos/SqueezyWeb/Database/commits{/sha}","git_commits_url":"https://api.github.com/repos/SqueezyWeb/Database/git/commits{/sha}","comments_url":"https://api.github.com/repos/SqueezyWeb/Database/comments{/number}","issue_comment_url":"https://api.github.com/repos/SqueezyWeb/Database/issues/comments{/number}","contents_url":"https://api.github.com/repos/SqueezyWeb/Database/contents/{+path}","compare_url":"https://api.github.com/repos/SqueezyWeb/Database/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SqueezyWeb/Database/merges","archive_url":"https://api.github.com/repos/SqueezyWeb/Database/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SqueezyWeb/Database/downloads","issues_url":"https://api.github.com/repos/SqueezyWeb/Database/issues{/number}","pulls_url":"https://api.github.com/repos/SqueezyWeb/Database/pulls{/number}","milestones_url":"https://api.github.com/repos/SqueezyWeb/Database/milestones{/number}","notifications_url":"https://api.github.com/repos/SqueezyWeb/Database/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SqueezyWeb/Database/labels{/name}","releases_url":"https://api.github.com/repos/SqueezyWeb/Database/releases{/id}","deployments_url":"https://api.github.com/repos/SqueezyWeb/Database/deployments","created_at":"2016-02-01T09:03:37Z","updated_at":"2016-02-01T09:39:25Z","pushed_at":"2016-02-03T16:29:43Z","git_url":"git://github.com/SqueezyWeb/Database.git","ssh_url":"git@github.com:SqueezyWeb/Database.git","clone_url":"https://github.com/SqueezyWeb/Database.git","svn_url":"https://github.com/SqueezyWeb/Database","homepage":null,"size":31,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":4,"forks":0,"open_issues":4,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3"},"html":{"href":"https://github.com/SqueezyWeb/Database/pull/3"},"issue":{"href":"https://api.github.com/repos/SqueezyWeb/Database/issues/3"},"comments":{"href":"https://api.github.com/repos/SqueezyWeb/Database/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SqueezyWeb/Database/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/SqueezyWeb/Database/statuses/e27779c3521bd780061e1dc66011fb58aefd6e24"}}}}