- PHP Framework upgrade 3.5.0 -> 3.5.1 (fixes some issues with CREST cURL caching, and SESSION management)

- #138 added "cookie logout" to "logout" menu entry
This commit is contained in:
Exodus4D
2016-05-06 18:09:28 +02:00
parent eb1e365020
commit dfd1e8501d
28 changed files with 575 additions and 388 deletions

View File

@@ -45,7 +45,7 @@ final class Base extends Prefab implements ArrayAccess {
//@{ Framework details
const
PACKAGE='Fat-Free Framework',
VERSION='3.5.0-Release';
VERSION='3.5.1-Release';
//@}
//@{ HTTP status codes (RFC 2616)
@@ -179,8 +179,8 @@ final class Base extends Prefab implements ArrayAccess {
}
/**
* assemble url from alias name
* @return NULL
* Assemble url from alias name
* @return string
* @param $name string
* @param $params array|string
**/
@@ -215,7 +215,7 @@ final class Base extends Prefab implements ArrayAccess {
function compile($str) {
$fw=$this;
return preg_replace_callback(
'/(?<!\w)@(\w(?:[\w\.\[\]\(]|\->|::)*)/',
'/(?<!\w)@(\w(?:[\h\w\.\[\]\(]|\->|::)*)/',
function($var) use($fw) {
return '$'.preg_replace_callback(
'/\.(\w+)\(|\.(\w+)|\[((?:[^\[\]]*|(?R))*)\]/',
@@ -226,7 +226,7 @@ final class Base extends Prefab implements ArrayAccess {
('['.var_export($expr[1],TRUE).']')).'('):
('['.var_export(
isset($expr[3])?
$fw->compile($expr[3]):
trim($fw->compile($expr[3])):
(ctype_digit($expr[2])?
(int)$expr[2]:
$expr[2]),TRUE).']');
@@ -305,10 +305,11 @@ final class Base extends Prefab implements ArrayAccess {
/**
* Return TRUE if hive key is empty and not cached
* @return bool
* @param $key string
* @param $val mixed
* @return bool
**/
function devoid($key) {
function devoid($key,&$val=NULL) {
$val=$this->ref($key,FALSE);
return empty($val) &&
(!Cache::instance()->exists($this->hash($key).'.var',$val) ||
@@ -422,8 +423,7 @@ final class Base extends Prefab implements ArrayAccess {
// End session
session_unset();
session_destroy();
unset($_COOKIE[session_name()]);
header_remove('Set-Cookie');
$this->clear('COOKIE.'.session_name());
}
$this->sync('SESSION');
}
@@ -679,6 +679,19 @@ final class Base extends Prefab implements ArrayAccess {
return $num?($num/abs($num)):0;
}
/**
* Extract values of an associative array whose keys start with the given prefix
* @return array
* @param $arr array
* @param $prefix string
**/
function extract($arr,$prefix) {
$out=array();
foreach (preg_grep('/^'.preg_quote($prefix,'/').'/',array_keys($arr)) as $key)
$out[substr($key,strlen($prefix))]=$arr[$key];
return $out;
}
/**
* Convert class constants to array
* @return array
@@ -687,14 +700,7 @@ final class Base extends Prefab implements ArrayAccess {
**/
function constants($class,$prefix='') {
$ref=new ReflectionClass($class);
$out=array();
foreach (preg_grep('/^'.$prefix.'/',array_keys($ref->getconstants()))
as $val) {
$out[$key=substr($val,strlen($prefix))]=
constant((is_object($class)?get_class($class):$class).'::'.$prefix.$key);
}
unset($ref);
return $out;
return $this->extract($ref->getconstants(),$prefix);
}
/**
@@ -843,9 +849,12 @@ final class Base extends Prefab implements ArrayAccess {
return number_format(
$args[$pos],0,'',$thousands_sep);
case 'currency':
if (function_exists('money_format'))
$int=$cstm=false;
if (isset($prop) && $cstm=!$int=($prop=='int'))
$currency_symbol=$prop;
if (!$cstm && function_exists('money_format'))
return money_format(
'%n',$args[$pos]);
'%'.($int?'i':'n'),$args[$pos]);
$fmt=array(
0=>'(nc)',1=>'(n c)',
2=>'(nc)',10=>'+nc',
@@ -878,7 +887,8 @@ final class Base extends Prefab implements ArrayAccess {
$frac_digits,
$decimal_point,
$thousands_sep),
$currency_symbol),
$int?$int_curr_symbol
:$currency_symbol),
$fmt[(int)(
(${$pre.'_cs_precedes'}%2).
(${$pre.'_sign_posn'}%5).
@@ -891,8 +901,8 @@ final class Base extends Prefab implements ArrayAccess {
$thousands_sep).'%';
case 'decimal':
return number_format(
$args[$pos],$prop,$decimal_point,
$thousands_sep);
$args[$pos],isset($prop)?$prop:2,
$decimal_point,$thousands_sep);
}
break;
case 'date':
@@ -1022,7 +1032,7 @@ final class Base extends Prefab implements ArrayAccess {
**/
function status($code) {
$reason=@constant('self::HTTP_'.$code);
if (PHP_SAPI!='cli')
if (PHP_SAPI!='cli' && !headers_sent())
header($_SERVER['SERVER_PROTOCOL'].' '.$code.' '.$reason);
return $reason;
}
@@ -1089,11 +1099,12 @@ final class Base extends Prefab implements ArrayAccess {
}
/**
* Return formatted stack trace
* @return string
* Return filtered, formatted stack trace
* @return string|array
* @param $trace array|NULL
* @param $format bool
**/
function trace(array $trace=NULL) {
function trace(array $trace=NULL, $format=TRUE) {
if (!$trace) {
$trace=debug_backtrace(FALSE);
$frame=$trace[0];
@@ -1111,6 +1122,8 @@ final class Base extends Prefab implements ArrayAccess {
'__call|call_user_func)/',$frame['function']));
}
);
if (!$format)
return $trace;
$out='';
$eol="\n";
// Analyze stack trace
@@ -1369,7 +1382,7 @@ final class Base extends Prefab implements ArrayAccess {
$url=$this->rel($this->hive['URI']);
$case=$this->hive['CASELESS']?'i':'';
preg_match('/^'.
preg_replace('/@(\w+\b)/','(?P<\1>[^\/\?]+)',
preg_replace('/((\\\{)?@(\w+\b)(?(2)\\\}))/','(?P<\3>[^\/\?]+)',
str_replace('\*','([^\?]+)',preg_quote($pattern,'/'))).
'\/?(?:\?.*)?$/'.$case.'um',$url,$args);
return $args;
@@ -1394,7 +1407,7 @@ final class Base extends Prefab implements ArrayAccess {
array_multisort($paths,SORT_DESC,$keys,$vals);
$this->hive['ROUTES']=array_combine($keys,$vals);
// Convert to BASE-relative URL
$req=$this->rel($this->hive['URI']);
$req=$this->rel(urldecode($this->hive['URI']));
if ($cors=(isset($this->hive['HEADERS']['Origin']) &&
$this->hive['CORS']['origin'])) {
$cors=$this->hive['CORS'];
@@ -1428,7 +1441,7 @@ final class Base extends Prefab implements ArrayAccess {
if (is_numeric($key) && $key)
unset($args[$key]);
// Capture values of route pattern tokens
$this->hive['PARAMS']=$args=array_map('urldecode',$args);
$this->hive['PARAMS']=$args;
// Save matching route
$this->hive['ALIAS']=$alias;
$this->hive['PATTERN']=$pattern;
@@ -1437,9 +1450,10 @@ final class Base extends Prefab implements ArrayAccess {
implode(',',$cors['expose']):$cors['expose']));
if (is_string($handler)) {
// Replace route pattern tokens in handler if any
$handler=preg_replace_callback('/@(\w+\b)/',
$handler=preg_replace_callback('/({)?@(\w+\b)(?(1)})/',
function($id) use($args) {
return isset($args[$id[1]])?$args[$id[1]]:$id[0];
$pid=count($id)>2?2:1;
return isset($args[$id[$pid]])?$args[$id[$pid]]:$id[0];
},
$handler
);
@@ -1458,7 +1472,7 @@ final class Base extends Prefab implements ArrayAccess {
$cached=$cache->exists(
$hash=$this->hash($this->hive['VERB'].' '.
$this->hive['URI']).'.url',$data);
if ($cached && $cached[0]+$ttl>$now) {
if ($cached) {
if (isset($headers['If-Modified-Since']) &&
strtotime($headers['If-Modified-Since'])+
$ttl>$now) {
@@ -1520,10 +1534,13 @@ final class Base extends Prefab implements ArrayAccess {
// Unhandled HTTP method
header('Allow: '.implode(',',array_unique($allowed)));
if ($cors) {
header('Access-Control-Allow-Methods: OPTIONS,'.implode(',',$allowed));
header('Access-Control-Allow-Methods: OPTIONS,'.
implode(',',$allowed));
if ($cors['headers'])
header('Access-Control-Allow-Headers: '.(is_array($cors['headers'])?
implode(',',$cors['headers']):$cors['headers']));
header('Access-Control-Allow-Headers: '.
(is_array($cors['headers'])?
implode(',',$cors['headers']):
$cors['headers']));
if ($cors['ttl']>0)
header('Access-Control-Max-Age: '.$cors['ttl']);
}
@@ -1546,28 +1563,26 @@ final class Base extends Prefab implements ArrayAccess {
$time=time();
$limit=max(0,min($timeout,$max=ini_get('max_execution_time')-1));
$out='';
$flag=FALSE;
// Turn output buffering on
ob_start();
// Not for the weak of heart
while (
// No error occurred
!$this->hive['ERROR'] &&
// Still alive?
!connection_aborted() &&
// Got time left?
(time()-$time+1<$limit) &&
// Restart session
$flag=@session_start() &&
@session_start() &&
// CAUTION: Callback will kill host if it never becomes truthy!
!($out=$this->call($func,$args))) {
session_commit();
ob_flush();
flush();
// Hush down
sleep(1);
}
if ($flag) {
session_commit();
ob_flush();
flush();
}
ob_flush();
flush();
return $out;
}
@@ -1577,9 +1592,11 @@ final class Base extends Prefab implements ArrayAccess {
function abort() {
@session_start();
session_commit();
header('Content-Length: 0');
$out='';
while (ob_get_level())
ob_end_clean();
$out=ob_get_clean().$out;
header('Content-Length: '.strlen($out));
echo $out;
flush();
if (function_exists('fastcgi_finish_request'))
fastcgi_finish_request();
@@ -1826,7 +1843,7 @@ final class Base extends Prefab implements ArrayAccess {
$out='';
$pre=FALSE;
$text=trim($text);
if (!preg_match('/^<\?php/',$text)) {
if ($text && !preg_match('/^<\?php/',$text)) {
$text='<?php '.$text;
$pre=TRUE;
}
@@ -2083,9 +2100,9 @@ final class Base extends Prefab implements ArrayAccess {
'CONFIG'=>NULL,
'CORS'=>array(
'headers'=>'',
'origin'=>false,
'credentials'=>false,
'expose'=>false,
'origin'=>FALSE,
'credentials'=>FALSE,
'expose'=>FALSE,
'ttl'=>0),
'DEBUG'=>0,
'DIACRITICS'=>array(),
@@ -2131,7 +2148,7 @@ final class Base extends Prefab implements ArrayAccess {
'SERIALIZER'=>extension_loaded($ext='igbinary')?$ext:'php',
'TEMP'=>'tmp/',
'TIME'=>microtime(TRUE),
'TZ'=>(@ini_get('date.timezone'))?:'UTC',
'TZ'=>@date_default_timezone_get(),
'UI'=>'./',
'UNLOAD'=>NULL,
'UPLOADS'=>'./',
@@ -2500,8 +2517,7 @@ class View extends Prefab {
function render($file,$mime='text/html',array $hive=NULL,$ttl=0) {
$fw=Base::instance();
$cache=Cache::instance();
$cached=$cache->exists($hash=$fw->hash($file),$data);
if ($cached && $cached[0]+$ttl>microtime(TRUE))
if ($cache->exists($hash=$fw->hash($file),$data))
return $data;
foreach ($fw->split($fw->get('UI').';./') as $dir)
if (is_file($this->view=$fw->fixslashes($dir.$file))) {
@@ -2516,7 +2532,7 @@ class View extends Prefab {
foreach($this->trigger['afterrender'] as $func)
$data=$fw->call($func,$data);
if ($ttl)
$cache->set($hash,$data);
$cache->set($hash,$data,$ttl);
return $data;
}
user_error(sprintf(Base::E_Open,$file),E_USER_ERROR);
@@ -2539,7 +2555,7 @@ class Preview extends View {
//! MIME type
$mime,
//! token filter
$filter = array(
$filter=array(
'esc'=>'$this->esc',
'raw'=>'$this->raw',
'alias'=>'\Base::instance()->alias',
@@ -2552,15 +2568,24 @@ class Preview extends View {
* @param $str string
**/
function token($str) {
return trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
$str=trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
Base::instance()->compile($str)));
if (preg_match('/^(.+)(?<!\|)\|((?:\h*\w+(?:\h*[,;]?))+)$/s',
$str,$parts)) {
$str=trim($parts[1]);
foreach (Base::instance()->split($parts[2]) as $func)
$str=is_string($cmd=$this->filter($func))?$cmd.'('.$str.')':
'\Base::instance()->call('.
'$this->filter(\''.$func.'\'),array('.$str.'))';
}
return $str;
}
/**
* register token filter
* Register or get (a specific one or all) token filters
* @param string $key
* @param string $func
* @return array
* @param string|closure $func
* @return array|closure|string
*/
function filter($key=NULL,$func=NULL) {
if (!$key)
@@ -2578,19 +2603,15 @@ class Preview extends View {
protected function build($node) {
$self=$this;
return preg_replace_callback(
'/\{\-(.+?)\-\}|\{\{(.+?)\}\}(\n+)?/s',
'/\{\-(.+?)\-\}|\{\{(.+?)\}\}(\n+)?|(\{\*.*?\*\})/s',
function($expr) use($self) {
if ($expr[1])
return $expr[1];
$str=trim($self->token($expr[2]));
if (preg_match('/^([^|]+?)\h*\|(\h*\w+(?:\h*[,;]\h*\w+)*)/',
$str,$parts)) {
$str=$parts[1];
foreach (Base::instance()->split($parts[2]) as $func)
$str=$self->filter($func).'('.$str.')';
}
return '<?php echo '.$str.'; ?>'.
(isset($expr[3])?$expr[3]."\n":'');
return empty($expr[4])?
('<?php echo '.$str.'; ?>'.
(isset($expr[3])?$expr[3]."\n":'')):
'';
},
preg_replace_callback(
'/\{~(.+?)~\}/s',
@@ -2631,8 +2652,7 @@ class Preview extends View {
if (!is_dir($tmp=$fw->get('TEMP')))
mkdir($tmp,Base::MODE,TRUE);
foreach ($fw->split($fw->get('UI')) as $dir) {
$cached=$cache->exists($hash=$fw->hash($dir.$file),$data);
if ($cached && $cached[0]+$ttl>microtime(TRUE))
if ($cache->exists($hash=$fw->hash($dir.$file),$data))
return $data;
if (is_file($view=$fw->fixslashes($dir.$file))) {
if (!is_file($this->view=($tmp.
@@ -2659,7 +2679,7 @@ class Preview extends View {
foreach ($this->trigger['afterrender'] as $func)
$data = $fw->call($func, $data);
if ($ttl)
$cache->set($hash,$data);
$cache->set($hash,$data,$ttl);
return $data;
}
}