Exception:
Array
(
    [message] => Call to undefined function Vvveb\config()
    [file] => /home/www/vvveb/www/system/core/startup.php
    [line_no] => 463
    [line] => if ($connection) {	 // <==

    [lines] => Array
        (
            [0] => register_shutdown_function('Vvveb\System\Core\fatalErrorHandler');

            [1] => 

            [2] => require DIR_ROOT . '/vendor/autoload.php';

            [3] => 

            [4] => $dbDefault  = \Vvveb\config('db.default', 'default');

            [5] => $connection = \Vvveb\config('db.connections.' . $dbDefault, []);

            [6] => 

            [7] => if ($connection) {	 // <==

            [8] => 	// Define default database configuration

            [9] => 	// skipped when an entry point (e.g. the test bootstrap) already defined

            [10] => 	// DB_ENGINE to run against a different backend than config/db.php

            [11] => 	if (!defined('DB_ENGINE')) {

            [12] => 		define('DB_ENGINE', $connection['engine']);

            [13] => 		define('DB_HOST', $connection['host'] ?? '');

        )

    [trace] => #0 /home/www/vvveb/www/index.php(102): require_once()
#1 /home/www/vvveb/www/public/index.php(28): include('...')
#2 {main}
    [code] => Array
        (
            [0] =>  

            [2] => /**

            [3] =>  * Vvveb

            [4] =>  *

            [5] =>  * Copyright (C) 2022  Ziadin Givan

            [6] =>  *

            [7] =>  * This program is free software: you can redistribute it and/or modify

            [8] =>  * it under the terms of the GNU Affero General Public License as

            [9] =>  * published by the Free Software Foundation, either version 3 of the

            [10] =>  * License, or (at your option) any later version.

            [11] =>  *

            [12] =>  * This program is distributed in the hope that it will be useful,

            [13] =>  * but WITHOUT ANY WARRANTY; without even the implied warranty of

            [14] =>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

            [15] =>  * GNU Affero General Public License for more details.

            [16] =>  *

            [17] =>  * You should have received a copy of the GNU Affero General Public License

            [18] =>  * along with this program.  If not, see .

            [19] =>  *

            [20] =>  */

            [21] => 

            [22] => namespace Vvveb\System\Core;

            [23] => 

            [24] => use function Vvveb\camelToUnderscore;

            [25] => use Vvveb\System\Extensions\Plugins;

            [26] => use Vvveb\System\Sites;

            [27] => use Vvveb\System\Sqlp\Sqlp;

            [28] => 

            [29] => $storage_dir = DIR_ROOT . 'storage' . DS;

            [30] => 

            [31] => if (is_writable($storage_dir)) {

            [32] => 	define('DIR_STORAGE', $storage_dir);

            [33] => } else {

            [34] => 	$storage_dir = sys_get_temp_dir() . DS . 'storage' . DS;

            [35] => 

            [36] => 	if (!is_dir($storage_dir)) {

            [37] => 		@mkdir($storage_dir);

            [38] => 		@mkdir($storage_dir . 'compiled-templates' . DS);

            [39] => 		@mkdir($storage_dir . 'cache');

            [40] => 		@mkdir($storage_dir . 'model');

            [41] => 		@mkdir($storage_dir . implode(DS, ['model', 'admin']) . DS);

            [42] => 		@mkdir($storage_dir . implode(DS, ['model/app']) . DS);

            [43] => 		@mkdir($storage_dir . implode(DS, ['model/install']) . DS);

            [44] => 	}

            [45] => 

            [46] => 	define('DIR_STORAGE', $storage_dir);

            [47] => }

            [48] => 

            [49] => define('DIR_CACHE', DIR_ROOT . implode(DS, ['storage', 'cache']) . DS);

            [50] => define('DIR_PLUGINS', DIR_ROOT . 'plugins' . DS);

            [51] => define('DIR_COMPILED_TEMPLATES', DIR_STORAGE . 'compiled-templates' . DS);

            [52] => define('DIR_BACKUP', DIR_STORAGE . 'backup' . DS);

            [53] => define('DIR_THEMES', DIR_ROOT . implode(DS, ['public', 'themes']) . DS);

            [54] => define('DIR_PUBLIC', DIR_ROOT . 'public' . DS);

            [55] => 

            [56] => if (APP === 'app') {

            [57] => 	define('DIR_THEME', DIR_ROOT . implode(DS, ['public', 'themes']) . DS);

            [58] => } else {

            [59] => 	define('DIR_THEME', DIR_ROOT . 'public' . DS . APP . DS);

            [60] => }

            [61] => 

            [62] => define('DIR_APP', DIR_ROOT . APP . DS);

            [63] => define('DIR_TEMPLATE', DIR_APP . 'template' . DS);

            [64] => define('DIR_MEDIA', DIR_PUBLIC . 'media' . DS);

            [65] => define('CDATA_START', ' define('CDATA_END', ']]>');

            [67] => 

            [68] => $error_log = ini_get('error_log');

            [69] => 

            [70] => if (!$error_log || $error_log === '/dev/null') {

            [71] => 	ini_set('error_log', $storage_dir . 'logs/error_log');

            [72] => }

            [73] => 

            [74] => include DIR_SYSTEM . 'session.php';

            [75] => 

            [76] => include DIR_SYSTEM . 'component/component.php';

            [77] => 

            [78] => require_once DIR_SYSTEM . 'core/frontcontroller.php';

            [79] => 

            [80] => require_once DIR_SYSTEM . 'core/view.php';

            [81] => 

            [82] => require_once DIR_SYSTEM . 'functions.php';

            [83] => 

            [84] => require_once DIR_SYSTEM . 'event.php';

            [85] => 

            [86] => /**

            [87] =>  * Log an error message to the error log.

            [88] =>  *

            [89] =>  * @param string $message The error message to log.

            [90] =>  *

            [91] =>  * @return bool True if the message was successfully written to the log.

            [92] =>  */

            [93] => function logError(string $message): bool {

            [94] => 	return error_log($message);

            [95] => }

            [96] => 

            [97] => /**

            [98] =>  * Regenerate a SQL model file from a SQL definition file.

            [99] =>  *

            [100] =>  * @param string $sqlFile   Path to the SQL definition file.

            [101] =>  * @param string $file      Output path for the generated PHP model file.

            [102] =>  * @param string $modelName The model class name.

            [103] =>  * @param string $namespace The namespace for the generated model class.

            [104] =>  *

            [105] =>  * @return void

            [106] =>  */

            [107] => function regenerateSQL(string $sqlFile, string $file, string $modelName, string $namespace): void {

            [108] => 	$sqlp = new Sqlp();

            [109] => 

            [110] => 	$sqlp->parseSqlPfile($sqlFile, $modelName, $namespace);

            [111] => 

            [112] => 	$dir = dirname($file);

            [113] => 

            [114] => 	if (!file_exists($dir)) {

            [115] => 		@mkdir($dir, 0755, true);

            [116] => 	}

            [117] => 

            [118] => 	$code = "generateModel();

            [119] => 

            [120] => 	//never write a broken model to disk, a parse error here would poison every request

            [121] => 	try {

            [122] => 		$tokens = token_get_all($code, TOKEN_PARSE);

            [123] => 		unset($tokens);

            [124] => 	} catch (\ParseError $e) {

            [125] => 		throw new \Exception(sprintf('Generated SQL model %s has invalid syntax!', $file));

            [126] => 	}

            [127] => 

            [128] => 	//write to a temp file and rename, so concurrent requests never read a half written model

            [129] => 	$tmp = $file . '.' . getmypid() . '.tmp';

            [130] => 	file_put_contents($tmp, $code);

            [131] => 	rename($tmp, $file);

            [132] => }

            [133] => 

            [134] => /**

            [135] =>  * Autoload classes under the Vvveb namespace.

            [136] =>  *

            [137] =>  * Resolves class names to file paths, handling plugin SQL classes,

            [138] =>  * system classes, app/admin controllers, and standard models.

            [139] =>  *

            [140] =>  * @param string $class The fully-qualified class name to autoload.

            [141] =>  *

            [142] =>  * @return void

            [143] =>  */

            [144] => function autoload(string $class): void {

            [145] => 	// project-specific namespace prefix

            [146] => 	$prefix = 'Vvveb\\';

            [147] => 

            [148] => 	// does the class use the namespace prefix?

            [149] => 	$len = strlen($prefix);

            [150] => 

            [151] => 	if (strncmp($prefix, $class, $len) !== 0) {

            [152] => 		// no, move to the next registered autoloader

            [153] => 		return;

            [154] => 	}

            [155] => 

            [156] => 	$relativeClass = substr($class, $len);

            [157] => 	$isPlugin      = str_starts_with($relativeClass, 'Plugins\\');

            [158] => 	// replace the namespace prefix with the base directory, replace namespace

            [159] => 	// separators with directory separators in the relative class name, append

            [160] => 	// with .inc

            [161] => 	$root = DIR_APP;

            [162] => 

            [163] => 	//if namespace is Vvveb\System or Vvveb\Plugins load from root dir above app dir

            [164] => 	if ((str_starts_with($relativeClass, 'System\\')) ||

            [165] => 		$isPlugin ||

            [166] => 		(str_starts_with($relativeClass, 'Sql\Plugins\\'))) {

            [167] => 		$root = DIR_ROOT;

            [168] => 	}

            [169] => 

            [170] => 	//if namespace is App change to app dir

            [171] => 	if (str_starts_with($relativeClass, 'App\\')) {

            [172] => 		$root = DIR_ROOT . 'app' . DS;

            [173] => 	}

            [174] => 

            [175] => 	//if namespace is Admin change to admin dir

            [176] => 	if (str_starts_with($relativeClass, 'Admin\\')) {

            [177] => 		$root = DIR_ROOT . 'admin' . DS;

            [178] => 	}

            [179] => 

            [180] => 	//sql model classes are generated from .sql definition files

            [181] => 	if (substr_compare($relativeClass, 'SQL', -3, 3) === 0) {

            [182] => 		autoloadSqlModel($relativeClass);

            [183] => 

            [184] => 		return;

            [185] => 	}

            [186] => 

            [187] => 	//load any other Vvveb class from the app/system/plugin directories

            [188] => 	$file     = $root . str_replace('\\', '/', camelToUnderscore($relativeClass)) . '.php';

            [189] => 	$fileAsIs = $root . str_replace('\\', '/', strtolower($relativeClass)) . '.php';

            [190] => 

            [191] => 	if ($isPlugin && ($isController = str_contains($relativeClass, 'Controller\\'))) {

            [192] => 		$file     = str_replace('/controller/', '/' . APP . '/controller/', $file);

            [193] => 		$fileAsIs = str_replace('/controller/', '/' . APP . '/controller/', $fileAsIs);

            [194] => 	}

            [195] => 

            [196] => 	foreach ([$file, $fileAsIs] as $f) {

            [197] => 		if (file_exists($f)) {

            [198] => 			require_once $f;

            [199] => 

            [200] => 			return;

            [201] => 		}

            [202] => 	}

            [203] => 

            [204] => 	//throw(new \Exception($class . ' could not be loaded!'));

            [205] => }

            [206] => 

            [207] => /**

            [208] =>  * Resolve, generate when stale, and load a SQL model class.

            [209] =>  *

            [210] =>  * Called by autoload() for every class ending in SQL.

            [211] =>  *

            [212] =>  * @param string $relativeClass Class name without the Vvveb\ prefix.

            [213] =>  */

            [214] => function autoloadSqlModel(string $relativeClass): void {

            [215] => 	$isFromPlugin = str_contains($relativeClass, 'Plugins\\');

            [216] => 	$sqlFile      = str_replace(['Sql', '\\'], ['', DS], substr($relativeClass, 0, -3));

            [217] => 

            [218] => 	if ($isFromPlugin) {

            [219] => 		//convert camelCase to snake_case

            [220] => 		$sqlFile    = strtolower(preg_replace('/(? 		$file       = basename($sqlFile);

            [222] => 		$pluginName = str_replace([DS . 'plugins' . DS, DS . $file], '', $sqlFile) . DS;

            [223] => 		$pluginName = strtolower(preg_replace('/(? 		$sqlFile    = DIR_PLUGINS . $pluginName . 'sql' . DS . DB_ENGINE . DS . $file . '.sql';

            [225] => 	} else {

            [226] => 		$sqlFile   = strtolower($sqlFile) . '.sql';

            [227] => 	}

            [228] => 

            [229] => 	$name      = str_replace(['\\', 'sql' . DS], [DS, ''], strtolower($relativeClass));

            [230] => 	$modelName = ucwords(basename(str_replace('sql', '', $name)));

            [231] => 	$namespace = ucwords(dirname($name));

            [232] => 

            [233] => 	if ($namespace !== '.') {

            [234] => 		$namespace = str_replace('/', '\\', "\\$namespace");

            [235] => 	} else {

            [236] => 		$namespace = '';

            [237] => 	}

            [238] => 

            [239] => 	$file       = DIR_STORAGE . 'model' . DS . APP . DS . $name . '.' . DB_ENGINE . '.php';

            [240] => 	$fileExists = file_exists($file);

            [241] => 

            [242] => 	if (SQL_CHECK || !$fileExists) {

            [243] => 		$sqlExists = false;

            [244] => 

            [245] => 		if ($isFromPlugin) {

            [246] => 			$fullSqlFile = $sqlFile;

            [247] => 			$sqlExists   = file_exists($fullSqlFile);

            [248] => 		} else {

            [249] => 			//fallback to admin if sql file is missing in APP

            [250] => 			foreach ([APP, 'admin'] as $app) {

            [251] => 				$fullSqlFile = DIR_ROOT . $app . DS . 'sql' . DS . DB_ENGINE . DS . $sqlFile;

            [252] => 

            [253] => 				if (file_exists($fullSqlFile)) {

            [254] => 					$sqlExists = true;

            [255] => 

            [256] => 					break;

            [257] => 				}

            [258] => 			}

            [259] => 		}

            [260] => 

            [261] => 		if (!$sqlExists) {

            [262] => 			throw new \Exception(sprintf(\Vvveb\__('SQL file %s does not exist for %s!'), $sqlFile, $relativeClass));

            [263] => 		}

            [264] => 		//if the file has not been generated yet or sql files is changed recompile

            [265] => 		if (!$fileExists || ((filemtime($fullSqlFile) > filemtime($file)))) {

            [266] => 			regenerateSQL($fullSqlFile, $file, $modelName, $namespace);

            [267] => 			$fileExists = true;

            [268] => 		}

            [269] => 	}

            [270] => 

            [271] => 	require_once $file;

            [272] => }

            [273] => 

            [274] => /**

            [275] =>  * Autoload vendor classes from the vendor directory.

            [276] =>  *

            [277] =>  * @param string $class The fully-qualified class name to autoload.

            [278] =>  *

            [279] =>  * @return void

            [280] =>  */

            [281] => function autoload_vendor(string $class): void {

            [282] => 	$path = str_replace('\\', DS, $class);

            [283] => 

            [284] => 	$file = DIR_ROOT . 'vendor' . DS . "$path.php";

            [285] => 

            [286] => 	// if the file exists, require it

            [287] => 	if (file_exists($file)) {

            [288] => 		require_once $file;

            [289] => 	}

            [290] => }

            [291] => 

            [292] => /**

            [293] =>  * Convert a throwable exception to a structured array with context.

            [294] =>  *

            [295] =>  * Extracts the message, file, line, surrounding code context, and stack trace

            [296] =>  * from the given exception.

            [297] =>  *

            [298] =>  * @param \Throwable   $exception The exception to convert.

            [299] =>  * @param string|false $file      Optional override for the source file path.

            [300] =>  *

            [301] =>  * @return array Structured error information array.

            [302] =>  */

            [303] => function exceptionToArray(\Throwable $exception, string|false $file = false): array {

            [304] => 	$file   = $exception->getFile() ?: $file;

            [305] => 	$lineNo = $exception->getLine() - 1;

            [306] => 	//$code = $exception->getCode();

            [307] => 	$class     = $exception::class;

            [308] => 	$lines     = [];

            [309] => 	$codeLines = [];

            [310] => 	$line      = $lineNo;

            [311] => 	$code      = '';

            [312] => 

            [313] => 	if (!defined('CLI') && $file && file_exists($file) && ($codeLines = file($file)) && isset($codeLines[$lineNo])) {

            [314] => 		$codeLines[$lineNo] = preg_replace("/\n$/", "\t // <==\n", $codeLines[$lineNo]);

            [315] => 		$lines              = array_slice($codeLines, $lineNo - 7, 14);

            [316] => 		$line               = implode("\n", array_slice($codeLines, $lineNo, 1));

            [317] => 		$before             = implode("\n", array_slice($codeLines, $lineNo - 6, 5));

            [318] => 		$after              = implode("\n", array_slice($codeLines, $lineNo + 1, 5));

            [319] => 		$code               = "$before$line$after";

            [320] => 	}

            [321] => 

            [322] => 	return [

            [323] => 		'message' => $exception->getMessage(),

            [324] => 		'file'    => $file,

            [325] => 		'line_no' => $lineNo,

            [326] => 		'line'    => $line,

            [327] => 		'lines'   => $lines,

            [328] => 		'trace'   => $exception->getTraceAsString(),

            [329] => 		'code'    => $codeLines,

            [330] => 	];

            [331] => }

            [332] => 

            [333] => /**

            [334] =>  * Handle uncaught exceptions globally.

            [335] =>  *

            [336] =>  * Converts the exception to an array, checks for plugin errors, and

            [337] =>  * outputs debug information when DEBUG mode is enabled.

            [338] =>  *

            [339] =>  * @param \Throwable $exception The uncaught exception.

            [340] =>  *

            [341] =>  * @return void

            [342] =>  */

            [343] => function exceptionHandler(\Throwable $exception): void {

            [344] => 	static $handled;

            [345] => 

            [346] => 	if ($handled) {

            [347] => 		if (DEBUG) {

            [348] => 			echo $exception->getMessage() . ' in ' . $exception->getFile() . ' on line ' . $exception->getLine();

            [349] => 		} else {

            [350] => 			exit('Server error, enable debug for more info');

            [351] => 		}

            [352] => 

            [353] => 		return;

            [354] => 	}

            [355] => 	$handled = true;

            [356] => 

            [357] => 	$message = exceptionToArray($exception, $exception->getFile());

            [358] => 	//check if error is generated by a plugin and disable it

            [359] => 	pluginErrorCheck($message['file']);

            [360] => 

            [361] => 	//FrontController::notFound(false, $message, 500);

            [362] => 	//pluginErrorCheck($file);

            [363] => 	if (DEBUG) {

            [364] => 		echo 'Exception:
';

            [365] => 		print_r($message);

            [366] => 		echo '
'; [367] => } [368] => } [369] => [370] => /** [371] => * Custom error handler for PHP errors. [372] => * [373] => * Checks if the error is within the current error_reporting level, handles [374] => * plugin errors, and throws ErrorException for fatal errors. [375] => * [376] => * @param int $errno The error level constant (e.g. E_WARNING). [377] => * @param string $errstr The error message. [378] => * @param string $errfile The filename where the error occurred. [379] => * @param int $errline The line number where the error occurred. [380] => * [381] => * @return bool Returns false to allow the PHP error handler to continue. [382] => */ [383] => function vErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool { [384] => if (!(error_reporting() && $errno)) { [385] => // This error code is not included in error_reporting, so let it fall [386] => // through to the standard PHP error handler [387] => return false; [388] => } [389] => // $errstr may need to be escaped: [390] => $errstr = htmlspecialchars($errstr); [391] => [392] => switch ($errno) { [393] => case E_USER_WARNING: [394] => case E_WARNING: [395] => case E_USER_NOTICE: [396] => break; [397] => [398] => case E_ERROR: [399] => case E_USER_ERROR: [400] => //check if error is generated by a plugin and disable it [401] => pluginErrorCheck($errfile); [402] => [403] => throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); [404] => //return FrontController::notFound(false, $errstr, 500); [405] => } [406] => [407] => // Don't execute PHP internal error handler [408] => return false; [409] => } [410] => [411] => /** [412] => * Check if an error originates from a plugin and optionally disable it. [413] => * [414] => * If the error file path is within the plugins directory, logs the error [415] => * and deactivates the offending plugin when DISABLE_PLUGIN_ON_ERORR is true. [416] => * [417] => * @param string $file The file path where the error occurred. [418] => * [419] => * @return bool True if the error is from a plugin, false otherwise. [420] => */ [421] => function pluginErrorCheck(string $file): bool { [422] => if (str_contains($file, DIR_PLUGINS)) { [423] => $plugin = \Vvveb\filter('@([^/]+)@', str_replace(DIR_PLUGINS, '', $file)); [424] => logError("'$plugin' plugin triggers fatal error."); [425] => [426] => if (DISABLE_PLUGIN_ON_ERORR) { [427] => logError(sprintf(\Vvveb\__('Disabling "%s" plugin.'), $plugin)); [428] => Plugins::deactivate($plugin); [429] => } [430] => [431] => return true; [432] => } [433] => [434] => return false; [435] => } [436] => [437] => /** [438] => * Shutdown handler that catches fatal errors. [439] => * [440] => * Retrieves the last error and delegates it to the custom error handler. [441] => * [442] => * @return void [443] => */ [444] => function fatalErrorHandler(): void { [445] => $message = error_get_last(); [446] => [447] => if ($message) { [448] => vErrorHandler($message['type'], $message['message'], $message['file'], $message['line']); [449] => } [450] => } [451] => [452] => spl_autoload_register('Vvveb\System\Core\autoload'); [453] => //spl_autoload_register('Vvveb\System\Core\autoload_vendor'); [454] => set_exception_handler('Vvveb\System\Core\exceptionHandler'); [455] => set_error_handler('Vvveb\System\Core\vErrorHandler'); [456] => register_shutdown_function('Vvveb\System\Core\fatalErrorHandler'); [457] => [458] => require DIR_ROOT . '/vendor/autoload.php'; [459] => [460] => $dbDefault = \Vvveb\config('db.default', 'default'); [461] => $connection = \Vvveb\config('db.connections.' . $dbDefault, []); [462] => [463] => if ($connection) { // <== [464] => // Define default database configuration [465] => // skipped when an entry point (e.g. the test bootstrap) already defined [466] => // DB_ENGINE to run against a different backend than config/db.php [467] => if (!defined('DB_ENGINE')) { [468] => define('DB_ENGINE', $connection['engine']); [469] => define('DB_HOST', $connection['host'] ?? ''); [470] => define('DB_USER', $connection['user'] ?? ''); [471] => define('DB_PASS', $connection['password'] ?? ''); [472] => define('DB_NAME', $connection['database'] ?? ''); [473] => define('DB_PREFIX', $connection['prefix'] ?? ''); [474] => define('DB_PORT', $connection['port'] ?? null); [475] => } [476] => } [477] => [478] => if (defined('DB_ENGINE')) { [479] => define('DIR_SQL', DIR_APP . 'sql' . DS . DB_ENGINE . DS); [480] => } [481] => [482] => /** [483] => * Bootstrap the application by resolving the site and dispatching the request. [484] => * [485] => * Loads the site configuration, defines site constants, loads plugins for [486] => * the frontend, and dispatches the request via the FrontController. [487] => * [488] => * @return void [489] => */ [490] => function start(): void { [491] => $site_id = false; [492] => $path = $_SERVER['REQUEST_URI'] ?? '/'; [493] => [494] => if (isset($_GET['site_id'])) { [495] => //check if admin user to allow site id override for editor [496] => if ($admin = \Vvveb\System\User\Admin :: current()) { [497] => $site_id = (int)$_GET['site_id']; [498] => } [499] => } [500] => [501] => $site = Sites :: getSiteData($site_id, $path); [502] => [503] => if ($site) { [504] => define('SITE_URL', $site['host']); [505] => define('SITE_ID', $site['site_id'] ?? 1); [506] => define('SITE_URI', $site['uri'] ?? ''); [507] => define('SITE_PATH', $site['path'] ?? ''); [508] => [509] => //load plugins first for APP [510] => if (APP !== 'admin') { [511] => Plugins :: loadPlugins(SITE_ID); [512] => } [513] => [514] => defined('DISPATCH') || define('DISPATCH', true); [515] => [516] => if (DISPATCH) { [517] => FrontController::dispatch($site); [518] => } [519] => } else { [520] => define('SITE_URL', $_SERVER['HTTP_HOST'] ?? 'localhost'); [521] => define('SITE_ID', 1); [522] => define('SITE_URI', ''); [523] => define('SITE_PATH', ''); [524] => FrontController::notFound(false, 'Website not found!'); [525] => } [526] => } ) )