Exception:Array
(
[message] => Call to undefined function Vvveb\config()
[file] => /home/www/vvveb/www/system/core/startup.php
[line_no] => 460
[line] => $dbDefault = \Vvveb\config('db.default', 'default'); // <==
[lines] => Array
(
[0] => //spl_autoload_register('Vvveb\System\Core\autoload_vendor');
[1] => set_exception_handler('Vvveb\System\Core\exceptionHandler');
[2] => set_error_handler('Vvveb\System\Core\vErrorHandler');
[3] => register_shutdown_function('Vvveb\System\Core\fatalErrorHandler');
[4] =>
[5] => require DIR_ROOT . '/vendor/autoload.php';
[6] =>
[7] => $dbDefault = \Vvveb\config('db.default', 'default'); // <==
[8] => $connection = \Vvveb\config('db.connections.' . $dbDefault, []);
[9] =>
[10] => if ($connection) {
[11] => // Define default database configuration
[12] => // skipped when an entry point (e.g. the test bootstrap) already defined
[13] => // DB_ENGINE to run against a different backend than config/db.php
)
[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] => }
)
)