- upgraded "Fat Free Framework" PHP core framework v3.6.4 → v3.6.5
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
165
app/lib/base.php
165
app/lib/base.php
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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']:'';
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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']:'';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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']:'';
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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=[];
|
||||
|
||||
@@ -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']:'';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user