- upgraded "Fat Free Framework" PHP core framework v3.6.4v3.6.5

This commit is contained in:
Mark Friedrich
2018-12-26 14:29:37 +01:00
parent 58db5962fb
commit e56740e8a0
22 changed files with 394 additions and 245 deletions

View File

@@ -1,5 +1,48 @@
CHANGELOG
3.6.5 (24 December 2018)
* NEW: Log, added timestamp to each line
* NEW: Auth, added support for custom compare method, [#116](https://github.com/bcosca/fatfree-core/issues/116)
* NEW: cache tag support for mongo & jig mapper, ref [#166](https://github.com/bcosca/fatfree-core/issues/116)
* NEW: Allow PHP functions as template token filters
* Web: Fix double redirect bug when running cURL with open_basedir disabled
* Web: Cope with responses from HTTP/2 servers
* Web->filler: remove very first space, when $std is false
* Web\OAuth2: Cope with HTTP/2 responses
* Web\OAuth2: take Content-Type header into account for json decoding, [#250](https://github.com/bcosca/fatfree-core/issues/250) [#251](https://github.com/bcosca/fatfree-core/issues/251)
* Web\OAuth2: fixed empty results on some endpoints [#250](https://github.com/bcosca/fatfree-core/issues/250)
* DB\SQL\Mapper: optimize mapper->count memory usage
* DB\SQL\Mapper: New table alias operator
* DB\SQL\Mapper: fix count() performance on non-grouped result sets, [bcosca/fatfree#1114](https://github.com/bcosca/fatfree/issues/1114)
* DB\SQL: Support for CTE in postgreSQL, [bcosca/fatfree#1107](https://github.com/bcosca/fatfree/issues/1107), [bcosca/fatfree#1116](https://github.com/bcosca/fatfree/issues/1116), [bcosca/fatfree#1021](https://github.com/bcosca/fatfree/issues/1021)
* DB\SQL->log: Remove extraneous whitespace
* DB\SQL: Added ability to add inline comments per SQL query
* CLI\WS, Refactoring: Streamline socket server
* CLI\WS: Add option for dropping query in OAuth2 URI
* CLI\WS: Add URL-safe base64 encoding
* CLI\WS: Detect errors in returned JSON values
* CLI\WS: Added support for Sec-WebSocket-Protocol header
* Matrix->calendar: Allow unix timestamp as date argument
* Basket: Access basket item by _id [#260](https://github.com/bcosca/fatfree-core/issues/260)
* SMTP: Added TLS 1.2 support [bcosca/fatfree#1115](https://github.com/bcosca/fatfree/issues/1115)
* SMTP->send: Respect $log argument
* Base->cast: recognize binary and octal numbers in config
* Base->cast: add awareness of hexadecimal literals
* Base->abort: Remove unnecessary Content-Encoding header
* Base->abort: Ensure headers have not been flushed
* Base->format: Differentiate between long- and full-date (with localized weekday) formats
* Base->format: Conform with intl extension's number output
* Enable route handler to override Access-Control headers in response to OPTIONS request, [#257](https://github.com/bcosca/fatfree-core/issues/257)
* Augment filters with a var_export function
* Bug fix php7.3: Fix template parse regex to be compatible with strict PCRE2 rules for hyphen placement in a character class
* Bug fix, Cache->set: update creation time when updating existing cache entries
* Bug fix: incorrect ICU date/time formatting
* Bug fix, Jig: lazy write on empty data
* Bug fix: Method uppercase to avoid route failure [#252](https://github.com/bcosca/fatfree-core/issues/252)
* Fixed error description when (PSR-11) `CONTAINER` fails to resolve a class [#253](https://github.com/bcosca/fatfree-core/issues/253)
* Mitigate CSRF predictability/vulnerability
* Expose Mapper->factory() method
3.6.4 (19 April 2018)
* NEW: Added Dependency Injection support with CONTAINER variable [#221](https://github.com/bcosca/fatfree-core/issues/221)
* NEW: configurable LOGGABLE error codes [#1091](https://github.com/bcosca/fatfree/issues/1091#issuecomment-364674701)

View File

@@ -2,7 +2,7 @@
/*
Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.
Copyright (c) 2009-2018 F3::Factory/Bong Cosca, All rights reserved.
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
@@ -35,7 +35,9 @@ class Auth {
//! Mapper object
$mapper,
//! Storage options
$args;
$args,
//! Custom compare function
$func;
/**
* Jig storage handler
@@ -45,22 +47,26 @@ class Auth {
* @param $realm string
**/
protected function _jig($id,$pw,$realm) {
return (bool)
$success = (bool)
call_user_func_array(
[$this->mapper,'load'],
[
array_merge(
[
'@'.$this->args['id'].'==? AND '.
'@'.$this->args['pw'].'==?'.
'@'.$this->args['id'].'==?'.
($this->func?'':' AND @'.$this->args['pw'].'==?').
(isset($this->args['realm'])?
(' AND @'.$this->args['realm'].'==?'):''),
$id,$pw
$id
],
($this->func?[]:[$pw]),
(isset($this->args['realm'])?[$realm]:[])
)
]
);
if ($success && $this->func)
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
return $success;
}
/**
@@ -71,15 +77,16 @@ class Auth {
* @param $realm string
**/
protected function _mongo($id,$pw,$realm) {
return (bool)
$success = (bool)
$this->mapper->load(
[
$this->args['id']=>$id,
$this->args['pw']=>$pw
]+
[$this->args['id']=>$id]+
($this->func?[]:[$this->args['pw']=>$pw])+
(isset($this->args['realm'])?
[$this->args['realm']=>$realm]:[])
);
if ($success && $this->func)
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
return $success;
}
/**
@@ -90,22 +97,26 @@ class Auth {
* @param $realm string
**/
protected function _sql($id,$pw,$realm) {
return (bool)
$success = (bool)
call_user_func_array(
[$this->mapper,'load'],
[
array_merge(
[
$this->args['id'].'=? AND '.
$this->args['pw'].'=?'.
$this->args['id'].'=?'.
($this->func?'':' AND '.$this->args['pw'].'=?').
(isset($this->args['realm'])?
(' AND '.$this->args['realm'].'=?'):''),
$id,$pw
$id
],
($this->func?[]:[$pw]),
(isset($this->args['realm'])?[$realm]:[])
)
]
);
if ($success && $this->func)
$success = call_user_func($this->func,$pw,$this->mapper->get($this->args['pw']));
return $success;
}
/**
@@ -234,8 +245,9 @@ class Auth {
* @return object
* @param $storage string|object
* @param $args array
* @param $func callback
**/
function __construct($storage,array $args=NULL) {
function __construct($storage,array $args=NULL,$func=NULL) {
if (is_object($storage) && is_a($storage,'DB\Cursor')) {
$this->storage=$storage->dbtype();
$this->mapper=$storage;
@@ -244,6 +256,7 @@ class Auth {
else
$this->storage=$storage;
$this->args=$args;
$this->func=$func;
}
}

View File

@@ -45,7 +45,7 @@ final class Base extends Prefab implements ArrayAccess {
//@{ Framework details
const
PACKAGE='Fat-Free Framework',
VERSION='3.6.4-Release';
VERSION='3.6.5-Release';
//@}
//@{ HTTP status codes (RFC 2616)
@@ -207,11 +207,13 @@ final class Base extends Prefab implements ArrayAccess {
}
/**
* cast string variable to php type or constant
* Cast string variable to PHP type or constant
* @param $val
* @return mixed
*/
function cast($val) {
if (preg_match('/^(?:0x[0-9a-f]+|0[0-7]+|0b[01]+)$/i',$val))
return intval($val,0);
if (is_numeric($val))
return $val+0;
$val=trim($val);
@@ -241,13 +243,13 @@ final class Base extends Prefab implements ArrayAccess {
$out='['.
(isset($sub[3])?
$this->compile($sub[3]):
var_export($sub[1],TRUE)).
$this->export($sub[1])).
']';
}
else
$out=function_exists($sub[1])?
$sub[0]:
('['.var_export($sub[1],TRUE).']'.$sub[2]);
('['.$this->export($sub[1]).']'.$sub[2]);
return $out;
},
$expr[2]
@@ -680,7 +682,7 @@ final class Base extends Prefab implements ArrayAccess {
$str='';
foreach (get_object_vars($arg) as $key=>$val)
$str.=($str?',':'').
var_export($key,TRUE).'=>'.
$this->export($key).'=>'.
$this->stringify($val,
array_merge($stack,[$arg]));
return get_class($arg).'::__set_state(['.$str.'])';
@@ -690,11 +692,11 @@ final class Base extends Prefab implements ArrayAccess {
ctype_digit(implode('',array_keys($arg)));
foreach ($arg as $key=>$val)
$str.=($str?',':'').
($num?'':(var_export($key,TRUE).'=>')).
($num?'':($this->export($key).'=>')).
$this->stringify($val,array_merge($stack,[$arg]));
return '['.$str.']';
default:
return var_export($arg,TRUE);
return $this->export($arg);
}
}
@@ -890,8 +892,14 @@ final class Base extends Prefab implements ArrayAccess {
return $expr[0];
if (isset($type)) {
if (isset($this->hive['FORMATS'][$type]))
return $this->call($this->hive['FORMATS'][$type],
[$args[$pos],isset($mod)?$mod:null,isset($prop)?$prop:null]);
return $this->call(
$this->hive['FORMATS'][$type],
[
$args[$pos],
isset($mod)?$mod:null,
isset($prop)?$prop:null
]
);
switch ($type) {
case 'plural':
preg_match_all('/(?<tag>\w+)'.
@@ -964,16 +972,22 @@ final class Base extends Prefab implements ArrayAccess {
$args[$pos]*100,0,$decimal_point,
$thousands_sep).'%';
}
$frac=$args[$pos]-(int)$args[$pos];
return number_format(
$args[$pos],isset($prop)?$prop:2,
$args[$pos],
isset($prop)?
$prop:
$frac?strlen($frac)-2:0,
$decimal_point,$thousands_sep);
case 'date':
$prop='%d %B %Y';
if (empty($mod) || $mod=='short')
$prop='%x';
elseif ($mod=='long')
$prop='%A, %d %B %Y';
elseif ($mod=='full')
$prop='%A, '.$prop;
return strftime($prop,$args[$pos]);
case 'time':
$prop='%r';
if (empty($mod) || $mod=='short')
$prop='%X';
return strftime($prop,$args[$pos]);
@@ -987,6 +1001,15 @@ final class Base extends Prefab implements ArrayAccess {
);
}
/**
* Return string representation of expression
* @return string
* @param $expr mixed
**/
function export($expr) {
return var_export($expr,TRUE);
}
/**
* Assign/auto-detect language
* @return string
@@ -1244,7 +1267,8 @@ final class Base extends Prefab implements ArrayAccess {
if (!is_array($loggable))
$loggable=$this->split($loggable);
foreach ($loggable as $status)
if ($status=='*' || preg_match('/^'.preg_replace('/\D/','\d',$status).'$/',$code)) {
if ($status=='*' ||
preg_match('/^'.preg_replace('/\D/','\d',$status).'$/',$code)) {
error_log($text);
foreach (explode("\n",$trace) as $nexus)
if ($nexus)
@@ -1270,7 +1294,14 @@ final class Base extends Prefab implements ArrayAccess {
'beforeroute,afterroute')===FALSE) &&
!$prior && !$this->hive['CLI'] && !$this->hive['QUIET'])
echo $this->hive['AJAX']?
json_encode(array_diff_key($this->hive['ERROR'],$this->hive['DEBUG']?[]:['trace'=>1])):
json_encode(
array_diff_key(
$this->hive['ERROR'],
$this->hive['DEBUG']?
[]:
['trace'=>1]
)
):
('<!DOCTYPE html>'.$eol.
'<html>'.$eol.
'<head>'.
@@ -1557,7 +1588,7 @@ final class Base extends Prefab implements ArrayAccess {
$cors=$this->hive['CORS'];
header('Access-Control-Allow-Origin: '.$cors['origin']);
header('Access-Control-Allow-Credentials: '.
var_export($cors['credentials'],TRUE));
$this->export($cors['credentials']));
$preflight=
isset($this->hive['HEADERS']['Access-Control-Request-Method']);
}
@@ -1674,12 +1705,15 @@ final class Base extends Prefab implements ArrayAccess {
// URL doesn't match any route
$this->error(404);
elseif (!$this->hive['CLI']) {
// Unhandled HTTP method
header('Allow: '.implode(',',array_unique($allowed)));
if (!preg_grep('/Allow:/',$headers_send=headers_list()))
// Unhandled HTTP method
header('Allow: '.implode(',',array_unique($allowed)));
if ($cors) {
header('Access-Control-Allow-Methods: OPTIONS,'.
implode(',',$allowed));
if ($cors['headers'])
if (!preg_grep('/Access-Control-Allow-Methods:/',$headers_send))
header('Access-Control-Allow-Methods: OPTIONS,'.
implode(',',$allowed));
if ($cors['headers'] &&
!preg_grep('/Access-Control-Allow-Headers:/',$headers_send))
header('Access-Control-Allow-Headers: '.
(is_array($cors['headers'])?
implode(',',$cors['headers']):
@@ -1733,7 +1767,9 @@ final class Base extends Prefab implements ArrayAccess {
}
/**
* Disconnect HTTP client
* Disconnect HTTP client;
* Set FcgidOutputBufferSize to zero if server uses mod_fcgid;
* Disable mod_deflate when rendering text/html output
**/
function abort() {
if (!headers_sent() && session_status()!=PHP_SESSION_ACTIVE)
@@ -1741,9 +1777,10 @@ final class Base extends Prefab implements ArrayAccess {
$out='';
while (ob_get_level())
$out=ob_get_clean().$out;
header('Content-Encoding: none');
header('Content-Length: '.strlen($out));
header('Connection: close');
if (!headers_sent()) {
header('Content-Length: '.strlen($out));
header('Connection: close');
}
session_commit();
echo $out;
flush();
@@ -1771,11 +1808,13 @@ final class Base extends Prefab implements ArrayAccess {
$parts[1]=call_user_func([$container,'get'],$parts[1]);
elseif (is_callable($container))
$parts[1]=call_user_func($container,$parts[1],$args);
elseif (is_string($container) && is_subclass_of($container,'Prefab'))
$parts[1]=call_user_func($container.'::instance')->get($parts[1]);
elseif (is_string($container) &&
is_subclass_of($container,'Prefab'))
$parts[1]=call_user_func($container.'::instance')->
get($parts[1]);
else
user_error(sprintf(self::E_Class,
$this->stringify($container)),
$this->stringify($parts[1])),
E_USER_ERROR);
}
else {
@@ -1916,7 +1955,8 @@ final class Base extends Prefab implements ArrayAccess {
call_user_func_array(
[$this,$cmd[1]],
array_merge([$match['lval']],
str_getcsv($cmd[1]=='config'?$this->cast($match['rval']):
str_getcsv($cmd[1]=='config'?
$this->cast($match['rval']):
$match['rval']))
);
}
@@ -1931,9 +1971,11 @@ final class Base extends Prefab implements ArrayAccess {
$args=array_map(
function($val) {
$val=$this->cast($val);
return is_string($val)
? preg_replace('/\\\\"/','"',$val)
: $val;
if (is_string($val))
$val=strlen($val)?
preg_replace('/\\\\"/','"',$val):
NULL;
return $val;
},
// Mark quoted strings with 0x00 whitespace
str_getcsv(preg_replace(
@@ -2221,6 +2263,7 @@ final class Base extends Prefab implements ArrayAccess {
);
if (!isset($_SERVER['SERVER_NAME']) || $_SERVER['SERVER_NAME']==='')
$_SERVER['SERVER_NAME']=gethostname();
$headers=[];
if ($cli=PHP_SAPI=='cli') {
// Emulate HTTP request
$_SERVER['REQUEST_METHOD']='GET';
@@ -2251,33 +2294,30 @@ final class Base extends Prefab implements ArrayAccess {
$_SERVER['REQUEST_URI']=$req;
parse_str($query,$GLOBALS['_GET']);
}
$headers=[];
if (!$cli) {
if (function_exists('getallheaders')) {
foreach (getallheaders() as $key=>$val) {
$tmp=strtoupper(strtr($key,'-','_'));
// TODO: use ucwords delimiters for php 5.4.32+ & 5.5.16+
$key=strtr(ucwords(strtolower(strtr($key,'-',' '))),' ','-');
$headers[$key]=$val;
if (isset($_SERVER['HTTP_'.$tmp]))
$headers[$key]=&$_SERVER['HTTP_'.$tmp];
}
}
else {
if (isset($_SERVER['CONTENT_LENGTH']))
$headers['Content-Length']=&$_SERVER['CONTENT_LENGTH'];
if (isset($_SERVER['CONTENT_TYPE']))
$headers['Content-Type']=&$_SERVER['CONTENT_TYPE'];
foreach (array_keys($_SERVER) as $key)
if (substr($key,0,5)=='HTTP_')
$headers[strtr(ucwords(strtolower(strtr(
substr($key,5),'_',' '))),' ','-')]=&$_SERVER[$key];
elseif (function_exists('getallheaders')) {
foreach (getallheaders() as $key=>$val) {
$tmp=strtoupper(strtr($key,'-','_'));
// TODO: use ucwords delimiters for php 5.4.32+ & 5.5.16+
$key=strtr(ucwords(strtolower(strtr($key,'-',' '))),' ','-');
$headers[$key]=$val;
if (isset($_SERVER['HTTP_'.$tmp]))
$headers[$key]=&$_SERVER['HTTP_'.$tmp];
}
}
else {
if (isset($_SERVER['CONTENT_LENGTH']))
$headers['Content-Length']=&$_SERVER['CONTENT_LENGTH'];
if (isset($_SERVER['CONTENT_TYPE']))
$headers['Content-Type']=&$_SERVER['CONTENT_TYPE'];
foreach (array_keys($_SERVER) as $key)
if (substr($key,0,5)=='HTTP_')
$headers[strtr(ucwords(strtolower(strtr(
substr($key,5),'_',' '))),' ','-')]=&$_SERVER[$key];
}
if (isset($headers['X-HTTP-Method-Override']))
$_SERVER['REQUEST_METHOD']=$headers['X-HTTP-Method-Override'];
elseif ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['_method']))
$_SERVER['REQUEST_METHOD']=$_POST['_method'];
$_SERVER['REQUEST_METHOD']=strtoupper($_POST['_method']);
$scheme=isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on' ||
isset($headers['X-Forwarded-Proto']) &&
$headers['X-Forwarded-Proto']=='https'?'https':'http';
@@ -2493,10 +2533,9 @@ class Cache extends Prefab {
if (!$this->dsn)
return TRUE;
$ndx=$this->prefix.'.'.$key;
$time=microtime(TRUE);
if ($cached=$this->exists($key))
list($time,$ttl)=$cached;
$data=$fw->serialize([$val,$time,$ttl]);
$ttl=$cached[1];
$data=$fw->serialize([$val,microtime(TRUE),$ttl]);
$parts=explode('=',$this->dsn,2);
switch ($parts[0]) {
case 'apc':
@@ -2513,7 +2552,8 @@ class Cache extends Prefab {
case 'xcache':
return xcache_set($ndx,$data,$ttl);
case 'folder':
return $fw->write($parts[1].str_replace(['/','\\'],'',$ndx),$data);
return $fw->write($parts[1].
str_replace(['/','\\'],'',$ndx),$data);
}
return FALSE;
}
@@ -2824,6 +2864,7 @@ class Preview extends View {
'c'=>'$this->c',
'esc'=>'$this->esc',
'raw'=>'$this->raw',
'export'=>'Base::instance()->export',
'alias'=>'Base::instance()->alias',
'format'=>'Base::instance()->format'
];
@@ -2833,7 +2874,7 @@ class Preview extends View {
$interpolation=true;
/**
* enable/disable markup parsing interpolation
* Enable/disable markup parsing interpolation
* mainly used for adding appropriate newlines
* @param $bool bool
*/
@@ -2867,16 +2908,18 @@ class Preview extends View {
$str,$parts)) {
$str=trim($parts[1]);
foreach ($fw->split(trim($parts[2],"\xC2\xA0")) as $func)
$str=is_string($cmd=$this->filter($func))?
$str=((empty($this->filter[$cmd=$func]) &&
function_exists($cmd)) ||
is_string($cmd=$this->filter($func)))?
$cmd.'('.$str.')':
'Base::instance()->'.
'call($this->filter(\''.$func.'\'),['.$str.'])';
'call($this->filter(\''.$func.'\'),['.$str.'])';
}
return $str;
}
/**
* Register or get (a specific one or all) token filters
* Register or get (one specific or all) token filters
* @param string $key
* @param string|closure $func
* @return array|closure|string

View File

@@ -90,7 +90,8 @@ class Basket extends Magic {
if (isset($_SESSION[$this->key])) {
foreach ($_SESSION[$this->key] as $id=>$item)
if (!isset($key) ||
array_key_exists($key,$item) && $item[$key]==$val) {
array_key_exists($key,$item) && $item[$key]==$val ||
$key=='_id' && $id==$val) {
$obj=clone($this);
$obj->id=$id;
$obj->item=$item;

View File

@@ -52,6 +52,7 @@ class WS {
$ctx,
$wait,
$sockets,
$protocol,
$agents=[],
$events=[];
@@ -61,16 +62,14 @@ class WS {
* @param $socket resource
**/
function alloc($socket) {
if (is_bool($str=$this->read($socket))) {
$this->close($socket);
if (is_bool($buf=$this->read($socket)))
return;
}
// Get WebSocket headers
$hdrs=[];
$CRLF="\r\n";
$EOL="\r\n";
$verb=NULL;
$uri=NULL;
foreach (explode($CRLF,trim($str)) as $line)
foreach (explode($EOL,trim($buf)) as $line)
if (preg_match('/^(\w+)\s(.+)\sHTTP\/1\.\d$/',
trim($line),$match)) {
$verb=$match[1];
@@ -98,35 +97,29 @@ class WS {
if ($verb && $uri)
$this->write(
$socket,
$str='HTTP/1.1 400 Bad Request'.$CRLF.
'Connection: close'.$CRLF.$CRLF
'HTTP/1.1 400 Bad Request'.$EOL.
'Connection: close'.$EOL.$EOL
);
$this->close($socket);
return;
}
// Handshake
$bytes=$this->write(
$socket,
$str='HTTP/1.1 101 Switching Protocols'.$CRLF.
'Upgrade: websocket'.$CRLF.
'Connection: Upgrade'.$CRLF.
'Sec-WebSocket-Accept: '.
base64_encode(
sha1(
$hdrs['Sec-Websocket-Key'].
self::Magic,
TRUE
)
).$CRLF.$CRLF
);
if ($bytes) {
$buf='HTTP/1.1 101 Switching Protocols'.$EOL.
'Upgrade: websocket'.$EOL.
'Connection: Upgrade'.$EOL;
if (isset($hdrs['Sec-Websocket-Protocol']))
$buf.='Sec-WebSocket-Protocol: '.
$hdrs['Sec-Websocket-Protocol'].$EOL;
$buf.='Sec-WebSocket-Accept: '.
base64_encode(
sha1($hdrs['Sec-Websocket-Key'].WS::Magic,TRUE)
).$EOL.$EOL;
if ($this->write($socket,$buf)) {
// Connect agent to server
$this->sockets[]=$socket;
$this->sockets[(int)$socket]=$socket;
$this->agents[(int)$socket]=
new Agent($this,$socket,$verb,$uri,$hdrs);
}
else
$this->close($socket);
}
/**
@@ -135,34 +128,26 @@ class WS {
* @param $socket resource
**/
function close($socket) {
if (isset($this->agents[(int)$socket]))
unset($this->sockets[(int)$socket],$this->agents[(int)$socket]);
stream_socket_shutdown($socket,STREAM_SHUT_WR);
@fclose($socket);
}
/**
* Free stream socket
* @return bool
* @param $socket resource
**/
function free($socket) {
unset($this->sockets[array_search($socket,$this->sockets)]);
unset($this->agents[(int)$socket]);
$this->close($socket);
}
/**
* Read from stream socket
* @return string|FALSE
* @param $socket resource
**/
function read($socket) {
if (is_string($str=@fread($socket,self::Packet)) &&
strlen($str) &&
strlen($str)<self::Packet)
return $str;
if (is_string($buf=@fread($socket,WS::Packet)) &&
strlen($buf) &&
strlen($buf)<WS::Packet)
return $buf;
if (isset($this->events['error']) &&
is_callable($func=$this->events['error']))
$func($this);
$this->close($socket);
return FALSE;
}
@@ -170,16 +155,17 @@ class WS {
* Write to stream socket
* @return int|FALSE
* @param $socket resource
* @param $str string
* @param $buf string
**/
function write($socket,$str) {
for ($i=0,$bytes=0;$i<strlen($str);$i+=$bytes) {
if (($bytes=@fwrite($socket,substr($str,$i))) &&
function write($socket,$buf) {
for ($i=0,$bytes=0;$i<strlen($buf);$i+=$bytes) {
if (($bytes=@fwrite($socket,substr($buf,$i))) &&
@fflush($socket))
continue;
if (isset($this->events['error']) &&
is_callable($func=$this->events['error']))
$func($this);
$this->close($socket);
return FALSE;
}
return $bytes;
@@ -248,7 +234,7 @@ class WS {
register_shutdown_function(function() use($listen) {
foreach ($this->sockets as $socket)
if ($socket!=$listen)
$this->free($socket);
$this->close($socket);
$this->close($listen);
if (isset($this->events['stop']) &&
is_callable($func=$this->events['stop']))
@@ -259,7 +245,7 @@ class WS {
if (isset($this->events['start']) &&
is_callable($func=$this->events['start']))
$func($this);
$this->sockets=[$listen];
$this->sockets=[(int)$listen=>$listen];
$empty=[];
$wait=$this->wait;
while (TRUE) {
@@ -289,26 +275,8 @@ class WS {
}
else {
$id=(int)$socket;
if (isset($this->agents[$id]) &&
$raw=$this->agents[$id]->fetch()) {
list($op,$data)=$raw;
// Dispatch
switch ($op & self::OpCode) {
case self::Ping:
$this->agents[$id]->send(self::Pong);
break;
case self::Close:
$this->free($socket);
break;
case self::Text:
$data=trim($data);
case self::Binary:
if (isset($this->events['receive']) &&
is_callable($func=$this->events['receive']))
$func($this->agents[$id],$op,$data);
break;
}
}
if (isset($this->agents[$id]))
$this->agents[$id]->fetch();
}
}
$wait-=microtime(TRUE)-$mark;
@@ -319,10 +287,9 @@ class WS {
}
if (!$count) {
$mark=microtime(TRUE);
foreach ($this->sockets as $socket) {
foreach ($this->sockets as $id=>$socket) {
if (!is_resource($socket))
continue;
$id=(int)$socket;
if ($socket!=$listen &&
isset($this->agents[$id]) &&
isset($this->events['idle']) &&
@@ -362,8 +329,7 @@ class Agent {
$verb,
$uri,
$headers,
$events,
$buffer;
$events;
/**
* Return server instance
@@ -381,6 +347,14 @@ class Agent {
return $this->id;
}
/**
* Return socket
* @return object
**/
function socket() {
return $this->socket;
}
/**
* Return request method
* @return string
@@ -413,22 +387,20 @@ class Agent {
* @param $payload string
**/
function send($op,$data='') {
$server=$this->server;
$mask=WS::Finale | $op & WS::OpCode;
$len=strlen($data);
$str='';
$buf='';
if ($len>0xffff)
$str=pack('CCNN',$mask,0x7f,$len);
$buf=pack('CCNN',$mask,0x7f,$len);
else
if ($len>0x7d)
$str=pack('CCn',$mask,0x7e,$len);
$buf=pack('CCn',$mask,0x7e,$len);
else
$str=pack('CC',$mask,$len);
$str.=$data;
$server=$this->server();
if (is_bool($server->write($this->socket,$str))) {
$this->free();
$buf=pack('CC',$mask,$len);
$buf.=$data;
if (is_bool($server->write($this->socket,$buf)))
return FALSE;
}
if (!in_array($op,[WS::Pong,WS::Close]) &&
isset($this->events['send']) &&
is_callable($func=$this->events['send']))
@@ -442,12 +414,9 @@ class Agent {
**/
function fetch() {
// Unmask payload
$server=$this->server();
if (is_bool($buf=$server->read($this->socket))) {
$this->free();
$server=$this->server;
if (is_bool($buf=$server->read($this->socket)))
return FALSE;
}
$buf=($this->buffer.=$buf);
$op=ord($buf[0]) & WS::OpCode;
$len=ord($buf[1]) & WS::Length;
$pos=2;
@@ -468,18 +437,25 @@ class Agent {
return FALSE;
for ($i=0,$data='';$i<$len;$i++)
$data.=chr(ord($buf[$pos+$i])^$mask[$i%4]);
$this->buffer='';
// Dispatch
switch ($op & WS::OpCode) {
case WS::Ping:
$this->send(WS::Pong);
break;
case WS::Close:
$server->close($this->socket);
break;
case WS::Text:
$data=trim($data);
case WS::Binary:
if (isset($this->events['receive']) &&
is_callable($func=$this->events['receive']))
$func($this,$op,$data);
break;
}
return [$op,$data];
}
/**
* Free stream socket
* @return NULL
**/
function free() {
$this->server->free($this->socket);
}
/**
* Destroy object
* @return NULL
@@ -507,7 +483,6 @@ class Agent {
$this->uri=$uri;
$this->headers=$hdrs;
$this->events=$server->events();
$this->buffer='';
if (isset($this->events['connect']) &&
is_callable($func=$this->events['connect']))
$func($this);

View File

@@ -167,7 +167,7 @@ class Jig {
function __destruct() {
if ($this->lazy) {
$this->lazy = FALSE;
foreach ($this->data as $file => $data)
foreach ($this->data?:[] as $file => $data)
$this->write($file,$data);
}
}

View File

@@ -93,7 +93,7 @@ class Mapper extends \DB\Cursor {
* @param $id string
* @param $row array
**/
protected function factory($id,$row) {
function factory($id,$row) {
$mapper=clone($this);
$mapper->reset();
$mapper->id=$id;
@@ -153,7 +153,7 @@ class Mapper extends \DB\Cursor {
* @return static[]|FALSE
* @param $filter array
* @param $options array
* @param $ttl int
* @param $ttl int|array
* @param $log bool
**/
function find($filter=NULL,array $options=NULL,$ttl=0,$log=TRUE) {
@@ -170,9 +170,12 @@ class Mapper extends \DB\Cursor {
$db=$this->db;
$now=microtime(TRUE);
$data=[];
$tag='';
if (is_array($ttl))
list($ttl,$tag)=$ttl;
if (!$fw->CACHE || !$ttl || !($cached=$cache->exists(
$hash=$fw->hash($this->db->dir().
$fw->stringify([$filter,$options])).'.jig',$data)) ||
$fw->stringify([$filter,$options])).($tag?'.'.$tag:'').'.jig',$data)) ||
$cached[0]+$ttl<microtime(TRUE)) {
$data=$db->read($this->file);
if (is_null($data))
@@ -347,7 +350,7 @@ class Mapper extends \DB\Cursor {
* @return int
* @param $filter array
* @param $options array
* @param $ttl int
* @param $ttl int|array
**/
function count($filter=NULL,array $options=NULL,$ttl=0) {
$now=microtime(TRUE);

View File

@@ -180,7 +180,11 @@ class Session extends Mapper {
register_shutdown_function('session_commit');
$fw=\Base::instance();
$headers=$fw->HEADERS;
$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
$this->_csrf=$fw->hash($fw->SEED.
extension_loaded('openssl')?
implode(unpack('L',openssl_random_pseudo_bytes(4))):
mt_rand()
);
if ($key)
$fw->$key=$this->_csrf;
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';

View File

@@ -91,7 +91,7 @@ class Mapper extends \DB\Cursor {
* @return static
* @param $row array
**/
protected function factory($row) {
function factory($row) {
$mapper=clone($this);
$mapper->reset();
foreach ($row as $key=>$val)
@@ -119,7 +119,7 @@ class Mapper extends \DB\Cursor {
* @param $fields string
* @param $filter array
* @param $options array
* @param $ttl int
* @param $ttl int|array
**/
function select($fields=NULL,$filter=NULL,array $options=NULL,$ttl=0) {
if (!$options)
@@ -130,10 +130,13 @@ class Mapper extends \DB\Cursor {
'limit'=>0,
'offset'=>0
];
$tag='';
if (is_array($ttl))
list($ttl,$tag)=$ttl;
$fw=\Base::instance();
$cache=\Cache::instance();
if (!($cached=$cache->exists($hash=$fw->hash($this->db->dsn().
$fw->stringify([$fields,$filter,$options])).'.mongo',
$fw->stringify([$fields,$filter,$options])).($tag?'.'.$tag:'').'.mongo',
$result)) || !$ttl || $cached[0]+$ttl<microtime(TRUE)) {
if ($options['group']) {
$grp=$this->collection->group(
@@ -194,7 +197,7 @@ class Mapper extends \DB\Cursor {
* @return static[]
* @param $filter array
* @param $options array
* @param $ttl int
* @param $ttl int|array
**/
function find($filter=NULL,array $options=NULL,$ttl=0) {
if (!$options)
@@ -213,13 +216,16 @@ class Mapper extends \DB\Cursor {
* @return int
* @param $filter array
* @param $options array
* @param $ttl int
* @param $ttl int|array
**/
function count($filter=NULL,array $options=NULL,$ttl=0) {
$fw=\Base::instance();
$cache=\Cache::instance();
$tag='';
if (is_array($ttl))
list($ttl,$tag)=$ttl;
if (!($cached=$cache->exists($hash=$fw->hash($fw->stringify(
[$filter])).'.mongo',$result)) || !$ttl ||
[$filter])).($tag?'.'.$tag:'').'.mongo',$result)) || !$ttl ||
$cached[0]+$ttl<microtime(TRUE)) {
$result=$this->collection->count($filter?:[]);
if ($fw->CACHE && $ttl)

View File

@@ -180,7 +180,11 @@ class Session extends Mapper {
register_shutdown_function('session_commit');
$fw=\Base::instance();
$headers=$fw->HEADERS;
$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
$this->_csrf=$fw->hash($fw->SEED.
extension_loaded('openssl')?
implode(unpack('L',openssl_random_pseudo_bytes(4))):
mt_rand()
);
if ($key)
$fw->$key=$this->_csrf;
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';

View File

@@ -220,7 +220,7 @@ class SQL {
'/';
}
if ($log)
$this->log.=($stamp?(date('r').' '):'').' (-0ms) '.
$this->log.=($stamp?(date('r').' '):'').'(-0ms) '.
preg_replace($keys,$vals,
str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
$query->execute();
@@ -235,7 +235,7 @@ class SQL {
user_error('PDOStatement: '.$error[2],E_USER_ERROR);
}
if (preg_match('/(?:^[\s\(]*'.
'(?:EXPLAIN|SELECT|PRAGMA|SHOW)|RETURNING)\b/is',$cmd) ||
'(?:WITH|EXPLAIN|SELECT|PRAGMA|SHOW)|RETURNING)\b/is',$cmd) ||
(preg_match('/^\s*(?:CALL|EXEC)\b/is',$cmd) &&
$query->columnCount())) {
$result=$query->fetchall(\PDO::FETCH_ASSOC);

View File

@@ -34,6 +34,8 @@ class Mapper extends \DB\Cursor {
$source,
//! SQL table (quoted)
$table,
//! Alias for SQL table
$as,
//! Last insert ID
$_id,
//! Defined fields
@@ -156,7 +158,7 @@ class Mapper extends \DB\Cursor {
* @return static
* @param $row array
**/
protected function factory($row) {
function factory($row) {
$mapper=clone($this);
$mapper->reset();
foreach ($row as $key=>$val) {
@@ -207,10 +209,13 @@ class Mapper extends \DB\Cursor {
'group'=>NULL,
'order'=>NULL,
'limit'=>0,
'offset'=>0
'offset'=>0,
'comment'=>NULL
];
$db=$this->db;
$sql='SELECT '.$fields.' FROM '.$this->table;
if (isset($this->as))
$sql.=' AS '.$this->db->quotekey($this->as);
$args=[];
if (is_array($filter)) {
$args=isset($filter[1]) && is_array($filter[1])?
@@ -237,9 +242,10 @@ class Mapper extends \DB\Cursor {
}
if ($options['order']) {
$char=substr($db->quotekey(''),0,1);// quoting char
$order=' ORDER BY '.(FALSE===strpos($options['order'],$char)?
$order=' ORDER BY '.(is_bool(strpos($options['order'],$char))?
implode(',',array_map(function($str) use($db) {
return preg_match('/^\h*(\w+[._\-\w]*)(?:\h+((?:ASC|DESC)[\w\h]*))?\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;
@@ -281,6 +287,8 @@ class Mapper extends \DB\Cursor {
if ($options['offset'])
$sql.=' OFFSET '.(int)$options['offset'];
}
if ($options['comment'])
$sql.="\n".' /* '.$options['comment'].' */';
return [$sql,$args];
}
@@ -345,19 +353,30 @@ class Mapper extends \DB\Cursor {
* @param $ttl int|array
**/
function count($filter=NULL,array $options=NULL,$ttl=0) {
$adhoc='';
if (!($subquery_mode=($options && !empty($options['group']))))
$this->adhoc['_rows']=['expr'=>'COUNT(*)','value'=>NULL];
$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;
// Add all adhoc fields
// (make them available for grouping, sorting, having)
$adhoc[]=$field['expr'].' AS '.$this->db->quotekey($key);
$fields=implode(',',$adhoc);
if ($subquery_mode) {
if (empty($fields))
// Select at least one field, ideally the grouping fields
// or sqlsrv fails
$fields=preg_replace('/HAVING.+$/i','',$options['group']);
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');
if ($subquery_mode)
$sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '.
'FROM ('.$sql.') AS '.$this->db->quotekey('_temp');
$result=$this->db->exec($sql,$args,$ttl);
unset($this->adhoc['_rows']);
return (int)$result[0]['_rows'];
}
/**
* Return record at specified offset using same criteria as
* previous load() call and make it active
@@ -651,6 +670,15 @@ class Mapper extends \DB\Cursor {
return new \ArrayIterator($this->cast());
}
/**
* Assign alias for table
* @param $alias string
**/
function alias($alias) {
$this->as=$alias;
return $this;
}
/**
* Instantiate class
* @param $db \DB\SQL

View File

@@ -204,7 +204,11 @@ class Session extends Mapper {
register_shutdown_function('session_commit');
$fw=\Base::instance();
$headers=$fw->HEADERS;
$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
$this->_csrf=$fw->hash($fw->SEED.
extension_loaded('openssl')?
implode(unpack('L',openssl_random_pseudo_bytes(4))):
mt_rand()
);
if ($key)
$fw->$key=$this->_csrf;
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';

View File

@@ -35,14 +35,15 @@ class Log {
**/
function write($text,$format='r') {
$fw=Base::instance();
$fw->write(
$this->file,
date($format).
(isset($_SERVER['REMOTE_ADDR'])?
(' ['.$_SERVER['REMOTE_ADDR'].']'):'').' '.
trim($text).PHP_EOL,
TRUE
);
foreach (preg_split('/\r?\n|\r/',trim($text)) as $line)
$fw->write(
$this->file,
date($format).
(isset($_SERVER['REMOTE_ADDR'])?
(' ['.$_SERVER['REMOTE_ADDR'].']'):'').' '.
trim($line).PHP_EOL,
TRUE
);
}
/**

View File

@@ -92,13 +92,15 @@ class Matrix extends Prefab {
* Return month calendar of specified date, with optional setting for
* first day of week (0 for Sunday)
* @return array
* @param $date string
* @param $date string|int
* @param $first int
**/
function calendar($date='now',$first=0) {
$out=FALSE;
if (extension_loaded('calendar')) {
$parts=getdate(strtotime($date));
if (is_string($date))
$date=strtotime($date);
$parts=getdate($date);
$days=cal_days_in_month(CAL_GREGORIAN,$parts['mon'],$parts['year']);
$ref=date('w',strtotime(date('Y-m',$parts[0]).'-01'))+(7-$first)%7;
$out=[];

View File

@@ -182,7 +182,11 @@ class Session {
register_shutdown_function('session_commit');
$fw=\Base::instance();
$headers=$fw->HEADERS;
$this->_csrf=$fw->SEED.'.'.$fw->hash(mt_rand());
$this->_csrf=$fw->hash($fw->SEED.
extension_loaded('openssl')?
implode(unpack('L',openssl_random_pseudo_bytes(4))):
mt_rand()
);
if ($key)
$fw->$key=$this->_csrf;
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';

View File

@@ -204,14 +204,19 @@ class SMTP extends Magic {
stream_set_blocking($socket,TRUE);
}
// Get server's initial response
$this->dialog(NULL,TRUE,$mock);
$this->dialog(NULL,$log,$mock);
// Announce presence
$reply=$this->dialog('EHLO '.$fw->HOST,$log,$mock);
if (strtolower($this->scheme)=='tls') {
$this->dialog('STARTTLS',$log,$mock);
if (!$mock)
stream_socket_enable_crypto(
$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
if (!$mock) {
$method=STREAM_CRYPTO_METHOD_TLS_CLIENT;
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
$method|=STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
$method|=STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
stream_socket_enable_crypto($socket,TRUE,$method);
}
$reply=$this->dialog('EHLO '.$fw->HOST,$log,$mock);
}
$message=wordwrap($message,998);

View File

@@ -273,7 +273,7 @@ class Template extends Preview {
// Build tree structure
for ($ptr=0,$w=5,$len=strlen($text),$tree=[],$tmp='';$ptr<$len;)
if (preg_match('/^(.{0,'.$w.'}?)<(\/?)(?:F3:)?'.
'('.$this->tags.')\b((?:\s+[\w-.:@!]+'.
'('.$this->tags.')\b((?:\s+[\w.:@!-]+'.
'(?:\h*=\h*(?:"(?:.*?)"|\'(?:.*?)\'))?|'.
'\h*\{\{.+?\}\})*)\h*(\/?)>/is',
substr($text,$ptr),$match)) {

View File

@@ -269,7 +269,7 @@ class Web extends Prefab {
**/
protected function _curl($url,$options) {
$curl=curl_init($url);
if (!ini_get('open_basedir'))
if (!$open_basedir=ini_get('open_basedir'))
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,
$options['follow_location']);
curl_setopt($curl,CURLOPT_MAXREDIRS,
@@ -306,7 +306,7 @@ class Web extends Prefab {
curl_close($curl);
$body=ob_get_clean();
if (!$err &&
$options['follow_location'] &&
$options['follow_location'] && $open_basedir &&
preg_grep('/HTTP\/1\.\d 3\d{2}/',$headers) &&
preg_match('/^Location: (.+)$/m',implode(PHP_EOL,$headers),$loc)) {
$options['max_redirects']--;
@@ -350,7 +350,7 @@ class Web extends Prefab {
if (is_string($body)) {
$match=NULL;
foreach ($headers as $header)
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
if (preg_match('/Content-Encoding: (.+)/i',$header,$match))
break;
if ($match)
switch ($match[1]) {
@@ -442,7 +442,7 @@ class Web extends Prefab {
$headers=array_merge($headers,$current=explode($eol,$html[0]));
$match=NULL;
foreach ($current as $header)
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
if (preg_match('/Content-Encoding: (.+)/i',$header,$match))
break;
if ($match)
switch ($match[1]) {
@@ -550,7 +550,7 @@ class Web extends Prefab {
);
if (isset($options['content']) && is_string($options['content'])) {
if ($options['method']=='POST' &&
!preg_grep('/^Content-Type:/',$options['header']))
!preg_grep('/^Content-Type:/i',$options['header']))
$this->subst($options['header'],
'Content-Type: application/x-www-form-urlencoded');
$this->subst($options['header'],
@@ -588,7 +588,7 @@ class Web extends Prefab {
$result['cached']=TRUE;
}
elseif (preg_match('/Cache-Control:(?:.*)max-age=(\d+)(?:,?.*'.
preg_quote($eol).')/',implode($eol,$result['headers']),$exp))
preg_quote($eol).')/i',implode($eol,$result['headers']),$exp))
$cache->set($hash,$result,$exp[1]);
}
$req=[$options['method'].' '.$url];
@@ -903,7 +903,7 @@ class Web extends Prefab {
for ($i=0,$add=$count-(int)$std;$i<$add;$i++) {
shuffle($rnd);
$words=array_slice($rnd,0,mt_rand(3,$max));
$out.=' '.ucfirst(implode(' ',$words)).'.';
$out.=(!$std&&$i==0?'':' ').ucfirst(implode(' ',$words)).'.';
}
return $out;
}

View File

@@ -64,8 +64,11 @@ class Geo extends \Prefab {
$out=@geoip_record_by_name($ip)) {
$out['request']=$ip;
$out['region_code']=$out['region'];
$out['region_name']=(!empty($out['country_code']) && !empty($out['region']))
? geoip_region_name_by_code($out['country_code'],$out['region']) : '';
$out['region_name']='';
if (!empty($out['country_code']) && !empty($out['region']))
$out['region_name']=geoip_region_name_by_code(
$out['country_code'],$out['region']
);
unset($out['country_code3'],$out['region'],$out['postal_code']);
return $out;
}

View File

@@ -33,9 +33,10 @@ class OAuth2 extends \Magic {
* Return OAuth2 authentication URI
* @return string
* @param $endpoint string
* @param $query bool
**/
function uri($endpoint) {
return $endpoint.'?'.http_build_query($this->args);
function uri($endpoint,$query=TRUE) {
return $endpoint.($query?('?'.http_build_query($this->args)):'');
}
/**
@@ -54,7 +55,7 @@ class OAuth2 extends \Magic {
];
if ($token)
array_push($options['header'],'Authorization: Bearer '.$token);
elseif ($method=='POST')
elseif ($method=='POST' && isset($this->args['client_id']))
array_push($options['header'],'Authorization: Basic '.
base64_encode(
$this->args['client_id'].':'.
@@ -64,10 +65,20 @@ class OAuth2 extends \Magic {
$response=$web->request($uri,$options);
if ($response['error'])
user_error($response['error'],E_USER_ERROR);
return $response['body'] &&
preg_grep('/HTTP\/1\.\d 200/',$response['headers'])?
json_decode($response['body'],TRUE):
FALSE;
if (isset($response['body'])) {
if (preg_grep('/^Content-Type:.*application\/json/i',
$response['headers'])) {
$token=json_decode($response['body'],TRUE);
if (isset($token['error_description']))
user_error($token['error_description'],E_USER_ERROR);
if (isset($token['error']))
user_error($token['error'],E_USER_ERROR);
return $token;
}
else
return $response['body'];
}
return FALSE;
}
/**
@@ -78,16 +89,21 @@ class OAuth2 extends \Magic {
function jwt($token) {
return json_decode(
base64_decode(
str_replace(
['-','_'],
['+','/'],
explode('.',$token)[1]
)
str_replace(['-','_'],['+','/'],explode('.',$token)[1])
),
TRUE
);
}
/**
* URL-safe base64 encoding
* @return array
* @param $data string
**/
function b64url($data) {
return trim(strtr(base64_encode($data),'+/','-_'),'=');
}
/**
* Return TRUE if scope/claim exists
* @return bool

View File

@@ -30,7 +30,6 @@ class Config extends \Prefab {
*/
const DOWNTIME_BUFFER = 1;
const ERROR_CONF_PATHFINDER = 'Config value missing in pathfinder.ini file [%s]';
const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. -> Check installed Composer packages';
@@ -392,13 +391,8 @@ class Config extends \Prefab {
*/
static function getPathfinderData($key = ''){
$hiveKey = self::HIVE_KEY_PATHFINDER . ($key ? '.' . strtoupper($key) : '');
$data = null; // make sure it is always defined
try{
if( !\Base::instance()->exists($hiveKey, $data) ){
throw new Exception\ConfigException(sprintf(self::ERROR_CONF_PATHFINDER, $hiveKey));
}
}catch (Exception\ConfigException $e){
LogController::getLogger('ERROR')->write($e->getMessage());
if( !\Base::instance()->exists($hiveKey, $data) ){
$data = null;
}
return $data;
}