DaPortal

<?php //$Id$
//Copyright (c) 2011-2016 Pierre Pronchery <khorben@defora.org>
//This file is part of DeforaOS Web DaPortal
//
//This program 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, version 3 of the License.
//
//This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
//Engine
abstract class Engine
{
//public
//methods
//essential
abstract public function match();
abstract public function attach();
//useful
//Engine::render
public function render(Response $response)
{
if($this->verbose == 0)
return $response->getCode();
return $response->render($this);
}
//methods
//accessors
//Engine::getAuth
public function getAuth()
{
return ($this->_attachAuth() !== FALSE) ? $this->auth : FALSE;
}
//Engine::getCredentials
public function getCredentials()
{
if($this->_attachAuth() === FALSE)
return new AuthCredentials;
return $this->auth->getCredentials($this);
}
//Engine::getDatabase
public function getDatabase()
{
if($this->database !== FALSE)
return $this->database;
if(($this->database = Database::attachDefault($this))
!== FALSE)
return $this->database;
$this->database = new DummyDatabase('dummy');
$this->ret = 2;
return $this->database;
}
//Engine::getDebug
public function getDebug()
{
return $this->debug;
}
//Engine::getDefaultType
public function getDefaultType()
{
return 'text/html';
}
//Engine::getModules
public function getModules($reset = FALSE)
{
static $modules = array();
$database = $this->getDatabase();
$query = static::$query_modules;
if($reset !== FALSE)
$modules = array();
else if(count($modules) != 0)
return $modules;
if(($res = $database->query(NULL, $query)) === FALSE)
return $modules;
foreach($res as $r)
$modules[$r['id']] = $r['name'];
return $modules;
}
//Engine::getRequest
public function getRequest()
{
global $config;
//return the default request
return new Request($config->get('defaults', 'module'),
$config->get('defaults', 'action'),
$config->get('defaults', 'id'));
}
//Engine::getReturn
public function getReturn()
{
return $this->ret;
}
//Engine::getURL
public function getURL(Request $request = NULL, $absolute = TRUE)
{
if($absolute === FALSE)
return $_SERVER['SCRIPT_NAME'];
$filename = $_SERVER['SCRIPT_NAME'][0] == '/'
? $_SERVER['SCRIPT_NAME']
: $_SERVER['PWD'].'/'.$_SERVER['SCRIPT_NAME'];
return realpath($filename);
}
//Engine::getVerbose
public function getVerbose()
{
return $this->verbose;
}
//Engine::setCredentials
public function setCredentials(AuthCredentials $credentials = NULL)
{
if($this->_attachAuth() === FALSE)
return FALSE;
return $this->auth->setCredentials($this, $credentials);
}
//Engine::setDebug
public function setDebug($debug)
{
$this->debug = $debug ? TRUE : FALSE;
if($this->debug)
{
set_error_handler(function($errno, $errstr,
$errfile = FALSE, $errline = FALSE,
$errcontext = FALSE)
{
if((error_reporting() & $errno) == 0)
return FALSE;
return $this->logBacktrace();
});
//XXX no type hint for compatibility with PHP 7
set_exception_handler(function($e)
{
$this->logException($e, LOG_ERR);
exit(125);
});
}
else
{
restore_exception_handler();
restore_error_handler();
}
}
//Engine::setVerbose
public function setVerbose($verbose)
{
$this->verbose = $verbose ? 2 : 0;
}
//useful
//Engine::log
public function log($priority, $message)
{
$priority = $this->logPriority($priority);
if(($message = $this->logMessage($priority, $message))
!== FALSE)
error_log($message, 0);
return FALSE;
}
//Engine::process
public function process(Request $request, $internal = FALSE)
{
//return an empty page if no valid request is provided
if(($module = $request->getModule()) === FALSE)
$ret = new PageResponse(FALSE);
else
{
//obtain the response
$action = $request->getAction();
$message = 'Processing'.($internal ? ' internal' : '')
." request: module $module"
.(($action !== FALSE)
? ", action $action" : '');
$this->log(LOG_DEBUG, $message);
if(($module = Module::load($this, $module)) === FALSE)
{
$message = _('Could not load the module');
$ret = new ErrorResponse($message,
Response::$CODE_ENOENT);
}
else
$ret = $module->call($this, $request,
$internal);
if($internal)
return $ret;
if(!($ret instanceof Response))
{
$message = 'Unknown response type';
$this->log(LOG_ERR, $message);
return new ErrorResponse();
}
}
//restore the type if not already enforced
if(($type = $request->getType()) !== FALSE
&& $ret->getType() === FALSE)
$ret->setType($type);
return $ret;
}
//static
//useful
//Engine::attachDefault
static public function attachDefault($prefix = FALSE,
$sysconfdir = FALSE)
{
global $config;
$ret = FALSE;
$priority = 0;
if($sysconfdir === FALSE && $prefix !== FALSE)
$sysconfdir = ($prefix == '/usr')
? '/etc' : $prefix.'/etc';
//XXX ignore errors
static::configLoad($sysconfdir, TRUE);
if(($name = $config->get('engine', 'backend')) !== FALSE)
{
$class = $name.'Engine';
$ret = new $class();
}
else if(($dir = opendir('engines')) !== FALSE)
{
while(($de = readdir($dir)) !== FALSE)
{
if(substr($de, -4) != '.php')
continue;
$n = substr($de, 0, -4);
$class = $n.'Engine';
$engine = new $class();
if(($p = $engine->match()) <= $priority)
continue;
$ret = $engine;
$name = $n;
$priority = $p;
}
closedir($dir);
}
if($ret === FALSE)
return error_log('Could not load any engine');
//XXX ignore errors
static::configLoadEngine($prefix, $name, FALSE);
$ret->log(LOG_DEBUG, 'Attaching '.get_class($ret)
.' with priority '.$priority);
$ret->attach();
$debug = ($config->get(FALSE, 'debug')
|| $config->get("engine::$name", 'debug'));
$ret->setDebug($debug);
static::_defaultBootstrap();
return $ret;
}
static protected function _defaultBootstrap()
{
$dirname = 'bootstrap';
if(!is_dir($dirname))
return TRUE;
if(!is_readable($dirname))
return FALSE;
if(($dir = opendir($dirname)) === FALSE)
return FALSE;
while(($de = readdir($dir)) !== FALSE)
if(substr($de, -4) == '.php')
require("$dirname/$de");
closedir($dir);
return TRUE;
}
//Engine::configLoad
//loads the default configuration file
static public function configLoad($sysconfdir = FALSE, $reset = TRUE)
{
$daportalconf = ($sysconfdir !== FALSE)
? $sysconfdir.'/daportal.conf' : FALSE;
if(($d = getenv('DAPORTALCONF')) !== FALSE)
$daportalconf = $d;
if($daportalconf === FALSE)
{
error_log('Could not load any configuration file');
return FALSE;
}
return static::configLoadFilename($daportalconf, $reset);
}
//Engine::configLoadEngine
//loads the default configuration file for a specific engine
static public function configLoadEngine($sysconfdir = FALSE,
$name = FALSE, $reset = TRUE)
{
$daportalconf = ($sysconfdir !== FALSE)
? $sysconfdir.'/daportal.conf' : FALSE;
if(($d = getenv('DAPORTALCONF')) !== FALSE)
return FALSE;
if($daportalconf === FALSE)
{
error_log($name.': Could not load configuration file');
return FALSE;
}
if(!file_exists($daportalconf))
return TRUE;
return static::configLoadFilename($daportalconf, $reset);
}
//Engine::configLoadFilename
//loads a specific configuration file
static public function configLoadFilename($filename, $reset = TRUE)
{
global $config;
if($reset)
$config = new Config();
if($config->load($filename) === FALSE)
{
$error = 'Could not load configuration file';
error_log($filename.': '.$error);
return FALSE;
}
return TRUE;
}
//protected
//properties
static protected $priorities = array('LOG_ALERT' => LOG_ALERT,
'LOG_CRIT' => LOG_CRIT,
'LOG_EMERG' => LOG_EMERG,
'LOG_DEBUG' => LOG_DEBUG,
'LOG_ERR' => LOG_ERR,
'LOG_WARNING' => LOG_WARNING,
'LOG_INFO' => LOG_INFO,
'LOG_NOTICE' => LOG_NOTICE);
protected $verbose = 1;
//queries
static protected $query_modules = "SELECT module_id AS id, name
FROM daportal_module
WHERE enabled='1'
ORDER BY name ASC";
//methods
//Engine::logBacktrace
protected function logBacktrace($priority = LOG_DEBUG)
{
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
array_shift($backtrace);
return $this->logTrace($backtrace, $priority);
}
//Engine::logException
protected function logException(Exception $exception,
$priority = LOG_DEBUG)
{
$this->logTrace($exception->getTrace());
$message = "Uncaught exception '".$exception->getMessage()."'";
if(($code = $exception->getCode()) != 0)
$message .= " (code $code)";
$message .= ' in file '.$exception->getFile().':'
.$exception->getLine();
return $this->log($priority, $message);
}
//Engine::logPriority
protected function logPriority($priority)
{
if(in_array($priority, static::$priorities, TRUE))
return $priority;
//support the old API with strings
if(isset(static::$priorities[$priority]))
return static::$priorities[$priority];
return LOG_WARNING;
}
//Engine::logTrace
protected function logTrace($trace, $priority = LOG_DEBUG)
{
$ret = '';
$sep = '';
for($cnt = count($trace), $i = $cnt; $i > 0;)
{
$ret .= $sep.'#'.(--$i).': ';
if(isset($trace[$i]['class']))
$ret .= $trace[$i]['class'];
if(isset($trace[$i]['type']))
$ret .= $trace[$i]['type'];
if(isset($trace[$i]['function']))
$ret .= $trace[$i]['function'].'()';
$at = '';
if(isset($trace[$i]['file']))
{
$at .= '['.$trace[$i]['file'];
if(isset($trace[$i]['line']))
$at .= ':'.$trace[$i]['line'];
$at .= ']';
}
else if(isset($trace[$i]['line']))
$at .= 'line '.$trace[$i]['line'];
if(!empty($at))
$ret .= " called at $at";
$sep = "\n";
}
return $this->log($priority, $ret);
}
//Engine::logMessage
protected function logMessage($priority, $message)
{
switch($priority)
{
case LOG_ALERT:
case LOG_CRIT:
case LOG_EMERG:
$level = 'Alert';
break;
case LOG_DEBUG:
if($this->debug !== TRUE)
return FALSE;
$level = 'Debug';
break;
case LOG_ERR:
$level = 'Error';
break;
case LOG_WARNING:
$level = 'Warning';
break;
case LOG_NOTICE:
$level = 'Notice';
break;
case LOG_INFO:
if($this->verbose < 2 && $this->debug !== TRUE)
return FALSE;
$level = 'Info';
break;
default:
if($this->debug !== TRUE)
return FALSE;
$level = 'Unknown';
break;
}
if(!is_string($message))
$message = var_export($message, TRUE);
$prefix = $_SERVER['SCRIPT_FILENAME'].": $level: ";
$message = str_replace("\n", "\n$prefix", $message);
return $prefix.$message;
}
//private
//properties
private $auth = FALSE;
private $database = FALSE;
private $debug = FALSE;
private $ret = 0;
//methods
private function _attachAuth()
{
if($this->auth !== FALSE)
return TRUE;
$this->auth = Auth::attachDefault($this);
return ($this->auth !== FALSE) ? TRUE : FALSE;
}
}
?>