From f8e9ed98f1ac4b1c86cceff30a92ffd6107a05d2 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 24 Jul 2024 17:16:49 +0300 Subject: [PATCH 01/17] chore: Code Quality Improvments --- .../framework/test/PrivilegesGroupTest.php | 2 + .../test/scheduler/SchedulerTaskTest.php | 131 +++++++++++++++++- webfiori/framework/scheduler/AbstractTask.php | 57 +++++--- 3 files changed, 166 insertions(+), 24 deletions(-) diff --git a/tests/webfiori/framework/test/PrivilegesGroupTest.php b/tests/webfiori/framework/test/PrivilegesGroupTest.php index bad1387ed..54d5e65fe 100644 --- a/tests/webfiori/framework/test/PrivilegesGroupTest.php +++ b/tests/webfiori/framework/test/PrivilegesGroupTest.php @@ -105,6 +105,7 @@ public function testRemoveParentGroup00($gArr) { public function testRemoveParentGroup01() { $child = new PrivilegesGroup('CH_GROUP_1', 'Child Group #1'); $this->assertFalse($child->setParentGroup()); + } /** * @test @@ -141,6 +142,7 @@ public function testSetID01() { public function testSetParentGroup00() { $child = new PrivilegesGroup('CH_GROUP_1', 'Child Group #1'); $parentGroup = new PrivilegesGroup('PARENT_1', 'Parent Group #1'); + $this->assertTrue($parentGroup->setParentGroup(null)); $this->assertFalse($child->setParentGroup($child)); $this->assertTrue($child->setParentGroup($parentGroup)); $this->assertSame($parentGroup,$child->getParentGroup()); diff --git a/tests/webfiori/framework/test/scheduler/SchedulerTaskTest.php b/tests/webfiori/framework/test/scheduler/SchedulerTaskTest.php index e06af5c37..6f77a85e8 100644 --- a/tests/webfiori/framework/test/scheduler/SchedulerTaskTest.php +++ b/tests/webfiori/framework/test/scheduler/SchedulerTaskTest.php @@ -4,6 +4,8 @@ use Exception; use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use webfiori\framework\exceptions\InvalidCRONExprException; +use webfiori\framework\Privilege; use webfiori\framework\scheduler\BaseTask; use webfiori\framework\scheduler\TaskArgument; use webfiori\framework\scheduler\TasksManager; @@ -79,7 +81,7 @@ public function testAttributes06() { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Invalid argument type. Expected 'string' or 'webfiori\\framework\\scheduler\\TaskArgument'"); $job = new BaseTask(); - $job->addExecutionArg(new \webfiori\framework\Privilege()); + $job->addExecutionArg(new Privilege()); } /** * @test @@ -376,6 +378,30 @@ public function testConstructor28() { $this->expectExceptionMessage('Invalid cron expression: \'5-a * * * *\'.'); $task = new BaseTask('5-a * * * *'); } + /** + * @test + */ + public function testConstructor29() { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid cron expression: \'5-7,8- * * * *\'.'); + $task = new BaseTask('5-7,8- * * * *'); + } + /** + * @test + */ + public function testConstructor30() { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid cron expression: \'5/,8- * * * *\'.'); + $task = new BaseTask('5/,8- * * * *'); + } + /** + * @test + */ + public function testConstructor31() { + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid cron expression: \'5/b,8- * * * *\'.'); + $task = new BaseTask('5/b,8- * * * *'); + } /** * @test */ @@ -708,6 +734,37 @@ public function testIsDayOfWeek02() { TasksManager::setDayOfWeek(5); $this->assertFalse($job->isDayOfWeek()); } + /** + * @test + */ + public function testIsDayOfWeek03() { + $job = new BaseTask(); + $job->everyWeek(); + + TasksManager::setDayOfWeek(0); + TasksManager::setHour(0); + $this->assertTrue($job->isDayOfWeek()); + $this->assertTrue($job->isHour()); + + } + /** + * @test + */ + public function testIsDayOfWeek04() { + $this->expectException(InvalidCRONExprException::class); + $this->expectExceptionMessage("Invalid cron expression: '5 4 * * 0-a,5-6'"); + $job = new BaseTask('5 4 * * 0-a,5-6'); + + } + /** + * @test + */ + public function testIsDayOfWeek05() { + $this->expectException(InvalidCRONExprException::class); + $this->expectExceptionMessage("Invalid cron expression: '5 4 * * 0-,5-6'"); + $job = new BaseTask('5 4 * * 0-,5-6'); + + } /** * @test */ @@ -774,7 +831,40 @@ public function testIsHour02() { $this->assertFalse($job->isHour()); } + /** + * @test + */ + public function testIsHour03() { + $job = new BaseTask(); + $job->everyXHour(2); + + for ($x = 0 ; $x < 24 ; $x++) { + TasksManager::setHour($x); + + if ($x % 2 == 0) { + $this->assertTrue($job->isHour()); + } else { + $this->assertFalse($job->isHour()); + } + } + } + /** + * @test + */ + public function testIsHour04() { + $job = new BaseTask(); + $job->everyXHour(3); + for ($x = 0 ; $x < 24 ; $x++) { + TasksManager::setHour($x); + + if ($x % 3 == 0) { + $this->assertTrue($job->isHour()); + } else { + $this->assertFalse($job->isHour()); + } + } + } /** * @test */ @@ -914,6 +1004,24 @@ public function testIsMonth02() { TasksManager::setMinute(1); $this->assertFalse($job->isMinute()); } + /** + * @test + */ + public function testIsMonth03() { + $this->expectException(InvalidCRONExprException::class); + $this->expectExceptionMessage("Invalid cron expression: '* 4 * 1-3,9-b *"); + $job = new BaseTask('* 4 * 1-3,9-b *'); + + } + /** + * @test + */ + public function testIsMonth04() { + $this->expectException(InvalidCRONExprException::class); + $this->expectExceptionMessage("Invalid cron expression: '* 4 * 1-3,9- *"); + $job = new BaseTask('* 4 * 1-3,9- *'); + + } /** * @test */ @@ -1255,4 +1363,25 @@ public function testEveryXMinute00() { $this->assertTrue($task->isDayOfMonth()); } + /** + * @test + */ + public function testEveryXMinute01() { + $task = new BaseTask(); + $task->everyXMinuts(17); + TasksManager::setDayOfMonth(15); + TasksManager::setHour(23); + TasksManager::setMonth(5); + TasksManager::setMinute(33); + + for ($x = 1 ; $x < 60 ; $x++) { + TasksManager::setMinute($x); + if ($x % 17 == 0) { + $this->assertTrue($task->isMinute()); + } else { + $this->assertFalse($task->isMinute()); + } + } + + } } diff --git a/webfiori/framework/scheduler/AbstractTask.php b/webfiori/framework/scheduler/AbstractTask.php index 0e0af20e0..c548cb20f 100644 --- a/webfiori/framework/scheduler/AbstractTask.php +++ b/webfiori/framework/scheduler/AbstractTask.php @@ -353,6 +353,33 @@ public function dailyAt(int $hour = 0, int $minute = 0) : bool { public function everyHour() { $this->cron('0 * * * *'); } + /** + * Schedules a task to run weekly at specific week day and time. + * + * @param int $dayNameOrNum A 3 letter day name (such as 'sun' or 'tue') or a day number from 0 to 6. + * 0 for sunday. Default is 0. + * + * @param string $time A time in the form 'HH:MM'. HH can have any value + * between 0 and 23 inclusive. MM can have any value between 0 and 59 inclusive. + * default is '00:00'. + * + * @return bool If the time for the task is set, the method will + * return true. If not, it will return false. + */ + public function everyWeek($dayNameOrNum = 0, string $time = '00:00') : bool { + return $this->weeklyOn($dayNameOrNum, $time); + } + /** + * Schedule a task to run every specific number of hours. + * + * The expression that will be used is "At minute 0 past every X hour" where + * x is the number of hours. + * + * @param int $xHour The number of hours at which the job will be executed. + */ + public function everyXHour(int $xHour) { + $this->cron('0 */'.$xHour.' * * *'); + } /** * Schedules a task to run every month on specific day and time. * @@ -1190,7 +1217,7 @@ private function checkHoursHelper(string $hoursField) { } $hoursAttrs['every-x-hour'][] = $stepVal; } else if ($exprType == self::SPECIFIC_VAL) { - if (!$this->isNumberHelper($subExpr)) { + if (!is_numeric($subExpr)) { $isValidExpr = false; break; } @@ -1231,6 +1258,10 @@ private function checkMinutesHelper(string $minutesField) { break; } else if ($exprType == self::RANGE_VAL) { $range = explode('-', $subExpr); + if (!(is_numeric($range[0]) && is_numeric($range[1]))) { + $isValidExpr = false; + break; + } $start = intval($range[0]); $end = intval($range[1]); @@ -1248,7 +1279,7 @@ private function checkMinutesHelper(string $minutesField) { } $minuteAttrs['every-x-minute'][] = $stepVal; } else if ($exprType == self::SPECIFIC_VAL) { - if (!$this->isNumberHelper($subExpr)) { + if (!is_numeric($subExpr)) { $isValidExpr = false; break; } @@ -1402,7 +1433,7 @@ private function getArgValFromTerminal($name) { */ private function getSubExprType(string $expr): string { $retVal = self::ANY_VAL; - + if ($expr != '*') { $split0 = explode('/', $expr); $count = count($split0); @@ -1472,26 +1503,6 @@ private function isMinuteHelper($minuteArr, $current) { return $retVal; } - /** - * Checks if a given string represents a number or not. - * @param string $str - * @return bool - */ - private function isNumberHelper(string $str): bool { - $len = strlen($str); - - if ($len != 0) { - for ($x = 0 ; $x < $len ; $x++) { - $ch = $str[$x]; - - if (!($ch >= '0' && $ch <= '9')) { - return false; - } - } - } - - return true; - } private function isValidRange(int $start, int $end, int $min, int $max): bool { $isValidExpr = true; From 36ca13657fc2dd1e6867d15957d12b9b6da7a643 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 24 Jul 2024 18:48:39 +0300 Subject: [PATCH 02/17] test: Added Additional Tests --- tests/webfiori/framework/test/cli/CreateMiddlewareTest.php | 4 ++++ tests/webfiori/framework/test/cli/CreateTaskTest.php | 5 ++++- .../framework/test/cli/UpdateSettingsCommandTest.php | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php b/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php index c9f3146bc..34372c5f8 100644 --- a/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php +++ b/tests/webfiori/framework/test/cli/CreateMiddlewareTest.php @@ -65,7 +65,9 @@ public function testCreateMiddleware01() { $runner->setInputs([ 'NewCool', 'app\middleware', + ' ', 'Check is cool', + '22', 'y', 'global', @@ -77,6 +79,8 @@ public function testCreateMiddleware01() { "Enter a name for the new class:\n", "Enter an optional namespace for the class: Enter = 'app\middleware'\n", "Enter a name for the middleware:\n", + "Error: Invalid input is given. Try again.\n", + "Enter a name for the middleware:\n", "Enter middleware priority: Enter = '0'\n", "Would you like to add the middleware to a group?(y/N)\n", "Enter group name:\n", diff --git a/tests/webfiori/framework/test/cli/CreateTaskTest.php b/tests/webfiori/framework/test/cli/CreateTaskTest.php index c31f0ff32..68e8de580 100644 --- a/tests/webfiori/framework/test/cli/CreateTaskTest.php +++ b/tests/webfiori/framework/test/cli/CreateTaskTest.php @@ -123,7 +123,8 @@ public function test02() { '', 'Invalid#', 'Create Round task', - 'The task will do nothing.', + ' ', + ' The task will do nothing. ', 'N', '', ]); @@ -149,6 +150,8 @@ public function test02() { "Error: Provided name is invalid!\n", "Enter a name for the task:\n", "Provide short description of what does the task will do:\n", + "Error: Invalid input is given. Try again.\n", + "Provide short description of what does the task will do:\n", "Would you like to add arguments to the task?(y/N)\n", "Info: New class was created at \"".ROOT_PATH.DS.'app'.DS."tasks\".\n", ], $runner->getOutput()); diff --git a/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php b/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php index 8b9796b91..32d3afe2e 100644 --- a/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php +++ b/tests/webfiori/framework/test/cli/UpdateSettingsCommandTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use webfiori\framework\App; use webfiori\framework\config\Controller; +use webfiori\framework\config\JsonDriver; use webfiori\framework\router\Router; /** @@ -16,6 +17,7 @@ class UpdateSettingsCommandTest extends TestCase { * @test */ public function test00() { + JsonDriver::setConfigFileName('app-config.json'); $runner = App::getRunner(); $runner->setArgsVector([ 'webfiori', From 3a02a60d0ed3a2decf0059ce889325fc02f64893 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 24 Jul 2024 18:49:10 +0300 Subject: [PATCH 03/17] fix: Fix to Setting Middleware Name --- webfiori/framework/cli/helpers/CreateMiddleware.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfiori/framework/cli/helpers/CreateMiddleware.php b/webfiori/framework/cli/helpers/CreateMiddleware.php index 2c4f0c4d6..79d1e9c2c 100644 --- a/webfiori/framework/cli/helpers/CreateMiddleware.php +++ b/webfiori/framework/cli/helpers/CreateMiddleware.php @@ -63,7 +63,7 @@ private function getGroups() { private function getMiddlewareName() : string { return $this->getInput('Enter a name for the middleware:', null, new InputValidator(function ($val) { - if (strlen($val) > 0) { + if (strlen(trim($val)) > 0) { return true; } From f7932a216a52b38b372a5a0e9c4f93df73cc222b Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 24 Jul 2024 18:49:33 +0300 Subject: [PATCH 04/17] refactor: Removed Unreachable Statement --- webfiori/framework/cli/commands/UpdateTableCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/webfiori/framework/cli/commands/UpdateTableCommand.php b/webfiori/framework/cli/commands/UpdateTableCommand.php index 26c90c879..50d8f392c 100644 --- a/webfiori/framework/cli/commands/UpdateTableCommand.php +++ b/webfiori/framework/cli/commands/UpdateTableCommand.php @@ -62,8 +62,6 @@ public function exec() : int { $tableHelper->updateColumn(); } else if ($whatToDo == 'Drop foreign key.') { $tableHelper->removeForeignKey(); - } else { - $this->error('Option not implemented.'); } return 0; From b90d520a1d5ae57145c6f25280ed4396b2b37471 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Thu, 8 Aug 2024 02:08:51 +0300 Subject: [PATCH 05/17] Update DB.php --- webfiori/framework/DB.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/webfiori/framework/DB.php b/webfiori/framework/DB.php index caa6a3edd..f4b787c72 100644 --- a/webfiori/framework/DB.php +++ b/webfiori/framework/DB.php @@ -52,18 +52,9 @@ public function __construct($connName) { } $conn = App::getConfig()->getDBConnection($connName); - if (!($conn instanceof ConnectionInfo)) { - $connectionsArr = App::getConfig()->getDBConnections(); - - foreach ($connectionsArr as $xCon) { - if ($xCon->getName() == $connName) { - $conn = $xCon; - } - } - if (!($conn instanceof ConnectionInfo)) { - throw new DatabaseException("No connection was found which has the name '$connName'. Driver: ".get_class(App::getConfig()).'.'); - } + if (!($conn instanceof ConnectionInfo)) { + throw new DatabaseException("No connection was found which has the name '$connName'. Driver: ".get_class(App::getConfig()).'.'); } parent::__construct($conn); } From fe442452672b27dc91e5bc124fbe16429799cfe2 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Fri, 9 Aug 2024 20:21:37 +0300 Subject: [PATCH 06/17] refactor(session): Improvements to Sessions Manager --- .../test/session/SessionsManagerTest.php | 9 +++-- .../session/DefaultSessionStorage.php | 7 +--- webfiori/framework/session/SessionDB.php | 23 +++++++----- .../framework/session/SessionsManager.php | 36 +++++++++++++++---- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/tests/webfiori/framework/test/session/SessionsManagerTest.php b/tests/webfiori/framework/test/session/SessionsManagerTest.php index cc0b05fe3..3aeb2f6c8 100644 --- a/tests/webfiori/framework/test/session/SessionsManagerTest.php +++ b/tests/webfiori/framework/test/session/SessionsManagerTest.php @@ -27,8 +27,10 @@ public function test00() { $this->assertFalse(SessionsManager::remove('xyz')); $this->assertFalse(SessionsManager::set('xyz','hello')); $this->assertNull(SessionsManager::pull('xyz')); - + + $this->assertFalse(SessionsManager::hasCookie()); SessionsManager::start('hello'); + $this->assertFalse(SessionsManager::hasCookie()); $this->assertEquals(1, count(SessionsManager::getSessions())); $activeSesstion = SessionsManager::getActiveSession(); $this->assertFalse($activeSesstion->isRefresh()); @@ -39,7 +41,7 @@ public function test00() { $this->assertEquals(7200, $activeSesstion->getDuration()); $this->assertEquals(SessionStatus::NEW, $activeSesstion->getStatus()); - $activeSesstion->set('var-1', 'Good'); + SessionsManager::set('var-1', 'Good'); $activeSesstion->set('var-2', 'Bad'); $activeSesstion->set('var-3', 'Average'); $activeSesstion->set('var-4', 'Almost Good'); @@ -107,6 +109,7 @@ public function test00() { SessionsManager::destroy(); $this->assertNull(SessionsManager::getActiveSession()); $this->assertEquals(SessionStatus::KILLED, $active2->getStatus()); + SessionsManager::validateStorage(); $active2->start(); $this->assertEquals(SessionStatus::NEW, $active2->getStatus()); $this->assertNotNull(SessionsManager::getActiveSession()); @@ -162,8 +165,8 @@ public function testDatabaseSession01() { $conn = new ConnectionInfo('mysql', 'root', '123456', 'testing_db', '127.0.0.1'); $conn->setName('sessions-connection'); App::getConfig()->addOrUpdateDBConnection($conn); - SessionsManager::reset(); SessionsManager::setStorage(new DatabaseSessionStorage()); + SessionsManager::getStorage()->getController()->removeTables(); SessionsManager::start('hello'); } /** diff --git a/webfiori/framework/session/DefaultSessionStorage.php b/webfiori/framework/session/DefaultSessionStorage.php index e1d4ae59c..fecc34ec5 100644 --- a/webfiori/framework/session/DefaultSessionStorage.php +++ b/webfiori/framework/session/DefaultSessionStorage.php @@ -66,12 +66,7 @@ public function gc() { } $sessionsFiles = array_diff(scandir($this->storeLoc), ['.','..']); - if (defined('SESSION_GC') && SESSION_GC > 0) { - $olderThan = time() - SESSION_GC; - } else { - //Clear any session which is older than 30 days - $olderThan = time() - 60 * 60 * 24 * 30; - } + $olderThan = SessionsManager::getGCTime(); foreach ($sessionsFiles as $file) { $fileObj = new File($this->storeLoc.DS.$file); diff --git a/webfiori/framework/session/SessionDB.php b/webfiori/framework/session/SessionDB.php index c3163c38e..556a63ee1 100644 --- a/webfiori/framework/session/SessionDB.php +++ b/webfiori/framework/session/SessionDB.php @@ -36,7 +36,20 @@ public function __construct() { $this->addTable(new MSSQLSessionDataTable()); $this->addTable(new MySQLSessionDataTable()); } - + /** + * Removes database tables which are used to store session information. + */ + public function removeTables() { + + $this->transaction(function (DB $db) { + try { + $db->table('session_data')->drop()->execute(); + $db->table('sessions')->drop()->execute(); + } catch (DatabaseException $ex) { + return; + } + }); + } /** * Clears the sessions which are older than the constant 'SESSION_GC' or * older than 30 days if the constant is not defined. @@ -45,13 +58,7 @@ public function __construct() { * @since 1.0 */ public function gc() { - if (defined('SESSION_GC') && SESSION_GC > 0) { - $olderThan = time() - SESSION_GC; - } else { - //Clear any session which is older than 30 days - $olderThan = time() - 60 * 60 * 24 * 30; - } - $date = date('Y-m-d H:i:s', $olderThan); + $date = SessionsManager::getGCTime(); $ids = $this->getSessionsIDs($date); foreach ($ids as $id) { diff --git a/webfiori/framework/session/SessionsManager.php b/webfiori/framework/session/SessionsManager.php index 38ea9d62d..51925dddd 100644 --- a/webfiori/framework/session/SessionsManager.php +++ b/webfiori/framework/session/SessionsManager.php @@ -104,6 +104,30 @@ public static function destroy() { self::getInstance()->activeSession = null; } } + /** + * Returns a date string that represents the time at which all sessions + * that was created before it will be cleared. + * + * This method will try to use the environment variable 'SESSION_GC' to + * decide on the time. If this environment variable does not exist, + * it will use the value 30 days to create the date time string which + * indicates that any session created 30 days ago will be cleared. + * + * @return string A date string in the format 'YYYY-MM-DD HH:MM:SS'. + */ + public static function getGCTime() : string { + $olderThan = time() - 60 * 60 * 24 * 30; + $fromEnv = getenv('SESSION_GC') !== false ? intval(getenv('SESSION_GC')) : 0; + $fromConst = defined('SESSION_GC') && intval(SESSION_GC) > 0 ? intval(SESSION_GC) : 0; + + if ($fromEnv != 0) { + $olderThan = $fromEnv; + } else if ($fromConst != 0) { + $olderThan = $fromConst; + } + + return date('Y-m-d H:i:s', $olderThan); + } /** * Returns the value of a session variable. * @@ -193,8 +217,7 @@ public static function getSessionIDFromCookie(string $sessionName) { * @return string|bool If session ID is found, the method will * return it. Note that if it is in a cookie, the name of the cookie must * be the name of the session in order to take the ID from it. If it is - * in GET or POST request, it must be in a parameter with the name - * 'session-id'. + * in GET or POST request, it must be in a parameter with the name of the session. * * @since 1.0 */ @@ -233,11 +256,12 @@ public static function getStorage() : SessionStorage { return self::getInstance()->sessionStorage; } /** - * Checks if the given session name has a cookie or not. + * Checks if the active session has a cookie or not. * - * @return bool true if a cookie with the name of the session is found. false otherwise. - * - * @since 1.0 + * Note that in command line, the method will always return false. + * + * @return bool true if The active session has a cookie. False if not. If no + * session is active, false is returned. */ public static function hasCookie() : bool { $active = self::getActiveSession(); From 0fcc3a5b7e1ca7ae1552b3cd3264923b05697c3d Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Sun, 11 Aug 2024 23:48:45 +0300 Subject: [PATCH 07/17] test: Fix Test Case --- tests/webfiori/framework/test/cli/SchedulerCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php index 506289db2..e601fc306 100644 --- a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php +++ b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php @@ -173,8 +173,8 @@ public function test05() { "Line: 44\n", "Stack Trace:\n", "#0 At class app\\tasks\Fail2TestTask line 44\n", - "#1 At class webfiori\\framework\scheduler\AbstractTask line 1101\n", - "#2 At class webfiori\\framework\scheduler\AbstractTask line 422\n", + "#1 At class webfiori\\framework\scheduler\AbstractTask line 1128\n", + "#2 At class webfiori\\framework\scheduler\AbstractTask line 449\n", "#3 At class webfiori\\framework\scheduler\AbstractTask line 951\n", "#4 At class webfiori\\framework\scheduler\TasksManager line 673\n", "#5 At class webfiori\\framework\scheduler\TasksManager line 139\n", From f5e179e124a23005dae84149810891dc2db17782 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 12 Aug 2024 00:07:24 +0300 Subject: [PATCH 08/17] Update PrivilegesGroupTest.php --- tests/webfiori/framework/test/PrivilegesGroupTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/webfiori/framework/test/PrivilegesGroupTest.php b/tests/webfiori/framework/test/PrivilegesGroupTest.php index 54d5e65fe..ecbe8adc0 100644 --- a/tests/webfiori/framework/test/PrivilegesGroupTest.php +++ b/tests/webfiori/framework/test/PrivilegesGroupTest.php @@ -142,7 +142,7 @@ public function testSetID01() { public function testSetParentGroup00() { $child = new PrivilegesGroup('CH_GROUP_1', 'Child Group #1'); $parentGroup = new PrivilegesGroup('PARENT_1', 'Parent Group #1'); - $this->assertTrue($parentGroup->setParentGroup(null)); + $this->assertFalse($parentGroup->setParentGroup(null)); $this->assertFalse($child->setParentGroup($child)); $this->assertTrue($child->setParentGroup($parentGroup)); $this->assertSame($parentGroup,$child->getParentGroup()); From fc8f531b2f8c2ecced23e41ab61274af97494aa3 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 10:38:12 +0300 Subject: [PATCH 09/17] Update JsonDriverTest.php --- tests/webfiori/framework/test/config/JsonDriverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/webfiori/framework/test/config/JsonDriverTest.php b/tests/webfiori/framework/test/config/JsonDriverTest.php index f703829a2..8281ec7ee 100644 --- a/tests/webfiori/framework/test/config/JsonDriverTest.php +++ b/tests/webfiori/framework/test/config/JsonDriverTest.php @@ -466,7 +466,6 @@ public function testDatabaseConnections02() { } /** * @test - * @depends testSetConfigFileName00 */ public function testAppWithError00() { $this->expectExceptionMessage('The property "username" of the connection "New_Connection" is missing.'); @@ -475,4 +474,5 @@ public function testAppWithError00() { $driver->initialize(); $driver->getDBConnections(); } + } From 76f153933680c4ae4d7b067e8fca95273412ab2d Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 12:30:41 +0300 Subject: [PATCH 10/17] feat: Added a Way to Handle Configuration Errors --- webfiori/framework/App.php | 24 ++++++++---- webfiori/framework/config/Controller.php | 17 ++++++-- .../framework/handlers/APICallErrHandler.php | 1 + webfiori/framework/ui/WebPage.php | 39 +++++++++++-------- .../ui/serverErrPage/server-err-header.php | 5 ++- 5 files changed, 57 insertions(+), 29 deletions(-) diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index d405928a8..f5c65eaed 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -118,6 +118,7 @@ private function __construct() { mb_regex_encoding($encoding); } $this->initAutoLoader(); + $this->setHandlers(); Controller::get()->updateEnv(); /** * Set memory limit. @@ -130,7 +131,6 @@ private function __construct() { date_default_timezone_set(defined('DATE_TIMEZONE') ? DATE_TIMEZONE : 'Asia/Riyadh'); - $this->setHandlers(); //Initialize CLI self::getRunner(); @@ -142,7 +142,7 @@ private function __construct() { } //Initialize privileges. //This step must be done before initializing anything. - call_user_func(APP_DIR.'\ini\InitPrivileges::init'); + self::call(APP_DIR.'\ini\InitPrivileges::init'); @@ -353,7 +353,7 @@ public static function getRunner() : Runner { $r->register(new $c()); } $r->setDefaultCommand('help'); - call_user_func(APP_DIR.'\ini\InitCommands::init'); + self::call(APP_DIR.'\ini\InitCommands::init'); }); } @@ -536,7 +536,7 @@ private function initAutoLoader() { Ini::createAppDirs(); Ini::get()->createIniClass('InitAutoLoad', 'Add user-defined directories to the set of directories at which the framework will search for classes.'); } - call_user_func(APP_DIR.'\ini\InitAutoLoad::init'); + self::call(APP_DIR.'\ini\InitAutoLoad::init'); } private function initFrameworkVersionInfo() { /** @@ -575,9 +575,17 @@ private function initMiddleware() { if (!class_exists(APP_DIR.'\ini\InitMiddleware')) { Ini::get()->createIniClass('InitMiddleware', 'Register middleware which are created outside the folder \'[APP_DIR]/middleware\'.'); } - call_user_func(APP_DIR.'\ini\InitMiddleware::init'); + self::call(APP_DIR.'\ini\InitMiddleware::init'); + } + private static function call($func) { + try { + call_user_func($func); + } catch (Exception $ex) { + if (self::getRunner()->isCLI()) { + printf("WARNING: ".$ex->getMessage().' at '.$ex->getFile().':'.$ex->getLine()."\n"); + } + } } - /** * @throws FileException */ @@ -588,7 +596,7 @@ private function initRoutes() { if (!class_exists(APP_DIR.'\\ini\\routes\\'.$className)) { Ini::get()->createRoutesClass($className); } - call_user_func(APP_DIR.'\ini\routes\\'.$className.'::create'); + self::call(APP_DIR.'\ini\routes\\'.$className.'::create'); } if (Router::routesCount() != 0) { @@ -617,7 +625,7 @@ private function initScheduler() { } TasksManager::getPassword(self::getConfig()->getSchedulerPassword()); //initialize scheduler tasks only if in CLI or scheduler is enabled through HTTP. - call_user_func(APP_DIR.'\ini\InitTasks::init'); + self::call(APP_DIR.'\ini\InitTasks::init'); TasksManager::registerTasks(); } } diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index 254dc4eb2..a80d027a8 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -12,6 +12,10 @@ */ class Controller { const NL = "\n"; + /** + * + * @var ConfigurationDriver + */ private $driver; private static $singleton; /** @@ -20,7 +24,14 @@ class Controller { public function __construct() { $driverClazz = App::getConfigDriver(); $this->driver = new $driverClazz(); - $this->driver->initialize(); + $this->init($this->driver); + } + private function init(ConfigurationDriver $driver, bool $reCreate = false) { + try { + $this->driver->initialize($reCreate); + } catch (Exception $ex) { + throw new InitializationException('Unable to initialize configuration driver due to an error: '.$ex->getMessage(), $ex->getCode(), $ex); + } } /** * Adds new environment variable to the configuration of the app. @@ -73,7 +84,7 @@ public function copy(ConfigurationDriver $new) { $new->setSchedulerPassword($current->getSchedulerPassword()); $new->setHomePage($current->getHomePage()); $new->setTitleSeparator($current->getTitleSeparator()); - $new->initialize(true); + $this->init($new, true); } /** * Returns a single instance of the class. @@ -103,7 +114,7 @@ public static function getDriver() : ConfigurationDriver { */ public static function setDriver(ConfigurationDriver $driver) { self::get()->driver = $driver; - $driver->initialize(); + $this->init($driver); } /** * Reads application environment variables and updates the class which holds diff --git a/webfiori/framework/handlers/APICallErrHandler.php b/webfiori/framework/handlers/APICallErrHandler.php index e2ee0bfde..852f53c73 100644 --- a/webfiori/framework/handlers/APICallErrHandler.php +++ b/webfiori/framework/handlers/APICallErrHandler.php @@ -60,6 +60,7 @@ public function handle() { } else { $j = new Json([ 'message' => '500 - General Server Error.', + 'details' => $this->getMessage(), 'type' => 'error', ]); } diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index 87fcf4b3d..a437d5296 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -15,6 +15,7 @@ use TypeError; use webfiori\collections\LinkedList; use webfiori\framework\App; +use webfiori\framework\exceptions\InitializationException; use webfiori\framework\exceptions\MissingLangException; use webfiori\framework\exceptions\SessionException; use webfiori\framework\exceptions\UIException; @@ -258,7 +259,7 @@ public function addBeforeRender(callable $callable, $priority = 0, array $params */ public function addCSS(string $href, array $attrs = []) { if (!isset($attrs['revision'])) { - $attrs['revision'] = App::getConfig()->getAppVersion(); + $attrs['revision'] = $this->getConfigVar('getAppVersion', '1.0'); } $this->getDocument()->getHeadNode()->addCSS($href, $attrs); } @@ -278,7 +279,7 @@ public function addCSS(string $href, array $attrs = []) { */ public function addJS(string $src, array $attrs = []) { if (!isset($attrs['revision'])) { - $attrs['revision'] = App::getConfig()->getAppVersion(); + $attrs['revision'] = $this->getConfigVar('getAppVersion', '1.0'); } $this->getDocument()->getHeadNode()->addJs($src, $attrs); } @@ -773,6 +774,13 @@ public function render(bool $formatted = false, bool $returnResult = false) { return $this->getDocument(); } + private function getConfigVar(string $meth, string $default, array $params = []) { + try{ + return call_user_func_array([App::getConfig(), $meth], $params); + } catch (InitializationException $ex) { + return $default; + } + } /** * Resets page attributes to default values. * @@ -784,17 +792,11 @@ public function reset() { $this->checkLang(); $this->usingLanguage(); - $appName = App::getConfig()->getAppName($this->getLangCode()); - $appName !== null ? $this->setWebsiteName($appName) : $this->setWebsiteName('New Website'); - - $websiteDesc = App::getConfig()->getDescription($this->getLangCode()); - $websiteDesc !== null ? $this->setWebsiteName($websiteDesc) : ''; - - $pageTitle = App::getConfig()->getTitle($this->getLangCode()); - $pageTitle !== null ? $this->setTitle($pageTitle) : $this->setTitle('Hello World'); - - - $this->setTitleSep(App::getConfig()->getTitleSeparator()); + + $this->setWebsiteName($this->getConfigVar('getAppName', 'WebFiori App', [$this->getLangCode()])); + $this->setDescription($this->getConfigVar('getDescription', '', [$this->getLangCode()])); + $this->setTitle($this->getConfigVar('getTitle', 'Hello World', [$this->getLangCode()])); + $this->setTitleSep($this->getConfigVar('getTitleSeparator', '|')); try { $langObj = $this->getTranslation(); @@ -1012,7 +1014,7 @@ public function setTheme($themeNameOrClass = null) { if ($themeNameOrClass !== null && strlen(trim($themeNameOrClass)) == 0) { return; } - $xthemeName = $themeNameOrClass === null ? App::getConfig()->getTheme() : $themeNameOrClass; + $xthemeName = $this->getConfigVar('getTheme', '');; if (strlen($xthemeName) === 0) { return; @@ -1180,7 +1182,7 @@ private function checkLang() { $langCodeFromRequest = Request::getParam('lang'); if ($langCodeFromRequest === null) { - $this->setLang(App::getConfig()->getPrimaryLanguage()); + $this->setLang($this->getConfigVar('getPrimaryLanguage', 'EN')); return; } @@ -1194,10 +1196,13 @@ private function checkLang() { private function getHead() { $loadedTheme = $this->getTheme(); + $defaultBase = Util::getBaseURL(); + $base = $this->getConfigVar('getBaseURL', $defaultBase); + $headNode = new HeadNode( $this->getTitle().$this->getTitleSep().$this->getWebsiteName(), $this->getCanonical(), - App::getConfig()->getBaseURL() + $base ); if ($loadedTheme !== null) { @@ -1210,7 +1215,7 @@ private function getHead() { } $headNode->addMeta('charset','UTF-8',true); $headNode->setPageTitle($this->getTitle().$this->getTitleSep().$this->getWebsiteName()); - $headNode->setBase(App::getConfig()->getBaseURL()); + $headNode->setBase($base); $headNode->setCanonical($this->getCanonical()); if ($this->getDescription() != null) { diff --git a/webfiori/framework/ui/serverErrPage/server-err-header.php b/webfiori/framework/ui/serverErrPage/server-err-header.php index 6ac231dc7..79603599f 100644 --- a/webfiori/framework/ui/serverErrPage/server-err-header.php +++ b/webfiori/framework/ui/serverErrPage/server-err-header.php @@ -5,7 +5,7 @@ - + + + Error Details: getMessage()?> + From 0d3ead5cad177efd50e4b285222fcdbaf8beab66 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 12:33:04 +0300 Subject: [PATCH 11/17] chore: Updated Dependencies Version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 66848a708..f926e61d0 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "webfiori/ui":"v2.6.3", "webfiori/collections":"v1.1.4", "webfiori/database":"v0.8.10", - "webfiori/cli":"v1.2.2", + "webfiori/cli":"v1.3.0", "webfiori/mailer":"v1.2.0", "webfiori/err":"v1.1.0" }, From e04233a0b4b65b903d92029dcb80ec4814dd5a08 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 12:35:26 +0300 Subject: [PATCH 12/17] fix: Made `init` Static --- webfiori/framework/config/Controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index a80d027a8..68b2776c8 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -1,6 +1,7 @@ driver = new $driverClazz(); $this->init($this->driver); } - private function init(ConfigurationDriver $driver, bool $reCreate = false) { + private static function init(ConfigurationDriver $driver, bool $reCreate = false) { try { $this->driver->initialize($reCreate); } catch (Exception $ex) { From c8fe65361aba162cd2266e0eb8aa12b1830345f0 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 12:35:37 +0300 Subject: [PATCH 13/17] Update Controller.php --- webfiori/framework/config/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index 68b2776c8..aa680fc4d 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -115,7 +115,7 @@ public static function getDriver() : ConfigurationDriver { */ public static function setDriver(ConfigurationDriver $driver) { self::get()->driver = $driver; - $this->init($driver); + self::init($driver); } /** * Reads application environment variables and updates the class which holds From 5ce527064a830327411fcb43fe09d9d0edb8c0cf Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 12:36:40 +0300 Subject: [PATCH 14/17] Update Controller.php --- webfiori/framework/config/Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index aa680fc4d..4b5dcaaf1 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -29,7 +29,7 @@ public function __construct() { } private static function init(ConfigurationDriver $driver, bool $reCreate = false) { try { - $this->driver->initialize($reCreate); + $driver->initialize($reCreate); } catch (Exception $ex) { throw new InitializationException('Unable to initialize configuration driver due to an error: '.$ex->getMessage(), $ex->getCode(), $ex); } From c188f9474e596b2458abaa267852a6c86b14d0fc Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 13:02:44 +0300 Subject: [PATCH 15/17] test: Updated Test Case --- tests/webfiori/framework/test/cli/SchedulerCommandTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php index e601fc306..5f36151f7 100644 --- a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php +++ b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php @@ -179,7 +179,7 @@ public function test05() { "#4 At class webfiori\\framework\scheduler\TasksManager line 673\n", "#5 At class webfiori\\framework\scheduler\TasksManager line 139\n", "#6 At class webfiori\\framework\cli\commands\SchedulerCommand line 86\n", - "#7 At class webfiori\\framework\cli\commands\SchedulerCommand line 328\n", + "#7 At class webfiori\\framework\cli\commands\SchedulerCommand line 331\n", "#8 At class webfiori\\cli\CLICommand line 409\n", "#9 At class webfiori\\cli\Runner line 684\n", "#10 At class webfiori\\cli\Runner line 615\n", From ce674903b6358824c61360a2ae335b8399c38309 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 13:03:14 +0300 Subject: [PATCH 16/17] fix: Fix to Bug in Loading Themes --- tests/webfiori/framework/test/PageTest.php | 5 ++++- webfiori/framework/ui/WebPage.php | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/webfiori/framework/test/PageTest.php b/tests/webfiori/framework/test/PageTest.php index d313a828c..f499416a4 100644 --- a/tests/webfiori/framework/test/PageTest.php +++ b/tests/webfiori/framework/test/PageTest.php @@ -650,7 +650,10 @@ public function testTheme03() { $page->setTheme($secondThemeName); $fTheme = $page->getTheme(); - + + $this->assertEquals($firstThemeName, $theme3->getName()); + $this->assertEquals($secondThemeName, $fTheme->getName()); + $this->assertFalse($theme3 === $fTheme); $this->assertNotEquals($firstThemeName,$fTheme->getName()); $page->setTheme($secondThemeName); diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index a437d5296..531934b94 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -774,7 +774,7 @@ public function render(bool $formatted = false, bool $returnResult = false) { return $this->getDocument(); } - private function getConfigVar(string $meth, string $default, array $params = []) { + private function getConfigVar(string $meth, string $default = null, array $params = []) { try{ return call_user_func_array([App::getConfig(), $meth], $params); } catch (InitializationException $ex) { @@ -1014,10 +1014,14 @@ public function setTheme($themeNameOrClass = null) { if ($themeNameOrClass !== null && strlen(trim($themeNameOrClass)) == 0) { return; } - $xthemeName = $this->getConfigVar('getTheme', '');; + $xthemeName = $themeNameOrClass; + + if (strlen(trim($themeNameOrClass.'')) == 0) { + $xthemeName = $this->getConfigVar('getTheme', $themeNameOrClass); + } - if (strlen($xthemeName) === 0) { - return; + if (strlen($xthemeName) == 0) { + $xthemeName = $themeNameOrClass; } $tmpTheme = ThemeLoader::usingTheme($xthemeName); From 574976385c4e956d619dc4cebf2f41e9f73b4bc4 Mon Sep 17 00:00:00 2001 From: Ibrahim Date: Wed, 21 Aug 2024 13:09:10 +0300 Subject: [PATCH 17/17] Update SchedulerCommandTest.php --- tests/webfiori/framework/test/cli/SchedulerCommandTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php index 5f36151f7..a3aa7c7fc 100644 --- a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php +++ b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php @@ -181,8 +181,8 @@ public function test05() { "#6 At class webfiori\\framework\cli\commands\SchedulerCommand line 86\n", "#7 At class webfiori\\framework\cli\commands\SchedulerCommand line 331\n", "#8 At class webfiori\\cli\CLICommand line 409\n", - "#9 At class webfiori\\cli\Runner line 684\n", - "#10 At class webfiori\\cli\Runner line 615\n", + "#9 At class webfiori\\cli\Runner line 725\n", + "#10 At class webfiori\\cli\Runner line 656\n", "#11 At class webfiori\cli\Runner line 156\n", "Skip"]; $actual = $runner->getOutput();