diff --git a/WebFiori/Framework/Access.php b/WebFiori/Framework/Access.php index 4cc6ead8e..289447df3 100644 --- a/WebFiori/Framework/Access.php +++ b/WebFiori/Framework/Access.php @@ -198,6 +198,7 @@ public static function hasPrivilege(string $id, ?string $groupId = null): bool { * @param string $groupId The ID of the group. The ID must not contain * any of the following characters: ';','-',',' or a space. If the name contains * any of the given characters, the group will not be created. + * @param string|null $parentGroupId Optional parent group ID for creating nested groups. * * @return bool If the group is created, the method will return true. * If not, the method will return false. @@ -356,6 +357,14 @@ private function checkID(string $id, PrivilegesGroup $group): bool { return $bool; } + /** + * Private helper that validates and creates a new privileges group. + * + * @param string $groupId The group ID to create. + * @param string|null $parentGroupID Optional parent group ID for nested groups. + * + * @return bool True if group is created, false otherwise. + */ private function createGroupHelper($groupId, ?string $parentGroupID = null): bool { $trimmedId = trim($groupId); @@ -715,6 +724,15 @@ private function isChildGroupHasPrivilege($prId, $groupId, $group): bool { return $retVal; } + /** + * Private helper that validates an ID string. + * + * Checks that ID doesn't contain forbidden characters (';', ' ', '-', ','). + * + * @param string $id The ID string to validate. + * + * @return bool True if ID is valid, false otherwise. + */ private function validateId($id): bool { $len = strlen($id); diff --git a/WebFiori/Framework/App.php b/WebFiori/Framework/App.php index 48fda0dcc..ed7857ee1 100644 --- a/WebFiori/Framework/App.php +++ b/WebFiori/Framework/App.php @@ -271,6 +271,11 @@ public static function getConfig(): ConfigurationDriver { public static function getConfigDriver() : string { return self::$ConfigDriver; } + /** + * Calculates application root path by removing vendor framework path from current directory. + * + * @return string The application root path. + */ private static function getRoot() { //Following lines of code assumes that the class exist on the folder: //\vendor\WebFiori\framework\WebFiori\Framework @@ -367,7 +372,7 @@ public static function initiate(string $appFolder = 'App', string $publicFolder /** * Path to WebFiori's core library. */ - define('WF_CORE_PATH', ROOT_PATH.DS.'vendor'.DS.'WebFiori'.DS.'framework'.DS.'WebFiori'.DS.'framework'); + define('WF_CORE_PATH', ROOT_PATH.DS.'vendor'.DS.'webfiori'.DS.'framework'.DS.'WebFiori'.DS.'Framework'); } self::initAutoLoader(); self::checkStandardLibs(); @@ -472,6 +477,11 @@ public static function start(): App { return self::$LC; } + /** + * Helper for automatic class registration using reflection with configuration options. + * + * @param array $options Configuration array with dir, php-file, folder, class-name, params, callback, constructor-params. + */ private static function autoRegisterHelper($options) { $dir = $options['dir']; $phpFile = $options['php-file']; @@ -498,6 +508,11 @@ private static function autoRegisterHelper($options) { } catch (Error $ex) { } } + /** + * Safe function caller with CLI/web-aware exception handling. + * + * @param callable $func The function to call. + */ private static function call($func) { try { call_user_func($func); @@ -509,6 +524,9 @@ private static function call($func) { } } } + /** + * Validates and defines APP_DIR constant, checking for invalid characters. + */ private function checkAppDir() { if (!defined('APP_DIR')) { @@ -616,6 +634,10 @@ private static function initAutoLoader() { */ if (!class_exists('WebFiori\Framework\Autoload\ClassLoader',false)) { $autoloader = WF_CORE_PATH.DIRECTORY_SEPARATOR.'Autoload'.DIRECTORY_SEPARATOR.'ClassLoader.php'; + + if (!file_exists($autoloader)) { + throw new \Exception('Unable to locate the autoloader class.'); + } require_once $autoloader; } self::$AU = ClassLoader::get(); @@ -642,7 +664,7 @@ public static function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_VERSION', '3.0.0-Beta.30'); + define('WF_VERSION', '3.0.0-beta.31'); /** * A constant that tells the type of framework version. * @@ -658,7 +680,7 @@ public static function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_RELEASE_DATE', '2025-10-22'); + define('WF_RELEASE_DATE', '2025-10-28'); } /** @@ -719,6 +741,9 @@ private function initScheduler() { TasksManager::registerTasks(); } } + /** + * Defines THEMES_PATH constant pointing to the themes directory. + */ private function initThemesPath() { if (!defined('THEMES_PATH')) { $themesDirName = 'Themes'; diff --git a/WebFiori/Framework/Autoload/ClassLoader.php b/WebFiori/Framework/Autoload/ClassLoader.php index 34715a4a3..2f86ce546 100644 --- a/WebFiori/Framework/Autoload/ClassLoader.php +++ b/WebFiori/Framework/Autoload/ClassLoader.php @@ -218,6 +218,7 @@ public function addClassMap(string $className, string $classWithNs, string $path * * * + * @param array $options Array of configuration options (define-root, search-folders, root, on-load-failure). * * @return ClassLoader * * @throws Exception @@ -560,6 +561,12 @@ private function addSearchDirectory(string $dir, $incSubFolders = true, $appendR } } } + /** + * Private helper that recursively processes directories using a stack-based approach. + * + * @param string $cleanDir The directory path to process. + * @param bool $appendRoot Whether to append root path to directory. + */ private function addSearchDirectoryHelper($cleanDir, $appendRoot) { $dirsStack = [$cleanDir]; $root = $this->getRoot(); @@ -576,6 +583,16 @@ private function addSearchDirectoryHelper($cleanDir, $appendRoot) { } } } + /** + * Private helper that scans subdirectories and adds them to the processing stack. + * + * @param string $xDir The current directory being processed. + * @param string $fullPath The full path to the directory. + * @param array $dirsStack The stack of directories to process. + * @param bool $appendRoot Whether to append root path. + * + * @return array Updated directories stack. + */ private function addSearchDirectoryHelper2($xDir, $fullPath, $dirsStack, $appendRoot) { $subDirs = scandir($fullPath); @@ -590,6 +607,12 @@ private function addSearchDirectoryHelper2($xDir, $fullPath, $dirsStack, $append return $dirsStack; } + /** + * Private helper that attempts to create the cache directory if it doesn't exist. + * + * @param string $autoloadCachePath The path where cache should be created. + * @param mixed $autoloadCache The cache data. + */ private function attemptCreateCache($autoloadCachePath, $autoloadCache) { if (!file_exists($autoloadCachePath)) { mkdir($autoloadCachePath, 0777, true); diff --git a/WebFiori/Framework/Cli/Commands/WHelpCommand.php b/WebFiori/Framework/Cli/Commands/WHelpCommand.php index f0ddf589f..594fd7737 100644 --- a/WebFiori/Framework/Cli/Commands/WHelpCommand.php +++ b/WebFiori/Framework/Cli/Commands/WHelpCommand.php @@ -20,7 +20,7 @@ class WHelpCommand extends HelpCommand { public function exec() : int { $argV = $this->getOwner()->getArgsVector(); - if (count($argV) == 0) { + if (count(array_diff($argV, ['--ansi'])) == 0) { $this->printLogo(); } $formattingOptions = [ diff --git a/WebFiori/Framework/Ini.php b/WebFiori/Framework/Ini.php index 053c8b96c..80320b3e9 100644 --- a/WebFiori/Framework/Ini.php +++ b/WebFiori/Framework/Ini.php @@ -34,6 +34,9 @@ class Ini { * */ private static $singleton; + /** + * Private constructor that initializes documentation formatting properties. + */ private function __construct() { $this->docStart = '/**'; $this->docEnd = ' **/'; @@ -57,11 +60,12 @@ public static function createAppDirs() { self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Langs'); self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Apis'); self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Config'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'sto'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'sto'.$DS.'uploads'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'sto'.$DS.'logs'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'sto'.$DS.'sessions'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Storage'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Storage'.$DS.'Uploads'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Storage'.$DS.'Logs'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'Storage'.$DS.'Sessions'); self::mkdir(ROOT_PATH.$DS.'public'); + self::mkdir(ROOT_PATH.$DS.'tests'); } @@ -151,6 +155,13 @@ public static function get(): Ini { return self::$singleton; } + /** + * Attempts to create a directory if it does not exist. + * + * If directory creation fails, code execution stops and outputs JSON with error details. + * + * @param string $dir The directory path to create. + */ public static function mkdir($dir) { self::$DIR_TO_CREATE = $dir; if (!is_dir($dir)) { diff --git a/WebFiori/Framework/PrivilegesGroup.php b/WebFiori/Framework/PrivilegesGroup.php index e8c5a0af2..d63a5eab5 100644 --- a/WebFiori/Framework/PrivilegesGroup.php +++ b/WebFiori/Framework/PrivilegesGroup.php @@ -406,6 +406,11 @@ private function hasPrivilegeHelper(PrivilegesGroup $group, Privilege $p) { return $hasPr; } + /** + * Private helper that removes a child group by ID from the current group's children array. + * + * @param string $gId The ID of the child group to remove. + */ private function removeChildGroupHelper($gId) { for ($x = 0 ; $x < count($this->childGroups()) ; $x++) { $xG = $this->childGroups[$x]; diff --git a/WebFiori/Framework/Scheduler/AbstractTask.php b/WebFiori/Framework/Scheduler/AbstractTask.php index d417b125d..83e0ef97e 100644 --- a/WebFiori/Framework/Scheduler/AbstractTask.php +++ b/WebFiori/Framework/Scheduler/AbstractTask.php @@ -1037,6 +1037,11 @@ public function setTaskName(string $name) : bool { return false; } + /** + * Converts task to JSON with all task properties and time information. + * + * @return Json JSON object containing task data. + */ public function toJSON() : Json { $json = new Json([ 'name' => $this->getTaskName(), @@ -1330,6 +1335,13 @@ private function checkMonthHelper(string $monthField) { return $monthAttrs; } + /** + * Creates standard time attributes structure for scheduling. + * + * @param string $suffix The suffix for attribute names (e.g., 'hour', 'minute'). + * + * @return array Standard attributes array with every, every-x, at-every-x, and at-range keys. + */ private function createAttrs($suffix): array { return [ // * @@ -1388,6 +1400,13 @@ private function dayOfMonthHelper(string $dayOfMonthField) { return $monthDaysAttrs; } + /** + * Gets argument value from HTTP request parameters. + * + * @param string $name The argument name to retrieve. + * + * @return mixed|null The argument value or null if not found. + */ private function getArgValFromRequest($name) { if (Runner::isCLI()) { return null; @@ -1401,6 +1420,13 @@ private function getArgValFromRequest($name) { return $retVal; } + /** + * Gets argument value from CLI command. + * + * @param string $name The argument name to retrieve. + * + * @return mixed|null The argument value or null if not found. + */ private function getArgValFromTerminal($name) { $c = $this->getCommand(); @@ -1454,6 +1480,14 @@ private function getSubExprType(string $expr): string { return $retVal; } + /** + * Checks if current hour matches scheduling rules (exact or interval-based). + * + * @param array $hoursArr Array containing hour scheduling configuration. + * @param int $current The current hour to check. + * + * @return bool True if hour matches scheduling rules. + */ private function isHourHelper($hoursArr, $current) { $hours = $hoursArr['at-every-x-hour']; $retVal = in_array($current, $hours); @@ -1471,6 +1505,14 @@ private function isHourHelper($hoursArr, $current) { return $retVal; } + /** + * Checks if current minute matches scheduling rules (exact or interval-based). + * + * @param array $minuteArr Array containing minute scheduling configuration. + * @param int $current The current minute to check. + * + * @return bool True if minute matches scheduling rules. + */ private function isMinuteHelper($minuteArr, $current) { $minutes = $minuteArr['at-every-x-minute']; $retVal = in_array($current, $minutes); diff --git a/WebFiori/Framework/Scheduler/TasksManager.php b/WebFiori/Framework/Scheduler/TasksManager.php index cb61da33d..b680cfdcb 100644 --- a/WebFiori/Framework/Scheduler/TasksManager.php +++ b/WebFiori/Framework/Scheduler/TasksManager.php @@ -350,6 +350,11 @@ public static function getMonth() : int { public static function getPassword() : string { return self::get()->getPasswordHelper(); } + /** + * Returns all scheduled tasks as an array from the tasks queue. + * + * @return array Array of all scheduled tasks. + */ public static function getTasks() : array { return self::get()->tasksQueue()->toArray(); } @@ -491,6 +496,12 @@ public static function initRoutes() { * * @since 1.0.8 */ + /** + * Logs messages with optional CLI display formatting based on type. + * + * @param string $message The message to log. + * @param string $type The message type (success, error, info, none). + */ public static function log(string $message, string $type = 'none') { self::get()->logsArray[] = $message; @@ -896,6 +907,13 @@ private function getQueueHelper(): Queue { private function isLogEnabledHelper(): bool { return $this->isLogEnabled; } + /** + * Helper that writes task execution details to log file. + * + * @param bool $forced Whether the task was forced to execute. + * @param AbstractTask $task The task that was executed. + * @param File $file The log file to write to. + */ private function logExecHelper($forced, $task, File $file) { if ($forced) { $file->setRawData('Task \''.$task->getTaskName().'\' was forced to executed at '.date(DATE_RFC1123).". Request source IP: ".Util::getClientIP()."\n"); @@ -911,6 +929,12 @@ private function logExecHelper($forced, $task, File $file) { $file->write(); } + /** + * Handles task execution logging by creating log file and calling helper. + * + * @param AbstractTask $task The task that was executed. + * @param bool $forced Whether the task was forced to execute. + */ private function logTaskExecution($task,$forced = false) { if ($this->isLogEnabled) { $logsPath = ROOT_PATH.DS.APP_DIR.DS.'sto'.DS.'logs'; diff --git a/WebFiori/Framework/Ui/WebPage.php b/WebFiori/Framework/Ui/WebPage.php index 20283c9b7..2e0dc3f04 100644 --- a/WebFiori/Framework/Ui/WebPage.php +++ b/WebFiori/Framework/Ui/WebPage.php @@ -1249,10 +1249,18 @@ private function getHead() { return $headNode; } + /** + * Prepares for rendering by sorting and invoking before-render callbacks. + */ public function beforeRender() { $this->beforeRenderCallbacks->insertionSort(false); $this->invokeBeforeRender(); } + /** + * Recursively executes before-render callbacks with dynamic callback handling. + * + * @param int $current The current callback index being processed. + */ private function invokeBeforeRender(int $current = 0) { $currentCount = count($this->beforeRenderCallbacks); diff --git a/WebFiori/Framework/Util.php b/WebFiori/Framework/Util.php index 99928cc13..c94bef02e 100644 --- a/WebFiori/Framework/Util.php +++ b/WebFiori/Framework/Util.php @@ -168,6 +168,15 @@ public static function binaryString($intVal) { return false; } + /** + * Extracts and formats a class name from a file path. + * + * Takes the filename, capitalizes the first letter, and removes the file extension. + * + * @param string $filePath The file path to extract class name from. + * + * @return string The formatted class name. + */ public static function extractClassName($filePath) { $expl = explode(DS, $filePath); $classFile = $expl[count($expl) - 1]; @@ -602,6 +611,17 @@ public static function uniord($u) { return false; } + /** + * Private helper that builds an array of 7 consecutive date strings. + * + * Handles month and year rollovers when days exceed month limits. + * + * @param int $startDay The starting day of the month. + * @param int $daysInMonth The number of days in the current month. + * @param int $startMonth The starting month. + * + * @return array Array of date strings in 'YYYY-MM-DD' format. + */ private static function _buildGdatesArr($startDay, $daysInMonth, $startMonth) { $datesArr = []; diff --git a/public/index.php b/public/index.php index 88e752ebc..a671e5c9b 100644 --- a/public/index.php +++ b/public/index.php @@ -1,86 +1,13 @@ loadAppClass(); - /** - * This where magic will start. - * - * Planting application seed into the ground and make your work bloom. - */ - App::start(); +require __DIR__.$DS.'..'.$DS.'vendor'.$DS.'autoload.php'; - if (App::getRunner()->isCLI() === true) { - App::getRunner()->start(); - } else { - //route user request. - SessionsManager::start('wf-session'); - Router::route(Request::getRequestedURI()); - Response::send(); - } - } - /** - * Creates a single instance of the class. - */ - public static function create() { - if (self::$instance === null) { - self::$instance = new Index(); - } - } - /** - * Try to load the class 'App'. - * - * @throws Exception - */ - private function loadAppClass() { - $DS = DIRECTORY_SEPARATOR; - $frameworkPath = ROOT_PATH.$DS.'WebFiori'.$DS.'framework'; - $corePath = $frameworkPath; - $rootClass = $DS.'App.php'; +use WebFiori\Framework\App; - if (file_exists($corePath.$rootClass)) { - define('WF_CORE_PATH', $corePath); - require_once $corePath.$rootClass; - } else { - throw new Exception('Unable to locate the class "App".'); - } - } -} -Index::create(); +App::initiate('App', 'public', __DIR__); +App::start(); +App::handle();