Core.php
<?php
/**
* Bundle All Core Classes
* Core; Controller; Event; FxFilter
*/
namespace PHN\Core {
class Core{
public $cfg;
public $loader;
public $event; //system events
public $fxFilter; //function filters
public $plugins;
public $appName='Main'; //as default
public $controller;
public $controllerName='Index';
public $controllerAction='index';
public $callbacks;
public $errors=array(); //error logs
public $logs=array('queries'=>0,'queries_fail'=>0);//debug logs
/**
* allowed dynamic functions addon
* ex: $object->func = function($format) use ($object) {
echo date($format, $object->date);
};
*/
function __call($name, $arguments)
{
if (isset($this->$name) && $this->$name instanceof Closure) {
return call_user_func_array($this->$name, $arguments);
}
}
function __construct($cfg=null) {
global $_CFG,$Loader;
$this->loader = &$Loader ;
if (!empty($cfg)) $this->cfg = &$cfg;
else $this->cfg = &$_CFG;
$this->event = new EventDispatcher();
$this->fxFilter = new FxFilter();
$this->_initCallback();
}
private function _initCallback()
{
//set output gzip
if (isset($this->cfg['output_gzip']) && $this->cfg['output_gzip']) ob_start("ob_gzhandler");
//call when 404 encountered
$this->event->addListener('404', array($this,'__error404'));
//ip tracking to inform DOS attack
//$this->event->addListener('IP_TRACKING', array($this,'__iptracking'));
// register_shutdown_function(function(){
// //if (isset( $this->cfg['debug_verbose']) && $this->cfg['debug_verbose'] ) echo "<h2>shut down signal</h2>";
// //do batch shutdown event
// $this->event->dispatch('CLEANUP_USER_SESSION');
// $this->event->dispatch('UPDATE_USER_SESSION');
// $this->event->dispatch('IP_TRACKING');
// $this->event->dispatch('SHUTDOWN');
// });
register_shutdown_function(array($this,'__shutdown'));
//error custom callback
// nếu debug_mode != 1 ( NORMAL ) thì toàn bộ warning, exception sẽ được log riêng và trình bày cuối trang
// hoặc ghi ra console, file , email ...
if (isset($this->cfg['debug_mode']) && $this->cfg['debug_mode'] !=1)
{
set_error_handler(array($this,'__errorHandler'));
set_exception_handler(array($this,'__exceptionHandler'));
}
}
// <editor-fold desc="Callback Methods" >
public function __error404(){
//echo "<h1>Error 404 Resource Not Found</h1><br/>";
header('HTTP/1.0 404 Not Found');
}
public function __shutdown(){
$this->event->dispatch('CLEANUP_USER_SESSION');
$this->event->dispatch('UPDATE_USER_SESSION');
$this->event->dispatch('IP_TRACKING');
$this->event->dispatch('SHUTDOWN');
if (isset( $this->cfg['debug_mode']) && $this->cfg['debug_mode'] )
{
if (isset( $this->cfg['debug_benchmark']) && $this->cfg['debug_benchmark'])
{
$execute_s = (microtime(true) - START_TIME)* 1000 ;
$this->logs['benchmark'] = array (
"<b>Execute (s) :</b> " . $execute_s ,
"<b>Memory consume : </b>" . memory_get_usage(1) . ' bytes',
"<b>Peak memory consume : </b>" .memory_get_peak_usage (1) . ' bytes'
);
}
echo "<hr style='clear:both'><b>Debug Information</b><br>";
if (!empty($this->logs['benchmark'] ))
{
echo "<p style=\"margin-left:8px\"><u>Benchmark </u>:<br>";
echo implode('<br>',$this->logs['benchmark'] );
echo "</p>";
}
if (!empty($this->logs['error'] ))
{
echo "<p style=\"margin-left:8px\"><u>Runtime Errors : </u>:<br>";
echo implode('<br>',$this->logs['error'] );
echo "</p>";
}
if (!empty($this->logs['db_server_error'] ))
{
echo "<p style=\"margin-left:8px\"><u>Database Server Error </u>:<br>";
echo "<b>Detail :</b> <div style='margin-left:20px;' >";
echo implode('<br>',$this->logs['db'] );
echo "</div></p>";
}
if (!empty($this->logs['queries'] ) && $this->cfg['debug_db'])
{
echo "<p style=\"margin-left:8px\"><u>Database </u>:<br>";
echo "<b>Database connections :</b> " . count(\PHN\Model\Model::$dboCache) . "<br>";
echo "<b>Queries :</b> " . $this->logs['queries'] . "<br>";
if ($this->logs['queries_fail']) echo "<b style='color:red'>Queries Failed :</b> " . $this->logs['queries_fail'] . "<br>";
if ($this->cfg['debug_db_dump']) {
echo "<b>Queries Detail :</b> <div style='margin-left:20px;' >";
echo implode('<br>',$this->logs['db'] );
}
echo "</div></p>";
}
}
}
public function __errorHandler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno)) {
// This error code is not included in error_reporting
return;
}
$e = new \ErrorException($errstr,$errno,0,$errfile,$errline);
$this->__exceptionHandler($e);
/* Don't execute PHP internal error handler */
return true;
}
public function __exceptionHandler($e){
/*
* final public string Exception::getMessage ( void )
final public Exception Exception::getPrevious ( void )
final public mixed Exception::getCode ( void )
final public string Exception::getFile ( void )
final public int Exception::getLine ( void )
final public array Exception::getTrace ( void )
final public string Exception::getTraceAsString ( void )
public string Exception::__toString ( void )
final private void Exception::__clone ( void )
*/
$errno= $e->getCode();
$errstr= $e->getMessage();
$errfile= $e->getFile();
$errline=$e->getLine();
$traces = $e->getTrace();
if (!isset($this->logs['error'])) $this->logs['error']=array();
$message = &$this->logs['error'];
switch ($errno) {
case E_USER_ERROR:
$message[]= "<b>Error</b> [$errno] $errstr on line $errline in file $errfile";
break;
case E_USER_WARNING:
$message[]= "<b>Warning</b> [$errno] $errstr on line $errline in file $errfile";
break;
case E_USER_NOTICE:
$message[]= "<b>Notice</b> [$errno] $errstr on line $errline in file $errfile";
break;
default:
$message[]= "<b>Warning</b> <span style='color:red'>[$errno] $errstr </span>on line $errline in file $errfile";
break;
}
}
// </editor-fold >
}
class Controller{
public $app ;
/**
* shortcut to global &$_CFG
* @var hash
*/
public $cfg;
/**
* only access after call $this->updateUser() <br>
* ua_type, ua_name, ip, session_id, user_id, username, <br>
* login_utime, start_utime, current_utime <br>
* geo_country_code2, geo_country_code3, geo_country_name, geo_lang, geo_lat, geo_long
* @var hash
*/
public $user=array(); //user information
/**
* current loaded access list control
* @var hash
*/
public $acl=array();
/**
* current loaded model
* @var object
*/
public $model;
//caching
static $modelCache;
//caching
static $aclCache;
/**
* logs for debugging
* @var hash
*/
public $logs=array();
public function __construct(&$app){
//echo __NAMESPACE__ .' :: ' . __CLASS__ . '<br>';
$this->app = $app;
$this->cfg = &$this->app->cfg;
}
public function getSrcDir(){
return SRC_DIR;
}
public function getIndexDir(){
return INDEX_DIR;
}
/**
* Can be called anytime
* sh = session hash / server hash
*/
public function sessionStart(){
if (!isset($_SESSION)) {
session_set_cookie_params('', '/', '.' . $this->cfg['domain'], 0, 1);
if (!isset($_COOKIE['sh'])) {
$hash = uniqid();
setcookie('sh', $hash, 0, '/', '.' . $this->cfg['domain'], 0, 1);
session_name(md5($hash));
} else {
session_name(md5($_COOKIE['sh']));
}
session_start();
}
}
/**
* Automatically set locale based on default config / ($_SERVER['HTTP_ACCEPT_LANGUAGE']
* set geo graphic information to $this->user
*/
public function setLocale($loc=null)
{
$this->sessionStart();
//user selection
if (!empty($loc)) $this->locale = $loc;
if (!empty($_GET['loc'])) $_SESSION['loc'] = $_GET['loc'];
if (empty($this->locale) && isset($_SESSION['loc'])) $this->locale=$_SESSION['loc'];
else
{
//if not try with DB
if (empty($this->locale) && isset( $this->cfg['locale_by_db']) ){
//get from DB
$geo=$this->getGeoDataByIP();
$this->locale = $geo['lang'] . '_' . $geo['codetwo'];
}
//if not try with http header
if (empty($this->locale) && isset( $this->cfg['locale_by_header']) && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])){
//get from header
$this->locale=locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']);
}
//default locale fallback
if (empty($this->locale)){
if (isset( $this->cfg['locale'])) $this->locale = $this->cfg['locale'];
else $this->locale='en_US';
}
}
//
$parse=locale_parse($this->locale);
$this->locale_lang = $parse['language'];
$this->locale_country = isset($parse['region'])?$parse['region']:''; //codetwo in upper case
//var_dump($this->locale,$this->cfg['locale']['textdomain'],$this->cfg['locale']['translate_dir']);
putenv('LC_ALL='.$this->locale);
setlocale(LC_ALL,$this->locale);
foreach ($this->cfg['bindtextdomain'] as $key=>$value)
{
bindtextdomain($key,$value);
}
//select the domain for current gettext
textdomain($this->cfg['textdomain']);
}
const UA_BOT=1,UA_PC=2,UA_TABLET=3,UA_MOBILE=4;
/**
* @param bool $return_full if true then return array <b> ['type'=>BOT|TABLET|MOBILE|PC,'name'=>'UA name'] </b>
* else (default) return only type
* @param bool nocache nếu nocache true thì $_SESSION['UA'] sẽ bị xóa
* @return : PC nhóm desktop
* if UA = PC thi co them $_SESSION['UA_NAME'] = 'chrome','firefox','safari','opera','ie'
* @return : BOT | MOBILE | TABLET nhóm mobile
* if UA = BOT thi co them $_SESSION['UA_NAME'] = 'google','yahoo','bot','spider','crawl','ask','archiver','baidu','msnbot'
* hàm dùng session để cached
*
*/
public function getUA($return_full=false,$nocache=false) {
$this->sessionStart();
$ua = strtolower($_SERVER["HTTP_USER_AGENT"]); //nen luu session
if ($nocache && isset($_SESSION['UA'])) unset($_SESSION['UA']);
if (empty($_SESSION['UA']))
{
$bots = array('google', 'yahoo', 'bot', 'spider', 'crawl', 'jeeves', 'archiver', 'baidu', 'msnbot', 'sogou', 'yandex');
foreach ($bots as $bot)
{
if (strpos($ua, $bot) !== false) {
$_SESSION['UA'] = Controller::UA_BOT;
$_SESSION['UA_NAME'] = $bot;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
}
#is tablet ?
//iPad vừa có iPad vừa có chữ Mobile
//Blackberry tablet dùng Tablet
//Amazon Kindle 600x800 dùng Kindle
if (preg_match('#ipad#', $ua))
{
$_SESSION['UA_NAME'] = 'iPad';
$_SESSION['UA'] = Controller::UA_TABLET;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
elseif (preg_match('#kindle#', $ua))
{
$_SESSION['UA_NAME'] = 'Kindle';
$_SESSION['UA'] =Controller::UA_TABLET;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
elseif (preg_match('#tablet#', $ua))
{
$_SESSION['UA_NAME'] = 'General Tablet';
if (strpos('RIM',$ua)) $_SESSION['UA_NAME'] = 'BlackBerry';
$_SESSION['UA'] = Controller::UA_TABLET;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
#is mobile ?
//Android nếu có Mobile thì không phải Tablet
//windows phone : IEMobile/version
//những thiết bị khác có mobile hoặc mobi thì chắc là mobile
if (preg_match('#mobi#', $ua))
{
$_SESSION['UA_NAME'] = 'General Mobile';
if (strpos($ua,'android')) $_SESSION['UA_NAME'] = 'Android';
elseif (strpos($ua,'iphone')) $_SESSION['UA_NAME'] = 'iPhone';
elseif (strpos($ua,'blackberry')) $_SESSION['UA_NAME'] = 'BlackBerry';
elseif (strpos($ua,'msie')) $_SESSION['UA_NAME'] = 'MSIE';
$_SESSION['UA'] = Controller::UA_MOBILE;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
//nếu ở trên không có Mobile mà dưới này có Android thì => tablet
if (preg_match('#android#', $ua))
{
$_SESSION['UA_NAME'] = 'Android';
$_SESSION['UA'] =Controller::UA_TABLET;
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
//else desktop browser
if (preg_match('#firefox#', $ua))
{
$_SESSION['UA'] = Controller::UA_PC;
$_SESSION['UA_NAME'] = 'Firefox';
}
elseif (preg_match('#chrome#', $ua))
{
$_SESSION['UA'] = Controller::UA_PC;
$_SESSION['UA_NAME'] = 'Chrome';
}
elseif (preg_match('#msie#', $ua))
{
$_SESSION['UA'] = Controller::UA_PC;
$_SESSION['UA_NAME'] = 'IE';
}
elseif (preg_match('#safari#', $ua))
{
$_SESSION['UA'] = Controller::UA_PC;
$_SESSION['UA_NAME'] = 'Safari';
}
elseif (preg_match('#opera#', $ua))
{
$_SESSION['UA'] = Controller::UA_PC;
$_SESSION['UA_NAME'] = 'Opera';
}
else
$_SESSION['UA'] = null;
}
if (!$return_full) return $_SESSION['UA'];
else return array('type'=>$_SESSION['UA'],'name'=>$_SESSION['UA_NAME']);
}
/**
* tra ve cac record ve dia ly tu ip cho truoc
* ham nay khong tich hop trong app ma de trong CORE::routeRequest
* vi su dung trong
* @param $ip null thi lay $_SERVER['REMOTE_ADDR']
* @return array|null [lang,countryname,codetwo,codethree,lat,long,registry]
*/
public function getGeoDataByIP($ip = null) {
$loc = null;
if (empty($ip)) $ip = $_SERVER['REMOTE_ADDR'];
if (isset($_SESSION['geo_' . $ip])) return $_SESSION['geo_' . $ip];
$IPNUM = sprintf("%u", ip2long($ip));
$shareDB = $this->db('share');
$geo = $shareDB->getOne("
SELECT rangestart,rangeend,_geo_ip_country.codetwo,_geo_ip_country.codethree,_geo_country.country as countryname,_geo_ip_country.registy as registry,
_geo_country.lang,_geo_country.lat,_geo_country.long
FROM _geo_ip_country,_geo_country
WHERE $IPNUM BETWEEN rangestart AND rangeend
AND _geo_ip_country.codetwo = _geo_country.codetwo
LIMIT 1
");
$_SESSION['geo_' . $ip] = $geo;
return $geo;
}
/**
*
* @return string current Url
*/
public function getCurrentUrl(){
$url = ( !isset($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] != 'on' ) ? 'http://' . $_SERVER["SERVER_NAME"] : 'https://' . $_SERVER["SERVER_NAME"];
$url .= ( $_SERVER["SERVER_PORT"] !== '80' ) ? ":" . $_SERVER["SERVER_PORT"] : "";
$url .= $_SERVER["REQUEST_URI"];
return $url;
}
// <editor-fold defaultstate="collapsed" desc=" Authenticate && User ">
/**
* check if auth cookie is valid
* @param array $param : changing param['expire'|'expire_delta','randsig','passsalt']
* @return true/false
*/
public function validateAuthCookie($param){
if (isset($_COOKIE['usrsig'])){
$validate = false;
$parts = explode('_',$_COOKIE['usrsig']);
if (count($parts) >= 3) {
$sig = $parts[0];
$expire = $parts[1];
$uid = $parts[2];
if (!$param['db']) {
$db = $this->db();
$db->table('user_auth');
}
else $db = $param['db'];
$row=$db->getOne(array('select'=>'passalt,randsig','where'=>array('uid = ?',$uid)));
if ($row) {
$ua = $_SERVER['HTTP_USER_AGENT'];
$server_sig = md5($row['randsig'] . $row['passsalt'] . $ua );
if ($sig == $server_sig) {
if (isset($param['expire'])) $expire = $param['expire'];
elseif (isset($param['expire_delta'])) $expire +=$param['expire_delta'];
//update randsig
$randsig = md5($uid.time());
$db->update(array('randsig'=>$randsig),array('uid = ?',$uid));
$token = md5($randsig . $row['passsalt'] . $ua ) . '_' . $expire . '_' . $uid;
if (isset($this->user['cookie_path'])) $path=$this->user['cookie_path']; else $path ='/';
if (isset($this->user['cookie_domain'])) $domain=$this->user['cookie_domain']; else $domain =$this->user['domain'];
setcookie('usrsig',$token,$expire,$path,$domain);
$validate = true;
}
}
}
if (!$validate) {
setcookie('usrsig','',time() - 3600);//remove the tained cookie
return false;
}
else return true;
}
else return false;
}
/**
*
* @param type $param [username||email|password ( md5 ), 'db' ]
* @param mixed $msg return 401 : unauthorized, 404 user not found, $user if success ( from db )
* @return true/false
*/
public function validateAuth($param,&$msg){
if (!$param['db']) {
$db = $this->db();
$db->table('user_auth');
}
else $db = $param['db'];
if (isset($param['username'])) $where = array('username = ?',$param['username']);
elseif (isset($param['email'])) $where = array('email = ?',$param['email']);
$row = $db->getOne(array(
'select'=>"user_id,username,uid,email,passsalt,password,last_login",
'where'=>$where
));
if ($row){
$password = md5( $param['password'] . $row['username'] . $row['passsalt'] ) ;
if ($password == $row['password'] ) {
$err = $row;
return true;
}
$msg = 401;//invalid password
return false;
}
else {
$msg = 404;//user not found
return false;
}
}
/**
*
* @param array $user
*/
public function regAuthCookie($param){
$uid=$param['uid'];
$randsig = $param['randsig'];
$passsalt = $param['passalt'];
$expire = $param['expire'];
if (!$expire) $expire = 1209600; // 2 weeks
$ua = $_SERVER['HTTP_USER_AGENT'];
$token = md5($randsig . $passsalt . $ua ) . '_' . $expire . '_' . $uid;
if (isset($this->user['cookie_path'])) $path=$this->user['cookie_path']; else $path ='/';
if (isset($this->user['cookie_domain'])) $domain=$this->user['cookie_domain']; else $domain =$this->user['domain'];
setcookie('usrsig',$token,$expire,$path,$domain);
}
/**
*
*/
public function isSessionExpired(){
if (empty($this->cfg['session_expire'])) $this->cfg['session_expire'] = 1800;//30mins
return ( (time() - $_SESSION['current_utime']) > $this->cfg['session_expire']) ;
}
/**
* check if user has logged or not.
* if page require login then call in the order as :
* if (!isLoggedIn()) {
* $this->unregAuthSession();
* $this->reRoute($this->cfg['route_default]['auth']); // auth controller :
* return;
* }
*
* $this->updateUser() ; //dont call updateUser before isLoggedIn() because updateUser
*
* @return boolean
*/
public function isLoggedIn(){
if ( isset($_SESSION['login_utime']) && !$this->isSessionExpired() ) return true;
else {
$this->unregAuthSession();
}
}
/**
* after logged in succesfully must call this to register Auth Session
* @param array $user=array(user_id,username)
*/
public function regAuthSession($user){
$db = $this->db();
$db->table('user_session');
//changing new session_id for securing
$old_session_id = $this->user['session_id'];
$session_id = session_regenerate_id();
$this->user['session_id']=$session_id;
$this->user['user_id'] = $_SESSION['uid']=$user['user_id'];
$this->user['username'] = $_SESSION['uname']=$user['username'];
//change sesssion_id for preventing session fixation
$db->update(array(
'session_id'=>$session_id
),"session_id='".$old_session_id."'",1);
//create login_utime
$this->user['login_utime']=$_SESSION['login_utime']=time();
}
/**
* unregister Auth Session but still keep normal session ( guest )
*/
public function unregAuthSession(){
unset($this->user['user_id'],$_SESSION['uid'],$_SESSION['login_utime']);
//keep 'username' to notice something (relog , warning ... )
}
/**
* update user info -> $this->user
* @param type $updateDB update to DB or not ( default = true )
* @todo nếu có if (isLoggedIn) {} thì gọi sau isLoggedIn {}
* @return void
*/
public function updateUser($updateDB = true){
$this->getUA();
$this->user['ua_type'] = $_SESSION['UA'];
$this->user['ua_name'] = $_SESSION['UA_NAME'];
$this->user['ip'] = $_SERVER['REMOTE_ADDR'];
$this->user['session_id'] = session_id();
$this->user['user_id'] = isset($_SESSION['uid'])? $_SESSION['uid']:null;
$this->user['username'] = isset($_SESSION['uname'])? $_SESSION['uname']:null;
$this->user['login_utime'] = isset($_SESSION['login_utime'])? $_SESSION['login_utime']:null;
if (!isset($_SESSION['start_utime'])) $_SESSION['start_utime'] = time();
$this->user['start_utime'] = $_SESSION['start_utime'];
$current = time();
$_SESSION['current_utime'] = $current;
$this->user['current_utime'] = $current;
//$this->user['app'] = $this->cfg['app'];
//$this->user['current_url'] = $_SERVER['HTTP_HOST'] .$_SERVER['REQUEST_URI'];
$geo = $this->getGeoDataByIP();
if (!empty($geo))
{
$this->user['geo_country_code2'] = isset($geo['codetwo'])?$geo['codetwo']:null;
$this->user['geo_country_code3'] = isset($geo['codethree'])?$geo['codethree']:null;
$this->user['geo_country_name'] = isset($geo['countryname'])?$geo['countryname']:null;
$this->user['geo_lang'] = isset($geo['lang'])?$geo['lang'] :null;
if (!empty($this->user['geo_country_code2'])) $this->user['geo_lang'].='_'.$this->user['geo_country_code2'];
$this->user['geo_lat'] = isset($geo['lat'])?$geo['lat']:null;
$this->user['geo_long'] = isset($geo['long'])?$geo['long']:null;
}
if ($updateDB){
$this->app->event->addListener('UPDATE_USER_SESSION', function(){
//_vd($this->user);
$now=time();
$db = $this->db();
//db server error
if (!$db) return;
$db->table('user_session');
$input = array(
'session_current_unixtime' => $now,
'user_id' => isset($this->user['id']) ?$this->user['id']:null ,
'username' => isset($this->user['name']) ?$this->user['name']:null,
'app_code' => $this->cfg['app'],
'current_page' => $this->getCurrentUrl(),
'hits' => array('++')
);
if (!$db->update($input,"session_id = '{$this->user['session_id']}'"))
{
//unset logged_session
$this->unregAuthSession();
$input = array_merge($input, array(
'session_id' => $this->user['session_id'],
'IP' => $_SERVER['REMOTE_ADDR'],
'port' => $_SERVER['REMOTE_PORT'],
'http_user_agent' => $_SERVER['HTTP_USER_AGENT'],
'session_start_unixtime' => $now,
'server' => $_SERVER['HTTP_HOST'],
'hits' => 1,
'geo_lang' => $this->user['geo_lang'],
'geo_country_code2' => $this->user['geo_country_code2'],
'geo_country_name' => $this->user['geo_country_name'],
'geo_lat' => $this->user['geo_lat'],
'geo_long' => $this->user['geo_long'],
));
$db->insert($input);
}
});
$this->app->event->addListener('CLEANUP_USER_SESSION', function(){
$db = $this->db();
//db error
if (!$db) return;
$now = time();
//xoa bot cac session row da het han trong vong $_CFG['session_login_expire'] unixtime
if (!isset($_SESSION['SESSION_CLEANUP']) || ( ($now - $_SESSION['SESSION_CLEANUP'] ) > $this->cfg['session_expire'])) {
$db->table('user_session')->delete("$now - session_current_unixtime > {$this->cfg['session_expire']} ");
$_SESSION['SESSION_CLEANUP'] = time(); // danh dau time
}
});
}
}
/**
* ['ua_type'] ['ua_name'] ['ip'] ['session_id'] ['user_id']['username'] ['login_utime'] ['start_utime']
['current_utime'] ['app'] ['current_url'] ['geo_country_code2'] ['geo_country_code3'] ['geo_country_name']
['geo_lang'] ['geo_country_code2'] ['geo_lat'] ['geo_long']
* @return array userInfo
*/
public function getUser(){
return $this->user;
}
// </editor-fold>
/**
* create a db
* @param mixed $cfg as string or array, if string then function use $_CFG['db']
* @return \PHN\Model\CommonDB
*/
public function db($cfg='default'){
if (is_string($cfg)) $cfg = $this->cfg['db'][$cfg];
try{
return \PHN\Model\Model::create($this,$cfg);
}
catch (\Exception $e){
return null;
}
}
/**
* load a model, save to $this->model and return $this->model
* @param string $class to load
* @return \PHN\Model\Model obj
*/
public function model($class=null){
//auto get model by this controller name
if (empty($class)) $class = $this->app->controllerName.'Model';
if (isset(self::$modelCache[$class])) return $this->model=&self::$modelCache[$class];
$name='\\'.$this->app->appName.'\\Model\\'.$class;
self::$modelCache[$class] = new $name($this);
return $this->model=&self::$modelCache[$class];
}
/**
* create acl object
* @param string|array $cfg
* @return \PHN\Model\Acl acl
*/
public function acl($cfg=null){
if (empty($cfg)) {
if ($this->app->appName != 'Main') $cfg=$this->app->appName.'/'.'global';
else $cfg = 'global';
}
if ( is_string($cfg)){
$cacheKey = $cfg;
if (isset(self::$aclCache[$cacheKey])) return self::$aclCache[$cacheKey];
$cfg = include(SRC_DIR.'/config/acl/'.$cfg.'.php');
try{
$this->acl = new \PHN\Model\Acl($cfg, $this);
self::$aclCache[$cacheKey] = $this->acl;
}
catch (\Exception $e){
//this ACL use db but could not connect
$this->acl=null;
}
}//cfg as array , no support for cache
else
try {
$this->acl = new \PHN\Model\Acl($cfg, $this);
}
catch (\Exception $e){
//this ACL use db but could not connect
$this->acl=null;
}
return $this->acl;
}
/**
*
* @param type $url
* @param type $query the query string if any
* nếu query == true thì tự động append ?redirect=current_url
*/
public function redirect($url,$query=null){
if (!empty($query)){
if (!is_array($query) && $query == true )
{
$query=array('redirect'=>"http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
}
$url .= '?'.http_build_query($query);
}
header('Location: '.$url);
}
/**
* $path default = 'Auth/index' or <br>
* array('app'=>'SomeApp','controller'=>'Index','action'=>'index','param'=>?)
* @param string/array $path
*/
public function reRoute($path) {
if (is_string($path)) {
$this->app->doRoute(null,$path);
}
//as array param
else $this->app->doRoute($path);
}
/**
* create a template engine
* @param type $cfg
* @return \PHN\Template\Template
*/
public function template($cfg=null){
if (!$cfg) $cfg = &$this->cfg;
return new \PHN\View\Template($cfg,$this);
}
/**
* load Form with config ( file or array ) <Br>
* if you load Form then no need to use ->template <Br>
* because Form extends Template <Br>
* cfg = "xxx" or 'App/xxx' relative to application/config
* default no param : cfg == $ControllerAction.cfg.php
* @param string/array $cfg
*/
public function form($cfg=null)
{
if (empty($cfg)) {
if ($this->app->appName != 'Main') $cfg=$this->app->appName.'/'.$this->app->controllerName;
else $cfg = $this->app->controllerName;
}
if ( is_string($cfg)){
$rules = include(SRC_DIR.'/config/form/'.$cfg.'.php');
$this->form =new \PHN\Model\Form($rules,$this);
}
else{
$this->form =new \PHN\Model\Form($cfg,$this);
}
return $this->form;
}
//<editor-fold desc="Event Helpers">
/**
*
* @param type $eventName
* @param type $callback
* @param int $priority default = 0
*/
public function addListener($eventName,$callback,$priority=0){
$this->app->event->addListener($eventName,$callback,$priority);
}
/**
*
* @param string $eventName
* @param function $listener , nếu function null thì remove all handle for the eventName
*/
public function removeListener($eventName, $listener = null){
$this->app->event->removeListener($eventName, $listener);
}
/**
*
* @param string $eventName
* @param \PHN\Core\Event $event , event sẽ được truyền vào callback($event)
* dùng event ( new Event() ) rồi gán các dynamic param để callback xử lý nếu cần thiết
*/
public function triggerEvent($eventName, Event $event = null){
$this->app->event->dispatch($eventName,$event);
}
//</editor-fold>
//<editor-fold desc="Fx Filtering Helpers">
/**
*
* @param type $filterName
* @param type $function
* @param type $priority
*/
public function addFxFilter($filterName, $function, $priority = 0){
$this->app->fxFilter->add($filterName, $function, $priority);
}
/**
*
* @param type $filterName
* @param type $function
*/
public function removeFxFilter($filterName, $function = null){
$this->app->fxFilter->remove($filterName, $function );
}
/**
*
* @param type $filterName
* @param type $param
*/
public function callFxFilter($filterName,$param=null){
$this->app->fxFilter->call($filterName,$param);
}
//</editor-fold>
}
/**
* Event is the base class for classes containing event data.
*
* This class contains no event data. It is used by events that do not pass
* state information to an event handler when an event is raised.
*
* You can call the method stopPropagation() to abort the execution of
* further listeners in your event listener.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @api
*/
class Event {
/**
* @var Boolean Whether no further event listeners should be triggered
*/
private $propagationStopped = false;
/**
* @var EventDispatcher Dispatcher that dispatched this event
*/
private $dispatcher;
/**
* @var string This event's name
*/
private $name;
/**
* Returns whether further event listeners should be triggered.
*
* @see Event::stopPropagation
* @return Boolean Whether propagation was already stopped for this event.
*
* @api
*/
public function isPropagationStopped() {
return $this->propagationStopped;
}
/**
* Stops the propagation of the event to further event listeners.
*
* If multiple event listeners are connected to the same event, no
* further event listener will be triggered once any trigger calls
* stopPropagation().
*
* @api
*/
public function stopPropagation() {
$this->propagationStopped = true;
}
/**
* Stores the EventDispatcher that dispatches this Event
*
* @param EventDispatcherInterface $dispatcher
*
* @api
*/
public function setDispatcher($dispatcher) {
$this->dispatcher = $dispatcher;
}
/**
* Returns the EventDispatcher that dispatches this Event
*
* @return EventDispatcherInterface
*
* @api
*/
public function getDispatcher() {
return $this->dispatcher;
}
/**
* Gets the event's name.
*
* @return string
*
* @api
*/
public function getName() {
return $this->name;
}
/**
* Sets the event's name property.
*
* @param string $name The event name.
*
* @api
*/
public function setName($name) {
$this->name = $name;
}
}
/**
* The EventDispatcherInterface is the central point of Symfony's event listener system.
*
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Jordan Alliot <jordan.alliot@gmail.com>
*
* @api
*/
class EventDispatcher {
private $listeners = array();
private $sorted = array();
/**
* @see EventDispatcherInterface::dispatch
*
* @api
*/
public function dispatch($eventName, Event $event = null) {
if (null === $event) {
$event = new Event();
}
$event->setDispatcher($this);
$event->setName($eventName);
if (!isset($this->listeners[$eventName])) {
return $event;
}
$this->doDispatch($this->getListeners($eventName), $eventName, $event);
return $event;
}
/**
* @see EventDispatcherInterface::getListeners
*/
public function getListeners($eventName = null) {
if (null !== $eventName) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
return $this->sorted[$eventName];
}
foreach (array_keys($this->listeners) as $eventName) {
if (!isset($this->sorted[$eventName])) {
$this->sortListeners($eventName);
}
}
return $this->sorted;
}
/**
* @see EventDispatcherInterface::hasListeners
*/
public function hasListeners($eventName = null) {
return (Boolean) count($this->getListeners($eventName));
}
/**
* @see EventDispatcherInterface::addListener
* $priority higher the earlier
* @api
*/
public function addListener($eventName, $listener, $priority = 0) {
$this->listeners[$eventName][$priority][] = $listener;
unset($this->sorted[$eventName]);
}
/**
* @see EventDispatcherInterface::removeListener
* @modified by NP $listener =null , remove all
*/
public function removeListener($eventName, $listener = null) {
if (!isset($this->listeners[$eventName])) {
return;
}
// <editor-fold desc="modified here::adding">
if (!$listener) {
unset($this->listeners[$eventName],$this->sorted[$eventName]);
return;
}
// </editor-fold>
foreach ($this->listeners[$eventName] as $priority => $listeners) {
if (false !== ($key = array_search($listener, $listeners, true))) {
unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]);
}
}
}
/**
* @see EventDispatcherInterface::addSubscriber
*
* @api
*/
public function addSubscriber(EventSubscriberInterface $subscriber) {
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->addListener($eventName, array($subscriber, $params));
} elseif (is_string($params[0])) {
$this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
} else {
foreach ($params as $listener) {
$this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
}
}
}
}
/**
* @see EventDispatcherInterface::removeSubscriber
*/
public function removeSubscriber(EventSubscriberInterface $subscriber) {
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
if (is_array($params) && is_array($params[0])) {
foreach ($params as $listener) {
$this->removeListener($eventName, array($subscriber, $listener[0]));
}
} else {
$this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
}
}
}
/**
* Triggers the listeners of an event.
*
* This method can be overridden to add functionality that is executed
* for each listener.
*
* @param array[callback] $listeners The event listeners.
* @param string $eventName The name of the event to dispatch.
* @param Event $event The event object to pass to the event handlers/listeners.
*/
protected function doDispatch($listeners, $eventName, Event $event) {
foreach ($listeners as $listener) {
call_user_func($listener, $event);
if ($event->isPropagationStopped()) {
break;
}
}
}
/**
* Sorts the internal list of listeners for the given event by priority.
*
* @param string $eventName The name of the event.
*/
private function sortListeners($eventName) {
$this->sorted[$eventName] = array();
if (isset($this->listeners[$eventName])) {
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]);
}
}
}
class FxFilter{
private $filters = array();
private $sorted = array();
public function get($filterName = null)
{
if (null !== $filterName)
{
if (!isset($this->sorted[$filterName]))
{
$this->sort($filterName);
}
return $this->sorted[$filterName];
}
foreach (array_keys($this->filters) as $filterName)
{
if (!isset($this->sorted[$filterName])) {
$this->sort($filterName);
}
}
return $this->sorted;
}
public function has($filterName = null) {
return (Boolean) count($this->get($filterName));
}
/**
*
* @param string $filterName
* @param callable $function
* @param int $priority
*/
public function add($filterName, $function, $priority = 0) {
$this->filters[$filterName][$priority][] = $function;
unset($this->sorted[$filterName]);
}
/**
*
* @param type $filterName
* @param type $function
*
*/
public function remove($filterName, $function = null) {
if (!isset($this->filters[$filterName])) {
return;
}
if (!$function) {
unset($this->filters[$filterName],$this->sorted[$filterName]);
return;
}
foreach ($this->filters[$filterName] as $priority => $filters) {
if (false !== ($key = array_search($function, $filters, true))) {
unset($this->filters[$filterName][$priority][$key], $this->sorted[$filterName]);
}
}
}
public function call($filterName,$param=null) {
if (!isset($this->filters[$filterName])) {
return $param;
}
$filters=$this->get($filterName);
$args = func_get_args();
foreach ($filters as $filter) {
$args[1] = $param;
$param=call_user_func_array($filter, array_slice($args, 1));
}
}
private function sort($filterName) {
$this->sorted[$filterName] = array();
if (isset($this->filters[$filterName])) {
krsort($this->filters[$filterName]);
$this->sorted[$filterName] = call_user_func_array('array_merge', $this->filters[$filterName]);
}
}
}
}//end namespace Core
// <editor-fold defaultstate="collapsed" desc="extra helper function">
namespace {
/**
* var_dump
*/
function _vd(){
$msgs=debug_backtrace();
//var_dump($msgs);exit;
$msg = $msgs[1];
echo '<div style="border:1px solid #dd0000; padding:8px;"><div style="color:#ff0000">Call <b>' . $msg['function'] . '()</b> #Line: <b> ' . @$msg['line'] . '</b> @ '.@$msg['file'] . '</div>';
echo '<div style="background-color:#eee; padding:8px;"><pre>';
call_user_func_array('var_dump',func_get_args());
echo '</pre></div></div>';
}
}
// </editor-fold>