- PHP framework upgrade 3.5.1 -> 3.6.0

This commit is contained in:
Exodus4D
2016-11-26 23:11:30 +01:00
parent c88e9e8c0f
commit 03c1d53fd6
34 changed files with 1949 additions and 881 deletions

View File

@@ -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

View 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).
@@ -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';

View 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).
@@ -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]:[])
)
)
]
);
}

File diff suppressed because it is too large Load Diff

View 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,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();
}

View File

@@ -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
View 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);
}
}

View 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).
@@ -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;
}

View 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).
@@ -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

View 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).
@@ -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();
}

View 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).
@@ -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']:'';

View 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).
@@ -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);
}

View 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).
@@ -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();
}

View 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).
@@ -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']:'';

View 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).
@@ -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);
}

View 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).
@@ -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;

View 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).
@@ -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']:'';

View 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).
@@ -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);
}
}

View 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).
@@ -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))

View 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).

View 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).
@@ -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);
}
/**

View 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).
@@ -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=[
'...'=>'&hellip;',
'(tm)'=>'&trade;',
'(r)'=>'&reg;',
'(c)'=>'&copy;'
);
];
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;

View 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).
@@ -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;
}

View 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).
@@ -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']:'';

View 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).
@@ -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;

View 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).
@@ -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(

View 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).
@@ -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()->

View 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).
@@ -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));
}

View 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).
@@ -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);

View 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)))?

View 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).

134
app/lib/web/oauth2.php Normal file
View 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=[];
}
}

View 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).
@@ -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 {
}
}

View 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).
@@ -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));
}