- upgraded FatFreeFramework 3.6.1 -> 3.6.2

This commit is contained in:
Mark Friedrich
2018-01-06 19:51:16 +01:00
parent 6fdb1ed91b
commit ac417fb19f
26 changed files with 842 additions and 580 deletions

View File

@@ -39,7 +39,9 @@ class Mapper extends \DB\Cursor {
//! Defined fields
$fields,
//! Adhoc fields
$adhoc=[];
$adhoc=[],
//! Dynamic properties
$props=[];
/**
* Return database type
@@ -57,15 +59,6 @@ class Mapper extends \DB\Cursor {
return $this->source;
}
/**
* Return TRUE if field is defined
* @return bool
* @param $key string
**/
function exists($key) {
return array_key_exists($key,$this->fields+$this->adhoc);
}
/**
* Return TRUE if any/specified field value has changed
* @return bool
@@ -80,6 +73,15 @@ class Mapper extends \DB\Cursor {
return FALSE;
}
/**
* Return TRUE if field is defined
* @return bool
* @param $key string
**/
function exists($key) {
return array_key_exists($key,$this->fields+$this->adhoc);
}
/**
* Assign value to field
* @return scalar
@@ -95,12 +97,14 @@ class Mapper extends \DB\Cursor {
$this->fields[$key]['changed']=TRUE;
return $this->fields[$key]['value']=$val;
}
// adjust result on existing expressions
// Adjust result on existing expressions
if (isset($this->adhoc[$key]))
$this->adhoc[$key]['value']=$val;
else
elseif (is_string($val))
// Parenthesize expression in case it's a subquery
$this->adhoc[$key]=['expr'=>'('.$val.')','value'=>NULL];
else
$this->props[$key]=$val;
return $val;
}
@@ -116,6 +120,8 @@ class Mapper extends \DB\Cursor {
return $this->fields[$key]['value'];
elseif (array_key_exists($key,$this->adhoc))
return $this->adhoc[$key]['value'];
elseif (array_key_exists($key,$this->props))
return $this->props[$key];
user_error(sprintf(self::E_Field,$key),E_USER_ERROR);
}
@@ -127,26 +133,22 @@ class Mapper extends \DB\Cursor {
function clear($key) {
if (array_key_exists($key,$this->adhoc))
unset($this->adhoc[$key]);
else
unset($this->props[$key]);
}
/**
* Get PHP type equivalent of PDO constant
* @return string
* @param $pdo string
* Invoke dynamic method
* @return mixed
* @param $func string
* @param $args array
**/
function type($pdo) {
switch ($pdo) {
case \PDO::PARAM_NULL:
return 'unset';
case \PDO::PARAM_INT:
return 'int';
case \PDO::PARAM_BOOL:
return 'bool';
case \PDO::PARAM_STR:
return 'string';
case \DB\SQL::PARAM_FLOAT:
return 'float';
}
function __call($func,$args) {
return call_user_func_array(
(array_key_exists($func,$this->props)?
$this->props[$func]:
$this->$func),$args
);
}
/**
@@ -192,14 +194,13 @@ class Mapper extends \DB\Cursor {
}
/**
* Build query string and execute
* @return static[]
* Build query string and arguments
* @return array
* @param $fields string
* @param $filter string|array
* @param $options array
* @param $ttl int|array
**/
function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
function stringify($fields,$filter=NULL,array $options=NULL) {
if (!$options)
$options=[];
$options+=[
@@ -224,7 +225,7 @@ class Mapper extends \DB\Cursor {
$sql.=' GROUP BY '.implode(',',array_map(
function($str) use($db) {
return preg_replace_callback(
'/\b(\w+)\h*(HAVING.+|$)/i',
'/\b(\w+[._\-\w]*)\h*(HAVING.+|$)/i',
function($parts) use($db) {
return $db->quotekey($parts[1]).
(isset($parts[2])?(' '.$parts[2]):'');
@@ -235,47 +236,63 @@ class Mapper extends \DB\Cursor {
explode(',',$options['group'])));
}
if ($options['order']) {
$sql.=' ORDER BY '.implode(',',array_map(
$order=' ORDER BY '.implode(',',array_map(
function($str) use($db) {
return preg_match('/^(\w+)(?:\h+(ASC|DESC))?\h*(?:,|$)/i',
return preg_match('/^\h*(\w+[._\-\w]*)(?:\h+((?:ASC|DESC)[\w\h]*))?\h*$/i',
$str,$parts)?
($db->quotekey($parts[1]).
(isset($parts[2])?(' '.$parts[2]):'')):$str;
},
explode(',',$options['order'])));
}
// SQL Server fixes
if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
($options['limit'] || $options['offset'])) {
$pkeys=[];
foreach ($this->fields as $key=>$field)
if ($field['pkey'])
$pkeys[]=$key;
// order by pkey when no ordering option was given
if (!$options['order'])
foreach ($this->fields as $key=>$field)
if ($field['pkey']) {
$order=' ORDER BY '.$db->quotekey($key);
break;
}
$ofs=$options['offset']?(int)$options['offset']:0;
$lmt=$options['limit']?(int)$options['limit']:0;
if (strncmp($db->version(),'11',2)>=0) {
// SQL Server 2012
if (!$options['order'])
$sql.=' ORDER BY '.$db->quotekey($pkeys[0]);
$sql.=' OFFSET '.$ofs.' ROWS';
// SQL Server >= 2012
$sql.=$order.' OFFSET '.$ofs.' ROWS';
if ($lmt)
$sql.=' FETCH NEXT '.$lmt.' ROWS ONLY';
}
else {
// SQL Server 2008
$sql=str_replace('SELECT',
$sql=preg_replace('/SELECT/',
'SELECT '.
($lmt>0?'TOP '.($ofs+$lmt):'').' ROW_NUMBER() '.
'OVER (ORDER BY '.
$db->quotekey($pkeys[0]).') AS rnum,',$sql);
'OVER ('.$order.') AS rnum,',$sql.$order,1);
$sql='SELECT * FROM ('.$sql.') x WHERE rnum > '.($ofs);
}
}
else {
if (isset($order))
$sql.=$order;
if ($options['limit'])
$sql.=' LIMIT '.(int)$options['limit'];
if ($options['offset'])
$sql.=' OFFSET '.(int)$options['offset'];
}
return [$sql,$args];
}
/**
* Build query string and execute
* @return object
* @param $fields string
* @param $filter string|array
* @param $options array
* @param $ttl int|array
**/
function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
list($sql,$args)=$this->stringify($fields,$filter,$options);
$result=$this->db->exec($sql,$args,$ttl);
$out=[];
foreach ($result as &$row) {
@@ -285,8 +302,6 @@ class Mapper extends \DB\Cursor {
$val=$this->db->value(
$this->fields[$field]['pdo_type'],$val);
}
elseif (array_key_exists($field,$this->adhoc))
$this->adhoc[$field]['value']=$val;
unset($val);
}
$out[]=$this->factory($row);
@@ -329,14 +344,17 @@ class Mapper extends \DB\Cursor {
* @param $ttl int|array
**/
function count($filter=NULL,array $options=NULL,$ttl=0) {
$expr='COUNT(*)';
$field='rows';
$this->adhoc[$field]=['expr'=>$expr,'value'=>NULL];
$result=$this->select($expr.' AS '.$this->db->quotekey($field),
$filter,$options,$ttl);
$out=$result[0]->adhoc[$field]['value'];
unset($this->adhoc[$field]);
return $out;
$adhoc='';
foreach ($this->adhoc as $key=>$field)
$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
$fields='*'.$adhoc;
if (preg_match('/mssql|dblib|sqlsrv/',$this->engine))
$fields='TOP 100 PERCENT '.$fields;
list($sql,$args)=$this->stringify($fields,$filter,$options);
$sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '.
'FROM ('.$sql.') AS '.$this->db->quotekey('_temp');
$result=$this->db->exec($sql,$args,$ttl);
return (int)$result[0]['_rows'];
}
/**
@@ -404,28 +422,27 @@ class Mapper extends \DB\Cursor {
$actr++;
$ckeys[]=$key;
}
$field['changed']=FALSE;
unset($field);
}
if ($fields) {
$this->db->exec(
$add='';
if ($this->engine=='pgsql') {
$names=array_keys($pkeys);
$aik=end($names);
$add=' RETURNING '.$this->db->quotekey($aik);
}
$lID=$this->db->exec(
(preg_match('/mssql|dblib|sqlsrv/',$this->engine) &&
array_intersect(array_keys($pkeys),$ckeys)?
'SET IDENTITY_INSERT '.$this->table.' ON;':'').
'INSERT INTO '.$this->table.' ('.$fields.') '.
'VALUES ('.$values.')',$args
'VALUES ('.$values.')'.$add,$args
);
$seq=NULL;
if ($this->engine=='pgsql') {
$names=array_keys($pkeys);
$aik=end($names);
if ($this->fields[$aik]['pdo_type']==\PDO::PARAM_INT)
$seq=$this->source.'_'.$aik.'_seq';
}
if ($this->engine!='oci' && !($this->engine=='pgsql' && !$seq))
$this->_id=$this->db->lastinsertid($seq);
if ($this->engine=='pgsql' && $lID)
$this->_id=$lID[0][$aik];
elseif ($this->engine!='oci')
$this->_id=$this->db->lastinsertid();
// Reload to obtain default and auto-increment field values
if ($inc || $filter)
if ($reload=($inc || $filter))
$this->load($inc?
[$inc.'=?',$this->db->value(
$this->fields[$inc]['pdo_type'],$this->_id)]:
@@ -433,6 +450,13 @@ class Mapper extends \DB\Cursor {
if (isset($this->trigger['afterinsert']))
\Base::instance()->call($this->trigger['afterinsert'],
[$this,$pkeys]);
// reset changed flag after calling afterinsert
if (!$reload)
foreach ($this->fields as $key=>&$field) {
$field['changed']=FALSE;
$field['initial']=$field['value'];
unset($field);
}
}
return $this;
}
@@ -484,10 +508,17 @@ class Mapper extends \DB\Cursor {
/**
* Delete current record
* @return int
* @param $quick bool
* @param $filter string|array
**/
function erase($filter=NULL) {
function erase($filter=NULL,$quick=TRUE) {
if (isset($filter)) {
if (!$quick) {
$out=0;
foreach ($this->find($filter) as $mapper)
$out+=$mapper->erase();
return $out;
}
$args=[];
if (is_array($filter)) {
$args=isset($filter[1]) && is_array($filter[1])?
@@ -497,7 +528,8 @@ class Mapper extends \DB\Cursor {
list($filter)=$filter;
}
return $this->db->
exec('DELETE FROM '.$this->table.($filter?' WHERE '.$filter:'').';',$args);
exec('DELETE FROM '.$this->table.
($filter?' WHERE '.$filter:'').';',$args);
}
$args=[];
$ctr=0;
@@ -561,7 +593,7 @@ class Mapper extends \DB\Cursor {
**/
function copyfrom($var,$func=NULL) {
if (is_string($var))
$var=\Base::instance()->get($var);
$var=\Base::instance()->$var;
if ($func)
$var=call_user_func($func,$var);
foreach ($var as $key=>$val)