- PHP framework upgrade 3.5.1 -> 3.6.0
This commit is contained in:
@@ -1,5 +1,112 @@
|
||||
CHANGELOG
|
||||
|
||||
3.6.0 (19 November 2016)
|
||||
* NEW: [cli] request type
|
||||
* NEW: console-friendly CLI mode
|
||||
* NEW: lexicon caching
|
||||
* NEW: Silent operator skips startup error check (#125)
|
||||
* NEW: DB\SQL->trans()
|
||||
* NEW: custom config section parser, i.e. [conf > Foo::bar]
|
||||
* NEW: support for cache tags in SQL
|
||||
* NEW: custom FORMATS
|
||||
* NEW: Mongo mapper fields whitelist
|
||||
* NEW: WebSocket server
|
||||
* NEW: Base->extend method (#158)
|
||||
* NEW: Implement framework variable caching via config, i.e. FOO = "bar" | 3600
|
||||
* NEW: Lightweight OAuth2 client
|
||||
* NEW: SEED variable, configurable app-specific hashing prefix (#149, bcosca/fatfree#951, bcosca/fatfree#884, bcosca/fatfree#629)
|
||||
* NEW: CLI variable
|
||||
* NEW: Web->send, specify custom filename (#124)
|
||||
* NEW: Web->send, added flushing flag (#131)
|
||||
* NEW: Indexed route wildcards, now exposed in PARAMS['*']
|
||||
* Changed: PHP 5.4 is now the minimum version requirement
|
||||
* Changed: Prevent database wrappers from being cloned
|
||||
* Changed: Router works on PATH instead of URI (#126) NB: PARAMS.0 no longer contains the query string
|
||||
* Changed: Removed ALIASES autobuilding (#118)
|
||||
* Changed: Route wildcards match empty strings (#119)
|
||||
* Changed: Disable default debug highlighting, HIGHLIGHT is false now
|
||||
* General PHP 5.4 optimizations
|
||||
* Optimized config parsing
|
||||
* Optimized Base->recursive
|
||||
* Optimized header extraction
|
||||
* Optimized cache/expire headers
|
||||
* Optimized session_start behaviour (bcosca/fatfree#673)
|
||||
* Optimized reroute regex
|
||||
* Tweaked cookie removal
|
||||
* Better route precedence order
|
||||
* Performance tweak: reduced cache calls
|
||||
* Refactored lexicon (LOCALES) build-up, much faster now
|
||||
* Added turkish locale bug workaround
|
||||
* Geo->tzinfo Update to UTC
|
||||
* Added Xcache reset (bcosca/fatfree#928)
|
||||
* Redis cache: allow db name in dsn
|
||||
* SMTP: Improve server emulation responses
|
||||
* SMTP: Optimize transmission envelope
|
||||
* SMTP: Implement mock transmission
|
||||
* SMTP: Various bug fixes and feature improvements
|
||||
* SMTP: quit on failed authentication
|
||||
* Geo->weather: force metric units
|
||||
* Base->until: Implement CLI interoperability
|
||||
* Base->format: looser plural syntax
|
||||
* Base->format: Force decimal as default number format
|
||||
* Base->merge: Added $keep flag to save result to the hive key
|
||||
* Base->reroute: Allow array as URL argument for aliasing
|
||||
* Base->alias: Allow query string (or array) to be appended to alias
|
||||
* Permit reroute to named routes with URL query segment
|
||||
* Sync COOKIE global on set()
|
||||
* Permit non-hive variables to use JS dot notation
|
||||
* RFC2616: Use absolute URIs for Location header
|
||||
* Matrix->calendar: Check if calendar extension is loaded
|
||||
* Markdown: require start of line/whitespace for text processing (#136)
|
||||
* DB\[SQL|Jig|Mongo]->log(FALSE) disables logging
|
||||
* DB\SQL->exec: Added timestamp toggle to db log
|
||||
* DB\SQL->schema: Remove unnecessary line terminators
|
||||
* DB\SQL\Mapper: allow array filter with empty string
|
||||
* DB\SQL\Mapper: optimized handling for key-less tables
|
||||
* DB\SQL\Mapper: added float support (#106)
|
||||
* DB\SQL\Session: increased default column sizes (#148, bcosca/fatfree#931, bcosca/fatfree#950)
|
||||
* Web: Catch cURL errors
|
||||
* Optimize Web->receive (bcosca/fatfree#930)
|
||||
* Web->minify: fix arbitrary file download vulnerability
|
||||
* Web->request: fix cache control max-age detection (bcosca/fatfree#908)
|
||||
* Web->request: Add request headers & error message to return value (bcosca/fatfree#737)
|
||||
* Web->request: Refactored response to HTTP request
|
||||
* Web->send flush while sending big files
|
||||
* Image->rgb: allow hex strings
|
||||
* Image->captcha: Check if GD module supports TrueType
|
||||
* Image->load: Return FALSE on load failure
|
||||
* Image->resize: keep aspect ratio when only width or height was given
|
||||
* Updated OpenID lib (bcosca/fatfree#965)
|
||||
* Audit->card: add new mastercard "2" BIN range (bcosca/fatfree#954)
|
||||
* Deprecated: Bcrypt class
|
||||
* Preview->render: optimized detection to remove short open PHP tags and allow xml tags (#133)
|
||||
* Display file and line number in exception handler (bcosca/fatfree#967)
|
||||
* Added error reporting level to Base->error and ERROR.level (bcosca/fatfree#957)
|
||||
* Added optional custom cache instance to Session (#141)
|
||||
* CLI-aware mock()
|
||||
* XFRAME and PACKAGE can be switched off now (#128)
|
||||
* Bug fix: wrong time calculation on memcache reset (#170)
|
||||
* Bug fix: encode CLI parameters
|
||||
* Bug fix: Close connection on abort explicitly (#162)
|
||||
* Bug fix: Image->identicon, Avoid double-size sprite rotation (and possible segfault)
|
||||
* Bug fix: Magic->offsetset, access property as array element (#147)
|
||||
* Bug fix: multi-line custom template tag parsing (bcosca/fatfree#935)
|
||||
* Bug fix: cache headers on errors (bcosca/fatfree#885)
|
||||
* Bug fix: Web, deprecated CURLOPT_SSL_VERIFYHOST in curl
|
||||
* Bug fix: Web, Invalid user error constant (bcosca/fatfree#962)
|
||||
* Bug fix: Web->request, redirections for domain-less location (#135)
|
||||
* Bug fix: DB\SQL\Mapper, reset changed flag after update (#142, #152)
|
||||
* Bug fix: DB\SQL\Mapper, fix changed flag when using assignment operator #143 #150 #151
|
||||
* Bug fix: DB\SQL\Mapper, revival of the HAVING clause
|
||||
* Bug fix: DB\SQL\Mapper, pgsql with non-integer primary keys (bcosca/fatfree#916)
|
||||
* Bug fix: DB\SQL\Session, quote table name (bcosca/fatfree#977)
|
||||
* Bug fix: snakeCase returns word starting with underscore (bcosca/fatfree#927)
|
||||
* Bug fix: mock does not populate PATH variable
|
||||
* Bug fix: Geo->weather API key (#129)
|
||||
* Bug fix: Incorrect compilation of array element with zero index
|
||||
* Bug fix: Compilation of array construct is incorrect
|
||||
* Bug fix: Trailing slash redirection on UTF-8 paths (#121)
|
||||
|
||||
3.5.1 (31 December 2015)
|
||||
* NEW: ttl attribute in <include> template tag
|
||||
* NEW: allow anonymous function for template filter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -47,7 +47,7 @@ class Audit extends Prefab {
|
||||
* @param $mx boolean
|
||||
**/
|
||||
function email($str,$mx=TRUE) {
|
||||
$hosts=array();
|
||||
$hosts=[];
|
||||
return is_string(filter_var($str,FILTER_VALIDATE_EMAIL)) &&
|
||||
(!$mx || getmxrr(substr($str,strrpos($str,'@')+1),$hosts));
|
||||
}
|
||||
@@ -166,7 +166,8 @@ class Audit extends Prefab {
|
||||
return 'Discover';
|
||||
if (preg_match('/^(?:2131|1800|35\d{3})\d{11}$/',$id))
|
||||
return 'JCB';
|
||||
if (preg_match('/^5[1-5][0-9]{14}$/',$id))
|
||||
if (preg_match('/^5[1-5][0-9]{14}$|'.
|
||||
'^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)\d{12}$/',$id))
|
||||
return 'MasterCard';
|
||||
if (preg_match('/^4[0-9]{12}(?:[0-9]{3})?$/',$id))
|
||||
return 'Visa';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//! Authorization/authentication plug-in
|
||||
class Auth {
|
||||
|
||||
@@ -48,19 +47,19 @@ class Auth {
|
||||
protected function _jig($id,$pw,$realm) {
|
||||
return (bool)
|
||||
call_user_func_array(
|
||||
array($this->mapper,'load'),
|
||||
array(
|
||||
[$this->mapper,'load'],
|
||||
[
|
||||
array_merge(
|
||||
array(
|
||||
[
|
||||
'@'.$this->args['id'].'==? AND '.
|
||||
'@'.$this->args['pw'].'==?'.
|
||||
(isset($this->args['realm'])?
|
||||
(' AND @'.$this->args['realm'].'==?'):''),
|
||||
$id,$pw
|
||||
),
|
||||
(isset($this->args['realm'])?array($realm):array())
|
||||
],
|
||||
(isset($this->args['realm'])?[$realm]:[])
|
||||
)
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,12 +73,12 @@ class Auth {
|
||||
protected function _mongo($id,$pw,$realm) {
|
||||
return (bool)
|
||||
$this->mapper->load(
|
||||
array(
|
||||
[
|
||||
$this->args['id']=>$id,
|
||||
$this->args['pw']=>$pw
|
||||
)+
|
||||
]+
|
||||
(isset($this->args['realm'])?
|
||||
array($this->args['realm']=>$realm):array())
|
||||
[$this->args['realm']=>$realm]:[])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,19 +92,19 @@ class Auth {
|
||||
protected function _sql($id,$pw,$realm) {
|
||||
return (bool)
|
||||
call_user_func_array(
|
||||
array($this->mapper,'load'),
|
||||
array(
|
||||
[$this->mapper,'load'],
|
||||
[
|
||||
array_merge(
|
||||
array(
|
||||
[
|
||||
$this->args['id'].'=? AND '.
|
||||
$this->args['pw'].'=?'.
|
||||
(isset($this->args['realm'])?
|
||||
(' AND '.$this->args['realm'].'=?'):''),
|
||||
$id,$pw
|
||||
),
|
||||
(isset($this->args['realm'])?array($realm):array())
|
||||
],
|
||||
(isset($this->args['realm'])?[$realm]:[])
|
||||
)
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
727
app/lib/base.php
727
app/lib/base.php
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -34,7 +34,7 @@ class Basket extends Magic {
|
||||
//! Current item identifier
|
||||
$id,
|
||||
//! Current item contents
|
||||
$item=array();
|
||||
$item=[];
|
||||
|
||||
/**
|
||||
* Return TRUE if field is defined
|
||||
@@ -86,7 +86,7 @@ class Basket extends Magic {
|
||||
* @param $val mixed
|
||||
**/
|
||||
function find($key=NULL,$val=NULL) {
|
||||
$out=array();
|
||||
$out=[];
|
||||
if (isset($_SESSION[$this->key])) {
|
||||
foreach ($_SESSION[$this->key] as $id=>$item)
|
||||
if (!isset($key) ||
|
||||
@@ -122,7 +122,7 @@ class Basket extends Magic {
|
||||
return $this->item=$found[0]->item;
|
||||
}
|
||||
$this->reset();
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,7 +175,7 @@ class Basket extends Magic {
|
||||
**/
|
||||
function reset() {
|
||||
$this->id=NULL;
|
||||
$this->item=array();
|
||||
$this->item=[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,7 +219,7 @@ class Basket extends Magic {
|
||||
unset($_SESSION[$this->key]);
|
||||
return $out;
|
||||
}
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +229,8 @@ class Basket extends Magic {
|
||||
**/
|
||||
function __construct($key='basket') {
|
||||
$this->key=$key;
|
||||
@session_start();
|
||||
if (session_status()!=PHP_SESSION_ACTIVE)
|
||||
session_start();
|
||||
Base::instance()->sync('SESSION');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Lightweight password hashing library
|
||||
*
|
||||
* Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
*
|
||||
* This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or later.
|
||||
*
|
||||
* Fat-Free Framework is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @deprecated use http://php.net/manual/en/ref.password.php instead (PHP 5.5+ only)
|
||||
**/
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
This is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or later.
|
||||
|
||||
Fat-Free Framework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
//! Lightweight password hashing library
|
||||
class Bcrypt extends Prefab {
|
||||
|
||||
//@{ Error messages
|
||||
|
||||
516
app/lib/cli/ws.php
Normal file
516
app/lib/cli/ws.php
Normal file
@@ -0,0 +1,516 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
This is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or later.
|
||||
|
||||
Fat-Free Framework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
namespace CLI;
|
||||
|
||||
//! RFC6455 WebSocket server
|
||||
class WS {
|
||||
|
||||
const
|
||||
//! UUID magic string
|
||||
Magic='258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
//! Max packet size
|
||||
Packet=65536;
|
||||
|
||||
//@{ Mask bits for first byte of header
|
||||
const
|
||||
Text=0x01,
|
||||
Binary=0x02,
|
||||
Close=0x08,
|
||||
Ping=0x09,
|
||||
Pong=0x0a,
|
||||
OpCode=0x0f,
|
||||
Finale=0x80;
|
||||
//@}
|
||||
|
||||
//@{ Mask bits for second byte of header
|
||||
const
|
||||
Length=0x7f;
|
||||
//@}
|
||||
|
||||
protected
|
||||
$addr,
|
||||
$ctx,
|
||||
$wait,
|
||||
$sockets,
|
||||
$agents=[],
|
||||
$events=[];
|
||||
|
||||
/**
|
||||
* Allocate stream socket
|
||||
* @return NULL
|
||||
* @param $socket resource
|
||||
**/
|
||||
function alloc($socket) {
|
||||
if (is_bool($str=$this->read($socket))) {
|
||||
$this->close($socket);
|
||||
return;
|
||||
}
|
||||
// Get WebSocket headers
|
||||
$hdrs=[];
|
||||
$CRLF="\r\n";
|
||||
$verb=NULL;
|
||||
$uri=NULL;
|
||||
foreach (explode($CRLF,trim($str)) as $line)
|
||||
if (preg_match('/^(\w+)\s(.+)\sHTTP\/1\.\d$/',
|
||||
trim($line),$match)) {
|
||||
$verb=$match[1];
|
||||
$uri=$match[2];
|
||||
}
|
||||
else
|
||||
if (preg_match('/^(.+): (.+)/',trim($line),$match))
|
||||
// Standardize header
|
||||
$hdrs[
|
||||
strtr(
|
||||
ucwords(
|
||||
strtolower(
|
||||
strtr($match[1],'-',' ')
|
||||
)
|
||||
),' ','-'
|
||||
)
|
||||
]=$match[2];
|
||||
else {
|
||||
$this->close($socket);
|
||||
return;
|
||||
}
|
||||
if (empty($hdrs['Upgrade']) &&
|
||||
empty($hdrs['Sec-Websocket-Key'])) {
|
||||
// Not a WebSocket request
|
||||
if ($verb && $uri)
|
||||
$this->write(
|
||||
$socket,
|
||||
$str='HTTP/1.1 400 Bad Request'.$CRLF.
|
||||
'Connection: close'.$CRLF.$CRLF
|
||||
);
|
||||
$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) {
|
||||
// Connect agent to server
|
||||
$this->sockets[]=$socket;
|
||||
$this->agents[(int)$socket]=
|
||||
new Agent($this,$socket,$verb,$uri,$hdrs);
|
||||
}
|
||||
else
|
||||
$this->close($socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close stream socket
|
||||
* @return NULL
|
||||
* @param $socket resource
|
||||
**/
|
||||
function close($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 (isset($this->events['error']) &&
|
||||
is_callable($func=$this->events['error']))
|
||||
$func($this);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to stream socket
|
||||
* @return int|FALSE
|
||||
* @param $socket resource
|
||||
* @param $str string
|
||||
**/
|
||||
function write($socket,$str) {
|
||||
for ($i=0,$bytes=0;$i<strlen($str);$i+=$bytes) {
|
||||
if (($bytes=@fwrite($socket,substr($str,$i))) &&
|
||||
@fflush($socket))
|
||||
continue;
|
||||
if (isset($this->events['error']) &&
|
||||
is_callable($func=$this->events['error']))
|
||||
$func($this);
|
||||
return FALSE;
|
||||
}
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return socket agents
|
||||
* @return array
|
||||
* @param $uri string
|
||||
***/
|
||||
function agents($uri=NULL) {
|
||||
return array_filter(
|
||||
$this->agents,
|
||||
function($val) use($uri) {
|
||||
return $uri?($val->uri()==$uri):TRUE;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return event handlers
|
||||
* @return array
|
||||
**/
|
||||
function events() {
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind function to event handler
|
||||
* @return object
|
||||
* @param $event string
|
||||
* @param $func callable
|
||||
**/
|
||||
function on($event,$func) {
|
||||
$this->events[$event]=$func;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate server
|
||||
* @return NULL
|
||||
* @param $signal int
|
||||
**/
|
||||
function kill($signal) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the server process
|
||||
* @return object
|
||||
**/
|
||||
function run() {
|
||||
$fw=\Base::instance();
|
||||
// Assign signal handlers
|
||||
declare(ticks=1);
|
||||
pcntl_signal(SIGINT,[$this,'kill']);
|
||||
pcntl_signal(SIGTERM,[$this,'kill']);
|
||||
gc_enable();
|
||||
// Activate WebSocket listener
|
||||
$listen=stream_socket_server(
|
||||
$this->addr,$errno,$errstr,
|
||||
STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,
|
||||
$this->ctx
|
||||
);
|
||||
$socket=socket_import_stream($listen);
|
||||
register_shutdown_function(function() use($listen) {
|
||||
foreach ($this->sockets as $socket)
|
||||
if ($socket!=$listen)
|
||||
$this->free($socket);
|
||||
$this->close($listen);
|
||||
if (isset($this->events['stop']) &&
|
||||
is_callable($func=$this->events['stop']))
|
||||
$func($this);
|
||||
});
|
||||
if ($errstr)
|
||||
user_error($errstr,E_USER_ERROR);
|
||||
if (isset($this->events['start']) &&
|
||||
is_callable($func=$this->events['start']))
|
||||
$func($this);
|
||||
$this->sockets=[$listen];
|
||||
$empty=[];
|
||||
$wait=$this->wait;
|
||||
while (TRUE) {
|
||||
$active=$this->sockets;
|
||||
$mark=microtime(TRUE);
|
||||
$count=@stream_select(
|
||||
$active,$empty,$empty,(int)$wait,round(1e6*($wait-(int)$wait))
|
||||
);
|
||||
if (is_bool($count) && $wait) {
|
||||
if (isset($this->events['error']) &&
|
||||
is_callable($func=$this->events['error']))
|
||||
$func($this);
|
||||
die;
|
||||
}
|
||||
if ($count) {
|
||||
// Process active connections
|
||||
foreach ($active as $socket) {
|
||||
if (!is_resource($socket))
|
||||
continue;
|
||||
if ($socket==$listen) {
|
||||
if ($socket=@stream_socket_accept($listen,0))
|
||||
$this->alloc($socket);
|
||||
else
|
||||
if (isset($this->events['error']) &&
|
||||
is_callable($func=$this->events['error']))
|
||||
$func($this);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$wait-=microtime(TRUE)-$mark;
|
||||
while ($wait<1e-6) {
|
||||
$wait+=$this->wait;
|
||||
$count=0;
|
||||
}
|
||||
}
|
||||
if (!$count) {
|
||||
$mark=microtime(TRUE);
|
||||
foreach ($this->sockets as $socket) {
|
||||
if (!is_resource($socket))
|
||||
continue;
|
||||
$id=(int)$socket;
|
||||
if ($socket!=$listen &&
|
||||
isset($this->agents[$id]) &&
|
||||
isset($this->events['idle']) &&
|
||||
is_callable($func=$this->events['idle']))
|
||||
$func($this->agents[$id]);
|
||||
}
|
||||
$wait=$this->wait-microtime(TRUE)+$mark;
|
||||
}
|
||||
gc_collect_cycles();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate object
|
||||
* @return object
|
||||
* @param $addr string
|
||||
* @param $ctx resource
|
||||
* @param $wait int
|
||||
**/
|
||||
function __construct($addr,$ctx=NULL,$wait=60) {
|
||||
$this->addr=$addr;
|
||||
$this->ctx=$ctx?:stream_context_create();
|
||||
$this->wait=$wait;
|
||||
$this->events=[];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//! RFC6455 remote socket
|
||||
class Agent {
|
||||
|
||||
protected
|
||||
$server,
|
||||
$id,
|
||||
$socket,
|
||||
$flag,
|
||||
$verb,
|
||||
$uri,
|
||||
$headers,
|
||||
$events,
|
||||
$buffer;
|
||||
|
||||
/**
|
||||
* Return server instance
|
||||
* @return object
|
||||
**/
|
||||
function server() {
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return socket ID
|
||||
* @return string
|
||||
**/
|
||||
function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return request method
|
||||
* @return string
|
||||
**/
|
||||
function verb() {
|
||||
return $this->verb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return request URI
|
||||
* @return string
|
||||
**/
|
||||
function uri() {
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return socket headers
|
||||
* @return string
|
||||
**/
|
||||
function headers() {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame and transmit payload
|
||||
* @return string|FALSE
|
||||
* @param $socket resource
|
||||
* @param $op int
|
||||
* @param $payload string
|
||||
**/
|
||||
function send($op,$data='') {
|
||||
$mask=WS::Finale | $op & WS::OpCode;
|
||||
$len=strlen($data);
|
||||
$str='';
|
||||
if ($len>0xffff)
|
||||
$str=pack('CCNN',$mask,0x7f,$len);
|
||||
else
|
||||
if ($len>0x7d)
|
||||
$str=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();
|
||||
return FALSE;
|
||||
}
|
||||
if (!in_array($op,[WS::Pong,WS::Close]) &&
|
||||
isset($this->events['send']) &&
|
||||
is_callable($func=$this->events['send']))
|
||||
$func($this,$op,$data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve and unmask payload
|
||||
* @return array|FALSE
|
||||
**/
|
||||
function fetch() {
|
||||
// Unmask payload
|
||||
$server=$this->server();
|
||||
if (is_bool($buf=$server->read($this->socket))) {
|
||||
$this->free();
|
||||
return FALSE;
|
||||
}
|
||||
$buf=($this->buffer.=$buf);
|
||||
$op=ord($buf[0]) & WS::OpCode;
|
||||
$len=ord($buf[1]) & WS::Length;
|
||||
$pos=2;
|
||||
if ($len==0x7e) {
|
||||
$len=ord($buf[2])*256+ord($buf[3]);
|
||||
$pos+=2;
|
||||
}
|
||||
else
|
||||
if ($len==0x7f) {
|
||||
for ($i=0,$len=0;$i<8;$i++)
|
||||
$len=$len*256+ord($buf[$i+2]);
|
||||
$pos+=8;
|
||||
}
|
||||
for ($i=0,$mask=[];$i<4;$i++)
|
||||
$mask[$i]=ord($buf[$pos+$i]);
|
||||
$pos+=4;
|
||||
if (strlen($buf)<$len+$pos)
|
||||
return FALSE;
|
||||
for ($i=0,$data='';$i<$len;$i++)
|
||||
$data.=chr(ord($buf[$pos+$i])^$mask[$i%4]);
|
||||
$this->buffer='';
|
||||
return [$op,$data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Free stream socket
|
||||
* @return NULL
|
||||
**/
|
||||
function free() {
|
||||
$this->server->free($this->socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy object
|
||||
* @return NULL
|
||||
**/
|
||||
function __destruct() {
|
||||
if (isset($this->events['disconnect']) &&
|
||||
is_callable($func=$this->events['disconnect']))
|
||||
$func($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate object
|
||||
* @return object
|
||||
* @param $server object
|
||||
* @param $socket resource
|
||||
* @param $verb string
|
||||
* @param $uri string
|
||||
* @param $hdrs array
|
||||
**/
|
||||
function __construct($server,$socket,$verb,$uri,array $hdrs) {
|
||||
$this->server=$server;
|
||||
$this->id=stream_socket_get_name($socket,TRUE);
|
||||
$this->socket=$socket;
|
||||
$this->verb=$verb;
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -32,11 +32,11 @@ abstract class Cursor extends \Magic implements \IteratorAggregate {
|
||||
|
||||
protected
|
||||
//! Query results
|
||||
$query=array(),
|
||||
$query=[],
|
||||
//! Current position
|
||||
$ptr=0,
|
||||
//! Event listeners
|
||||
$trigger=array();
|
||||
$trigger=[];
|
||||
|
||||
/**
|
||||
* Return database type
|
||||
@@ -126,7 +126,7 @@ abstract class Cursor extends \Magic implements \IteratorAggregate {
|
||||
**/
|
||||
function findone($filter=NULL,array $options=NULL,$ttl=0) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options=[];
|
||||
// Override limit
|
||||
$options['limit']=1;
|
||||
return ($data=$this->find($filter,$options,$ttl))?$data[0]:FALSE;
|
||||
@@ -148,11 +148,11 @@ abstract class Cursor extends \Magic implements \IteratorAggregate {
|
||||
$total=$this->count($filter,$ttl);
|
||||
$count=ceil($total/$size);
|
||||
$pos=max(0,min($pos,$count-1));
|
||||
return array(
|
||||
return [
|
||||
'subset'=>$this->find($filter,
|
||||
array_merge(
|
||||
$options?:array(),
|
||||
array('limit'=>$size,'offset'=>$pos*$size)
|
||||
$options?:[],
|
||||
['limit'=>$size,'offset'=>$pos*$size]
|
||||
),
|
||||
$ttl
|
||||
),
|
||||
@@ -160,7 +160,7 @@ abstract class Cursor extends \Magic implements \IteratorAggregate {
|
||||
'limit'=>$size,
|
||||
'count'=>$count,
|
||||
'pos'=>$pos<$count?$pos:0
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +378,7 @@ abstract class Cursor extends \Magic implements \IteratorAggregate {
|
||||
* @return NULL
|
||||
**/
|
||||
function reset() {
|
||||
$this->query=array();
|
||||
$this->query=[];
|
||||
$this->ptr=0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -51,7 +51,7 @@ class Jig {
|
||||
function &read($file) {
|
||||
if (!$this->dir || !is_file($dst=$this->dir.$file)) {
|
||||
if (!isset($this->data[$file]))
|
||||
$this->data[$file]=array();
|
||||
$this->data[$file]=[];
|
||||
return $this->data[$file];
|
||||
}
|
||||
$fw=\Base::instance();
|
||||
@@ -106,11 +106,14 @@ class Jig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return profiler results
|
||||
* Return profiler results (or disable logging)
|
||||
* @param $flag bool
|
||||
* @return string
|
||||
**/
|
||||
function log() {
|
||||
return $this->log;
|
||||
function log($flag=TRUE) {
|
||||
if ($flag)
|
||||
return $this->log;
|
||||
$this->log=FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,12 +132,16 @@ class Jig {
|
||||
**/
|
||||
function drop() {
|
||||
if (!$this->dir)
|
||||
$this->data=array();
|
||||
$this->data=[];
|
||||
elseif ($glob=@glob($this->dir.'/*',GLOB_NOSORT))
|
||||
foreach ($glob as $file)
|
||||
@unlink($file);
|
||||
}
|
||||
|
||||
//! Prohibit cloning
|
||||
private function __clone() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate class
|
||||
* @param $dir string
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -33,7 +33,7 @@ class Mapper extends \DB\Cursor {
|
||||
//! Document identifier
|
||||
$id,
|
||||
//! Document contents
|
||||
$document=array();
|
||||
$document=[];
|
||||
|
||||
/**
|
||||
* Return database type
|
||||
@@ -97,7 +97,7 @@ class Mapper extends \DB\Cursor {
|
||||
$mapper->id=$id;
|
||||
foreach ($row as $field=>$val)
|
||||
$mapper->document[$field]=$val;
|
||||
$mapper->query=array(clone($mapper));
|
||||
$mapper->query=[clone($mapper)];
|
||||
if (isset($mapper->trigger['load']))
|
||||
\Base::instance()->call($mapper->trigger['load'],$mapper);
|
||||
return $mapper;
|
||||
@@ -111,7 +111,7 @@ class Mapper extends \DB\Cursor {
|
||||
function cast($obj=NULL) {
|
||||
if (!$obj)
|
||||
$obj=$this;
|
||||
return $obj->document+array('_id'=>$this->id);
|
||||
return $obj->document+['_id'=>$this->id];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,20 +157,20 @@ class Mapper extends \DB\Cursor {
|
||||
**/
|
||||
function find($filter=NULL,array $options=NULL,$ttl=0,$log=TRUE) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options+=array(
|
||||
$options=[];
|
||||
$options+=[
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
);
|
||||
];
|
||||
$fw=\Base::instance();
|
||||
$cache=\Cache::instance();
|
||||
$db=$this->db;
|
||||
$now=microtime(TRUE);
|
||||
$data=array();
|
||||
$data=[];
|
||||
if (!$fw->get('CACHE') || !$ttl || !($cached=$cache->exists(
|
||||
$hash=$fw->hash($this->db->dir().
|
||||
$fw->stringify(array($filter,$options))).'.jig',$data)) ||
|
||||
$fw->stringify([$filter,$options])).'.jig',$data)) ||
|
||||
$cached[0]+$ttl<microtime(TRUE)) {
|
||||
$data=$db->read($this->file);
|
||||
if (is_null($data))
|
||||
@@ -188,8 +188,8 @@ class Mapper extends \DB\Cursor {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:array(1=>$args);
|
||||
$keys=$vals=array();
|
||||
$args=is_array($args)?$args:[1=>$args];
|
||||
$keys=$vals=[];
|
||||
$tokens=array_slice(
|
||||
token_get_all('<?php '.$this->token($expr)),1);
|
||||
$data=array_filter($data,
|
||||
@@ -248,7 +248,7 @@ class Mapper extends \DB\Cursor {
|
||||
$val1[$col]=NULL;
|
||||
if (!array_key_exists($col,$val2))
|
||||
$val2[$col]=NULL;
|
||||
list($v1,$v2)=array($val1[$col],$val2[$col]);
|
||||
list($v1,$v2)=[$val1[$col],$val2[$col]];
|
||||
if ($out=strnatcmp($v1,$v2)*
|
||||
(($order==SORT_ASC)*2-1))
|
||||
return $out;
|
||||
@@ -263,7 +263,7 @@ class Mapper extends \DB\Cursor {
|
||||
// Save to cache backend
|
||||
$cache->set($hash,$data,$ttl);
|
||||
}
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($data as $id=>&$doc) {
|
||||
unset($doc['_id']);
|
||||
$out[]=$this->factory($id,$doc);
|
||||
@@ -303,7 +303,7 @@ class Mapper extends \DB\Cursor {
|
||||
* @param $ofs int
|
||||
**/
|
||||
function skip($ofs=1) {
|
||||
$this->document=($out=parent::skip($ofs))?$out->document:array();
|
||||
$this->document=($out=parent::skip($ofs))?$out->document:[];
|
||||
$this->id=$out?$out->id:NULL;
|
||||
if ($this->document && isset($this->trigger['load']))
|
||||
\Base::instance()->call($this->trigger['load'],$this);
|
||||
@@ -324,10 +324,10 @@ class Mapper extends \DB\Cursor {
|
||||
!connection_aborted())
|
||||
usleep(mt_rand(0,100));
|
||||
$this->id=$id;
|
||||
$pkey=array('_id'=>$this->id);
|
||||
$pkey=['_id'=>$this->id];
|
||||
if (isset($this->trigger['beforeinsert']) &&
|
||||
\Base::instance()->call($this->trigger['beforeinsert'],
|
||||
array($this,$pkey))===FALSE)
|
||||
[$this,$pkey])===FALSE)
|
||||
return $this->document;
|
||||
$data[$id]=$this->document;
|
||||
$db->write($this->file,$data);
|
||||
@@ -335,8 +335,8 @@ class Mapper extends \DB\Cursor {
|
||||
$this->file.' [insert] '.json_encode($this->document));
|
||||
if (isset($this->trigger['afterinsert']))
|
||||
\Base::instance()->call($this->trigger['afterinsert'],
|
||||
array($this,$pkey));
|
||||
$this->load(array('@_id=?',$this->id));
|
||||
[$this,$pkey]);
|
||||
$this->load(['@_id=?',$this->id]);
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ class Mapper extends \DB\Cursor {
|
||||
$data=&$db->read($this->file);
|
||||
if (isset($this->trigger['beforeupdate']) &&
|
||||
\Base::instance()->call($this->trigger['beforeupdate'],
|
||||
array($this,array('_id'=>$this->id)))===FALSE)
|
||||
[$this,['_id'=>$this->id]])===FALSE)
|
||||
return $this->document;
|
||||
$data[$this->id]=$this->document;
|
||||
$db->write($this->file,$data);
|
||||
@@ -358,7 +358,7 @@ class Mapper extends \DB\Cursor {
|
||||
$this->file.' [update] '.json_encode($this->document));
|
||||
if (isset($this->trigger['afterupdate']))
|
||||
\Base::instance()->call($this->trigger['afterupdate'],
|
||||
array($this,array('_id'=>$this->id)));
|
||||
[$this,['_id'=>$this->id]]);
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ class Mapper extends \DB\Cursor {
|
||||
$db=$this->db;
|
||||
$now=microtime(TRUE);
|
||||
$data=&$db->read($this->file);
|
||||
$pkey=array('_id'=>$this->id);
|
||||
$pkey=['_id'=>$this->id];
|
||||
if ($filter) {
|
||||
foreach ($this->find($filter,NULL,FALSE) as $mapper)
|
||||
if (!$mapper->erase())
|
||||
@@ -386,14 +386,14 @@ class Mapper extends \DB\Cursor {
|
||||
return FALSE;
|
||||
if (isset($this->trigger['beforeerase']) &&
|
||||
\Base::instance()->call($this->trigger['beforeerase'],
|
||||
array($this,$pkey))===FALSE)
|
||||
[$this,$pkey])===FALSE)
|
||||
return FALSE;
|
||||
$db->write($this->file,$data);
|
||||
if ($filter) {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:array(1=>$args);
|
||||
$args=is_array($args)?$args:[1=>$args];
|
||||
foreach ($args as $key=>$val) {
|
||||
$vals[]=\Base::instance()->
|
||||
stringify(is_array($val)?$val[0]:$val);
|
||||
@@ -405,7 +405,7 @@ class Mapper extends \DB\Cursor {
|
||||
($filter?preg_replace($keys,$vals,$filter[0],1):''));
|
||||
if (isset($this->trigger['aftererase']))
|
||||
\Base::instance()->call($this->trigger['aftererase'],
|
||||
array($this,$pkey));
|
||||
[$this,$pkey]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ class Mapper extends \DB\Cursor {
|
||||
**/
|
||||
function reset() {
|
||||
$this->id=NULL;
|
||||
$this->document=array();
|
||||
$this->document=[];
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -63,12 +63,13 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function read($id) {
|
||||
$this->load(array('@session_id=?',$this->sid=$id));
|
||||
$this->load(['@session_id=?',$this->sid=$id]);
|
||||
if ($this->dry())
|
||||
return FALSE;
|
||||
if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
|
||||
$fw=\Base::instance();
|
||||
if (!isset($this->onsuspect) || FALSE===$fw->call($this->onsuspect,array($this,$id))) {
|
||||
if (!isset($this->onsuspect) ||
|
||||
$fw->call($this->onsuspect,[$this,$id])===FALSE) {
|
||||
//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
|
||||
$this->destroy($id);
|
||||
$this->close();
|
||||
@@ -101,7 +102,7 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function destroy($id) {
|
||||
$this->erase(array('@session_id=?',$id));
|
||||
$this->erase(['@session_id=?',$id]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ class Session extends Mapper {
|
||||
* @param $max int
|
||||
**/
|
||||
function cleanup($max) {
|
||||
$this->erase(array('@stamp+?<?',$max,time()));
|
||||
$this->erase(['@stamp+?<?',$max,time()]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -168,18 +169,17 @@ class Session extends Mapper {
|
||||
parent::__construct($db,$file);
|
||||
$this->onsuspect=$onsuspect;
|
||||
session_set_save_handler(
|
||||
array($this,'open'),
|
||||
array($this,'close'),
|
||||
array($this,'read'),
|
||||
array($this,'write'),
|
||||
array($this,'destroy'),
|
||||
array($this,'cleanup')
|
||||
[$this,'open'],
|
||||
[$this,'close'],
|
||||
[$this,'read'],
|
||||
[$this,'write'],
|
||||
[$this,'destroy'],
|
||||
[$this,'cleanup']
|
||||
);
|
||||
register_shutdown_function('session_commit');
|
||||
$fw=\Base::instance();
|
||||
$headers=$fw->get('HEADERS');
|
||||
$this->_csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash(mt_rand());
|
||||
$this->_csrf=$fw->get('SEED').'.'.$fw->hash(mt_rand());
|
||||
if ($key)
|
||||
$fw->set($key,$this->_csrf);
|
||||
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -57,21 +57,27 @@ class Mongo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return MongoDB profiler results
|
||||
* Return MongoDB profiler results (or disable logging)
|
||||
* @param $flag bool
|
||||
* @return string
|
||||
**/
|
||||
function log() {
|
||||
$cursor=$this->selectcollection('system.profile')->find();
|
||||
foreach (iterator_to_array($cursor) as $frame)
|
||||
if (!preg_match('/\.system\..+$/',$frame['ns']))
|
||||
$this->log.=date('r',$frame['ts']->sec).' ('.
|
||||
sprintf('%.1f',$frame['millis']).'ms) '.
|
||||
$frame['ns'].' ['.$frame['op'].'] '.
|
||||
(empty($frame['query'])?
|
||||
'':json_encode($frame['query'])).
|
||||
(empty($frame['command'])?
|
||||
'':json_encode($frame['command'])).
|
||||
PHP_EOL;
|
||||
function log($flag=TRUE) {
|
||||
if ($flag) {
|
||||
$cursor=$this->selectcollection('system.profile')->find();
|
||||
foreach (iterator_to_array($cursor) as $frame)
|
||||
if (!preg_match('/\.system\..+$/',$frame['ns']))
|
||||
$this->log.=date('r',$frame['ts']->sec).' ('.
|
||||
sprintf('%.1f',$frame['millis']).'ms) '.
|
||||
$frame['ns'].' ['.$frame['op'].'] '.
|
||||
(empty($frame['query'])?
|
||||
'':json_encode($frame['query'])).
|
||||
(empty($frame['command'])?
|
||||
'':json_encode($frame['command'])).
|
||||
PHP_EOL;
|
||||
} else {
|
||||
$this->log=FALSE;
|
||||
$this->setprofilinglevel(-1);
|
||||
}
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
@@ -81,7 +87,8 @@ class Mongo {
|
||||
**/
|
||||
function drop() {
|
||||
$out=$this->db->drop();
|
||||
$this->setprofilinglevel(2);
|
||||
if ($this->log!==FALSE)
|
||||
$this->setprofilinglevel(2);
|
||||
return $out;
|
||||
}
|
||||
|
||||
@@ -92,7 +99,11 @@ class Mongo {
|
||||
* @param $args array
|
||||
**/
|
||||
function __call($func,array $args) {
|
||||
return call_user_func_array(array($this->db,$func),$args);
|
||||
return call_user_func_array([$this->db,$func],$args);
|
||||
}
|
||||
|
||||
//! Prohibit cloning
|
||||
private function __clone() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +115,7 @@ class Mongo {
|
||||
function __construct($dsn,$dbname,array $options=NULL) {
|
||||
$this->uuid=\Base::instance()->hash($this->dsn=$dsn);
|
||||
$class=class_exists('\MongoClient')?'\MongoClient':'\Mongo';
|
||||
$this->db=new \MongoDB(new $class($dsn,$options?:array()),$dbname);
|
||||
$this->db=new \MongoDB(new $class($dsn,$options?:[]),$dbname);
|
||||
$this->setprofilinglevel(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -31,9 +31,11 @@ class Mapper extends \DB\Cursor {
|
||||
//! Mongo collection
|
||||
$collection,
|
||||
//! Mongo document
|
||||
$document=array(),
|
||||
$document=[],
|
||||
//! Mongo cursor
|
||||
$cursor;
|
||||
$cursor,
|
||||
//! Defined fields
|
||||
$fields;
|
||||
|
||||
/**
|
||||
* Return database type
|
||||
@@ -92,7 +94,7 @@ class Mapper extends \DB\Cursor {
|
||||
$mapper->reset();
|
||||
foreach ($row as $key=>$val)
|
||||
$mapper->document[$key]=$val;
|
||||
$mapper->query=array(clone($mapper));
|
||||
$mapper->query=[clone($mapper)];
|
||||
if (isset($mapper->trigger['load']))
|
||||
\Base::instance()->call($mapper->trigger['load'],$mapper);
|
||||
return $mapper;
|
||||
@@ -119,48 +121,48 @@ class Mapper extends \DB\Cursor {
|
||||
**/
|
||||
function select($fields=NULL,$filter=NULL,array $options=NULL,$ttl=0) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options+=array(
|
||||
$options=[];
|
||||
$options+=[
|
||||
'group'=>NULL,
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
);
|
||||
];
|
||||
$fw=\Base::instance();
|
||||
$cache=\Cache::instance();
|
||||
if (!($cached=$cache->exists($hash=$fw->hash($this->db->dsn().
|
||||
$fw->stringify(array($fields,$filter,$options))).'.mongo',
|
||||
$fw->stringify([$fields,$filter,$options])).'.mongo',
|
||||
$result)) || !$ttl || $cached[0]+$ttl<microtime(TRUE)) {
|
||||
if ($options['group']) {
|
||||
$grp=$this->collection->group(
|
||||
$options['group']['keys'],
|
||||
$options['group']['initial'],
|
||||
$options['group']['reduce'],
|
||||
array(
|
||||
[
|
||||
'condition'=>$filter,
|
||||
'finalize'=>$options['group']['finalize']
|
||||
)
|
||||
]
|
||||
);
|
||||
$tmp=$this->db->selectcollection(
|
||||
$fw->get('HOST').'.'.$fw->get('BASE').'.'.
|
||||
uniqid(NULL,TRUE).'.tmp'
|
||||
);
|
||||
$tmp->batchinsert($grp['retval'],array('w'=>1));
|
||||
$filter=array();
|
||||
$tmp->batchinsert($grp['retval'],['w'=>1]);
|
||||
$filter=[];
|
||||
$collection=$tmp;
|
||||
}
|
||||
else {
|
||||
$filter=$filter?:array();
|
||||
$filter=$filter?:[];
|
||||
$collection=$this->collection;
|
||||
}
|
||||
$this->cursor=$collection->find($filter,$fields?:array());
|
||||
$this->cursor=$collection->find($filter,$fields?:[]);
|
||||
if ($options['order'])
|
||||
$this->cursor=$this->cursor->sort($options['order']);
|
||||
if ($options['limit'])
|
||||
$this->cursor=$this->cursor->limit($options['limit']);
|
||||
if ($options['offset'])
|
||||
$this->cursor=$this->cursor->skip($options['offset']);
|
||||
$result=array();
|
||||
$result=[];
|
||||
while ($this->cursor->hasnext())
|
||||
$result[]=$this->cursor->getnext();
|
||||
if ($options['group'])
|
||||
@@ -169,7 +171,7 @@ class Mapper extends \DB\Cursor {
|
||||
// Save to cache backend
|
||||
$cache->set($hash,$result,$ttl);
|
||||
}
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($result as $doc)
|
||||
$out[]=$this->factory($doc);
|
||||
return $out;
|
||||
@@ -184,14 +186,14 @@ class Mapper extends \DB\Cursor {
|
||||
**/
|
||||
function find($filter=NULL,array $options=NULL,$ttl=0) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options+=array(
|
||||
$options=[];
|
||||
$options+=[
|
||||
'group'=>NULL,
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
);
|
||||
return $this->select(NULL,$filter,$options,$ttl);
|
||||
];
|
||||
return $this->select($this->fields,$filter,$options,$ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,9 +206,9 @@ class Mapper extends \DB\Cursor {
|
||||
$fw=\Base::instance();
|
||||
$cache=\Cache::instance();
|
||||
if (!($cached=$cache->exists($hash=$fw->hash($fw->stringify(
|
||||
array($filter))).'.mongo',$result)) || !$ttl ||
|
||||
[$filter])).'.mongo',$result)) || !$ttl ||
|
||||
$cached[0]+$ttl<microtime(TRUE)) {
|
||||
$result=$this->collection->count($filter?:array());
|
||||
$result=$this->collection->count($filter?:[]);
|
||||
if ($fw->get('CACHE') && $ttl)
|
||||
// Save to cache backend
|
||||
$cache->set($hash,$result,$ttl);
|
||||
@@ -221,7 +223,7 @@ class Mapper extends \DB\Cursor {
|
||||
* @param $ofs int
|
||||
**/
|
||||
function skip($ofs=1) {
|
||||
$this->document=($out=parent::skip($ofs))?$out->document:array();
|
||||
$this->document=($out=parent::skip($ofs))?$out->document:[];
|
||||
if ($this->document && isset($this->trigger['load']))
|
||||
\Base::instance()->call($this->trigger['load'],$this);
|
||||
return $out;
|
||||
@@ -236,13 +238,13 @@ class Mapper extends \DB\Cursor {
|
||||
return $this->update();
|
||||
if (isset($this->trigger['beforeinsert']) &&
|
||||
\Base::instance()->call($this->trigger['beforeinsert'],
|
||||
array($this,array('_id'=>$this->document['_id'])))===FALSE)
|
||||
[$this,['_id'=>$this->document['_id']]])===FALSE)
|
||||
return $this->document;
|
||||
$this->collection->insert($this->document);
|
||||
$pkey=array('_id'=>$this->document['_id']);
|
||||
$pkey=['_id'=>$this->document['_id']];
|
||||
if (isset($this->trigger['afterinsert']))
|
||||
\Base::instance()->call($this->trigger['afterinsert'],
|
||||
array($this,$pkey));
|
||||
[$this,$pkey]);
|
||||
$this->load($pkey);
|
||||
return $this->document;
|
||||
}
|
||||
@@ -252,16 +254,16 @@ class Mapper extends \DB\Cursor {
|
||||
* @return array
|
||||
**/
|
||||
function update() {
|
||||
$pkey=array('_id'=>$this->document['_id']);
|
||||
$pkey=['_id'=>$this->document['_id']];
|
||||
if (isset($this->trigger['beforeupdate']) &&
|
||||
\Base::instance()->call($this->trigger['beforeupdate'],
|
||||
array($this,$pkey))===FALSE)
|
||||
[$this,$pkey])===FALSE)
|
||||
return $this->document;
|
||||
$this->collection->update(
|
||||
$pkey,$this->document,array('upsert'=>TRUE));
|
||||
$pkey,$this->document,['upsert'=>TRUE]);
|
||||
if (isset($this->trigger['afterupdate']))
|
||||
\Base::instance()->call($this->trigger['afterupdate'],
|
||||
array($this,$pkey));
|
||||
[$this,$pkey]);
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
@@ -273,17 +275,17 @@ class Mapper extends \DB\Cursor {
|
||||
function erase($filter=NULL) {
|
||||
if ($filter)
|
||||
return $this->collection->remove($filter);
|
||||
$pkey=array('_id'=>$this->document['_id']);
|
||||
$pkey=['_id'=>$this->document['_id']];
|
||||
if (isset($this->trigger['beforeerase']) &&
|
||||
\Base::instance()->call($this->trigger['beforeerase'],
|
||||
array($this,$pkey))===FALSE)
|
||||
[$this,$pkey])===FALSE)
|
||||
return FALSE;
|
||||
$result=$this->collection->
|
||||
remove(array('_id'=>$this->document['_id']));
|
||||
remove(['_id'=>$this->document['_id']]);
|
||||
parent::erase();
|
||||
if (isset($this->trigger['aftererase']))
|
||||
\Base::instance()->call($this->trigger['aftererase'],
|
||||
array($this,$pkey));
|
||||
[$this,$pkey]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -292,7 +294,7 @@ class Mapper extends \DB\Cursor {
|
||||
* @return NULL
|
||||
**/
|
||||
function reset() {
|
||||
$this->document=array();
|
||||
$this->document=[];
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
@@ -351,10 +353,12 @@ class Mapper extends \DB\Cursor {
|
||||
* @return void
|
||||
* @param $db object
|
||||
* @param $collection string
|
||||
* @param $fields array
|
||||
**/
|
||||
function __construct(\DB\Mongo $db,$collection) {
|
||||
function __construct(\DB\Mongo $db,$collection,$fields=NULL) {
|
||||
$this->db=$db;
|
||||
$this->collection=$db->selectcollection($collection);
|
||||
$this->fields=$fields;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -63,12 +63,13 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function read($id) {
|
||||
$this->load(array('session_id'=>$this->sid=$id));
|
||||
$this->load(['session_id'=>$this->sid=$id]);
|
||||
if ($this->dry())
|
||||
return FALSE;
|
||||
if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
|
||||
$fw=\Base::instance();
|
||||
if (!isset($this->onsuspect) || FALSE===$fw->call($this->onsuspect,array($this,$id))) {
|
||||
if (!isset($this->onsuspect) ||
|
||||
$fw->call($this->onsuspect,[$this,$id])===FALSE) {
|
||||
//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
|
||||
$this->destroy($id);
|
||||
$this->close();
|
||||
@@ -101,7 +102,7 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function destroy($id) {
|
||||
$this->erase(array('session_id'=>$id));
|
||||
$this->erase(['session_id'=>$id]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ class Session extends Mapper {
|
||||
* @param $max int
|
||||
**/
|
||||
function cleanup($max) {
|
||||
$this->erase(array('$where'=>'this.stamp+'.$max.'<'.time()));
|
||||
$this->erase(['$where'=>'this.stamp+'.$max.'<'.time()]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -168,18 +169,17 @@ class Session extends Mapper {
|
||||
parent::__construct($db,$table);
|
||||
$this->onsuspect=$onsuspect;
|
||||
session_set_save_handler(
|
||||
array($this,'open'),
|
||||
array($this,'close'),
|
||||
array($this,'read'),
|
||||
array($this,'write'),
|
||||
array($this,'destroy'),
|
||||
array($this,'cleanup')
|
||||
[$this,'open'],
|
||||
[$this,'close'],
|
||||
[$this,'read'],
|
||||
[$this,'write'],
|
||||
[$this,'destroy'],
|
||||
[$this,'cleanup']
|
||||
);
|
||||
register_shutdown_function('session_commit');
|
||||
$fw=\Base::instance();
|
||||
$headers=$fw->get('HEADERS');
|
||||
$this->_csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash(mt_rand());
|
||||
$this->_csrf=$fw->get('SEED').'.'.$fw->hash(mt_rand());
|
||||
if ($key)
|
||||
$fw->set($key,$this->_csrf);
|
||||
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -30,6 +30,9 @@ class SQL {
|
||||
E_PKey='Table %s does not have a primary key';
|
||||
//@}
|
||||
|
||||
const
|
||||
PARAM_FLOAT='float';
|
||||
|
||||
protected
|
||||
//! UUID
|
||||
$uuid,
|
||||
@@ -78,6 +81,14 @@ class SQL {
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction flag
|
||||
* @return bool
|
||||
**/
|
||||
function trans() {
|
||||
return $this->trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map data type of argument to a PDO constant
|
||||
* @return int
|
||||
@@ -93,6 +104,8 @@ class SQL {
|
||||
return \PDO::PARAM_INT;
|
||||
case 'resource':
|
||||
return \PDO::PARAM_LOB;
|
||||
case 'float':
|
||||
return self::PARAM_FLOAT;
|
||||
default:
|
||||
return \PDO::PARAM_STR;
|
||||
}
|
||||
@@ -106,6 +119,10 @@ class SQL {
|
||||
**/
|
||||
function value($type,$val) {
|
||||
switch ($type) {
|
||||
case self::PARAM_FLOAT:
|
||||
return (float)(is_string($val)
|
||||
? str_replace(',','.',preg_replace('/([.,])(?!\d+$)/','',$val))
|
||||
: $val);
|
||||
case \PDO::PARAM_NULL:
|
||||
return (unset)$val;
|
||||
case \PDO::PARAM_INT:
|
||||
@@ -124,15 +141,19 @@ class SQL {
|
||||
* @return array|int|FALSE
|
||||
* @param $cmds string|array
|
||||
* @param $args string|array
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
* @param $log bool
|
||||
* @param $stamp bool
|
||||
**/
|
||||
function exec($cmds,$args=NULL,$ttl=0,$log=TRUE) {
|
||||
function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) {
|
||||
$tag='';
|
||||
if (is_array($ttl))
|
||||
list($ttl,$tag)=$ttl;
|
||||
$auto=FALSE;
|
||||
if (is_null($args))
|
||||
$args=array();
|
||||
$args=[];
|
||||
elseif (is_scalar($args))
|
||||
$args=array(1=>$args);
|
||||
$args=[1=>$args];
|
||||
if (is_array($cmds)) {
|
||||
if (count($args)<($count=count($cmds)))
|
||||
// Apply arguments to SQL commands
|
||||
@@ -144,9 +165,11 @@ class SQL {
|
||||
}
|
||||
else {
|
||||
$count=1;
|
||||
$cmds=array($cmds);
|
||||
$args=array($args);
|
||||
$cmds=[$cmds];
|
||||
$args=[$args];
|
||||
}
|
||||
if ($this->log===FALSE)
|
||||
$log=FALSE;
|
||||
$fw=\Base::instance();
|
||||
$cache=\Cache::instance();
|
||||
$result=FALSE;
|
||||
@@ -161,10 +184,10 @@ class SQL {
|
||||
if (!preg_replace('/(^\s+|[\s;]+$)/','',$cmd))
|
||||
continue;
|
||||
$now=microtime(TRUE);
|
||||
$keys=$vals=array();
|
||||
$keys=$vals=[];
|
||||
if ($fw->get('CACHE') && $ttl && ($cached=$cache->exists(
|
||||
$hash=$fw->hash($this->dsn.$cmd.
|
||||
$fw->stringify($arg)).'.sql',$result)) &&
|
||||
$fw->stringify($arg)).($tag?'.'.$tag:'').'.sql',$result)) &&
|
||||
$cached[0]+$ttl>microtime(TRUE)) {
|
||||
foreach ($arg as $key=>$val) {
|
||||
$vals[]=$fw->stringify(is_array($val)?$val[0]:$val);
|
||||
@@ -172,7 +195,7 @@ class SQL {
|
||||
'/';
|
||||
}
|
||||
if ($log)
|
||||
$this->log.=date('r').' ('.
|
||||
$this->log.=($stamp?(date('r').' '):'').'('.
|
||||
sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
|
||||
'[CACHED] '.
|
||||
preg_replace($keys,$vals,
|
||||
@@ -182,20 +205,22 @@ class SQL {
|
||||
foreach ($arg as $key=>$val) {
|
||||
if (is_array($val)) {
|
||||
// User-specified data type
|
||||
$query->bindvalue($key,$val[0],$val[1]);
|
||||
$query->bindvalue($key,$val[0],
|
||||
$val[1]==self::PARAM_FLOAT?\PDO::PARAM_STR:$val[1]);
|
||||
$vals[]=$fw->stringify($this->value($val[1],$val[0]));
|
||||
}
|
||||
else {
|
||||
// Convert to PDO data type
|
||||
$query->bindvalue($key,$val,
|
||||
$type=$this->type($val));
|
||||
($type=$this->type($val))==self::PARAM_FLOAT?
|
||||
\PDO::PARAM_STR:$type);
|
||||
$vals[]=$fw->stringify($this->value($type,$val));
|
||||
}
|
||||
$keys[]='/'.preg_quote(is_numeric($key)?chr(0).'?':$key).
|
||||
'/';
|
||||
}
|
||||
if ($log)
|
||||
$this->log.=date('r').' ('.
|
||||
$this->log.=($stamp?(date('r').' '):'').'('.
|
||||
sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms) '.
|
||||
preg_replace($keys,$vals,
|
||||
str_replace('?',chr(0).'?',$cmd),1).PHP_EOL;
|
||||
@@ -216,7 +241,7 @@ class SQL {
|
||||
if (preg_match('/sqlite2?/',$this->engine))
|
||||
foreach ($result as $pos=>$rec) {
|
||||
unset($result[$pos]);
|
||||
$result[$pos]=array();
|
||||
$result[$pos]=[];
|
||||
foreach ($rec as $key=>$val)
|
||||
$result[$pos][trim($key,'\'"[]`')]=$val;
|
||||
}
|
||||
@@ -269,20 +294,20 @@ class SQL {
|
||||
* @return array|FALSE
|
||||
* @param $table string
|
||||
* @param $fields array|string
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
**/
|
||||
function schema($table,$fields=NULL,$ttl=0) {
|
||||
if (strpos($table,'.'))
|
||||
list($schema,$table)=explode('.',$table);
|
||||
// Supported engines
|
||||
$cmd=array(
|
||||
'sqlite2?'=>array(
|
||||
'PRAGMA table_info("'.$table.'");',
|
||||
'name','type','dflt_value','notnull',0,'pk',TRUE),
|
||||
'mysql'=>array(
|
||||
'SHOW columns FROM `'.$this->dbname.'`.`'.$table.'`;',
|
||||
'Field','Type','Default','Null','YES','Key','PRI'),
|
||||
'mssql|sqlsrv|sybase|dblib|pgsql|odbc'=>array(
|
||||
$cmd=[
|
||||
'sqlite2?'=>[
|
||||
'PRAGMA table_info("'.$table.'")',
|
||||
'name','type','dflt_value','notnull',0,'pk',TRUE],
|
||||
'mysql'=>[
|
||||
'SHOW columns FROM `'.$this->dbname.'`.`'.$table.'`',
|
||||
'Field','Type','Default','Null','YES','Key','PRI'],
|
||||
'mssql|sqlsrv|sybase|dblib|pgsql|odbc'=>[
|
||||
'SELECT '.
|
||||
'c.column_name AS field,'.
|
||||
'c.data_type AS type,'.
|
||||
@@ -309,10 +334,9 @@ class SQL {
|
||||
'c.table_name='.$this->quote($table).
|
||||
($this->dbname?
|
||||
(' AND c.table_catalog='.
|
||||
$this->quote($this->dbname)):'').
|
||||
';',
|
||||
'field','type','defval','nullable','YES','pkey','PRIMARY KEY'),
|
||||
'oci'=>array(
|
||||
$this->quote($this->dbname)):''),
|
||||
'field','type','defval','nullable','YES','pkey','PRIMARY KEY'],
|
||||
'oci'=>[
|
||||
'SELECT c.column_name AS field, '.
|
||||
'c.data_type AS type, '.
|
||||
'c.data_default AS defval, '.
|
||||
@@ -326,35 +350,35 @@ class SQL {
|
||||
'AND constraint_type='.$this->quote('P').') AS pkey '.
|
||||
'FROM all_tab_cols c '.
|
||||
'WHERE c.table_name='.$this->quote($table),
|
||||
'FIELD','TYPE','DEFVAL','NULLABLE','Y','PKEY','P')
|
||||
);
|
||||
'FIELD','TYPE','DEFVAL','NULLABLE','Y','PKEY','P']
|
||||
];
|
||||
if (is_string($fields))
|
||||
$fields=\Base::instance()->split($fields);
|
||||
foreach ($cmd as $key=>$val)
|
||||
if (preg_match('/'.$key.'/',$this->engine)) {
|
||||
// Improve InnoDB performance on MySQL with
|
||||
// SET GLOBAL innodb_stats_on_metadata=0;
|
||||
// This requires SUPER privilege!
|
||||
$rows=array();
|
||||
$rows=[];
|
||||
foreach ($this->exec($val[0],NULL,$ttl) as $row) {
|
||||
if (!$fields || in_array($row[$val[1]],$fields))
|
||||
$rows[$row[$val[1]]]=array(
|
||||
$rows[$row[$val[1]]]=[
|
||||
'type'=>$row[$val[2]],
|
||||
'pdo_type'=>
|
||||
preg_match('/int\b|integer/i',$row[$val[2]])?
|
||||
\PDO::PARAM_INT:
|
||||
(preg_match('/bool/i',$row[$val[2]])?
|
||||
\PDO::PARAM_BOOL:
|
||||
(preg_match('/blob|bytea|image|binary/i',
|
||||
$row[$val[2]])?
|
||||
\PDO::PARAM_LOB:
|
||||
\PDO::PARAM_STR)),
|
||||
(preg_match(
|
||||
'/blob|bytea|image|binary/i',
|
||||
$row[$val[2]])?\PDO::PARAM_LOB:
|
||||
(preg_match(
|
||||
'/float|decimal|real|numeric|double/i',
|
||||
$row[$val[2]])?self::PARAM_FLOAT:
|
||||
\PDO::PARAM_STR))),
|
||||
'default'=>is_string($row[$val[3]])?
|
||||
preg_replace('/^\s*([\'"])(.*)\1\s*/','\2',
|
||||
$row[$val[3]]):$row[$val[3]],
|
||||
'nullable'=>$row[$val[4]]==$val[5],
|
||||
'pkey'=>$row[$val[6]]==$val[7]
|
||||
);
|
||||
];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
@@ -420,20 +444,22 @@ class SQL {
|
||||
* Return quoted identifier name
|
||||
* @return string
|
||||
* @param $key
|
||||
**/
|
||||
function quotekey($key) {
|
||||
$delims=array(
|
||||
* @param bool $split
|
||||
**/
|
||||
function quotekey($key, $split=TRUE) {
|
||||
$delims=[
|
||||
'mysql'=>'``',
|
||||
'sqlite2?|pgsql|oci'=>'""',
|
||||
'mssql|sqlsrv|odbc|sybase|dblib'=>'[]'
|
||||
);
|
||||
];
|
||||
$use='';
|
||||
foreach ($delims as $engine=>$delim)
|
||||
if (preg_match('/'.$engine.'/',$this->engine)) {
|
||||
$use=$delim;
|
||||
break;
|
||||
}
|
||||
return $use[0].implode($use[1].'.'.$use[0],explode('.',$key)).$use[1];
|
||||
return $use[0].($split ? implode($use[1].'.'.$use[0],explode('.',$key))
|
||||
: $key).$use[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,7 +469,11 @@ class SQL {
|
||||
* @param $args array
|
||||
**/
|
||||
function __call($func,array $args) {
|
||||
return call_user_func_array(array($this->pdo,$func),$args);
|
||||
return call_user_func_array([$this->pdo,$func],$args);
|
||||
}
|
||||
|
||||
//! Prohibit cloning
|
||||
private function __clone() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,10 +489,10 @@ class SQL {
|
||||
if (preg_match('/^.+?(?:dbname|database)=(.+?)(?=;|$)/is',$dsn,$parts))
|
||||
$this->dbname=$parts[1];
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options=[];
|
||||
if (isset($parts[0]) && strstr($parts[0],':',TRUE)=='mysql')
|
||||
$options+=array(\PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES '.
|
||||
strtolower(str_replace('-','',$fw->get('ENCODING'))).';');
|
||||
$options+=[\PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES '.
|
||||
strtolower(str_replace('-','',$fw->get('ENCODING'))).';'];
|
||||
$this->pdo=new \PDO($dsn,$user,$pw,$options);
|
||||
$this->engine=$this->pdo->getattribute(\PDO::ATTR_DRIVER_NAME);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -39,7 +39,7 @@ class Mapper extends \DB\Cursor {
|
||||
//! Defined fields
|
||||
$fields,
|
||||
//! Adhoc fields
|
||||
$adhoc=array();
|
||||
$adhoc=[];
|
||||
|
||||
/**
|
||||
* Return database type
|
||||
@@ -90,7 +90,7 @@ class Mapper extends \DB\Cursor {
|
||||
if (array_key_exists($key,$this->fields)) {
|
||||
$val=is_null($val) && $this->fields[$key]['nullable']?
|
||||
NULL:$this->db->value($this->fields[$key]['pdo_type'],$val);
|
||||
if ($this->fields[$key]['value']!==$val ||
|
||||
if ($this->fields[$key]['initial']!==$val ||
|
||||
$this->fields[$key]['default']!==$val && is_null($val))
|
||||
$this->fields[$key]['changed']=TRUE;
|
||||
return $this->fields[$key]['value']=$val;
|
||||
@@ -100,7 +100,7 @@ class Mapper extends \DB\Cursor {
|
||||
$this->adhoc[$key]['value']=$val;
|
||||
else
|
||||
// Parenthesize expression in case it's a subquery
|
||||
$this->adhoc[$key]=array('expr'=>'('.$val.')','value'=>NULL);
|
||||
$this->adhoc[$key]=['expr'=>'('.$val.')','value'=>NULL];
|
||||
return $val;
|
||||
}
|
||||
|
||||
@@ -144,6 +144,8 @@ class Mapper extends \DB\Cursor {
|
||||
return 'bool';
|
||||
case \PDO::PARAM_STR:
|
||||
return 'string';
|
||||
case \DB\SQL::PARAM_FLOAT:
|
||||
return 'float';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +165,11 @@ class Mapper extends \DB\Cursor {
|
||||
else
|
||||
continue;
|
||||
$mapper->{$var}[$key]['value']=$val;
|
||||
$mapper->{$var}[$key]['initial']=$val;
|
||||
if ($var=='fields' && $mapper->{$var}[$key]['pkey'])
|
||||
$mapper->{$var}[$key]['previous']=$val;
|
||||
}
|
||||
$mapper->query=array(clone($mapper));
|
||||
$mapper->query=[clone($mapper)];
|
||||
if (isset($mapper->trigger['load']))
|
||||
\Base::instance()->call($mapper->trigger['load'],$mapper);
|
||||
return $mapper;
|
||||
@@ -194,37 +197,36 @@ class Mapper extends \DB\Cursor {
|
||||
* @param $fields string
|
||||
* @param $filter string|array
|
||||
* @param $options array
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
**/
|
||||
function select($fields,$filter=NULL,array $options=NULL,$ttl=0) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options+=array(
|
||||
$options=[];
|
||||
$options+=[
|
||||
'group'=>NULL,
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
);
|
||||
];
|
||||
$db=$this->db;
|
||||
$sql='SELECT '.$fields.' FROM '.$this->table;
|
||||
$args=array();
|
||||
if ($filter) {
|
||||
if (is_array($filter)) {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:array(1=>$args);
|
||||
list($filter)=$filter;
|
||||
}
|
||||
$sql.=' WHERE '.$filter;
|
||||
$args=[];
|
||||
if (is_array($filter)) {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:[1=>$args];
|
||||
list($filter)=$filter;
|
||||
}
|
||||
if ($filter)
|
||||
$sql.=' WHERE '.$filter;
|
||||
if ($options['group']) {
|
||||
$sql.=' GROUP BY '.implode(',',array_map(
|
||||
function($str) use($db) {
|
||||
return preg_replace_callback(
|
||||
'/\b(\w+)\h*(HAVING.+|$)/i',
|
||||
function($parts) use($db) {
|
||||
return $db->quotekey($parts[1]);
|
||||
return $db->quotekey($parts[1]).(isset($parts[2])?(' '.$parts[2]):'');
|
||||
},
|
||||
$str
|
||||
);
|
||||
@@ -243,7 +245,7 @@ class Mapper extends \DB\Cursor {
|
||||
}
|
||||
if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) &&
|
||||
($options['limit'] || $options['offset'])) {
|
||||
$pkeys=array();
|
||||
$pkeys=[];
|
||||
foreach ($this->fields as $key=>$field)
|
||||
if ($field['pkey'])
|
||||
$pkeys[]=$key;
|
||||
@@ -274,7 +276,7 @@ class Mapper extends \DB\Cursor {
|
||||
$sql.=' OFFSET '.(int)$options['offset'];
|
||||
}
|
||||
$result=$this->db->exec($sql,$args,$ttl);
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($result as &$row) {
|
||||
foreach ($row as $field=>&$val) {
|
||||
if (array_key_exists($field,$this->fields)) {
|
||||
@@ -297,24 +299,24 @@ class Mapper extends \DB\Cursor {
|
||||
* @return static[]
|
||||
* @param $filter string|array
|
||||
* @param $options array
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
**/
|
||||
function find($filter=NULL,array $options=NULL,$ttl=0) {
|
||||
if (!$options)
|
||||
$options=array();
|
||||
$options+=array(
|
||||
$options=[];
|
||||
$options+=[
|
||||
'group'=>NULL,
|
||||
'order'=>NULL,
|
||||
'limit'=>0,
|
||||
'offset'=>0
|
||||
);
|
||||
];
|
||||
$adhoc='';
|
||||
foreach ($this->adhoc as $key=>$field)
|
||||
$adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key);
|
||||
return $this->select(
|
||||
($options['group'] && !preg_match('/mysql|sqlite/',$this->engine)?
|
||||
$options['group']:
|
||||
implode(',',array_map(array($this->db,'quotekey'),
|
||||
implode(',',array_map([$this->db,'quotekey'],
|
||||
array_keys($this->fields)))).$adhoc,$filter,$options,$ttl);
|
||||
}
|
||||
|
||||
@@ -322,18 +324,18 @@ class Mapper extends \DB\Cursor {
|
||||
* Count records that match criteria
|
||||
* @return int
|
||||
* @param $filter string|array
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
**/
|
||||
function count($filter=NULL,$ttl=0) {
|
||||
$sql='SELECT COUNT(*) AS '.
|
||||
$this->db->quotekey('rows').' FROM '.$this->table;
|
||||
$args=array();
|
||||
$args=[];
|
||||
if ($filter) {
|
||||
if (is_array($filter)) {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:array(1=>$args);
|
||||
$args=is_array($args)?$args:[1=>$args];
|
||||
list($filter)=$filter;
|
||||
}
|
||||
$sql.=' WHERE '.$filter;
|
||||
@@ -353,6 +355,7 @@ class Mapper extends \DB\Cursor {
|
||||
$dry=$this->dry();
|
||||
foreach ($this->fields as $key=>&$field) {
|
||||
$field['value']=$dry?NULL:$out->fields[$key]['value'];
|
||||
$field['initial']=$field['value'];
|
||||
$field['changed']=FALSE;
|
||||
if ($field['pkey'])
|
||||
$field['previous']=$dry?NULL:$out->fields[$key]['value'];
|
||||
@@ -372,22 +375,22 @@ class Mapper extends \DB\Cursor {
|
||||
* @return object
|
||||
**/
|
||||
function insert() {
|
||||
$args=array();
|
||||
$args=[];
|
||||
$actr=0;
|
||||
$nctr=0;
|
||||
$fields='';
|
||||
$values='';
|
||||
$filter='';
|
||||
$pkeys=array();
|
||||
$nkeys=array();
|
||||
$ckeys=array();
|
||||
$pkeys=[];
|
||||
$nkeys=[];
|
||||
$ckeys=[];
|
||||
$inc=NULL;
|
||||
foreach ($this->fields as $key=>$field)
|
||||
if ($field['pkey'])
|
||||
$pkeys[$key]=$field['previous'];
|
||||
if (isset($this->trigger['beforeinsert']) &&
|
||||
\Base::instance()->call($this->trigger['beforeinsert'],
|
||||
array($this,$pkeys))===FALSE)
|
||||
[$this,$pkeys])===FALSE)
|
||||
return $this;
|
||||
foreach ($this->fields as $key=>&$field) {
|
||||
if ($field['pkey']) {
|
||||
@@ -396,13 +399,13 @@ class Mapper extends \DB\Cursor {
|
||||
empty($field['value']) && !$field['nullable'])
|
||||
$inc=$key;
|
||||
$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
|
||||
$nkeys[$nctr+1]=array($field['value'],$field['pdo_type']);
|
||||
$nkeys[$nctr+1]=[$field['value'],$field['pdo_type']];
|
||||
$nctr++;
|
||||
}
|
||||
if ($field['changed'] && $key!=$inc) {
|
||||
$fields.=($actr?',':'').$this->db->quotekey($key);
|
||||
$values.=($actr?',':'').'?';
|
||||
$args[$actr+1]=array($field['value'],$field['pdo_type']);
|
||||
$args[$actr+1]=[$field['value'],$field['pdo_type']];
|
||||
$actr++;
|
||||
$ckeys[]=$key;
|
||||
}
|
||||
@@ -420,18 +423,21 @@ class Mapper extends \DB\Cursor {
|
||||
$seq=NULL;
|
||||
if ($this->engine=='pgsql') {
|
||||
$names=array_keys($pkeys);
|
||||
$seq=$this->source.'_'.end($names).'_seq';
|
||||
$aik=end($names);
|
||||
if ($this->fields[$aik]['pdo_type']==\PDO::PARAM_INT)
|
||||
$seq=$this->source.'_'.$aik.'_seq';
|
||||
}
|
||||
if ($this->engine!='oci')
|
||||
if ($this->engine!='oci' && !($this->engine=='pgsql' && !$seq))
|
||||
$this->_id=$this->db->lastinsertid($seq);
|
||||
// Reload to obtain default and auto-increment field values
|
||||
$this->load($inc?
|
||||
array($inc.'=?',$this->db->value(
|
||||
$this->fields[$inc]['pdo_type'],$this->_id)):
|
||||
array($filter,$nkeys));
|
||||
if ($inc || $filter)
|
||||
$this->load($inc?
|
||||
[$inc.'=?',$this->db->value(
|
||||
$this->fields[$inc]['pdo_type'],$this->_id)]:
|
||||
[$filter,$nkeys]);
|
||||
if (isset($this->trigger['afterinsert']))
|
||||
\Base::instance()->call($this->trigger['afterinsert'],
|
||||
array($this,$pkeys));
|
||||
[$this,$pkeys]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@@ -441,38 +447,42 @@ class Mapper extends \DB\Cursor {
|
||||
* @return object
|
||||
**/
|
||||
function update() {
|
||||
$args=array();
|
||||
$args=[];
|
||||
$ctr=0;
|
||||
$pairs='';
|
||||
$filter='';
|
||||
$pkeys=array();
|
||||
$pkeys=[];
|
||||
foreach ($this->fields as $key=>$field)
|
||||
if ($field['pkey'])
|
||||
$pkeys[$key]=$field['previous'];
|
||||
if (isset($this->trigger['beforeupdate']) &&
|
||||
\Base::instance()->call($this->trigger['beforeupdate'],
|
||||
array($this,$pkeys))===FALSE)
|
||||
[$this,$pkeys])===FALSE)
|
||||
return $this;
|
||||
foreach ($this->fields as $key=>$field)
|
||||
if ($field['changed']) {
|
||||
$pairs.=($pairs?',':'').$this->db->quotekey($key).'=?';
|
||||
$args[$ctr+1]=array($field['value'],$field['pdo_type']);
|
||||
$ctr++;
|
||||
$args[++$ctr]=[$field['value'],$field['pdo_type']];
|
||||
}
|
||||
foreach ($this->fields as $key=>$field)
|
||||
if ($field['pkey']) {
|
||||
$filter.=($filter?' AND ':' WHERE ').
|
||||
$this->db->quotekey($key).'=?';
|
||||
$args[$ctr+1]=array($field['previous'],$field['pdo_type']);
|
||||
$ctr++;
|
||||
$args[++$ctr]=[$field['previous'],$field['pdo_type']];
|
||||
}
|
||||
if ($pairs) {
|
||||
$sql='UPDATE '.$this->table.' SET '.$pairs.$filter;
|
||||
$this->db->exec($sql,$args);
|
||||
if (isset($this->trigger['afterupdate']))
|
||||
\Base::instance()->call($this->trigger['afterupdate'],
|
||||
array($this,$pkeys));
|
||||
[$this,$pkeys]);
|
||||
}
|
||||
// reset changed flag after calling afterupdate
|
||||
foreach ($this->fields as $key=>&$field) {
|
||||
$field['changed']=FALSE;
|
||||
$field['initial']=$field['value'];
|
||||
unset($field);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -483,25 +493,25 @@ class Mapper extends \DB\Cursor {
|
||||
**/
|
||||
function erase($filter=NULL) {
|
||||
if ($filter) {
|
||||
$args=array();
|
||||
$args=[];
|
||||
if (is_array($filter)) {
|
||||
$args=isset($filter[1]) && is_array($filter[1])?
|
||||
$filter[1]:
|
||||
array_slice($filter,1,NULL,TRUE);
|
||||
$args=is_array($args)?$args:array(1=>$args);
|
||||
$args=is_array($args)?$args:[1=>$args];
|
||||
list($filter)=$filter;
|
||||
}
|
||||
return $this->db->
|
||||
exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
|
||||
}
|
||||
$args=array();
|
||||
$args=[];
|
||||
$ctr=0;
|
||||
$filter='';
|
||||
$pkeys=array();
|
||||
$pkeys=[];
|
||||
foreach ($this->fields as $key=>&$field) {
|
||||
if ($field['pkey']) {
|
||||
$filter.=($filter?' AND ':'').$this->db->quotekey($key).'=?';
|
||||
$args[$ctr+1]=array($field['previous'],$field['pdo_type']);
|
||||
$args[$ctr+1]=[$field['previous'],$field['pdo_type']];
|
||||
$pkeys[$key]=$field['previous'];
|
||||
$ctr++;
|
||||
}
|
||||
@@ -518,13 +528,13 @@ class Mapper extends \DB\Cursor {
|
||||
parent::erase();
|
||||
if (isset($this->trigger['beforeerase']) &&
|
||||
\Base::instance()->call($this->trigger['beforeerase'],
|
||||
array($this,$pkeys))===FALSE)
|
||||
[$this,$pkeys])===FALSE)
|
||||
return 0;
|
||||
$out=$this->db->
|
||||
exec('DELETE FROM '.$this->table.' WHERE '.$filter.';',$args);
|
||||
if (isset($this->trigger['aftererase']))
|
||||
\Base::instance()->call($this->trigger['aftererase'],
|
||||
array($this,$pkeys));
|
||||
[$this,$pkeys]);
|
||||
return $out;
|
||||
}
|
||||
|
||||
@@ -535,6 +545,7 @@ class Mapper extends \DB\Cursor {
|
||||
function reset() {
|
||||
foreach ($this->fields as &$field) {
|
||||
$field['value']=NULL;
|
||||
$field['initial']=NULL;
|
||||
$field['changed']=FALSE;
|
||||
if ($field['pkey'])
|
||||
$field['previous']=NULL;
|
||||
@@ -591,7 +602,7 @@ class Mapper extends \DB\Cursor {
|
||||
* @param $adhoc bool
|
||||
**/
|
||||
function fields($adhoc=TRUE) {
|
||||
return array_keys($this->fields+($adhoc?$this->adhoc:array()));
|
||||
return array_keys($this->fields+($adhoc?$this->adhoc:[]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,7 +628,7 @@ class Mapper extends \DB\Cursor {
|
||||
* @param $db object
|
||||
* @param $table string
|
||||
* @param $fields array|string
|
||||
* @param $ttl int
|
||||
* @param $ttl int|array
|
||||
**/
|
||||
function __construct(\DB\SQL $db,$table,$fields=NULL,$ttl=60) {
|
||||
$this->db=$db;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -63,12 +63,13 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function read($id) {
|
||||
$this->load(array('session_id=?',$this->sid=$id));
|
||||
$this->load(['session_id=?',$this->sid=$id]);
|
||||
if ($this->dry())
|
||||
return FALSE;
|
||||
if ($this->get('ip')!=$this->_ip || $this->get('agent')!=$this->_agent) {
|
||||
$fw=\Base::instance();
|
||||
if (!isset($this->onsuspect) || FALSE===$fw->call($this->onsuspect,array($this,$id))) {
|
||||
if (!isset($this->onsuspect) ||
|
||||
$fw->call($this->onsuspect,[$this,$id])===FALSE) {
|
||||
//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
|
||||
$this->destroy($id);
|
||||
$this->close();
|
||||
@@ -101,7 +102,7 @@ class Session extends Mapper {
|
||||
* @param $id string
|
||||
**/
|
||||
function destroy($id) {
|
||||
$this->erase(array('session_id=?',$id));
|
||||
$this->erase(['session_id=?',$id]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -111,7 +112,7 @@ class Session extends Mapper {
|
||||
* @param $max int
|
||||
**/
|
||||
function cleanup($max) {
|
||||
$this->erase(array('stamp+?<?',$max,time()));
|
||||
$this->erase(['stamp+?<?',$max,time()]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -176,12 +177,12 @@ class Session extends Mapper {
|
||||
'CREATE TABLE dbo.'):
|
||||
('CREATE TABLE IF NOT EXISTS '.
|
||||
((($name=$db->name())&&$db->driver()!='pgsql')?
|
||||
($name.'.'):''))).
|
||||
$table.' ('.$eol.
|
||||
($db->quotekey($name,FALSE).'.'):''))).
|
||||
$db->quotekey($table,FALSE).' ('.$eol.
|
||||
$tab.$db->quotekey('session_id').' VARCHAR(255),'.$eol.
|
||||
$tab.$db->quotekey('data').' TEXT,'.$eol.
|
||||
$tab.$db->quotekey('ip').' VARCHAR(45),'.$eol.
|
||||
$tab.$db->quotekey('agent').' VARCHAR(255),'.$eol.
|
||||
$tab.$db->quotekey('agent').' VARCHAR(300),'.$eol.
|
||||
$tab.$db->quotekey('stamp').' INTEGER,'.$eol.
|
||||
$tab.'PRIMARY KEY ('.$db->quotekey('session_id').')'.$eol.
|
||||
');'
|
||||
@@ -190,18 +191,17 @@ class Session extends Mapper {
|
||||
parent::__construct($db,$table);
|
||||
$this->onsuspect=$onsuspect;
|
||||
session_set_save_handler(
|
||||
array($this,'open'),
|
||||
array($this,'close'),
|
||||
array($this,'read'),
|
||||
array($this,'write'),
|
||||
array($this,'destroy'),
|
||||
array($this,'cleanup')
|
||||
[$this,'open'],
|
||||
[$this,'close'],
|
||||
[$this,'read'],
|
||||
[$this,'write'],
|
||||
[$this,'destroy'],
|
||||
[$this,'cleanup']
|
||||
);
|
||||
register_shutdown_function('session_commit');
|
||||
$fw=\Base::instance();
|
||||
$headers=$fw->get('HEADERS');
|
||||
$this->_csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash(mt_rand());
|
||||
$this->_csrf=$fw->get('SEED').'.'.$fw->hash(mt_rand());
|
||||
if ($key)
|
||||
$fw->set($key,$this->_csrf);
|
||||
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -36,7 +36,7 @@ class F3 {
|
||||
static function __callstatic($func,array $args) {
|
||||
if (!self::$fw)
|
||||
self::$fw=Base::instance();
|
||||
return call_user_func_array(array(self::$fw,$func),$args);
|
||||
return call_user_func_array([self::$fw,$func],$args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -28,6 +28,7 @@ class Image {
|
||||
E_Color='Invalid color specified: %s',
|
||||
E_File='File not found',
|
||||
E_Font='CAPTCHA font not found',
|
||||
E_TTF='No TrueType support in GD module',
|
||||
E_Length='Invalid CAPTCHA length: %s';
|
||||
//@}
|
||||
|
||||
@@ -54,9 +55,11 @@ class Image {
|
||||
/**
|
||||
* Convert RGB hex triad to array
|
||||
* @return array|FALSE
|
||||
* @param $color int
|
||||
* @param $color int|string
|
||||
**/
|
||||
function rgb($color) {
|
||||
if (is_string($color))
|
||||
$color=hexdec($color);
|
||||
$hex=str_pad($hex=dechex($color),$color<4096?3:6,'0',STR_PAD_LEFT);
|
||||
if (($len=strlen($hex))>6)
|
||||
user_error(sprintf(self::E_Color,'0x'.$hex),E_USER_ERROR);
|
||||
@@ -225,9 +228,17 @@ class Image {
|
||||
* @param $crop bool
|
||||
* @param $enlarge bool
|
||||
**/
|
||||
function resize($width,$height,$crop=TRUE,$enlarge=TRUE) {
|
||||
function resize($width=NULL,$height=NULL,$crop=TRUE,$enlarge=TRUE) {
|
||||
if (is_null($width) && is_null($height))
|
||||
return $this;
|
||||
$origw=$this->width();
|
||||
$origh=$this->height();
|
||||
if (is_null($width))
|
||||
$width=round(($height/$origh)*$origw);
|
||||
if (is_null($height))
|
||||
$height=round(($width/$origw)*$origh);
|
||||
// Adjust dimensions; retain aspect ratio
|
||||
$ratio=($origw=imagesx($this->data))/($origh=imagesy($this->data));
|
||||
$ratio=$origw/$origh;
|
||||
if (!$crop) {
|
||||
if ($width/$ratio<=$height)
|
||||
$height=round($width/$ratio);
|
||||
@@ -331,24 +342,24 @@ class Image {
|
||||
* @param $blocks int
|
||||
**/
|
||||
function identicon($str,$size=64,$blocks=4) {
|
||||
$sprites=array(
|
||||
array(.5,1,1,0,1,1),
|
||||
array(.5,0,1,0,.5,1,0,1),
|
||||
array(.5,0,1,0,1,1,.5,1,1,.5),
|
||||
array(0,.5,.5,0,1,.5,.5,1,.5,.5),
|
||||
array(0,.5,1,0,1,1,0,1,1,.5),
|
||||
array(1,0,1,1,.5,1,1,.5,.5,.5),
|
||||
array(0,0,1,0,1,.5,0,0,.5,1,0,1),
|
||||
array(0,0,.5,0,1,.5,.5,1,0,1,.5,.5),
|
||||
array(.5,0,.5,.5,1,.5,1,1,.5,1,.5,.5,0,.5),
|
||||
array(0,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1),
|
||||
array(0,.5,.5,1,1,.5,.5,0,1,0,1,1,0,1),
|
||||
array(.5,0,1,0,1,1,.5,1,1,.75,.5,.5,1,.25),
|
||||
array(0,.5,.5,0,.5,.5,1,0,1,.5,.5,1,.5,.5,0,1),
|
||||
array(0,0,1,0,1,1,0,1,1,.5,.5,.25,.5,.75,0,.5,.5,.25),
|
||||
array(0,.5,.5,.5,.5,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1),
|
||||
array(0,0,1,0,.5,.5,.5,0,0,.5,1,.5,.5,1,.5,.5,0,1)
|
||||
);
|
||||
$sprites=[
|
||||
[.5,1,1,0,1,1],
|
||||
[.5,0,1,0,.5,1,0,1],
|
||||
[.5,0,1,0,1,1,.5,1,1,.5],
|
||||
[0,.5,.5,0,1,.5,.5,1,.5,.5],
|
||||
[0,.5,1,0,1,1,0,1,1,.5],
|
||||
[1,0,1,1,.5,1,1,.5,.5,.5],
|
||||
[0,0,1,0,1,.5,0,0,.5,1,0,1],
|
||||
[0,0,.5,0,1,.5,.5,1,0,1,.5,.5],
|
||||
[.5,0,.5,.5,1,.5,1,1,.5,1,.5,.5,0,.5],
|
||||
[0,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1],
|
||||
[0,.5,.5,1,1,.5,.5,0,1,0,1,1,0,1],
|
||||
[.5,0,1,0,1,1,.5,1,1,.75,.5,.5,1,.25],
|
||||
[0,.5,.5,0,.5,.5,1,0,1,.5,.5,1,.5,.5,0,1],
|
||||
[0,0,1,0,1,1,0,1,1,.5,.5,.25,.5,.75,0,.5,.5,.25],
|
||||
[0,.5,.5,.5,.5,0,1,0,.5,.5,1,.5,.5,1,.5,.5,0,1],
|
||||
[0,0,1,0,.5,.5,.5,0,0,.5,1,.5,.5,1,.5,.5,0,1]
|
||||
];
|
||||
$hash=sha1($str);
|
||||
$this->data=imagecreatetruecolor($size,$size);
|
||||
list($r,$g,$b)=$this->rgb(hexdec(substr($hash,-3)));
|
||||
@@ -360,15 +371,10 @@ class Image {
|
||||
for ($i=$j,$x=$blocks-1-$j;$i<$x;$i++) {
|
||||
$sprite=imagecreatetruecolor($dim,$dim);
|
||||
imagefill($sprite,0,0,IMG_COLOR_TRANSPARENT);
|
||||
if ($block=$sprites[
|
||||
hexdec($hash[($j*$blocks+$i)*2])%$ctr]) {
|
||||
for ($k=0,$pts=count($block);$k<$pts;$k++)
|
||||
$block[$k]*=$dim;
|
||||
imagefilledpolygon($sprite,$block,$pts/2,$fg);
|
||||
}
|
||||
$sprite=imagerotate($sprite,
|
||||
90*(hexdec($hash[($j*$blocks+$i)*2+1])%4),
|
||||
imagecolorallocatealpha($sprite,0,0,0,127));
|
||||
$block=$sprites[hexdec($hash[($j*$blocks+$i)*2])%$ctr];
|
||||
for ($k=0,$pts=count($block);$k<$pts;$k++)
|
||||
$block[$k]*=$dim;
|
||||
imagefilledpolygon($sprite,$block,$pts/2,$fg);
|
||||
for ($k=0;$k<4;$k++) {
|
||||
imagecopyresampled($this->data,$sprite,
|
||||
$i*$dim/2,$j*$dim/2,0,0,$dim/2,$dim/2,$dim,$dim);
|
||||
@@ -398,6 +404,10 @@ class Image {
|
||||
user_error(sprintf(self::E_Length,$len),E_USER_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
if (!function_exists('imagettftext')) {
|
||||
user_error(self::E_TTF,E_USER_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
$fw=Base::instance();
|
||||
foreach ($fw->split($path?:$fw->get('UI').';./') as $dir)
|
||||
if (is_file($path=$dir.$font)) {
|
||||
@@ -405,7 +415,7 @@ class Image {
|
||||
$ssl?bin2hex(openssl_random_pseudo_bytes($len)):uniqid(),
|
||||
-$len));
|
||||
$block=$size*3;
|
||||
$tmp=array();
|
||||
$tmp=[];
|
||||
for ($i=0,$width=0,$height=0;$i<$len;$i++) {
|
||||
// Process at 2x magnification
|
||||
$box=imagettfbbox($size*2,0,$path,$seed[$i]);
|
||||
@@ -472,8 +482,10 @@ class Image {
|
||||
header('Content-Type: image/'.$format);
|
||||
header('X-Powered-By: '.Base::instance()->get('PACKAGE'));
|
||||
}
|
||||
call_user_func_array('image'.$format,
|
||||
array_merge(array($this->data),$args));
|
||||
call_user_func_array(
|
||||
'image'.$format,
|
||||
array_merge([$this->data,NULL],$args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,8 +496,10 @@ class Image {
|
||||
$args=func_get_args();
|
||||
$format=$args?array_shift($args):'png';
|
||||
ob_start();
|
||||
call_user_func_array('image'.$format,
|
||||
array_merge(array($this->data),$args));
|
||||
call_user_func_array(
|
||||
'image'.$format,
|
||||
array_merge([$this->data,NULL],$args)
|
||||
);
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
@@ -507,8 +521,7 @@ class Image {
|
||||
if (!is_dir($dir=$fw->get('TEMP')))
|
||||
mkdir($dir,Base::MODE,TRUE);
|
||||
$this->count++;
|
||||
$fw->write($dir.'/'.
|
||||
$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->write($dir.'/'.$fw->get('SEED').'.'.
|
||||
$fw->hash($this->file).'-'.$this->count.'.png',
|
||||
$this->dump());
|
||||
}
|
||||
@@ -523,8 +536,7 @@ class Image {
|
||||
function restore($state=1) {
|
||||
$fw=Base::instance();
|
||||
if ($this->flag && is_file($file=($path=$fw->get('TEMP').
|
||||
$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash($this->file).'-').$state.'.png')) {
|
||||
$fw->get('SEED').'.'.$fw->hash($this->file).'-').$state.'.png')) {
|
||||
if (is_resource($this->data))
|
||||
imagedestroy($this->data);
|
||||
$this->data=imagecreatefromstring($fw->read($file));
|
||||
@@ -553,11 +565,12 @@ class Image {
|
||||
|
||||
/**
|
||||
* Load string
|
||||
* @return object
|
||||
* @return object|FALSE
|
||||
* @param $str string
|
||||
**/
|
||||
function load($str) {
|
||||
$this->data=imagecreatefromstring($str);
|
||||
if (!$this->data=@imagecreatefromstring($str))
|
||||
return FALSE;
|
||||
imagesavealpha($this->data,TRUE);
|
||||
$this->save();
|
||||
return $this;
|
||||
@@ -592,9 +605,7 @@ class Image {
|
||||
if (is_resource($this->data)) {
|
||||
imagedestroy($this->data);
|
||||
$fw=Base::instance();
|
||||
$path=$fw->get('TEMP').
|
||||
$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash($this->file);
|
||||
$path=$fw->get('TEMP').$fw->get('SEED').'.'.$fw->hash($this->file);
|
||||
if ($glob=@glob($path.'*.png',GLOB_NOSORT))
|
||||
foreach ($glob as $match)
|
||||
if (preg_match('/-(\d+)\.png/',$match))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Magic implements ArrayAccess {
|
||||
**/
|
||||
function offsetset($key,$val) {
|
||||
return Base::instance()->visible($this,$key)?
|
||||
($this->key=$val):$this->set($key,$val);
|
||||
($this->$key=$val):$this->set($key,$val);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -321,7 +321,7 @@ class Markdown extends Prefab {
|
||||
$tmp='';
|
||||
while ($str!=$tmp)
|
||||
$str=preg_replace_callback(
|
||||
'/(?<!\\\\)([*_]{1,3})(.*?)(?!\\\\)\1(?=[\s[:punct:]]|$)/',
|
||||
'/(?<=\s|^)(?<!\\\\)([*_]{1,3})(.*?)(?!\\\\)\1(?=[\s[:punct:]]|$)/',
|
||||
function($expr) {
|
||||
switch (strlen($expr[1])) {
|
||||
case 1:
|
||||
@@ -427,12 +427,12 @@ class Markdown extends Prefab {
|
||||
**/
|
||||
function esc($str) {
|
||||
if (!$this->special)
|
||||
$this->special=array(
|
||||
$this->special=[
|
||||
'...'=>'…',
|
||||
'(tm)'=>'™',
|
||||
'(r)'=>'®',
|
||||
'(c)'=>'©'
|
||||
);
|
||||
];
|
||||
foreach ($this->special as $key=>$val)
|
||||
$str=preg_replace('/'.preg_quote($key,'/').'/i',$val,$str);
|
||||
return htmlspecialchars($str,ENT_COMPAT,
|
||||
@@ -454,7 +454,7 @@ class Markdown extends Prefab {
|
||||
* @param $str string
|
||||
**/
|
||||
function scan($str) {
|
||||
$inline=array('img','a','text','auto','code');
|
||||
$inline=['img','a','text','auto','code'];
|
||||
foreach ($inline as $func)
|
||||
$str=$this->{'_'.$func}($str);
|
||||
return $str;
|
||||
@@ -468,7 +468,7 @@ class Markdown extends Prefab {
|
||||
protected function build($str) {
|
||||
if (!$this->blocks) {
|
||||
// Regexes for capturing entire blocks
|
||||
$this->blocks=array(
|
||||
$this->blocks=[
|
||||
'blockquote'=>'/^(?:\h?>\h?.*?(?:\n+|$))+/',
|
||||
'pre'=>'/^(?:(?: {4}|\t).+?(?:\n+|$))+/',
|
||||
'fence'=>'/^`{3}\h*(\w+)?.*?[^\n]*\n+(.+?)`{3}[^\n]*'.
|
||||
@@ -486,7 +486,7 @@ class Markdown extends Prefab {
|
||||
'(?:\/>|>(?:(?>[^><]+)|(?R))*<\/\2>))'.
|
||||
'\h*(?:\n{2,}|\n*$)|<[\?%].+?[\?%]>\h*(?:\n?$|\n*))/s',
|
||||
'p'=>'/^(.+?(?:\n{2,}|\n*$))/s'
|
||||
);
|
||||
];
|
||||
}
|
||||
$self=$this;
|
||||
// Treat lines with nothing but whitespaces as empty lines
|
||||
@@ -546,7 +546,7 @@ class Markdown extends Prefab {
|
||||
if (preg_match($regex,substr($str,$ptr),$match)) {
|
||||
$ptr+=strlen($match[0]);
|
||||
$dst.=call_user_func_array(
|
||||
array($this,'_'.$func),
|
||||
[$this,'_'.$func],
|
||||
count($match)>1?array_slice($match,1):$match
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -45,7 +45,7 @@ class Matrix extends Prefab {
|
||||
* @param $var array
|
||||
**/
|
||||
function transpose(array &$var) {
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($var as $keyx=>$cols)
|
||||
foreach ($cols as $keyy=>$valy)
|
||||
$out[$keyy][$keyx]=$valy;
|
||||
@@ -63,7 +63,7 @@ class Matrix extends Prefab {
|
||||
uasort(
|
||||
$var,
|
||||
function($val1,$val2) use($col,$order) {
|
||||
list($v1,$v2)=array($val1[$col],$val2[$col]);
|
||||
list($v1,$v2)=[$val1[$col],$val2[$col]];
|
||||
$out=is_numeric($v1) && is_numeric($v2)?
|
||||
Base::instance()->sign($v1-$v2):strcmp($v1,$v2);
|
||||
if ($order==SORT_DESC)
|
||||
@@ -96,12 +96,15 @@ class Matrix extends Prefab {
|
||||
* @param $first int
|
||||
**/
|
||||
function calendar($date='now',$first=0) {
|
||||
$parts=getdate(strtotime($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=array();
|
||||
for ($i=0;$i<$days;$i++)
|
||||
$out[floor(($ref+$i)/7)][($ref+$i)%7]=$i+1;
|
||||
$out=FALSE;
|
||||
if (extension_loaded('calendar')) {
|
||||
$parts=getdate(strtotime($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=[];
|
||||
for ($i=0;$i<$days;$i++)
|
||||
$out[floor(($ref+$i)/7)][($ref+$i)%7]=$i+1;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -33,7 +33,9 @@ class Session {
|
||||
//! IP,
|
||||
$_ip,
|
||||
//! Suspect callback
|
||||
$onsuspect;
|
||||
$onsuspect,
|
||||
//! Cache instance
|
||||
$_cache;
|
||||
|
||||
/**
|
||||
* Open session
|
||||
@@ -61,11 +63,12 @@ class Session {
|
||||
**/
|
||||
function read($id) {
|
||||
$this->sid=$id;
|
||||
if (!$data=Cache::instance()->get($id.'.@'))
|
||||
if (!$data=$this->_cache->get($id.'.@'))
|
||||
return FALSE;
|
||||
if ($data['ip']!=$this->_ip || $data['agent']!=$this->_agent) {
|
||||
$fw=Base::instance();
|
||||
if (!isset($this->onsuspect) || FALSE===$fw->call($this->onsuspect,array($this,$id))) {
|
||||
if (!isset($this->onsuspect) ||
|
||||
$fw->call($this->onsuspect,[$this,$id])===FALSE) {
|
||||
//NB: `session_destroy` can't be called at that stage (`session_start` not completed)
|
||||
$this->destroy($id);
|
||||
$this->close();
|
||||
@@ -85,13 +88,13 @@ class Session {
|
||||
function write($id,$data) {
|
||||
$fw=Base::instance();
|
||||
$jar=$fw->get('JAR');
|
||||
Cache::instance()->set($id.'.@',
|
||||
array(
|
||||
$this->_cache->set($id.'.@',
|
||||
[
|
||||
'data'=>$data,
|
||||
'ip'=>$this->_ip,
|
||||
'agent'=>$this->_agent,
|
||||
'stamp'=>time()
|
||||
),
|
||||
],
|
||||
$jar['expire']?($jar['expire']-time()):0
|
||||
);
|
||||
return TRUE;
|
||||
@@ -103,7 +106,7 @@ class Session {
|
||||
* @param $id string
|
||||
**/
|
||||
function destroy($id) {
|
||||
Cache::instance()->clear($id.'.@');
|
||||
$this->_cache->clear($id.'.@');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@ class Session {
|
||||
* @param $max int
|
||||
**/
|
||||
function cleanup($max) {
|
||||
Cache::instance()->reset('.@',$max);
|
||||
$this->_cache->reset('.@',$max);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -148,7 +151,7 @@ class Session {
|
||||
function stamp() {
|
||||
if (!$this->sid)
|
||||
session_start();
|
||||
return Cache::instance()->exists($this->sid.'.@',$data)?
|
||||
return $this->_cache->exists($this->sid.'.@',$data)?
|
||||
$data['stamp']:FALSE;
|
||||
}
|
||||
|
||||
@@ -165,21 +168,21 @@ class Session {
|
||||
* @param $onsuspect callback
|
||||
* @param $key string
|
||||
**/
|
||||
function __construct($onsuspect=NULL,$key=NULL) {
|
||||
function __construct($onsuspect=NULL,$key=NULL,$cache=null) {
|
||||
$this->onsuspect=$onsuspect;
|
||||
$this->_cache=$cache?:Cache::instance();
|
||||
session_set_save_handler(
|
||||
array($this,'open'),
|
||||
array($this,'close'),
|
||||
array($this,'read'),
|
||||
array($this,'write'),
|
||||
array($this,'destroy'),
|
||||
array($this,'cleanup')
|
||||
[$this,'open'],
|
||||
[$this,'close'],
|
||||
[$this,'read'],
|
||||
[$this,'write'],
|
||||
[$this,'destroy'],
|
||||
[$this,'cleanup']
|
||||
);
|
||||
register_shutdown_function('session_commit');
|
||||
$fw=\Base::instance();
|
||||
$headers=$fw->get('HEADERS');
|
||||
$this->_csrf=$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash(mt_rand());
|
||||
$this->_csrf=$fw->get('SEED').'.'.$fw->hash(mt_rand());
|
||||
if ($key)
|
||||
$fw->set($key,$this->_csrf);
|
||||
$this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:'';
|
||||
|
||||
150
app/lib/smtp.php
150
app/lib/smtp.php
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -118,20 +118,41 @@ class SMTP extends Magic {
|
||||
* @return string
|
||||
* @param $cmd string
|
||||
* @param $log bool
|
||||
* @param $mock bool
|
||||
**/
|
||||
protected function dialog($cmd=NULL,$log=TRUE) {
|
||||
$socket=&$this->socket;
|
||||
if (!is_null($cmd))
|
||||
fputs($socket,$cmd."\r\n");
|
||||
protected function dialog($cmd=NULL,$log=TRUE,$mock=FALSE) {
|
||||
$reply='';
|
||||
while (!feof($socket) && ($info=stream_get_meta_data($socket)) &&
|
||||
!$info['timed_out'] && $str=fgets($socket,4096)) {
|
||||
$reply.=$str;
|
||||
if (preg_match('/(?:^|\n)\d{3} .+?\r\n/s',$reply))
|
||||
if ($mock) {
|
||||
$host=str_replace('ssl://','',$this->host);
|
||||
switch ($cmd) {
|
||||
case NULL:
|
||||
$reply='220 '.$host.' ESMTP ready'."\n";
|
||||
break;
|
||||
case 'DATA':
|
||||
$reply='354 Go ahead'."\n";
|
||||
break;
|
||||
case 'QUIT':
|
||||
$reply='221 '.$host.' closing connection'."\n";
|
||||
break;
|
||||
default:
|
||||
$reply='250 OK'."\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$socket=&$this->socket;
|
||||
if ($cmd)
|
||||
fputs($socket,$cmd."\r\n");
|
||||
while (!feof($socket) && ($info=stream_get_meta_data($socket)) &&
|
||||
!$info['timed_out'] && $str=fgets($socket,4096)) {
|
||||
$reply.=$str;
|
||||
if (preg_match('/(?:^|\n)\d{3} .+?\r\n/s',$reply))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($log) {
|
||||
$this->log.=$cmd."\n";
|
||||
if ($cmd)
|
||||
$this->log.=$cmd."\n";
|
||||
$this->log.=str_replace("\r",'',$reply);
|
||||
}
|
||||
return $reply;
|
||||
@@ -147,9 +168,9 @@ class SMTP extends Magic {
|
||||
function attach($file,$alias=NULL,$cid=NULL) {
|
||||
if (!is_file($file))
|
||||
user_error(sprintf(self::E_Attach,$file),E_USER_ERROR);
|
||||
if (is_string($alias))
|
||||
$file=array($alias=>$file);
|
||||
$this->attachments[]=array('filename'=>$file,'cid'=>$cid);
|
||||
if ($alias)
|
||||
$file=[$alias=>$file];
|
||||
$this->attachments[]=['filename'=>$file,'cid'=>$cid];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,8 +178,9 @@ class SMTP extends Magic {
|
||||
* @return bool
|
||||
* @param $message string
|
||||
* @param $log bool
|
||||
* @param $mock bool
|
||||
**/
|
||||
function send($message,$log=TRUE) {
|
||||
function send($message,$log=TRUE,$mock=FALSE) {
|
||||
if ($this->scheme=='ssl' && !extension_loaded('openssl'))
|
||||
return FALSE;
|
||||
// Message should not be blank
|
||||
@@ -168,20 +190,25 @@ class SMTP extends Magic {
|
||||
// Retrieve headers
|
||||
$headers=$this->headers;
|
||||
// Connect to the server
|
||||
$socket=&$this->socket;
|
||||
$socket=@fsockopen($this->host,$this->port);
|
||||
if (!$socket)
|
||||
return FALSE;
|
||||
stream_set_blocking($socket,TRUE);
|
||||
if (!$mock) {
|
||||
$socket=&$this->socket;
|
||||
$socket=@fsockopen($this->host,$this->port,$errno,$errstr);
|
||||
if (!$socket) {
|
||||
$fw->error(500,$errstr);
|
||||
return FALSE;
|
||||
}
|
||||
stream_set_blocking($socket,TRUE);
|
||||
}
|
||||
// Get server's initial response
|
||||
$this->dialog(NULL,FALSE);
|
||||
$this->dialog(NULL,TRUE,$mock);
|
||||
// Announce presence
|
||||
$reply=$this->dialog('EHLO '.$fw->get('HOST'),$log);
|
||||
$reply=$this->dialog('EHLO '.$fw->get('HOST'),$log,$mock);
|
||||
if (strtolower($this->scheme)=='tls') {
|
||||
$this->dialog('STARTTLS',$log);
|
||||
stream_socket_enable_crypto(
|
||||
$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
||||
$reply=$this->dialog('EHLO '.$fw->get('HOST'),$log);
|
||||
$this->dialog('STARTTLS',$log,$mock);
|
||||
if (!$mock)
|
||||
stream_socket_enable_crypto(
|
||||
$socket,TRUE,STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
||||
$reply=$this->dialog('EHLO '.$fw->get('HOST'),$log,$mock);
|
||||
}
|
||||
if (preg_match('/8BITMIME/',$reply))
|
||||
$headers['Content-Transfer-Encoding']='8bit';
|
||||
@@ -192,12 +219,22 @@ class SMTP extends Magic {
|
||||
}
|
||||
if ($this->user && $this->pw && preg_match('/AUTH/',$reply)) {
|
||||
// Authenticate
|
||||
$this->dialog('AUTH LOGIN',$log);
|
||||
$this->dialog(base64_encode($this->user),$log);
|
||||
$this->dialog(base64_encode($this->pw),$log);
|
||||
$this->dialog('AUTH LOGIN',$log,$mock);
|
||||
$this->dialog(base64_encode($this->user),$log,$mock);
|
||||
$auth_rply=$this->dialog(base64_encode($this->pw),$log,$mock);
|
||||
if (!preg_match('/^235\s.*/',$auth_rply)) {
|
||||
$this->dialog('QUIT',$log,$mock);
|
||||
if (!$mock && $socket)
|
||||
fclose($socket);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (empty($headers['Message-ID']))
|
||||
$headers['Message-ID']='<'.uniqid('',TRUE).'@'.$this->host.'>';
|
||||
if (empty($headers['Date']))
|
||||
$headers['Date']=date('r');
|
||||
// Required headers
|
||||
$reqd=array('From','To','Subject');
|
||||
$reqd=['From','To','Subject'];
|
||||
foreach ($reqd as $id)
|
||||
if (empty($headers[$id]))
|
||||
user_error(sprintf(self::E_Header,$id),E_USER_ERROR);
|
||||
@@ -205,21 +242,32 @@ class SMTP extends Magic {
|
||||
$str='';
|
||||
// Stringify headers
|
||||
foreach ($headers as $key=>&$val) {
|
||||
if (!in_array($key,$reqd) && (!$this->attachments ||
|
||||
$key!='Content-Type' && $key!='Content-Transfer-Encoding'))
|
||||
if (!in_array($key,$reqd) &&
|
||||
(!$this->attachments ||
|
||||
$key!='Content-Type' &&
|
||||
$key!='Content-Transfer-Encoding'))
|
||||
$str.=$key.': '.$val.$eol;
|
||||
if (in_array($key,array('From','To','Cc','Bcc')) &&
|
||||
!preg_match('/[<>]/',$val))
|
||||
$val='<'.$val.'>';
|
||||
if (in_array($key,['From','To','Cc','Bcc'])) {
|
||||
$email='';
|
||||
preg_match_all('/(?:".+?" )?(?:<.+?>|[^ ,]+)/',
|
||||
$val,$matches,PREG_SET_ORDER);
|
||||
foreach ($matches as $raw)
|
||||
$email.=($email?', ':'').
|
||||
(preg_match('/<.+?>/',$raw[0])?
|
||||
$raw[0]:
|
||||
('<'.$raw[0].'>'));
|
||||
$val=$email;
|
||||
}
|
||||
unset($val);
|
||||
}
|
||||
// Start message dialog
|
||||
$this->dialog('MAIL FROM: '.strstr($headers['From'],'<'),$log);
|
||||
$this->dialog('MAIL FROM: '.strstr($headers['From'],'<'),$log,$mock);
|
||||
foreach ($fw->split($headers['To'].
|
||||
(isset($headers['Cc'])?(';'.$headers['Cc']):'').
|
||||
(isset($headers['Bcc'])?(';'.$headers['Bcc']):'')) as $dst)
|
||||
$this->dialog('RCPT TO: '.strstr($dst,'<'),$log);
|
||||
$this->dialog('DATA',$log);
|
||||
(isset($headers['Bcc'])?(';'.$headers['Bcc']):'')) as $dst) {
|
||||
$this->dialog('RCPT TO: '.strstr($dst,'<'),$log,$mock);
|
||||
}
|
||||
$this->dialog('DATA',$log,$mock);
|
||||
if ($this->attachments) {
|
||||
// Replace Content-Type
|
||||
$type=$headers['Content-Type'];
|
||||
@@ -241,28 +289,25 @@ class SMTP extends Magic {
|
||||
$out.=$str.$eol;
|
||||
$out.=$message.$eol;
|
||||
foreach ($this->attachments as $attachment) {
|
||||
if (is_array($attachment['filename'])) {
|
||||
if (is_array($attachment['filename']))
|
||||
list($alias,$file)=each($attachment['filename']);
|
||||
$filename=$alias;
|
||||
$attachment['filename']=$file;
|
||||
}
|
||||
else
|
||||
$filename=basename($attachment['filename']);
|
||||
$alias=basename($file=$attachment['filename']);
|
||||
$out.='--'.$hash.$eol;
|
||||
$out.='Content-Type: application/octet-stream'.$eol;
|
||||
$out.='Content-Transfer-Encoding: base64'.$eol;
|
||||
if ($attachment['cid'])
|
||||
$out.='Content-ID: '.$attachment['cid'].$eol;
|
||||
$out.='Content-Disposition: attachment; '.
|
||||
'filename="'.$filename.'"'.$eol;
|
||||
'filename="'.$alias.'"'.$eol;
|
||||
$out.=$eol;
|
||||
$out.=chunk_split(base64_encode(
|
||||
file_get_contents($attachment['filename']))).$eol;
|
||||
file_get_contents($file))).$eol;
|
||||
}
|
||||
$out.=$eol;
|
||||
$out.='--'.$hash.'--'.$eol;
|
||||
$out.='.';
|
||||
$this->dialog($out,FALSE);
|
||||
$this->dialog($out,TRUE,$mock);
|
||||
}
|
||||
else {
|
||||
// Send mail headers
|
||||
@@ -274,10 +319,10 @@ class SMTP extends Magic {
|
||||
$out.=$message.$eol;
|
||||
$out.='.';
|
||||
// Send message
|
||||
$this->dialog($out);
|
||||
$this->dialog($out,TRUE,$mock);
|
||||
}
|
||||
$this->dialog('QUIT',$log);
|
||||
if ($socket)
|
||||
$this->dialog('QUIT',$log,$mock);
|
||||
if (!$mock && $socket)
|
||||
fclose($socket);
|
||||
return TRUE;
|
||||
}
|
||||
@@ -290,12 +335,13 @@ class SMTP extends Magic {
|
||||
* @param $user string
|
||||
* @param $pw string
|
||||
**/
|
||||
function __construct($host='localhost',$port=25,$scheme=null,$user=null,$pw=null) {
|
||||
$this->headers=array(
|
||||
function __construct(
|
||||
$host='localhost',$port=25,$scheme=NULL,$user=NULL,$pw=NULL) {
|
||||
$this->headers=[
|
||||
'MIME-Version'=>'1.0',
|
||||
'Content-Type'=>'text/plain; '.
|
||||
'charset='.Base::instance()->get('ENCODING')
|
||||
);
|
||||
];
|
||||
$this->host=$host;
|
||||
if (strtolower($this->scheme=strtolower($scheme))=='ssl')
|
||||
$this->host='ssl://'.$host;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -32,7 +32,7 @@ class Template extends Preview {
|
||||
//! Template tags
|
||||
$tags,
|
||||
//! Custom tag handlers
|
||||
$custom=array();
|
||||
$custom=[];
|
||||
|
||||
/**
|
||||
* Template -set- tag handler
|
||||
@@ -60,14 +60,14 @@ class Template extends Preview {
|
||||
($attrib['with']=$this->token($attrib['with'])) &&
|
||||
preg_match_all('/(\w+)\h*=\h*(.+?)(?=,|$)/',
|
||||
$attrib['with'],$pairs,PREG_SET_ORDER)?
|
||||
'array('.implode(',',
|
||||
('['.implode(',',
|
||||
array_map(function($pair) {
|
||||
return '\''.$pair[1].'\'=>'.
|
||||
(preg_match('/^\'.*\'$/',$pair[2]) ||
|
||||
preg_match('/\$/',$pair[2])?
|
||||
$pair[2]:
|
||||
\Base::instance()->stringify($pair[2]));
|
||||
},$pairs)).')+get_defined_vars()':
|
||||
},$pairs)).']+get_defined_vars()'):
|
||||
'get_defined_vars()';
|
||||
$ttl=isset($attrib['ttl'])?(int)$attrib['ttl']:0;
|
||||
return
|
||||
@@ -127,7 +127,7 @@ class Template extends Preview {
|
||||
(isset($attrib['counter'])?
|
||||
(($ctr=$this->token($attrib['counter'])).'=0; '):'').
|
||||
'foreach (('.
|
||||
$this->token($attrib['group']).'?:array()) as '.
|
||||
$this->token($attrib['group']).'?:[]) as '.
|
||||
(isset($attrib['key'])?
|
||||
($this->token($attrib['key']).'=>'):'').
|
||||
$this->token($attrib['value']).'):'.
|
||||
@@ -147,12 +147,12 @@ class Template extends Preview {
|
||||
// Grab <true> and <false> blocks
|
||||
foreach ($node as $pos=>$block)
|
||||
if (isset($block['true']))
|
||||
$true=array($pos,$block);
|
||||
$true=[$pos,$block];
|
||||
elseif (isset($block['false']))
|
||||
$false=array($pos,$block);
|
||||
$false=[$pos,$block];
|
||||
if (isset($true,$false) && $true[0]>$false[0])
|
||||
// Reverse <true> and <false> blocks
|
||||
list($node[$true[0]],$node[$false[0]])=array($false[1],$true[1]);
|
||||
list($node[$true[0]],$node[$false[0]])=[$false[1],$true[1]];
|
||||
return
|
||||
'<?php if ('.$this->token($attrib['if']).'): ?>'.
|
||||
$this->build($node).
|
||||
@@ -229,7 +229,7 @@ class Template extends Preview {
|
||||
* @return string
|
||||
* @param $node array|string
|
||||
**/
|
||||
protected function build($node) {
|
||||
function build($node) {
|
||||
if (is_string($node))
|
||||
return parent::build($node);
|
||||
$out='';
|
||||
@@ -259,7 +259,7 @@ class Template extends Preview {
|
||||
if ($func[0]=='_')
|
||||
return call_user_func_array($this->custom[$func],$args);
|
||||
if (method_exists($this,$func))
|
||||
return call_user_func_array(array($this,$func),$args);
|
||||
return call_user_func_array([$this,$func],$args);
|
||||
user_error(sprintf(self::E_Method,$func),E_USER_ERROR);
|
||||
}
|
||||
|
||||
@@ -270,9 +270,9 @@ class Template extends Preview {
|
||||
**/
|
||||
function parse($text) {
|
||||
// Build tree structure
|
||||
for ($ptr=0,$w=5,$len=strlen($text),$tree=array(),$tmp='';$ptr<$len;)
|
||||
for ($ptr=0,$w=5,$len=strlen($text),$tree=[],$tmp='';$ptr<$len;)
|
||||
if (preg_match('/^(.{0,'.$w.'}?)<(\/?)(?:F3:)?'.
|
||||
'('.$this->tags.')\b((?:\h+[\w-]+'.
|
||||
'('.$this->tags.')\b((?:\s+[\w-]+'.
|
||||
'(?:\h*=\h*(?:"(?:.*?)"|\'(?:.*?)\'))?|'.
|
||||
'\h*\{\{.+?\}\})*)\h*(\/?)>/is',
|
||||
substr($text,$ptr),$match)) {
|
||||
@@ -281,7 +281,7 @@ class Template extends Preview {
|
||||
// Element node
|
||||
if ($match[2]) {
|
||||
// Find matching start tag
|
||||
$stack=array();
|
||||
$stack=[];
|
||||
for($i=count($tree)-1;$i>=0;$i--) {
|
||||
$item = $tree[$i];
|
||||
if (is_array($item) && array_key_exists($match[3],$item)
|
||||
@@ -296,7 +296,7 @@ class Template extends Preview {
|
||||
else {
|
||||
// Start tag
|
||||
$node=&$tree[][$match[3]];
|
||||
$node=array();
|
||||
$node=[];
|
||||
if ($match[4]) {
|
||||
// Process attributes
|
||||
preg_match_all(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -32,7 +32,7 @@ class Test {
|
||||
|
||||
protected
|
||||
//! Test results
|
||||
$data=array(),
|
||||
$data=[],
|
||||
//! Success indicator
|
||||
$passed=TRUE;
|
||||
|
||||
@@ -61,7 +61,7 @@ class Test {
|
||||
function expect($cond,$text=NULL) {
|
||||
$out=(bool)$cond;
|
||||
if ($this->level==$out || $this->level==self::FLAG_Both) {
|
||||
$data=array('status'=>$out,'text'=>$text,'source'=>NULL);
|
||||
$data=['status'=>$out,'text'=>$text,'source'=>NULL];
|
||||
foreach (debug_backtrace() as $frame)
|
||||
if (isset($frame['file'])) {
|
||||
$data['source']=Base::instance()->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -180,7 +180,7 @@ class UTF extends Prefab {
|
||||
* @param $str string
|
||||
**/
|
||||
function emojify($str) {
|
||||
$map=array(
|
||||
$map=[
|
||||
':('=>'\u2639', // frown
|
||||
':)'=>'\u263a', // smile
|
||||
'<3'=>'\u2665', // heart
|
||||
@@ -191,7 +191,7 @@ class UTF extends Prefab {
|
||||
':,'=>'\u1f60f', // think
|
||||
':/'=>'\u1f623', // skeptic
|
||||
'8O'=>'\u1f632', // oops
|
||||
)+Base::instance()->get('EMOJI');
|
||||
]+Base::instance()->get('EMOJI');
|
||||
return $this->translate(str_replace(array_keys($map),
|
||||
array_values($map),$str));
|
||||
}
|
||||
|
||||
243
app/lib/web.php
243
app/lib/web.php
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -39,7 +39,7 @@ class Web extends Prefab {
|
||||
**/
|
||||
function mime($file) {
|
||||
if (preg_match('/\w+$/',$file,$ext)) {
|
||||
$map=array(
|
||||
$map=[
|
||||
'au'=>'audio/basic',
|
||||
'avi'=>'video/avi',
|
||||
'bmp'=>'image/bmp',
|
||||
@@ -77,7 +77,7 @@ class Web extends Prefab {
|
||||
'xls'=>'application/vnd.ms-excel',
|
||||
'xml'=>'application/xml',
|
||||
'zip'=>'application/x-zip-compressed'
|
||||
);
|
||||
];
|
||||
foreach ($map as $key=>$val)
|
||||
if (preg_match('/'.$key.'/',strtolower($ext[0])))
|
||||
return $val;
|
||||
@@ -93,7 +93,7 @@ class Web extends Prefab {
|
||||
* @param $list string|array
|
||||
**/
|
||||
function acceptable($list=NULL) {
|
||||
$accept=array();
|
||||
$accept=[];
|
||||
foreach (explode(',',str_replace(' ','',@$_SERVER['HTTP_ACCEPT']))
|
||||
as $mime)
|
||||
if (preg_match('/(.+?)(?:;q=([\d\.]+)|$)/',$mime,$parts))
|
||||
@@ -124,8 +124,10 @@ class Web extends Prefab {
|
||||
* @param $mime string
|
||||
* @param $kbps int
|
||||
* @param $force bool
|
||||
* @param $name string
|
||||
* @param $flush bool
|
||||
**/
|
||||
function send($file,$mime=NULL,$kbps=0,$force=TRUE) {
|
||||
function send($file,$mime=NULL,$kbps=0,$force=TRUE,$name=NULL,$flush=TRUE) {
|
||||
if (!is_file($file))
|
||||
return FALSE;
|
||||
$size=filesize($file);
|
||||
@@ -133,7 +135,7 @@ class Web extends Prefab {
|
||||
header('Content-Type: '.($mime?:$this->mime($file)));
|
||||
if ($force)
|
||||
header('Content-Disposition: attachment; '.
|
||||
'filename="'.basename($file).'"');
|
||||
'filename="'.($name!==NULL?$name:basename($file)).'"');
|
||||
header('Accept-Ranges: bytes');
|
||||
header('Content-Length: '.$size);
|
||||
header('X-Powered-By: '.Base::instance()->get('PACKAGE'));
|
||||
@@ -152,6 +154,10 @@ class Web extends Prefab {
|
||||
}
|
||||
// Send 1KiB and reset timer
|
||||
echo fread($handle,1024);
|
||||
if ($flush) {
|
||||
ob_flush();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
return $size;
|
||||
@@ -170,9 +176,7 @@ class Web extends Prefab {
|
||||
if (!is_dir($dir))
|
||||
mkdir($dir,Base::MODE,TRUE);
|
||||
if ($fw->get('VERB')=='PUT') {
|
||||
$tmp=$fw->get('TEMP').
|
||||
$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
$fw->hash(uniqid());
|
||||
$tmp=$fw->get('TEMP').$fw->get('SEED').'.'.$fw->hash(uniqid());
|
||||
if (!$fw->get('RAW'))
|
||||
$fw->write($tmp,$fw->get('BODY'));
|
||||
else {
|
||||
@@ -188,7 +192,7 @@ class Web extends Prefab {
|
||||
fclose($src);
|
||||
}
|
||||
$base=basename($fw->get('URI'));
|
||||
$file=array(
|
||||
$file=[
|
||||
'name'=>$dir.
|
||||
($slug && preg_match('/(.+?)(\.\w+)?$/',$base,$parts)?
|
||||
(is_callable($slug)?
|
||||
@@ -199,24 +203,24 @@ class Web extends Prefab {
|
||||
'tmp_name'=>$tmp,
|
||||
'type'=>$this->mime($base),
|
||||
'size'=>filesize($tmp)
|
||||
);
|
||||
];
|
||||
return (!file_exists($file['name']) || $overwrite) &&
|
||||
(!$func || $fw->call($func,array($file))!==FALSE) &&
|
||||
(!$func || $fw->call($func,[$file])!==FALSE) &&
|
||||
rename($tmp,$file['name']);
|
||||
}
|
||||
$fetch=function($arr)use(&$fetch){
|
||||
$fetch=function($arr) use(&$fetch) {
|
||||
if (!is_array($arr))
|
||||
return array($arr);
|
||||
$data=array();
|
||||
return [$arr];
|
||||
$data=[];
|
||||
foreach($arr as $k=>$sub)
|
||||
$data=array_merge($data,$fetch($sub));
|
||||
return $data;
|
||||
};
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($_FILES as $name=>$item) {
|
||||
$files=array();
|
||||
foreach($item as $k=>$mix)
|
||||
foreach($fetch($mix) as $i=>$val)
|
||||
$files=[];
|
||||
foreach ($item as $k=>$mix)
|
||||
foreach ($fetch($mix) as $i=>$val)
|
||||
$files[$i][$k]=$val;
|
||||
foreach ($files as $file) {
|
||||
if (empty($file['name']))
|
||||
@@ -230,9 +234,8 @@ class Web extends Prefab {
|
||||
(isset($parts[2])?$parts[2]:''))):
|
||||
$base);
|
||||
$out[$file['name']]=!$file['error'] &&
|
||||
is_uploaded_file($file['tmp_name']) &&
|
||||
(!file_exists($file['name']) || $overwrite) &&
|
||||
(!$func || $fw->call($func,array($file,$name))!==FALSE) &&
|
||||
(!$func || $fw->call($func,[$file,$name])!==FALSE) &&
|
||||
move_uploaded_file($file['tmp_name'],$file['name']);
|
||||
}
|
||||
}
|
||||
@@ -277,7 +280,7 @@ class Web extends Prefab {
|
||||
ini_get('default_socket_timeout');
|
||||
curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,$timeout);
|
||||
curl_setopt($curl,CURLOPT_TIMEOUT,$timeout);
|
||||
$headers=array();
|
||||
$headers=[];
|
||||
curl_setopt($curl,CURLOPT_HEADERFUNCTION,
|
||||
// Callback for response headers
|
||||
function($curl,$line) use(&$headers) {
|
||||
@@ -286,22 +289,32 @@ class Web extends Prefab {
|
||||
return strlen($line);
|
||||
}
|
||||
);
|
||||
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,2);
|
||||
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,FALSE);
|
||||
ob_start();
|
||||
curl_exec($curl);
|
||||
$err=curl_error($curl);
|
||||
curl_close($curl);
|
||||
$body=ob_get_clean();
|
||||
if ($options['follow_location'] &&
|
||||
if (!$err &&
|
||||
$options['follow_location'] &&
|
||||
preg_match('/^Location: (.+)$/m',implode(PHP_EOL,$headers),$loc)) {
|
||||
$options['max_redirects']--;
|
||||
if($loc[1][0] == '/') {
|
||||
$parts=parse_url($url);
|
||||
$loc[1]=$parts['scheme'].'://'.$parts['host'].
|
||||
((isset($parts['port']) && !in_array($parts['port'],[80,443]))
|
||||
?':'.$parts['port']:'').$loc[1];
|
||||
}
|
||||
return $this->request($loc[1],$options);
|
||||
}
|
||||
return array(
|
||||
return [
|
||||
'body'=>$body,
|
||||
'headers'=>$headers,
|
||||
'engine'=>'cURL',
|
||||
'cached'=>FALSE
|
||||
);
|
||||
'cached'=>FALSE,
|
||||
'error'=>$err
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,28 +327,36 @@ class Web extends Prefab {
|
||||
$eol="\r\n";
|
||||
$options['header']=implode($eol,$options['header']);
|
||||
$body=@file_get_contents($url,FALSE,
|
||||
stream_context_create(array('http'=>$options)));
|
||||
stream_context_create(['http'=>$options]));
|
||||
$headers=isset($http_response_header)?
|
||||
$http_response_header:array();
|
||||
$match=NULL;
|
||||
foreach ($headers as $header)
|
||||
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
|
||||
break;
|
||||
if ($match)
|
||||
switch ($match[1]) {
|
||||
case 'gzip':
|
||||
$body=gzdecode($body);
|
||||
$http_response_header:[];
|
||||
$err='';
|
||||
if (is_string($body)) {
|
||||
$match=NULL;
|
||||
foreach ($headers as $header)
|
||||
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
|
||||
break;
|
||||
case 'deflate':
|
||||
$body=gzuncompress($body);
|
||||
break;
|
||||
}
|
||||
return array(
|
||||
if ($match)
|
||||
switch ($match[1]) {
|
||||
case 'gzip':
|
||||
$body=gzdecode($body);
|
||||
break;
|
||||
case 'deflate':
|
||||
$body=gzuncompress($body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$tmp=error_get_last();
|
||||
$err=$tmp['message'];
|
||||
}
|
||||
return [
|
||||
'body'=>$body,
|
||||
'headers'=>$headers,
|
||||
'engine'=>'stream',
|
||||
'cached'=>FALSE
|
||||
);
|
||||
'cached'=>FALSE,
|
||||
'error'=>$err
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +367,7 @@ class Web extends Prefab {
|
||||
**/
|
||||
protected function _socket($url,$options) {
|
||||
$eol="\r\n";
|
||||
$headers=array();
|
||||
$headers=[];
|
||||
$body='';
|
||||
$parts=parse_url($url);
|
||||
$empty=empty($parts['port']);
|
||||
@@ -361,54 +382,54 @@ class Web extends Prefab {
|
||||
$parts['path']='/';
|
||||
if (empty($parts['query']))
|
||||
$parts['query']='';
|
||||
$socket=@fsockopen($parts['host'],$parts['port']);
|
||||
if (!$socket)
|
||||
return FALSE;
|
||||
stream_set_blocking($socket,TRUE);
|
||||
stream_set_timeout($socket,isset($options['timeout'])?
|
||||
$options['timeout']:ini_get('default_socket_timeout'));
|
||||
fputs($socket,$options['method'].' '.$parts['path'].
|
||||
($parts['query']?('?'.$parts['query']):'').' HTTP/1.0'.$eol
|
||||
);
|
||||
fputs($socket,implode($eol,$options['header']).$eol.$eol);
|
||||
if (isset($options['content']))
|
||||
fputs($socket,$options['content'].$eol);
|
||||
// Get response
|
||||
$content='';
|
||||
while (!feof($socket) &&
|
||||
($info=stream_get_meta_data($socket)) &&
|
||||
!$info['timed_out'] && !connection_aborted() &&
|
||||
$str=fgets($socket,4096))
|
||||
$content.=$str;
|
||||
fclose($socket);
|
||||
$html=explode($eol.$eol,$content,2);
|
||||
$body=isset($html[1])?$html[1]:'';
|
||||
$headers=array_merge($headers,$current=explode($eol,$html[0]));
|
||||
$match=NULL;
|
||||
foreach ($current as $header)
|
||||
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
|
||||
break;
|
||||
if ($match)
|
||||
switch ($match[1]) {
|
||||
case 'gzip':
|
||||
$body=gzdecode($body);
|
||||
break;
|
||||
case 'deflate':
|
||||
$body=gzuncompress($body);
|
||||
if ($socket=@fsockopen($parts['host'],$parts['port'],$code,$err)) {
|
||||
stream_set_blocking($socket,TRUE);
|
||||
stream_set_timeout($socket,isset($options['timeout'])?
|
||||
$options['timeout']:ini_get('default_socket_timeout'));
|
||||
fputs($socket,$options['method'].' '.$parts['path'].
|
||||
($parts['query']?('?'.$parts['query']):'').' HTTP/1.0'.$eol
|
||||
);
|
||||
fputs($socket,implode($eol,$options['header']).$eol.$eol);
|
||||
if (isset($options['content']))
|
||||
fputs($socket,$options['content'].$eol);
|
||||
// Get response
|
||||
$content='';
|
||||
while (!feof($socket) &&
|
||||
($info=stream_get_meta_data($socket)) &&
|
||||
!$info['timed_out'] && !connection_aborted() &&
|
||||
$str=fgets($socket,4096))
|
||||
$content.=$str;
|
||||
fclose($socket);
|
||||
$html=explode($eol.$eol,$content,2);
|
||||
$body=isset($html[1])?$html[1]:'';
|
||||
$headers=array_merge($headers,$current=explode($eol,$html[0]));
|
||||
$match=NULL;
|
||||
foreach ($current as $header)
|
||||
if (preg_match('/Content-Encoding: (.+)/',$header,$match))
|
||||
break;
|
||||
if ($match)
|
||||
switch ($match[1]) {
|
||||
case 'gzip':
|
||||
$body=gzdecode($body);
|
||||
break;
|
||||
case 'deflate':
|
||||
$body=gzuncompress($body);
|
||||
break;
|
||||
}
|
||||
if ($options['follow_location'] &&
|
||||
preg_match('/Location: (.+?)'.preg_quote($eol).'/',
|
||||
$html[0],$loc)) {
|
||||
$options['max_redirects']--;
|
||||
return $this->request($loc[1],$options);
|
||||
}
|
||||
if ($options['follow_location'] &&
|
||||
preg_match('/Location: (.+?)'.preg_quote($eol).'/',
|
||||
$html[0],$loc)) {
|
||||
$options['max_redirects']--;
|
||||
return $this->request($loc[1],$options);
|
||||
}
|
||||
return array(
|
||||
return [
|
||||
'body'=>$body,
|
||||
'headers'=>$headers,
|
||||
'engine'=>'socket',
|
||||
'cached'=>FALSE
|
||||
);
|
||||
'cached'=>FALSE,
|
||||
'error'=>$err
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,17 +440,17 @@ class Web extends Prefab {
|
||||
**/
|
||||
function engine($arg='curl') {
|
||||
$arg=strtolower($arg);
|
||||
$flags=array(
|
||||
$flags=[
|
||||
'curl'=>extension_loaded('curl'),
|
||||
'stream'=>ini_get('allow_url_fopen'),
|
||||
'socket'=>function_exists('fsockopen')
|
||||
);
|
||||
];
|
||||
if ($flags[$arg])
|
||||
return $this->wrapper=$arg;
|
||||
foreach ($flags as $key=>$val)
|
||||
if ($val)
|
||||
return $this->wrapper=$key;
|
||||
user_error(E_Request,E_USER_ERROR);
|
||||
user_error(self::E_Request,E_USER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,7 +461,7 @@ class Web extends Prefab {
|
||||
**/
|
||||
function subst(array &$old,$new) {
|
||||
if (is_string($new))
|
||||
$new=array($new);
|
||||
$new=[$new];
|
||||
foreach ($new as $hdr) {
|
||||
$old=preg_grep('/'.preg_quote(strstr($hdr,':',TRUE),'/').':.+/',
|
||||
$old,PREG_GREP_INVERT);
|
||||
@@ -469,11 +490,11 @@ class Web extends Prefab {
|
||||
elseif (!preg_match('/https?/',$parts['scheme']))
|
||||
return FALSE;
|
||||
if (!is_array($options))
|
||||
$options=array();
|
||||
$options=[];
|
||||
if (empty($options['header']))
|
||||
$options['header']=array();
|
||||
$options['header']=[];
|
||||
elseif (is_string($options['header']))
|
||||
$options['header']=array($options['header']);
|
||||
$options['header']=[$options['header']];
|
||||
if (!$this->wrapper)
|
||||
$this->engine();
|
||||
if ($this->wrapper!='stream') {
|
||||
@@ -487,13 +508,13 @@ class Web extends Prefab {
|
||||
$this->subst($options['header'],'Host: '.$parts['host']);
|
||||
}
|
||||
$this->subst($options['header'],
|
||||
array(
|
||||
[
|
||||
'Accept-Encoding: gzip,deflate',
|
||||
'User-Agent: '.(isset($options['user_agent'])?
|
||||
$options['user_agent']:
|
||||
'Mozilla/5.0 (compatible; '.php_uname('s').')'),
|
||||
'Connection: close'
|
||||
)
|
||||
]
|
||||
);
|
||||
if (isset($options['content']) && is_string($options['content'])) {
|
||||
if ($options['method']=='POST' &&
|
||||
@@ -508,13 +529,13 @@ class Web extends Prefab {
|
||||
'Authorization: Basic '.
|
||||
base64_encode($parts['user'].':'.$parts['pass'])
|
||||
);
|
||||
$options+=array(
|
||||
$options+=[
|
||||
'method'=>'GET',
|
||||
'header'=>$options['header'],
|
||||
'follow_location'=>TRUE,
|
||||
'max_redirects'=>20,
|
||||
'ignore_errors'=>FALSE
|
||||
);
|
||||
];
|
||||
$eol="\r\n";
|
||||
if ($fw->get('CACHE') &&
|
||||
preg_match('/GET|HEAD/',$options['method'])) {
|
||||
@@ -534,11 +555,14 @@ class Web extends Prefab {
|
||||
$result=$cache->get($hash);
|
||||
$result['cached']=TRUE;
|
||||
}
|
||||
elseif (preg_match('/Cache-Control: max-age=(.+?)'.
|
||||
preg_quote($eol).'/',implode($eol,$result['headers']),$exp))
|
||||
elseif (preg_match('/Cache-Control:(?:.*)max-age=(\d+)(?:,?.*'.
|
||||
preg_quote($eol).')/',implode($eol,$result['headers']),$exp))
|
||||
$cache->set($hash,$result,$exp[1]);
|
||||
}
|
||||
return $result;
|
||||
$req=[$options['method'].' '.$url];
|
||||
foreach ($options['header'] as $header)
|
||||
array_push($req,$header);
|
||||
return array_merge(['request'=>$req],$result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,7 +587,9 @@ class Web extends Prefab {
|
||||
$path=$fw->get('UI').';./';
|
||||
foreach ($fw->split($path,FALSE) as $dir)
|
||||
foreach ($files as $file)
|
||||
if (is_file($save=$fw->fixslashes($dir.$file))) {
|
||||
if (is_file($save=$fw->fixslashes($dir.$file)) &&
|
||||
is_bool(strpos($save,'../')) &&
|
||||
preg_match('/\.(css|js)$/i',$file)) {
|
||||
if ($fw->get('CACHE') &&
|
||||
($cached=$cache->exists(
|
||||
$hash=$fw->hash($save).'.'.$ext[0],$data)) &&
|
||||
@@ -637,7 +663,7 @@ class Web extends Prefab {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (in_array($src[$ptr],array('\'','"'))) {
|
||||
if (in_array($src[$ptr],['\'','"'])) {
|
||||
$match=$src[$ptr];
|
||||
$data.=$match;
|
||||
$ptr++;
|
||||
@@ -693,14 +719,14 @@ class Web extends Prefab {
|
||||
NULL,LIBXML_NOBLANKS|LIBXML_NOERROR);
|
||||
if (!is_object($xml))
|
||||
return FALSE;
|
||||
$out=array();
|
||||
$out=[];
|
||||
if (isset($xml->channel)) {
|
||||
$out['source']=(string)$xml->channel->title;
|
||||
$max=min($max,count($xml->channel->item));
|
||||
for ($i=0;$i<$max;$i++) {
|
||||
$item=$xml->channel->item[$i];
|
||||
$list=array(''=>NULL)+$item->getnamespaces(TRUE);
|
||||
$fields=array();
|
||||
$list=[''=>NULL]+$item->getnamespaces(TRUE);
|
||||
$fields=[];
|
||||
foreach ($list as $ns=>$uri)
|
||||
foreach ($item->children($uri) as $key=>$val)
|
||||
$fields[$ns.($ns?':':'').$key]=(string)$val;
|
||||
@@ -748,7 +774,7 @@ class Web extends Prefab {
|
||||
function slug($text) {
|
||||
return trim(strtolower(preg_replace('/([^\pL\pN])+/u','-',
|
||||
trim(strtr(str_replace('\'','',$text),
|
||||
array(
|
||||
[
|
||||
'Ǎ'=>'A','А'=>'A','Ā'=>'A','Ă'=>'A','Ą'=>'A','Å'=>'A',
|
||||
'Ǻ'=>'A','Ä'=>'Ae','Á'=>'A','À'=>'A','Ã'=>'A','Â'=>'A',
|
||||
'Æ'=>'AE','Ǽ'=>'AE','Б'=>'B','Ç'=>'C','Ć'=>'C','Ĉ'=>'C',
|
||||
@@ -796,7 +822,7 @@ class Web extends Prefab {
|
||||
'ǜ'=>'u','ǔ'=>'u','ǖ'=>'u','ũ'=>'u','ü'=>'ue','в'=>'v',
|
||||
'ŵ'=>'w','ы'=>'y','ÿ'=>'y','ý'=>'y','ŷ'=>'y','ź'=>'z',
|
||||
'ž'=>'z','з'=>'z','ż'=>'z','ж'=>'zh','ь'=>'','ъ'=>''
|
||||
)+Base::instance()->get('DIACRITICS'))))),'-');
|
||||
]+Base::instance()->get('DIACRITICS'))))),'-');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -850,8 +876,7 @@ if (!function_exists('gzdecode')) {
|
||||
$fw=Base::instance();
|
||||
if (!is_dir($tmp=$fw->get('TEMP')))
|
||||
mkdir($tmp,Base::MODE,TRUE);
|
||||
file_put_contents($file=$tmp.'/'.
|
||||
$fw->hash($fw->get('ROOT').$fw->get('BASE')).'.'.
|
||||
file_put_contents($file=$tmp.'/'.$fw->get('SEED').'.'.
|
||||
$fw->hash(uniqid(NULL,TRUE)).'.gz',$str,LOCK_EX);
|
||||
ob_start();
|
||||
readgzfile($file);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -34,14 +34,14 @@ class Geo extends \Prefab {
|
||||
$ref=new \DateTimeZone($zone);
|
||||
$loc=$ref->getLocation();
|
||||
$trn=$ref->getTransitions($now=time(),$now);
|
||||
$out=array(
|
||||
$out=[
|
||||
'offset'=>$ref->
|
||||
getOffset(new \DateTime('now',new \DateTimeZone('GMT')))/3600,
|
||||
getOffset(new \DateTime('now',new \DateTimeZone('UTC')))/3600,
|
||||
'country'=>$loc['country_code'],
|
||||
'latitude'=>$loc['latitude'],
|
||||
'longitude'=>$loc['longitude'],
|
||||
'dst'=>$trn[0]['isdst']
|
||||
);
|
||||
];
|
||||
unset($ref);
|
||||
return $out;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class Geo extends \Prefab {
|
||||
if (($req=$web->request('http://www.geoplugin.net/json.gp'.
|
||||
($public?('?ip='.$ip):''))) &&
|
||||
$data=json_decode($req['body'],TRUE)) {
|
||||
$out=array();
|
||||
$out=[];
|
||||
foreach ($data as $key=>$val)
|
||||
if (!strpos($key,'currency') && $key!=='geoplugin_status'
|
||||
&& $key!=='geoplugin_region')
|
||||
@@ -87,17 +87,17 @@ class Geo extends \Prefab {
|
||||
* @return array|FALSE
|
||||
* @param $latitude float
|
||||
* @param $longitude float
|
||||
* @param $key string
|
||||
**/
|
||||
function weather($latitude,$longitude) {
|
||||
function weather($latitude,$longitude,$key) {
|
||||
$fw=\Base::instance();
|
||||
$web=\Web::instance();
|
||||
$query=array(
|
||||
$query=[
|
||||
'lat'=>$latitude,
|
||||
'lon'=>$longitude
|
||||
);
|
||||
$req=$web->request(
|
||||
'http://api.openweathermap.org/data/2.5/weather?'.
|
||||
http_build_query($query));
|
||||
'lon'=>$longitude,
|
||||
'APPID'=>$key,
|
||||
'units'=>'metric'
|
||||
];
|
||||
return ($req=$web->request(
|
||||
'http://api.openweathermap.org/data/2.5/weather?'.
|
||||
http_build_query($query)))?
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
|
||||
134
app/lib/web/oauth2.php
Normal file
134
app/lib/web/oauth2.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
This is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or later.
|
||||
|
||||
Fat-Free Framework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
namespace Web;
|
||||
|
||||
//! Lightweight OAuth2 client
|
||||
class OAuth2 extends \Magic {
|
||||
|
||||
protected
|
||||
//! Scopes and claims
|
||||
$args=[];
|
||||
|
||||
/**
|
||||
* Return OAuth2 authentication URI
|
||||
* @return string
|
||||
* @param $endpoint string
|
||||
**/
|
||||
function uri($endpoint) {
|
||||
return $endpoint.'?'.http_build_query($this->args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to API/token endpoint
|
||||
* @return string|FALSE
|
||||
* @param $uri string
|
||||
* @param $method string
|
||||
* @param $token array
|
||||
**/
|
||||
function request($uri,$method,$token=NULL) {
|
||||
$web=\Web::instance();
|
||||
$options=[
|
||||
'method'=>$method,
|
||||
'content'=>http_build_query($this->args),
|
||||
'header'=>['Accept: application/json']
|
||||
];
|
||||
if ($token)
|
||||
array_push($options['header'],'Authorization: Bearer '.$token);
|
||||
elseif ($method=='POST')
|
||||
array_push($options['header'],'Authorization: Basic '.
|
||||
base64_encode(
|
||||
$this->args['client_id'].':'.
|
||||
$this->args['client_secret']
|
||||
)
|
||||
);
|
||||
$response=$web->request($uri,$options);
|
||||
return $response['body'] &&
|
||||
preg_grep('/HTTP\/1\.\d 200/',$response['headers'])?
|
||||
json_decode($response['body'],TRUE):
|
||||
FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse JSON Web token
|
||||
* @return array
|
||||
* @param $token string
|
||||
**/
|
||||
function jwt($token) {
|
||||
return json_decode(
|
||||
base64_decode(
|
||||
str_replace(
|
||||
['-','_'],
|
||||
['+','/'],
|
||||
explode('.',$token)[1]
|
||||
)
|
||||
),
|
||||
TRUE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return TRUE if scope/claim exists
|
||||
* @return bool
|
||||
* @param $key string
|
||||
**/
|
||||
function exists($key) {
|
||||
return isset($this->args[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind value to scope/claim
|
||||
* @return string
|
||||
* @param $key string
|
||||
* @param $val string
|
||||
**/
|
||||
function set($key,$val) {
|
||||
return $this->args[$key]=$val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return value of scope/claim
|
||||
* @return mixed
|
||||
* @param $key string
|
||||
**/
|
||||
function &get($key) {
|
||||
if (isset($this->args[$key]))
|
||||
$val=&$this->args[$key];
|
||||
else
|
||||
$val=NULL;
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove scope/claim
|
||||
* @return NULL
|
||||
* @param $key
|
||||
**/
|
||||
function clear($key=NULL) {
|
||||
if ($key)
|
||||
unset($this->args[$key]);
|
||||
else
|
||||
$this->args=[];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -29,7 +29,7 @@ class OpenID extends \Magic {
|
||||
//! OpenID provider endpoint URL
|
||||
$url,
|
||||
//! HTTP request parameters
|
||||
$args=array();
|
||||
$args=[];
|
||||
|
||||
/**
|
||||
* Determine OpenID provider
|
||||
@@ -38,11 +38,11 @@ class OpenID extends \Magic {
|
||||
**/
|
||||
protected function discover($proxy) {
|
||||
// Normalize
|
||||
if (!preg_match('/https?:\/\//i',$this->args['identity']))
|
||||
$this->args['identity']='http://'.$this->args['identity'];
|
||||
$url=parse_url($this->args['identity']);
|
||||
if (!preg_match('/https?:\/\//i',$this->args['endpoint']))
|
||||
$this->args['endpoint']='http://'.$this->args['endpoint'];
|
||||
$url=parse_url($this->args['endpoint']);
|
||||
// Remove fragment; reconnect parts
|
||||
$this->args['identity']=$url['scheme'].'://'.
|
||||
$this->args['endpoint']=$url['scheme'].'://'.
|
||||
(isset($url['user'])?
|
||||
($url['user'].
|
||||
(isset($url['pass'])?(':'.$url['pass']):'').'@'):'').
|
||||
@@ -50,7 +50,7 @@ class OpenID extends \Magic {
|
||||
(isset($url['query'])?('?'.$url['query']):'');
|
||||
// HTML-based discovery of OpenID provider
|
||||
$req=\Web::instance()->
|
||||
request($this->args['identity'],array('proxy'=>$proxy));
|
||||
request($this->args['endpoint'],['proxy'=>$proxy]);
|
||||
if (!$req)
|
||||
return FALSE;
|
||||
$type=array_values(preg_grep('/Content-Type:/',$req['headers']));
|
||||
@@ -63,8 +63,9 @@ class OpenID extends \Magic {
|
||||
$svc=$xrds['XRD']['Service'];
|
||||
if (isset($svc[0]))
|
||||
$svc=$svc[0];
|
||||
$svc_type=is_array($svc['Type'])?$svc['Type']:array($svc['Type']);
|
||||
if (preg_grep('/http:\/\/specs\.openid\.net\/auth\/2.0\/'.
|
||||
'(?:server|signon)/',$svc['Type'])) {
|
||||
'(?:server|signon)/',$svc_type)) {
|
||||
$this->args['provider']=$svc['URI'];
|
||||
if (isset($svc['LocalID']))
|
||||
$this->args['localidentity']=$svc['LocalID'];
|
||||
@@ -89,7 +90,7 @@ class OpenID extends \Magic {
|
||||
preg_match_all('/\b(rel|href)\h*=\h*'.
|
||||
'(?:"(.+?)"|\'(.+?)\')/s',$parts[1],$attr,
|
||||
PREG_SET_ORDER)) {
|
||||
$node=array();
|
||||
$node=[];
|
||||
foreach ($attr as $kv)
|
||||
$node[$kv[1]]=isset($kv[2])?$kv[2]:$kv[3];
|
||||
if (isset($node['rel']) &&
|
||||
@@ -140,7 +141,7 @@ class OpenID extends \Magic {
|
||||
* @param $attr array
|
||||
* @param $reqd string|array
|
||||
**/
|
||||
function auth($proxy=NULL,$attr=array(),array $reqd=NULL) {
|
||||
function auth($proxy=NULL,$attr=[],array $reqd=NULL) {
|
||||
$fw=\Base::instance();
|
||||
$root=$fw->get('SCHEME').'://'.$fw->get('HOST');
|
||||
if (empty($this->args['trust_root']))
|
||||
@@ -157,7 +158,7 @@ class OpenID extends \Magic {
|
||||
$this->args['ax.required']=is_string($reqd)?
|
||||
$reqd:implode(',',$reqd);
|
||||
}
|
||||
$var=array();
|
||||
$var=[];
|
||||
foreach ($this->args as $key=>$val)
|
||||
$var['openid.'.$key]=$val;
|
||||
$fw->reroute($this->url.'?'.http_build_query($var));
|
||||
@@ -179,16 +180,16 @@ class OpenID extends \Magic {
|
||||
$this->args['mode']!='error' &&
|
||||
$this->url=$this->discover($proxy)) {
|
||||
$this->args['mode']='check_authentication';
|
||||
$var=array();
|
||||
$var=[];
|
||||
foreach ($this->args as $key=>$val)
|
||||
$var['openid.'.$key]=$val;
|
||||
$req=\Web::instance()->request(
|
||||
$this->url,
|
||||
array(
|
||||
[
|
||||
'method'=>'POST',
|
||||
'content'=>http_build_query($var),
|
||||
'proxy'=>$proxy
|
||||
)
|
||||
]
|
||||
);
|
||||
return (bool)preg_match('/is_valid:true/i',$req['body']);
|
||||
}
|
||||
@@ -245,4 +246,3 @@ class OpenID extends \Magic {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2009-2015 F3::Factory/Bong Cosca, All rights reserved.
|
||||
Copyright (c) 2009-2016 F3::Factory/Bong Cosca, All rights reserved.
|
||||
|
||||
This file is part of the Fat-Free Framework (http://fatfreeframework.com).
|
||||
|
||||
@@ -38,7 +38,7 @@ class Pingback extends \Prefab {
|
||||
$web=\Web::instance();
|
||||
$req=$web->request($url);
|
||||
$found=FALSE;
|
||||
if ($req && $req['body']) {
|
||||
if ($req['body']) {
|
||||
// Look for pingback header
|
||||
foreach ($req['headers'] as $header)
|
||||
if (preg_match('/^X-Pingback:\h*(.+)/',$header,$href)) {
|
||||
@@ -71,7 +71,7 @@ class Pingback extends \Prefab {
|
||||
$doc=new \DOMDocument('1.0',$fw->get('ENCODING'));
|
||||
$doc->stricterrorchecking=FALSE;
|
||||
$doc->recover=TRUE;
|
||||
if ($req && @$doc->loadhtml($req['body'])) {
|
||||
if (@$doc->loadhtml($req['body'])) {
|
||||
// Parse anchor tags
|
||||
$links=$doc->getelementsbytagname('a');
|
||||
foreach ($links as $link) {
|
||||
@@ -79,17 +79,17 @@ class Pingback extends \Prefab {
|
||||
// Find pingback-enabled resources
|
||||
if ($permalink && $found=$this->enabled($permalink)) {
|
||||
$req=$web->request($found,
|
||||
array(
|
||||
[
|
||||
'method'=>'POST',
|
||||
'header'=>'Content-Type: application/xml',
|
||||
'content'=>xmlrpc_encode_request(
|
||||
'pingback.ping',
|
||||
array($source,$permalink),
|
||||
array('encoding'=>$fw->get('ENCODING'))
|
||||
[$source,$permalink],
|
||||
['encoding'=>$fw->get('ENCODING')]
|
||||
)
|
||||
)
|
||||
]
|
||||
);
|
||||
if ($req && $req['body'])
|
||||
if ($req['body'])
|
||||
$this->log.=date('r').' '.
|
||||
$permalink.' [permalink:'.$found.']'.PHP_EOL.
|
||||
$req['body'].PHP_EOL;
|
||||
@@ -118,7 +118,7 @@ class Pingback extends \Prefab {
|
||||
$path=$fw->get('BASE');
|
||||
$web=\Web::instance();
|
||||
$args=xmlrpc_decode_request($fw->get('BODY'),$method,$charset);
|
||||
$options=array('encoding'=>$charset);
|
||||
$options=['encoding'=>$charset];
|
||||
if ($method=='pingback.ping' && isset($args[0],$args[1])) {
|
||||
list($source,$permalink)=$args;
|
||||
$doc=new \DOMDocument('1.0',$fw->get('ENCODING'));
|
||||
@@ -138,8 +138,7 @@ class Pingback extends \Prefab {
|
||||
$links=$doc->getelementsbytagname('a');
|
||||
foreach ($links as $link) {
|
||||
if ($link->getattribute('href')==$permalink) {
|
||||
call_user_func_array($func,
|
||||
array($source,$req['body']));
|
||||
call_user_func_array($func,[$source,$req['body']]);
|
||||
// Success
|
||||
die(xmlrpc_encode_request(NULL,$source,$options));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user