table = $table; $this->notORM = $notORM; $this->single = $single; $this->primary = $notORM->structure->getPrimary($table); } /** Save data to cache and empty result */ function __destruct(){ if($this->notORM->cache && !$this->select && isset($this->rows)){ $access = $this->access; if(is_array($access)){ $access = array_filter($access); } $this->notORM->cache->save("$this->table;" . implode(",", $this->conditions), $access); } $this->rows = null; unset($this->data); } protected function limitString($limit, $offset = null){ $return = ""; if(isset($limit) && $this->notORM->driver != "oci" && $this->notORM->driver != "dblib" && $this->notORM->driver != "mssql" && $this->notORM->driver != "sqlsrv"){ $return .= " LIMIT $limit"; if($offset){ //$return .= " OFFSET $offset"; $return .= ",$offset"; //@dogstar 2014-10-24 } } return $return; } protected function removeExtraDots($expression){ return preg_replace('~(?:\\b[a-z_][a-z0-9_.:]*[.:])?([a-z_][a-z0-9_]*)[.:]([a-z_*])~i', '\\1.\\2', $expression); // rewrite tab1.tab2.col } protected function whereString(){ $return = ""; if($this->group){ $return .= " GROUP BY $this->group"; } if($this->having){ $return .= " HAVING $this->having"; } if($this->order){ $return .= " ORDER BY " . implode(", ", $this->order); } $return = $this->removeExtraDots($return); $where = $this->where; if(isset($this->limit) && $this->notORM->driver == "oci"){ $where[] = ($where ? " AND " : "") . "(" . ($this->offset ? "rownum > $this->offset AND " : "") . "rownum <= " . ($this->limit + $this->offset) . ")"; //! rownum > doesn't work - requires subselect (see adminer/drivers/oracle.inc.php) } if($where){ $return = " WHERE " . implode($where) . $return; } $return .= $this->limitString($this->limit, $this->offset); if(isset($this->lock)){ $return .= ($this->lock ? " FOR UPDATE" : " LOCK IN SHARE MODE"); } return $return; } /** * @param $limit * * @return string */ protected function topString( $limit){ if(isset($limit) && ($this->notORM->driver == "dblib" || $this->notORM->driver == "mssql" || $this->notORM->driver == "sqlsrv")){ return " TOP ($this->limit)"; //! offset is not supported } return ""; } protected function createJoins($val){ $return = array(); preg_match_all('~\\b([a-z_][a-z0-9_.:]*[.:])[a-z_*]~i', $val, $matches); foreach($matches[1] as $names){ $parent = $this->table; if($names != "$parent."){ // case-sensitive preg_match_all('~\\b([a-z_][a-z0-9_]*)([.:])~i', $names, $matches, PREG_SET_ORDER); foreach($matches as $match){ list(, $name, $delimiter) = $match; $table = $this->notORM->structure->getReferencedTable($name, $parent); $column = ($delimiter == ':' ? $this->notORM->structure->getPrimary($parent) : $this->notORM->structure->getReferencedColumn($name, $parent)); $primary = ($delimiter == ':' ? $this->notORM->structure->getReferencedColumn($parent, $table) : $this->notORM->structure->getPrimary($table)); $return[$name] = " LEFT JOIN $table" . ($table != $name ? " AS $name" : "") . " ON $parent.$column = $name.$primary"; // should use alias if the table is used on more places $parent = $name; } } } return $return; } /** Get SQL query * @return string */ function __toString(){ $return = "SELECT" . $this->topString($this->limit) . " "; $join = $this->createJoins(implode(",", $this->conditions) . "," . implode(",", $this->select) . ",$this->group,$this->having," . implode(",", $this->order)); if(!isset($this->rows) && $this->notORM->cache && !is_string($this->accessed)){ $this->accessed = $this->notORM->cache->load("$this->table;" . implode(",", $this->conditions)); $this->access = $this->accessed; } if($this->select){ $return .= $this->removeExtraDots(implode(", ", $this->select)); }elseif($this->accessed){ $return .= ($join ? "$this->table." : "") . implode(", " . ($join ? "$this->table." : ""), array_keys($this->accessed)); }else{ $return .= ($join ? "$this->table." : "") . "*"; } $return .= " FROM $this->table" . implode($join) . $this->whereString(); if($this->union){ $return = ($this->notORM->driver == "sqlite" || $this->notORM->driver == "oci" ? $return : "($return)") . implode($this->union); if($this->unionOrder){ $return .= " ORDER BY " . implode(", ", $this->unionOrder); } $return .= $this->limitString($this->unionLimit, $this->unionOffset); $top = $this->topString($this->unionLimit); if($top){ $return = "SELECT$top * FROM ($return) t"; } } return $return; } /** * 放开限制 protected -> public @scott 反馈 * * @param $query string * @param $parameters array * * @return bool|PDOStatement */ public function query($query, $parameters){ $debugTrace = array(); self::$queryTimes++; /** * 修正当参数过多时的SQLSTATE[HY093] @dogstar 2014-11-18 */ $parameters = array_map(array($this, 'formatValue'), $parameters); foreach($parameters as $key => $val){ if(substr($key, 0, 1) == ':' && stripos($query, $key) === false){ unset($parameters[$key]); } } if($this->notORM->debug){ $debugTrace['startTime'] = microtime(true); if(!is_callable($this->notORM->debug)){ $debug = "$query;"; if($parameters){ $debug .= " -- " . implode(", ", array_map(array($this, 'quote'), $parameters)); } $pattern = '(^' . preg_quote(dirname(__FILE__)) . '(\\.php$|[/\\\\]))'; // can be static foreach(debug_backtrace() as $backtrace){ if(isset($backtrace["file"]) && !preg_match($pattern, $backtrace["file"])){ // stop on first file outside NotORM source codes break; } } /** @var array $backtrace */ //error_log("{$backtrace['file']}:{$backtrace['line']}:$debug\n", 0); //if ($this->notORM->debug) echo "$debug
\n"; //@dogstar 2014-10-31 $debugTrace['sql'] = $debug; }elseif(call_user_func($this->notORM->debug, $query, $parameters) === false){ return false; } } $return = $this->notORM->connection->prepare($query); //对于替换参数进行处理 @喵了个咪 2015-12-25 $sum = 1; foreach($parameters as $key => $val){ if(strstr($key, ':')){ if(is_int($val)){ $return->bindValue($key, $val, PDO::PARAM_INT); }elseif(is_bool($val)){ $return->bindValue($key, $val, PDO::PARAM_BOOL); }elseif(is_null($val)){ $return->bindValue($key, $val, PDO::PARAM_NULL); }else{ $return->bindValue($key, $val); } }else{ if(is_int($val)){ $return->bindValue($sum, $val, PDO::PARAM_INT); }elseif(is_bool($val)){ $return->bindValue($sum, $val, PDO::PARAM_BOOL); }elseif(is_null($val)){ $return->bindValue($sum, $val, PDO::PARAM_NULL); }else{ $return->bindValue($sum, $val); } $sum++; } } $errorMessage = null; if(!$return || !$return->execute()){ $errorInfo = $return->errorInfo(); $errorMessage = isset($errorInfo[2]) ? $errorInfo[2] : $errorMessage; $return = false; } if($this->notORM->debug){ $debugTrace['endTime'] = microtime(true); $sqlInfo = sprintf("[%s - %sms]%s", self::$queryTimes, round(($debugTrace['endTime'] - $debugTrace['startTime']) * 1000, 2), $debugTrace['sql'] ); DI()->tracer->sql($sqlInfo); } //显式抛出异常,以让开发同学尽早发现SQL语法问题 @dogstar 20150426 if($return === false && $errorMessage !== null){ throw new PDOException($errorMessage); } if($this->notORM->debugTimer){ call_user_func($this->notORM->debugTimer); } return $return; } protected function formatValue($val){ if($val instanceof DateTime){ return $val->format("Y-m-d H:i:s"); //! may be driver specific } return $val; } protected function quote($val){ if(!isset($val)){ return "NULL"; } if(is_array($val)){ // (a, b) IN ((1, 2), (3, 4)) return "(" . implode(", ", array_map(array($this, 'quote'), $val)) . ")"; } $val = $this->formatValue($val); if(is_float($val)){ return sprintf("%F", $val); // otherwise depends on setlocale() } if($val === false){ return "0"; } if(is_int($val) || $val instanceof NotORM_Literal){ // number or SQL code - for example "NOW()" return (string) $val; } return $this->notORM->connection->quote($val); } /** Shortcut for call_user_func_array(array($this, 'insert'), $rows) * * @param array * * @return int number of affected rows or false in case of an error */ function insert_multi(array $rows){ if($this->notORM->freeze){ return false; } if(!$rows){ return 0; } $data = reset($rows); $parameters = array(); if($data instanceof NotORM_Result){ $parameters = $data->parameters; //! other parameters $data = (string) $data; }elseif($data instanceof Traversable){ $data = iterator_to_array($data); } $insert = $data; if(is_array($data)){ $values = array(); foreach($rows as $value){ if($value instanceof Traversable){ $value = iterator_to_array($value); } $values[] = $this->quote($value); foreach($value as $val){ if($val instanceof NotORM_Literal && $val->parameters){ $parameters = array_merge($parameters, $val->parameters); } } } $quoteChar = $this->getQuoteChar(); //! driver specific extended insert $insert = ($data || $this->notORM->driver == "mysql") ? "({$quoteChar}" . implode("{$quoteChar}, {$quoteChar}", array_keys($data)) . "{$quoteChar}) VALUES " . implode(", ", $values) : "DEFAULT VALUES"; } // requires empty $this->parameters $return = $this->query("INSERT INTO $this->table $insert", $parameters); if(!$return){ return false; } $this->rows = null; return $return->rowCount(); } /** Insert row in a table * * @param mixed array($column => $value)|Traversable for single row insert or NotORM_Result|string for INSERT ... SELECT * @param ... used for extended insert * * @return mixed inserted NotORM_Row or false in case of an error or number of affected rows for INSERT ... SELECT */ function insert($data){ $rows = func_get_args(); $return = $this->insert_multi($rows); if(!$return){ return false; } if(!is_array($data)){ return $return; } // #56 postgresql无法获取新增数据的主键ID @ clov4r-连友 201608 if ($this->notORM->driver == "pgsql") { if (!isset($data[$this->primary])) { //获取序列名称 $pgss = $this->query("SELECT pg_get_serial_sequence('" . $this->table . "', '" . $this->primary . "') pgss", $this->parameters)->fetch(); if (isset($pgss['pgss'])) { $rs = $this->query("select last_value id from " . $pgss['pgss'], $this->parameters)->fetch(); $data[$this->primary] = $rs['id']; $this->sequence = $rs['id']; } } } else { if (!isset($data[$this->primary]) && ($id = $this->notORM->connection->lastInsertId($this->notORM->structure->getSequence($this->table)))) { $data[$this->primary] = $id; } } return new $this->notORM->rowClass($data, $this); } /** Update all rows in result set * * @param array ($column => $value) * * @return int number of affected rows or false in case of an error */ function update(array $data){ if($this->notORM->freeze){ return false; } if(!$data){ return 0; } $values = array(); $parameters = array(); $quoteChar = $this->getQuoteChar(); foreach($data as $key => $val){ // doesn't use binding because $this->parameters can be filled by ? or :name $values[] = "{$quoteChar}{$key}{$quoteChar} = " . $this->quote($val); if($val instanceof NotORM_Literal && $val->parameters){ $parameters = array_merge($parameters, $val->parameters); } } if($this->parameters){ $parameters = array_merge($parameters, $this->parameters); } // joins in UPDATE are supported only in MySQL $return = $this->query("UPDATE" . $this->topString($this->limit) . " $this->table SET " . implode(", ", $values) . $this->whereString(), $parameters); if(!$return){ return false; } return $return->rowCount(); } /** * 获取数据库字段的引号 * @author dogstar 20170617 */ protected function getQuoteChar() { $allowDBs = array('mysql'); return in_array($this->notORM->driver, $allowDBs) ? '`' : ''; } /** Insert row or update if it already exists * * @param array ($column => $value) * @param array ($column => $value) * @param array ($column => $value), empty array means use $insert * * @return int number of affected rows or false in case of an error */ function insert_update(array $unique, array $insert, array $update = array()){ if(!$update){ $update = $insert; } $insert = $unique + $insert; $values = "(" . implode(", ", array_keys($insert)) . ") VALUES " . $this->quote($insert); //! parameters if($this->notORM->driver == "mysql"){ $set = array(); if(!$update){ $update = $unique; } $quoteChar = $this->getQuoteChar(); foreach($update as $key => $val){ $set[] = "{$quoteChar}{$key}{$quoteChar} = " . $this->quote($val); //! parameters } return $this->insert("$values ON DUPLICATE KEY UPDATE " . implode(", ", $set)); }else{ $connection = $this->notORM->connection; $errorMode = $connection->getAttribute(PDO::ATTR_ERRMODE); $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); try{ $return = $this->insert($values); $connection->setAttribute(PDO::ATTR_ERRMODE, $errorMode); return $return; } catch(PDOException $e){ $connection->setAttribute(PDO::ATTR_ERRMODE, $errorMode); if($e->getCode() == "23000" || $e->getCode() == "23505"){ // "23000" - duplicate key, "23505" unique constraint pgsql if(!$update){ return 0; } $clone = clone $this; $return = $clone->where($unique)->update($update); return ($return ? $return + 1 : $return); } if($errorMode == PDO::ERRMODE_EXCEPTION){ throw $e; }elseif($errorMode == PDO::ERRMODE_WARNING){ trigger_error("PDOStatement::execute(): " . $e->getMessage(), E_USER_WARNING); // E_WARNING is unusable } } } return 0; } /** Get last insert ID * @return string number */ function insert_id(){ return $this->notORM->connection->lastInsertId(); } /** Delete all rows in result set * @return bool|int number of affected rows or false in case of an error * @throws Exception */ function delete(){ if($this->notORM->freeze){ return false; } //防止误删,禁止全表的删除 //@dogstar - 2014-10-24 $where = $this->whereString(); if(empty($where)){ throw new Exception('sorry, you can not delete the whole table --dogstar'); } $return = $this->query("DELETE" . $this->topString($this->limit) . " FROM $this->table" . $where, $this->parameters); if(!$return){ return false; } return $return->rowCount(); } /** Add select clause, more calls appends to the end * * @param string $columns for example "column, MD5(column) AS column_md5", empty string to reset previously set columns * @param string ... * * @return NotORM_Result fluent interface */ function select($columns){ $this->__destruct(); if($columns != ""){ foreach(func_get_args() as $columns){ $this->select[] = $columns; } }else{ $this->select = array(); } return $this; } /** Add where condition, more calls appends with AND * * @param mixed string possibly containing ? or :name; or array($condition => $parameters, ...) * @param mixed array accepted by PDOStatement::execute or a scalar value * @param mixed ... * * @return NotORM_Result fluent interface */ function where($condition, $parameters = array()){ $args = func_get_args(); return $this->whereOperator("AND", $args); } protected function whereOperator($operator, array $args){ $condition = $args[0]; $parameters = (count($args) > 1 ? $args[1] : array()); if(is_array($condition)){ // where(array("column1" => 1, "column2 > ?" => 2)) foreach($condition as $key => $val){ $this->where($key, $val); } return $this; } $this->__destruct(); $this->conditions[] = "$operator $condition"; $condition = $this->removeExtraDots($condition); if(count($args) != 2 || strpbrk($condition, "?:")){ // where("column < ? OR column > ?", array(1, 2)) if(count($args) != 2 || !is_array($parameters)){ // where("column < ? OR column > ?", 1, 2) $parameters = array_slice($args, 1); } $this->parameters = array_merge($this->parameters, $parameters); }elseif($parameters === null){ // where("column", null) $condition .= " IS NULL"; }elseif($parameters instanceof NotORM_Result){ // where("column", $db->$table()) $clone = clone $parameters; if(!$clone->select){ $clone->select($this->notORM->structure->getPrimary($clone->table)); } if($this->notORM->driver != "mysql"){ if($clone instanceof NotORM_MultiResult){ array_shift($clone->select); $clone->single(); } $condition .= " IN ($clone)"; $this->parameters = array_merge($this->parameters, $clone->parameters); }else{ $in = array(); foreach($clone as $row){ $row = array_values(iterator_to_array($row)); if($clone instanceof NotORM_MultiResult && count($row) > 1){ array_shift($row); } if(count($row) == 1){ $in[] = $this->quote($row[0]); }else{ $in[] = $this->quote($row); } } if($in){ $condition .= " IN (" . implode(", ", $in) . ")"; }else{ $condition = "($condition) IS NOT NULL AND $condition IS NULL"; // $condition = "NOT id" } } }elseif(!is_array($parameters)){ // where("column", "x") $condition .= " = " . $this->quote($parameters); }else{ // where("column", array(1, 2)) $condition = $this->whereIn($condition, $parameters); } $this->where[] = (preg_match('~^\)+$~', $condition) ? $condition : ($this->where ? " $operator " : "") . "($condition)"); return $this; } protected function whereIn($condition, $parameters){ if(!$parameters){ $condition = "($condition) IS NOT NULL AND $condition IS NULL"; }elseif($this->notORM->driver != "oci"){ $column = $condition; $condition .= " IN " . $this->quote($parameters); $nulls = array_filter($parameters, 'is_null'); if($nulls){ $condition = "$condition OR $column IS NULL"; } }else{ // http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/expressions014.htm $or = array(); for($i = 0; $i < count($parameters); $i += 1000){ $or[] = "$condition IN " . $this->quote(array_slice($parameters, $i, 1000)); } $condition = implode(" OR ", $or); } return $condition; } function __call($name, array $args){ $operator = strtoupper($name); switch($operator){ case "AND": case "OR": return $this->whereOperator($operator, $args); } trigger_error("Call to undefined method NotORM_Result::$name()", E_USER_ERROR); return FALSE; } /** Shortcut for where() * * @param string * @param mixed * @param mixed ... * * @return NotORM_Result fluent interface */ function __invoke($where, $parameters = array()){ $args = func_get_args(); return $this->whereOperator("AND", $args); } /** Add order clause, more calls appends to the end * * @param string $columns for example "column1, column2 DESC", empty string to reset previous order * @param string ... * * @return NotORM_Result fluent interface */ function order($columns){ $this->rows = null; if($columns != ""){ foreach(func_get_args() as $columns){ if($this->union){ $this->unionOrder[] = $columns; }else{ $this->order[] = $columns; } } }elseif($this->union){ $this->unionOrder = array(); }else{ $this->order = array(); } return $this; } /** * 对查询进行limit操作 * 请注意以下两种用法,与NotORM原来的定义有所区别 * - limit(数量) * - limit(开始位置,数量) * Set limit clause, more calls rewrite old values * * @param int * @param int * * @return NotORM_Result fluent interface */ function limit($limit, $offset = null){ $this->rows = null; if($this->union){ $this->unionLimit = +$limit; $this->unionOffset = +$offset; }else{ $this->limit = +$limit; $this->offset = +$offset; } return $this; } /** Set group clause, more calls rewrite old values * * @param string * @param string * * @return NotORM_Result fluent interface */ function group($columns, $having = ""){ $this->__destruct(); $this->group = $columns; $this->having = $having; return $this; } /** Set select FOR UPDATE or LOCK IN SHARE MODE * * @param bool * * @return NotORM_Result fluent interface */ function lock($exclusive = true){ $this->lock = $exclusive; return $this; } /** * @param NotORM_Result * @param bool * * @return NotORM_Result fluent interface */ function union(NotORM_Result $result, $all = false){ $this->union[] = " UNION " . ($all ? "ALL " : "") . ($this->notORM->driver == "sqlite" || $this->notORM->driver == "oci" ? $result : "($result)"); $this->parameters = array_merge($this->parameters, $result->parameters); return $this; } /** Execute aggregation function * * @param string $function * * @return string */ function aggregation($function){ $join = $this->createJoins(implode(",", $this->conditions) . ",$function"); $query = "SELECT $function FROM $this->table" . implode($join); if($this->where){ $query .= " WHERE " . implode($this->where); } foreach($this->query($query, $this->parameters)->fetch() as $return){ return $return; } } /** Count number of rows * * @param string * * @return int */ function count($column = "*"){ return $this->aggregation("COUNT($column)"); } /** Return minimum value from a column * * @param string * * @return int */ function min($column){ return $this->aggregation("MIN($column)"); } /** Return maximum value from a column * * @param string * * @return int */ function max($column){ return $this->aggregation("MAX($column)"); } /** Return sum of values in a column * * @param string * * @return int */ function sum($column){ return $this->aggregation("SUM($column)"); } /** Execute the built query * */ protected function execute(){ if(!isset($this->rows)){ $result = false; $exception = null; $parameters = array(); foreach(array_merge($this->select, array( $this, $this->group, $this->having ), $this->order, $this->unionOrder) as $val){ if(($val instanceof NotORM_Literal || $val instanceof self) && $val->parameters){ $parameters = array_merge($parameters, $val->parameters); } } try{ $result = $this->query($this->__toString(), $parameters); } catch(PDOException $exception){ // handled later } if(!$result){ if(!$this->select && $this->accessed){ $this->accessed = ''; $this->access = array(); $result = $this->query($this->__toString(), $parameters); }elseif($exception){ throw $exception; } } $this->rows = array(); if($result){ $result->setFetchMode(PDO::FETCH_ASSOC); foreach($result as $key => $row){ if(isset($row[$this->primary])){ $key = $row[$this->primary]; if(!is_string($this->access)){ $this->access[$this->primary] = true; } } //$this->rows[$key] = new $this->notORM->rowClass($row, $this); if ($this->notORM->isKeepPrimaryKeyIndex) { //@dogstar 采用主键作为下标 2015-12-30 $this->rows[$key] = $row; } else { //@dogstar 改用返回数组 2014-11-01 $this->rows[] = $row; } } } $this->data = $this->rows; } } /** Fetch next row of result * * @param string $column name to return or an empty string for the whole row * * @return mixed string or null with $column, NotORM_Row without $column, false if there is no row */ function fetch($column = ''){ // no $this->select($column) because next calls can access different columns $this->execute(); $return = current($this->data); next($this->data); if($return && $column != ''){ return $return[$column]; } return $return; } /** * fetchRow别名,等效于NotORM_Result::fetchRow() * @author: dogstar 2015-04-26 * * @param string $column * * @return mixed */ function fetchOne($column = ''){ return $this->fetchRow($column); } /** * 只查询第一行纪录,等效于NotORM_Result::fetchOne() * @author: dogstar 2015-04-18 * * @param string $column * * @return mixed */ function fetchRow($column = ''){ $this->limit(1)->execute(); return $this->fetch($column); } /** * 返回全部的数据,等效于NotORM_Result::fetchRows() * @author: dogstar 2014-11-01 */ function fetchAll(){ $this->execute(); return $this->data; } /** * fetchAll别名,等效于NotORM_Result::fetchAll() * @author: dogstar 2015-04-26 */ function fetchRows(){ return $this->fetchAll(); } /** * queryRows别名,等效于NotORM_Result::queryRows($sql, $parmas) * @author: dogstar 2015-04-26 * * @param string $sql * @param array $parmas * * @return array */ function queryAll($sql, $parmas = array()){ return $this->queryRows($sql, $parmas); } /** * 根据SQL查询全部数据,等效于NotORM_Result::queryAll($sql, $parmas) * * @param string $sql * @param array $parmas * * @return array * @author: dogstar 2014-11-01 */ function queryRows($sql, $parmas = array()){ $result = $this->query($sql, $parmas); $rows = array(); if($result){ $result->setFetchMode(PDO::FETCH_ASSOC); foreach($result as $row){ if(isset($row[$this->primary])){ if(!is_string($this->access)){ $this->access[$this->primary] = true; } } $rows[] = $row; } } return $rows; } /** Fetch all rows as associative array * * @param string $key * @param string $value column name used for an array value or an empty string for the whole row * * @return array */ function fetchPairs($key, $value = ''){ $return = array(); $clone = clone $this; if($value != ""){ $clone->select = array(); $clone->select("$key, $value"); // MultiResult adds its column }elseif($clone->select){ array_unshift($clone->select, $key); }else{ $clone->select = array("$key, $this->table.*"); } foreach($clone as $row){ //$values = array_values(iterator_to_array($row)); //bugfixed: Argument 1 passed to iterator_to_array() must implement interface Traversable, array given //@Scott @dogstar 20151122 /** @var Traversable|array $row */ $values = ($row instanceof Traversable) ? array_values(iterator_to_array($row)) : array_values($row); if($value != "" && $clone instanceof NotORM_MultiResult){ array_shift($values); } $return[(string) $values[0]] = ($value != "" ? $values[(array_key_exists(1, $values) ? 1 : 0)] : $row); // isset($values[1]) - fetchPairs("id", "id") } return $return; } protected function access($key, $delete = false){ if($delete){ if(is_array($this->access)){ $this->access[$key] = false; } return false; } if(!isset($key)){ $this->access = ''; }elseif(!is_string($this->access)){ $this->access[$key] = true; } if(!$this->select && $this->accessed && (!isset($key) || !isset($this->accessed[$key]))){ $this->accessed = ''; $this->rows = null; return true; } return false; } protected function single(){ } // Iterator implementation (not IteratorAggregate because $this->data can be changed during iteration) function rewind(){ $this->execute(); $this->keys = array_keys($this->data); reset($this->keys); } /** @return NotORM_Row */ function current(){ return $this->data[current($this->keys)]; } /** @return string row ID */ function key(){ return current($this->keys); } function next(){ next($this->keys); } function valid(){ return current($this->keys) !== false; } // ArrayAccess implementation /** Test if row exists * * @param mixed $key * * @return bool * */ function offsetExists($key){ $row = $this->offsetGet($key); return isset($row); } /** Get specified row * * @param string $key row ID or array for where conditions * * @return NotORM_Row or null if there is no such row */ function offsetGet($key){ if($this->single && !isset($this->data)){ $clone = clone $this; if(is_array($key)){ $clone->where($key)->limit(1); }else{ $clone->where($this->primary, $key); } $return = $clone->fetch(); if($return){ return $return; } }else{ $this->execute(); if(is_array($key)){ foreach($this->data as $row){ foreach($key as $k => $v){ if((isset($v) && $row[$k] !== null ? $row[$k] != $v : $row[$k] !== $v)){ continue 2; } } return $row; } }elseif(isset($this->data[$key])){ return $this->data[$key]; } } return NULL; } /** Mimic row * * @param string $key row ID * @param NotORM_Row $value * */ function offsetSet($key, $value){ $this->execute(); $this->data[$key] = $value; } /** Remove row from result set * * @param string $key row ID * */ function offsetUnset($key){ $this->execute(); unset($this->data[$key]); } // JsonSerializable implementation function jsonSerialize(){ $this->execute(); if($this->notORM->jsonAsArray){ return array_values($this->data); }else{ return $this->data; } } }