VvebOIDC/system/core/startup.php

403 lines
12 KiB
PHP

<?php
/**
* Vvveb
*
* Copyright (C) 2022 Ziadin Givan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Vvveb\System\Core;
use function Vvveb\camelToUnderscore;
use Vvveb\System\Extensions\Plugins;
use Vvveb\System\Session;
use Vvveb\System\Sites;
use Vvveb\System\Sqlp\Sqlp;
$storage_dir = DIR_ROOT . 'storage' . DS;
if (is_writable($storage_dir)) {
define('DIR_STORAGE', $storage_dir);
} else {
$storage_dir = sys_get_temp_dir() . DS . 'storage' . DS;
if (! is_dir($storage_dir)) {
@mkdir($storage_dir);
@mkdir($storage_dir . 'compiled-templates' . DS);
@mkdir($storage_dir . 'cache');
@mkdir($storage_dir . 'model');
@mkdir($storage_dir . join(DS, ['model', 'admin']) . DS);
@mkdir($storage_dir . join(DS, ['model/app']) . DS);
@mkdir($storage_dir . join(DS, ['model/install']) . DS);
}
define('DIR_STORAGE', $storage_dir);
}
define('DIR_CACHE', DIR_ROOT . join(DS, ['storage', 'cache']) . DS);
define('DIR_PLUGINS', DIR_ROOT . 'plugins' . DS);
define('DIR_COMPILED_TEMPLATES', DIR_STORAGE . 'compiled-templates' . DS);
define('DIR_BACKUP', DIR_STORAGE . 'backup' . DS);
define('DIR_THEMES', DIR_ROOT . join(DS, ['public', 'themes']) . DS);
define('DIR_PUBLIC', DIR_ROOT . 'public' . DS);
if (APP == 'app') {
define('DIR_THEME', DIR_ROOT . join(DS, ['public', 'themes']) . DS);
} else {
define('DIR_THEME', DIR_ROOT . 'public' . DS . APP . DS);
}
define('DIR_APP', DIR_ROOT . APP . DS);
define('DIR_TEMPLATE', DIR_APP . 'template' . DS);
define('DIR_MEDIA', DIR_PUBLIC . 'media' . DS);
define('CDATA_START', '<![CDATA[');
define('CDATA_END', ']]>');
$error_log = ini_get('error_log');
if (! $error_log || $error_log == '/dev/null') {
ini_set('error_log', $storage_dir . 'logs/error_log');
}
include DIR_SYSTEM . 'session.php';
include DIR_SYSTEM . '/component/component.php';
require_once DIR_SYSTEM . '/core/frontcontroller.php';
require_once DIR_SYSTEM . '/core/view.php';
require_once DIR_SYSTEM . '/functions.php';
require_once DIR_SYSTEM . 'event.php';
function logError($message) {
return error_log($message);
}
function regenerateSQL($sqlFile, $file, $modelName, $namespace) {
$sqlp = new Sqlp();
$sqlp->parseSqlPfile($sqlFile, $modelName, $namespace);
$dir = dirname($file);
if (! file_exists($dir)) {
@mkdir($dir,0755,true);
}
file_put_contents($file, "<?php \n" . $sqlp->generateModel());
}
function autoload($class) {
// project-specific namespace prefix
$prefix = 'Vvveb\\';
// does the class use the namespace prefix?
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
// no, move to the next registered autoloader
return;
}
$relativeClass = substr($class, $len);
$isPlugin = (strncmp($relativeClass, 'Plugins\\', 7) === 0);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .inc
$root = DIR_APP;
//if namespace is Vvveb\System or Vvveb\Plugins load from root dir above app dir
if ((strncmp($relativeClass, 'System\\', 7) === 0) ||
$isPlugin ||
(strncmp($relativeClass, 'Sql\Plugins\\', 11) === 0)) {
$root = DIR_ROOT;
}
//if namespace is App change to app dir
if ((strncmp($relativeClass, 'App\\', 4) === 0)) {
$root = DIR_ROOT . 'app' . DS;
}
//if namespace is Admin change to admin dir
if ((strncmp($relativeClass, 'Admin\\', 6) === 0)) {
$root = DIR_ROOT . 'admin' . DS;
}
$isSql = (substr_compare($relativeClass, 'SQL', -3, 3) === 0);
//check if sql files are changed or missing to regenerate sql class
if ($isSql) {
$isFromPlugin = strpos($relativeClass, 'Plugins\\');
$sqlFile = str_replace(['Sql', '\\'], ['', DS], substr($relativeClass, 0, -3));
//$sqlFile = strtolower(preg_replace('/(?<!^)(?<!\/)[A-Z]/', '-$0', $sqlFile));
if ($isFromPlugin > 0) {
//convert camelCase to snake_case
$sqlFile = strtolower(preg_replace('/(?<!^)(?<!\/)(?<!\\\)[A-Z]/', '-$0', $sqlFile));
$file = basename($sqlFile);
$pluginName = str_replace([DS . 'plugins' . DS, DS . $file], '', $sqlFile) . DS;
$pluginName = strtolower(preg_replace('/(?<!^)(?<!\/)(?<!\\\)[A-Z]/', '-$0', $pluginName));
$sqlFile = DIR_PLUGINS . $pluginName . 'sql' . DS . DB_ENGINE . DS . $file . '.sql';
} else {
$sqlFile = strtolower($sqlFile) . '.sql';
//$sqlFile = DIR_SQL . $sqlFile . '.sql';
}
$name = str_replace(['\\', 'sql' . DS], [DS, ''], strtolower($relativeClass));
$modelName = ucwords(basename(str_replace('sql', '', $name)));
$namespace = ucwords(dirname($name));
if ($namespace != '.') {
$namespace = str_replace('/', '\\', "\\$namespace");
} else {
$namespace = '';
}
$file = DIR_STORAGE . 'model' . DS . APP . DS . $name . '.' . DB_ENGINE . '.php';
$fileExists = file_exists($file);
if (SQL_CHECK || ! $fileExists) {
$sqlExists = false;
if ($isFromPlugin > 0) {
$fullSqlFile = $sqlFile;
$sqlExists = file_exists($fullSqlFile);
} else {
//fallback to admin if sql file is missing in APP
foreach ([APP, 'admin'] as $app) {
$fullSqlFile = DIR_ROOT . $app . DS . 'sql' . DS . DB_ENGINE . DS . $sqlFile;
if (file_exists($fullSqlFile)) {
$sqlExists = true;
break;
}
}
}
if (! $sqlExists) {
throw new \Exception(sprintf(\Vvveb\__('SQL file %s does not exist for %s!'), $sqlFile, $relativeClass));
}
//if the file has not been generated yet or sql files is changed recompile
if (! $fileExists || ((filemtime($fullSqlFile) > filemtime($file)))) {
regenerateSQL($fullSqlFile, $file, $modelName, $namespace);
$fileExists = true;
}
}
if ($fileExists) {
require_once $file;
}
} else {
$file = $root . str_replace('\\', '/', camelToUnderscore($relativeClass)) . '.php';
if ($isPlugin && ($isController = strpos($relativeClass, 'Controller\\'))) {
$file= str_replace('/controller/', '/' . APP . '/controller/', $file);
}
$fileExists = file_exists($file);
if ($fileExists) {
require_once $file;
}
}
}
function autoload_vendor($class) {
$path = str_replace('\\', DS, $class);
$file = DIR_ROOT . 'vendor' . DS . "$path.php";
// if the file exists, require it
if (file_exists($file)) {
require_once $file;
}
}
function exceptionToArray($exception, $file = false) {
$file = $exception->getFile() ? $exception->getFile() : $file;
$lineNo = $exception->getLine() - 1;
//$code = $exception->getCode();
$class = get_class($exception);
$lines = [];
$codeLines = [];
$line = $lineNo;
$code = '';
if ($file && file_exists($file) && ($codeLines = file($file)) && isset($codeLines[$lineNo])) {
$codeLines[$lineNo] = preg_replace("/\n$/","\t // <==\n", $codeLines[$lineNo]);
$lines = array_slice($codeLines, $lineNo - 7, 14);
$line = implode("\n", array_slice($codeLines, $lineNo, 1));
$before = implode("\n", array_slice($codeLines, $lineNo - 6, 5));
$after = implode("\n", array_slice($codeLines, $lineNo + 1, 5));
$code = "$before<b>$line</b>$after";
}
$message = [
'message' => $exception->getMessage(),
'file' => $file,
'line_no' => $lineNo,
'line' => $line,
'lines' => $lines,
'trace' => $exception->getTraceAsString(),
'code' => $codeLines,
];
return $message;
}
function exceptionHandler($exception) {
$message = exceptionToArray($exception, $exception->getFile());
//check if error is generated by a plugin and disable it
pluginErrorCheck($message['file']);
return FrontController::notFound(false, $message, 500);
//pluginErrorCheck($file);
echo '<b>Exception:</b><pre>';
print_r($message);
echo '</pre>';
}
function vErrorHandler($errno, $errstr, $errfile, $errline) {
if (! (error_reporting() && $errno)) {
// This error code is not included in error_reporting, so let it fall
// through to the standard PHP error handler
return false;
}
// $errstr may need to be escaped:
$errstr = htmlspecialchars($errstr);
switch ($errno) {
case E_USER_WARNING:
case E_WARNING:
/*
throw new \Exception($errstr, $errno);
//throw new \Exception($errstr, $errno, 0, $errfile, $errline);
return true;
break;
*/
case E_USER_NOTICE:
break;
case E_ERROR:
case E_USER_ERROR:
/*
echo "<b>ERROR</b> [$errno] $errstr<br />\n";
echo " Fatal error on line $errline in file $errfile";
echo ', PHP ' . PHP_VERSION . ' (' . PHP_OS . ")<br />\n";
*/
//check if error is generated by a plugin and disable it
pluginErrorCheck($errfile);
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
return true;
//return FrontController::notFound(false, $errstr, 500);
}
/* Don't execute PHP internal error handler */
return false;
}
function pluginErrorCheck($file) {
if (($pos = strpos($file, DIR_PLUGINS)) !== false) {
$plugin = \Vvveb\filter('@([^/]+)@', str_replace(DIR_PLUGINS, '', $file));
logError("'$plugin' plugin triggers fatal error.");
if (DISABLE_PLUGIN_ON_ERORR) {
logError(sprintf(\Vvveb\__('Disabling "%s" plugin.'), $plugin));
Plugins::deactivate($plugin);
}
}
}
function fatalErrorHandler() {
$message = error_get_last();
if ($message) {
vErrorHandler($message['type'], $message['message'],$message['file'], $message['line']);
}
}
spl_autoload_register('Vvveb\System\Core\autoload');
//spl_autoload_register('Vvveb\System\Core\autoload_vendor');
set_exception_handler('Vvveb\System\Core\exceptionHandler');
set_error_handler('Vvveb\System\Core\vErrorHandler');
register_shutdown_function('Vvveb\System\Core\fatalErrorHandler');
require DIR_ROOT . '/vendor/autoload.php';
$dbDefault = \Vvveb\config('db.default', 'default');
$connection = \Vvveb\config('db.connections.' . $dbDefault, []);
if ($connection) {
// Define default database configuration
define('DB_ENGINE', $connection['engine']);
define('DB_HOST', $connection['host'] ?? '');
define('DB_USER', $connection['user'] ?? '');
define('DB_PASS', $connection['password'] ?? '');
define('DB_NAME', $connection['database'] ?? '');
define('DB_PREFIX', $connection['prefix'] ?? '');
define('DB_PORT', $connection['port'] ?? null);
} else {/*
define('DB_ENGINE', 'mysqli');
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'vvveb');
define('DB_PREFIX', '');
*/
}
if (defined('DB_ENGINE')) {
define('DIR_SQL', DIR_APP . 'sql' . DS . DB_ENGINE . DS);
}
function start() {
//start session
//Session :: getInstance();
$site_id = false;
if (isset($_GET['site_id'])) {
//check if admin user to allow site id override for editor
if ($admin = \Vvveb\System\User\Admin :: current()) {
$site_id = (int)$_GET['site_id'];
}
}
$site = Sites :: getSiteData($site_id);
if ($site) {
define('SITE_URL', $site['host']);
define('SITE_ID', $site['id'] ?? 1);
//load plugins first for APP
if (APP != 'admin') {
Plugins :: loadPlugins(SITE_ID);
}
FrontController::dispatch();
} else {
define('SITE_URL', $_SERVER['HTTP_HOST'] ?? 'localhost');
define('SITE_ID', 1);
FrontController::notFound(false, 'Website not found!');
}
}