From e31b3609ec7c100701ad3430fd2f98154090ba7b Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 15 Jan 2024 16:09:42 +0300 Subject: [PATCH 01/22] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1b5628c9..bf668feb 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "webfiori/collections":"v1.1.4", "webfiori/database":"v0.8.9", "webfiori/cli":"v1.1.4", - "webfiori/mailer":"v1.1.2", + "webfiori/mailer":"v1.1.3", "webfiori/err":"v1.0.8" }, "require-dev": { From db767c95a1e5f0c1f9b6532afd084316614a05f4 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 15 Jan 2024 16:31:00 +0300 Subject: [PATCH 02/22] Update Session.php --- webfiori/framework/session/Session.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webfiori/framework/session/Session.php b/webfiori/framework/session/Session.php index 8728f1a1..3e63fdbe 100644 --- a/webfiori/framework/session/Session.php +++ b/webfiori/framework/session/Session.php @@ -227,7 +227,7 @@ public function deserialize(string $serialized): bool { { throw new SessionException($errStr, $errNo); }); - $sessionObj = unserialize(base64_decode(trim($decrypted))); + $sessionObj = unserialize(base64_decode($decrypted)); restore_error_handler(); if ($sessionObj instanceof Session) { @@ -586,7 +586,7 @@ public function remove(string $varName) : bool { */ public function serialize() : string { // Serialize => Encode => [Encrypt] - $serializedSession = base64_encode(serialize($this)); + $serializedSession = base64_encode(trim(serialize($this))); $cipherMeth = 'aes-256-ctr'; if (in_array($cipherMeth, openssl_get_cipher_methods())) { From c00b18f8d28bd7eff2e0983d86af0c096be76c2f Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 15 Jan 2024 17:58:03 +0300 Subject: [PATCH 03/22] Update Session.php --- webfiori/framework/session/Session.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/webfiori/framework/session/Session.php b/webfiori/framework/session/Session.php index 3e63fdbe..cbd2d8bc 100644 --- a/webfiori/framework/session/Session.php +++ b/webfiori/framework/session/Session.php @@ -209,6 +209,9 @@ public function close() { */ public function deserialize(string $serialized): bool { $cipherMeth = 'aes-256-ctr'; + $split = explode('_', $serialized); + $len = $split[0]; + $serialized = $split[1]; // [Decrypt] => decode => deserialize if (in_array($cipherMeth, openssl_get_cipher_methods())) { @@ -220,14 +223,14 @@ public function deserialize(string $serialized): bool { $key = $this->getId().$userAgent; $iv = substr(hash('sha256', $key), 0,16); - $decrypted = openssl_decrypt($serialized, $cipherMeth, $key,0, $iv); + $decrypted = substr(openssl_decrypt(substr($serialized, 0, $len), $cipherMeth, $key,0, $iv), 0, $len); if (strlen($decrypted) > 0) { - set_error_handler(function ($errNo, $errStr) + set_error_handler(function ($errNo, $errStr, $errFile, $errLine) { - throw new SessionException($errStr, $errNo); + throw new SessionException($errStr.' at line '.$errLine, $errNo); }); - $sessionObj = unserialize(base64_decode($decrypted)); + $sessionObj = unserialize(base64_decode(trim($decrypted))); restore_error_handler(); if ($sessionObj instanceof Session) { @@ -587,6 +590,8 @@ public function remove(string $varName) : bool { public function serialize() : string { // Serialize => Encode => [Encrypt] $serializedSession = base64_encode(trim(serialize($this))); + $len = strlen($serializedSession); + $cipherMeth = 'aes-256-ctr'; if (in_array($cipherMeth, openssl_get_cipher_methods())) { @@ -598,11 +603,11 @@ public function serialize() : string { $key = $this->getId().$userAgent; $iv = substr(hash('sha256', $key), 0,16); - - return openssl_encrypt($serializedSession, $cipherMeth, $key,0, $iv); + $serializedSession = openssl_encrypt($serializedSession, $cipherMeth, $key,0, $iv); + $len = strlen($serializedSession); } - return $serializedSession; + return $len.'_'.$serializedSession; } /** * Sets session variable. From c2d7507ed8cd9cba7df79698551f97bc2605a402 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Mon, 15 Jan 2024 19:05:35 +0300 Subject: [PATCH 04/22] Update CreateWebService.php --- .../cli/helpers/CreateWebService.php | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/webfiori/framework/cli/helpers/CreateWebService.php b/webfiori/framework/cli/helpers/CreateWebService.php index 17f4c8f3..66481fe0 100644 --- a/webfiori/framework/cli/helpers/CreateWebService.php +++ b/webfiori/framework/cli/helpers/CreateWebService.php @@ -37,9 +37,7 @@ public function readClassInfo() { $this->setClassInfo(APP_DIR.'\\apis', 'Service'); $this->setServiceName(); - $methods = RequestMethod::getAll(); - array_multisort($methods); - $this->serviceObj->addRequestMethod($this->select('Request method:', $methods, 2)); + $this->addRequestMethods(); if ($this->confirm('Would you like to add request parameters to the service?', false)) { $this->addParamsToService(); @@ -49,16 +47,48 @@ public function readClassInfo() { $this->writeClass(); $this->info('Don\'t forget to add the service to a services manager.'); } + public function addRequestMethods() { + $added = $this->serviceObj->getRequestMethods(); + $methods = RequestMethod::getAll(); + $addOne = true; + + while ($addOne) { + $toSelect = []; + foreach ($methods as $method) { + if (!in_array($method, $added)) { + $toSelect[] = $method; + } + } + array_multisort($toSelect); + $this->serviceObj->addRequestMethod($this->select('Request method:', $toSelect, 2)); + if (count($toSelect) > 1) { + $addOne = $this->confirm('Would you like to add another request method?', false); + } else { + $addOne = false; + } + } + } private function addParamsToService() { do { $paramObj = new RequestParameter('h'); - $paramObj->setType($this->select('Choose parameter type:', ParamType::getTypes(), 0)); $this->setParamName($paramObj); + $paramObj->setType($this->select('Choose parameter type:', ParamType::getTypes(), 0)); + $paramObj->setDescription($this->getInput('Description:')); $added = $this->serviceObj->addParameter($paramObj); $paramObj->setIsOptional($this->confirm('Is this parameter optional?', true)); - + if ($paramObj->getType() == ParamType::STRING || $paramObj->getType() == ParamType::URL || $paramObj->getType() == ParamType::EMAIL) { + $paramObj->setIsEmptyStringAllowed($this->confirm('Are empty values allowed?', false)); + $paramObj->setMinLength($this->getCommand()->readInteger('Minimum length:', false)); + $paramObj->setMaxLength($this->getCommand()->readInteger('Maximum length:', false)); + } + if ($paramObj->getType() == ParamType::INT || $paramObj->getType() == ParamType::DOUBLE) { + $method = $paramObj->getType() == ParamType::INT ? 'readInteger' : 'readFloat'; + $paramObj->setMinValue($this->getCommand()->$method('Minimum value:', false)); + $paramObj->setMaxValue($this->getCommand()->$method('Maximum value:', false)); + } + if ($added) { - $this->success('New parameter added to the service \''.$this->serviceObj->getName().'\'.'); + $this->success('New parameter added.'); } else { $this->warning('The parameter was not added.'); } From 7ee0f9ec444f32162371ee53fcb593ea2a4fd2bc Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 01:43:17 +0300 Subject: [PATCH 05/22] Create CreateWebServiceTest.php --- .../test/cli/CreateWebServiceTest.php | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 tests/webfiori/framework/test/cli/CreateWebServiceTest.php diff --git a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php new file mode 100644 index 00000000..996ce59a --- /dev/null +++ b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php @@ -0,0 +1,224 @@ +setInputs([ + '2', + 'NewWeb', + '', + 'get-hello', + 'Service Desc', + '', + 'n', + 'y', + 'name', + '6', + 'Random desc', + 'n', + 'n', + '', + '', + '', + ]); + $runner->setArgsVector([ + 'webfiori', + 'create' + ]); + $result = $runner->start(); + //$this->assertEquals(0, $result); + $this->assertEquals([ + "What would you like to create?\n", + "0: Database table class.\n", + "1: Entity class from table.\n", + "2: Web service.\n", + "3: Background Task.\n", + "4: Middleware.\n", + "5: CLI Command.\n", + "6: Theme.\n", + "7: Database access class based on table.\n", + "8: Complete REST backend (Database table, entity, database access and web services).\n", + "9: Quit. <--\n", + "Enter a name for the new class:\n", + "Enter an optional namespace for the class: Enter = 'app\apis'\n", + "Enter a name for the new web service:\n", + "Description:\n", + "Request method:\n", + "0: CONNECT\n", + "1: DELETE\n", + "2: GET <--\n", + "3: HEAD\n", + "4: OPTIONS\n", + "5: POST\n", + "6: PUT\n", + "7: TRACE\n", + "Would you like to add another request method?(y/N)\n", + "Would you like to add request parameters to the service?(y/N)\n", + "Enter a name for the request parameter:\n", + "Choose parameter type:\n", + "0: array <--\n", + "1: boolean\n", + "2: email\n", + "3: double\n", + "4: integer\n", + "5: json-obj\n", + "6: string\n", + "7: url\n", + "Description:\n", + "Is this parameter optional?(Y/n)\n", + "Are empty values allowed?(y/N)\n", + "Minimum length: Enter = '0'\n", + "Maximum length: Enter = '0'\n", + "Success: New parameter added.\n", + "Would you like to add another parameter?(y/N)\n", + "Creating the class...\n", + 'Info: New class was created at "'.ROOT_PATH.DS.'app'.DS."apis\".\n", + "Info: Don't forget to add the service to a services manager.\n", + ], $runner->getOutput()); + $clazz = '\\app\\apis\\NewWebService'; + $this->assertTrue(class_exists($clazz)); + $this->removeClass('\\app\\apis\\NewWebService'); + $instance = new $clazz(); + $instance instanceof AbstractWebService; + $this->assertEquals('get-hello', $instance->getName()); + $this->assertEquals(1, count($instance->getParameters())); + $this->assertEquals('Service Desc', $instance->getDescription()); + $this->assertEquals([RequestMethod::GET], $instance->getRequestMethods()); + $param00 = $instance->getParameters()[0]; + $this->assertRequestParameter($param00, [ + ParamOption::NAME => 'name', + ParamOption::TYPE => ParamType::STRING, + ParamOption::DESCRIPTION => 'Random desc', + ParamOption::DEFAULT => null, + ParamOption::EMPTY => false, + ParamOption::MAX => null, + ParamOption::MAX_LENGTH => null, + ParamOption::MIN => null, + ParamOption::MIN_LENGTH => null, + ParamOption::OPTIONAL => false, + ]); + } + /** + * @test + */ + public function test01() { + $runner = $runner = App::getRunner(); + $runner->setInputs([ + 'NewWeb2', + '', + 'get-hello-2', + 'Service\'s Desc', + '', + 'y', + '5', + 'n', + 'y', + 'a-number', + '3', + 'Random\'s desc', + 'n', + '', + '', + ]); + $runner->setArgsVector([ + 'webfiori', + 'create', + '--c' => 'web-service' + ]); + $result = $runner->start(); + //$this->assertEquals(0, $result); + $this->assertEquals([ + "Enter a name for the new class:\n", + "Enter an optional namespace for the class: Enter = 'app\apis'\n", + "Enter a name for the new web service:\n", + "Description:\n", + "Request method:\n", + "0: CONNECT\n", + "1: DELETE\n", + "2: GET <--\n", + "3: HEAD\n", + "4: OPTIONS\n", + "5: POST\n", + "6: PUT\n", + "7: TRACE\n", + "Would you like to add another request method?(y/N)\n", + "Request method:\n", + "0: CONNECT\n", + "1: DELETE\n", + "2: GET <--\n", + "3: HEAD\n", + "4: OPTIONS\n", + "5: POST\n", + "6: PUT\n", + "7: TRACE\n", + "Would you like to add another request method?(y/N)\n", + "Would you like to add request parameters to the service?(y/N)\n", + "Enter a name for the request parameter:\n", + "Choose parameter type:\n", + "0: array <--\n", + "1: boolean\n", + "2: email\n", + "3: double\n", + "4: integer\n", + "5: json-obj\n", + "6: string\n", + "7: url\n", + "Description:\n", + "Is this parameter optional?(Y/n)\n", + "Would you like to set minimum and maximum limites?(y/N)\n", + "Success: New parameter added.\n", + "Would you like to add another parameter?(y/N)\n", + "Creating the class...\n", + 'Info: New class was created at "'.ROOT_PATH.DS.'app'.DS."apis\".\n", + "Info: Don't forget to add the service to a services manager.\n", + ], $runner->getOutput()); + $clazz = '\\app\\apis\\NewWeb2Service'; + $this->assertTrue(class_exists($clazz)); + $this->removeClass('\\app\\apis\\NewWeb2Service'); + $instance = new $clazz(); + $instance instanceof AbstractWebService; + $this->assertEquals('get-hello-2', $instance->getName()); + $this->assertEquals(1, count($instance->getParameters())); + $this->assertEquals('Service\'s Desc', $instance->getDescription()); + $this->assertEquals([RequestMethod::GET, RequestMethod::POST], $instance->getRequestMethods()); + $param00 = $instance->getParameters()[0]; + $this->assertRequestParameter($param00, [ + ParamOption::NAME => 'a-number', + ParamOption::TYPE => ParamType::DOUBLE, + ParamOption::DESCRIPTION => 'Random\'s desc', + ParamOption::DEFAULT => null, + ParamOption::EMPTY => false, + ParamOption::MAX => PHP_FLOAT_MAX, + ParamOption::MAX_LENGTH => null, + ParamOption::MIN => PHP_FLOAT_MIN, + ParamOption::MIN_LENGTH => null, + ParamOption::OPTIONAL => false, + ]); + } + private function assertRequestParameter(RequestParameter $param, array $expected) { + $this->assertEquals($expected[ParamOption::NAME], $param->getName()); + $this->assertEquals($expected[ParamOption::TYPE], $param->getType()); + $this->assertEquals($expected[ParamOption::DESCRIPTION], $param->getDescription()); + $this->assertEquals($expected[ParamOption::MIN_LENGTH], $param->getMinLength()); + $this->assertEquals($expected[ParamOption::MAX_LENGTH], $param->getMaxLength()); + $this->assertEquals($expected[ParamOption::MIN], $param->getMinValue()); + $this->assertEquals($expected[ParamOption::MAX], $param->getMaxValue()); + $this->assertEquals($expected[ParamOption::EMPTY], $param->isEmptyStringAllowed()); + $this->assertEquals($expected[ParamOption::OPTIONAL], $param->isOptional()); + $this->assertEquals($expected[ParamOption::DEFAULT], $param->getDefault()); + } +} From a2b3805435e7d80d2d88bee237b9f21257328079 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 01:43:23 +0300 Subject: [PATCH 06/22] Update CreateWebService.php --- .../cli/helpers/CreateWebService.php | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/webfiori/framework/cli/helpers/CreateWebService.php b/webfiori/framework/cli/helpers/CreateWebService.php index 66481fe0..8b8851c2 100644 --- a/webfiori/framework/cli/helpers/CreateWebService.php +++ b/webfiori/framework/cli/helpers/CreateWebService.php @@ -37,6 +37,7 @@ public function readClassInfo() { $this->setClassInfo(APP_DIR.'\\apis', 'Service'); $this->setServiceName(); + $this->serviceObj->setDescription($this->getInput('Description:')); $this->addRequestMethods(); if ($this->confirm('Would you like to add request parameters to the service?', false)) { @@ -48,17 +49,11 @@ public function readClassInfo() { $this->info('Don\'t forget to add the service to a services manager.'); } public function addRequestMethods() { - $added = $this->serviceObj->getRequestMethods(); - $methods = RequestMethod::getAll(); + $toSelect = RequestMethod::getAll(); $addOne = true; while ($addOne) { - $toSelect = []; - foreach ($methods as $method) { - if (!in_array($method, $added)) { - $toSelect[] = $method; - } - } + array_multisort($toSelect); $this->serviceObj->addRequestMethod($this->select('Request method:', $toSelect, 2)); if (count($toSelect) > 1) { @@ -78,13 +73,11 @@ private function addParamsToService() { $paramObj->setIsOptional($this->confirm('Is this parameter optional?', true)); if ($paramObj->getType() == ParamType::STRING || $paramObj->getType() == ParamType::URL || $paramObj->getType() == ParamType::EMAIL) { $paramObj->setIsEmptyStringAllowed($this->confirm('Are empty values allowed?', false)); - $paramObj->setMinLength($this->getCommand()->readInteger('Minimum length:', false)); - $paramObj->setMaxLength($this->getCommand()->readInteger('Maximum length:', false)); + $paramObj->setMinLength($this->getCommand()->readInteger('Minimum length:')); + $paramObj->setMaxLength($this->getCommand()->readInteger('Maximum length:')); } if ($paramObj->getType() == ParamType::INT || $paramObj->getType() == ParamType::DOUBLE) { - $method = $paramObj->getType() == ParamType::INT ? 'readInteger' : 'readFloat'; - $paramObj->setMinValue($this->getCommand()->$method('Minimum value:', false)); - $paramObj->setMaxValue($this->getCommand()->$method('Maximum value:', false)); + $this->setMinAndMax($paramObj); } if ($added) { @@ -95,6 +88,27 @@ private function addParamsToService() { $addMore = $this->confirm('Would you like to add another parameter?', false); } while ($addMore); } + private function setMinAndMax(RequestParameter $param) { + $setMinMax = $this->confirm('Would you like to set minimum and maximum limites?', false); + + if (!$setMinMax) { + return; + } + $isValid = false; + $method = $param->getType() == ParamType::INT ? 'readInteger' : 'readFloat'; + + while (!$isValid) { + $min = $this->getCommand()->$method('Minimum value:'); + $max = $this->getCommand()->$method('Maximum value:'); + if ($min < $max) { + $param->setMinValue($min); + $param->setMaxValue($max); + $isValid = true; + } else { + $this->error('Minimum and maximum should not overlap.'); + } + } + } /** * * @param RequestParameter $paramObj From b6000c61902d765105dc9f7bc09829eaabe9e7a5 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 01:43:30 +0300 Subject: [PATCH 07/22] Update WebServiceWriter.php --- .../framework/writers/WebServiceWriter.php | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 2b929374..709eddf6 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -14,6 +14,7 @@ use webfiori\http\AbstractWebService; use webfiori\http\ParamOption; use webfiori\http\ParamType; +use webfiori\http\RequestMethod; use webfiori\http\RequestParameter; /** @@ -53,6 +54,7 @@ public function __construct($webServicesObj = null) { $this->addUseStatement(EAbstractWebService::class); $this->addUseStatement(ParamType::class); $this->addUseStatement(ParamOption::class); + $this->addUseStatement(RequestMethod::class); $this->servicesObj = new ServiceHolder(); if ($webServicesObj instanceof AbstractWebService) { @@ -284,10 +286,46 @@ private function writeConstructor() { $this->f('__construct'), ], 1); $this->append('parent::__construct(\''.$this->servicesObj->getName().'\');', 2); - $this->append('$this->addRequestMethod(\''.$this->servicesObj->getRequestMethods()[0].'\');', 2); + $this->append('$this->setDescription(\''. str_replace("'", "\\'", $this->servicesObj->getDescription()).'\');', 2); + $this->append('$this->setRequestMethods([', 2); + foreach ($this->servicesObj->getRequestMethods() as $method) { + $this->append($this->getMethod($method).',', 3); + } + $this->append(']);', 2); $this->appendParams($this->servicesObj->getParameters()); $this->append('}', 1); } + private function getMethod($method) { + switch ($method) { + case RequestMethod::CONNECT:{ + return "RequestMethod::CONNECT"; + } + case RequestMethod::DELETE:{ + return "RequestMethod::DELETE"; + } + case RequestMethod::GET:{ + return "RequestMethod::GET"; + } + case RequestMethod::HEAD:{ + return "RequestMethod::HEAD"; + } + case RequestMethod::OPTIONS:{ + return "RequestMethod::OPTIONS"; + } + case RequestMethod::PATCH:{ + return "RequestMethod::PATCH"; + } + case RequestMethod::POST:{ + return "RequestMethod::POST"; + } + case RequestMethod::PUT:{ + return "RequestMethod::PUT"; + } + case RequestMethod::TRACE:{ + return "RequestMethod::TRACE"; + } + } + } private function writeServiceDoc($service) { $docArr = []; From 9a1bae9d997023e7af82b2b4bef32cb640b5bf4c Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 01:57:17 +0300 Subject: [PATCH 08/22] Update WebServiceWriter.php --- webfiori/framework/writers/WebServiceWriter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 709eddf6..56c20802 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -219,10 +219,10 @@ private function appendParam($param) { } if ($param->getType() == ParamType::INT || $param->getType() == ParamType::DOUBLE) { - if ($param->getMinValue() !== null) { + if ($param->getMinValue() !== null && ($param->getMinValue() != PHP_INT_MIN && $param->getMinValue() != PHP_FLOAT_MIN)) { $this->append("ParamOption::MIN => ".$param->getMinValue().",", 4); } - if ($param->getMaxValue() !== null) { + if ($param->getMaxValue() !== null && ($param->getMaxValue() != PHP_INT_MAX && $param->getMaxValue() != PHP_FLOAT_MAX)) { $this->append("ParamOption::MAX => ".$param->getMinValue().",", 4); } } From 54199847bfbcee22349f3cfd643bc446fb2019dd Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 22:55:23 +0300 Subject: [PATCH 09/22] Update WebServiceWritterTest.php --- .../framework/test/writers/WebServiceWritterTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/webfiori/framework/test/writers/WebServiceWritterTest.php b/tests/webfiori/framework/test/writers/WebServiceWritterTest.php index 08aca701..a2f0d18f 100644 --- a/tests/webfiori/framework/test/writers/WebServiceWritterTest.php +++ b/tests/webfiori/framework/test/writers/WebServiceWritterTest.php @@ -19,7 +19,8 @@ public function test00() { $this->assertEquals([ "webfiori\\framework\\EAbstractWebService", "webfiori\http\ParamType", - "webfiori\http\ParamOption" + "webfiori\http\ParamOption", + "webfiori\\http\\RequestMethod" ], $writter->getUseStatements()); } /** @@ -34,7 +35,8 @@ public function test01() { $this->assertEquals([ "webfiori\\framework\\EAbstractWebService", "webfiori\http\ParamType", - "webfiori\http\ParamOption" + "webfiori\http\ParamOption", + "webfiori\\http\\RequestMethod" ], $writter->getUseStatements()); $writter->addRequestParam([ 'name' => 'param-1', @@ -61,7 +63,8 @@ public function test02() { $this->assertEquals([ "webfiori\\framework\\EAbstractWebService", "webfiori\http\ParamType", - "webfiori\http\ParamOption" + "webfiori\http\ParamOption", + "webfiori\\http\\RequestMethod" ], $writter->getUseStatements()); $writter->addRequestParam([ 'name' => 'param-1', From eee2d5ca5b76bf3f26870eb35298a05a8c6fcab8 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 22:59:17 +0300 Subject: [PATCH 10/22] Update CreateCommandTest.php --- .../framework/test/cli/CreateCommandTest.php | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/tests/webfiori/framework/test/cli/CreateCommandTest.php b/tests/webfiori/framework/test/cli/CreateCommandTest.php index 556d1232..650f1311 100644 --- a/tests/webfiori/framework/test/cli/CreateCommandTest.php +++ b/tests/webfiori/framework/test/cli/CreateCommandTest.php @@ -62,78 +62,4 @@ public function testCreate01() { "9: Quit. <--\n", ], $runner->getOutput()); } - - - - - /** - * @test - */ - public function testCreateWebService00() { - $runner = $runner = App::getRunner(); - $runner->setInputs([ - '2', - 'NewWeb', - '', - 'get-hello', - '0', - 'y', - '6', - 'name', - 'n', - 'n', - '', - ]); - $runner->setArgsVector([ - 'webfiori', - 'create' - ]); - $result = $runner->start(); - $this->assertEquals(0, $result); - $this->assertEquals([ - "What would you like to create?\n", - "0: Database table class.\n", - "1: Entity class from table.\n", - "2: Web service.\n", - "3: Background Task.\n", - "4: Middleware.\n", - "5: CLI Command.\n", - "6: Theme.\n", - "7: Database access class based on table.\n", - "8: Complete REST backend (Database table, entity, database access and web services).\n", - "9: Quit. <--\n", - "Enter a name for the new class:\n", - "Enter an optional namespace for the class: Enter = 'app\apis'\n", - "Enter a name for the new web service:\n", - "Request method:\n", - "0: CONNECT\n", - "1: DELETE\n", - "2: GET <--\n", - "3: HEAD\n", - "4: OPTIONS\n", - "5: POST\n", - "6: PUT\n", - "7: TRACE\n", - - "Would you like to add request parameters to the service?(y/N)\n", - "Choose parameter type:\n", - "0: array <--\n", - "1: boolean\n", - "2: email\n", - "3: double\n", - "4: integer\n", - "5: json-obj\n", - "6: string\n", - "7: url\n", - "Enter a name for the request parameter:\n", - "Is this parameter optional?(Y/n)\n", - "Success: New parameter added to the service 'get-hello'.\n", - "Would you like to add another parameter?(y/N)\n", - "Creating the class...\n", - 'Info: New class was created at "'.ROOT_PATH.DS.'app'.DS."apis\".\n", - "Info: Don't forget to add the service to a services manager.\n", - ], $runner->getOutput()); - $this->assertTrue(class_exists('\\app\\apis\\NewWebService')); - $this->removeClass('\\app\\apis\\NewWebService'); - } } From 16e63f5cd38f0f2d511d2c82eb25237faaa9f4dd Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 23:16:57 +0300 Subject: [PATCH 11/22] Update CreateWebServiceTest.php --- tests/webfiori/framework/test/cli/CreateWebServiceTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php index 996ce59a..d42026dd 100644 --- a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php +++ b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php @@ -31,8 +31,7 @@ public function test00() { 'Random desc', 'n', 'n', - '', - '', + 'n', '', ]); $runner->setArgsVector([ @@ -81,8 +80,7 @@ public function test00() { "Description:\n", "Is this parameter optional?(Y/n)\n", "Are empty values allowed?(y/N)\n", - "Minimum length: Enter = '0'\n", - "Maximum length: Enter = '0'\n", + "Would you like to set minimum and maximum length?(y/N)\n", "Success: New parameter added.\n", "Would you like to add another parameter?(y/N)\n", "Creating the class...\n", From dceb8c70c4f1a1066c9988983543964af57d1afd Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 23:17:04 +0300 Subject: [PATCH 12/22] Update CreateWebService.php --- .../cli/helpers/CreateWebService.php | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/webfiori/framework/cli/helpers/CreateWebService.php b/webfiori/framework/cli/helpers/CreateWebService.php index 8b8851c2..fd501530 100644 --- a/webfiori/framework/cli/helpers/CreateWebService.php +++ b/webfiori/framework/cli/helpers/CreateWebService.php @@ -73,8 +73,7 @@ private function addParamsToService() { $paramObj->setIsOptional($this->confirm('Is this parameter optional?', true)); if ($paramObj->getType() == ParamType::STRING || $paramObj->getType() == ParamType::URL || $paramObj->getType() == ParamType::EMAIL) { $paramObj->setIsEmptyStringAllowed($this->confirm('Are empty values allowed?', false)); - $paramObj->setMinLength($this->getCommand()->readInteger('Minimum length:')); - $paramObj->setMaxLength($this->getCommand()->readInteger('Maximum length:')); + $this->setMinAndMaxLength($paramObj); } if ($paramObj->getType() == ParamType::INT || $paramObj->getType() == ParamType::DOUBLE) { $this->setMinAndMax($paramObj); @@ -88,6 +87,26 @@ private function addParamsToService() { $addMore = $this->confirm('Would you like to add another parameter?', false); } while ($addMore); } + private function setMinAndMaxLength(RequestParameter $param) { + $setMinMax = $this->confirm('Would you like to set minimum and maximum length?', false); + + if (!$setMinMax) { + return; + } + $isValid = false; + + while (!$isValid) { + $min = $this->getCommand()->readInteger('Minimum length:'); + $max = $this->getCommand()->readInteger('Maximum length:'); + if ($min < $max) { + $param->setMinLength($min); + $param->setMaxLength($max); + $isValid = true; + } else { + $this->error('Minimum and maximum should not overlap.'); + } + } + } private function setMinAndMax(RequestParameter $param) { $setMinMax = $this->confirm('Would you like to set minimum and maximum limites?', false); From cd3e41bd07c583d4461bedf8e6bdba3181a544ed Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 16 Jan 2024 23:33:54 +0300 Subject: [PATCH 13/22] Update WebServiceWriter.php --- webfiori/framework/writers/WebServiceWriter.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 56c20802..1568ad94 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -49,7 +49,12 @@ class WebServiceWriter extends ClassWriter { */ public function __construct($webServicesObj = null) { parent::__construct('NewWebService', APP_PATH.'apis', APP_DIR.'\\apis'); - + if (!defined('PHP_FLOAT_MAX')) { + define('PHP_FLOAT_MAX', 1.7976931348623E+308); + } + if (!defined('PHP_FLOAT_MIN')) { + define('PHP_FLOAT_MIN', 2.2250738585072E-308); + } $this->setSuffix('Service'); $this->addUseStatement(EAbstractWebService::class); $this->addUseStatement(ParamType::class); From 09f1ea1c0e15efdd3d9f57b8d12f6547f3aaf1b8 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 00:46:41 +0300 Subject: [PATCH 14/22] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fdfb1ad9..9a34166d 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "ext-mbstring": "*", "ext-fileinfo": "*", "ext-openssl": "*", - "webfiori/http":"v3.3.8", + "webfiori/http":"v3.3.9", "webfiori/file":"v1.3.4", "webfiori/jsonx":"v3.3.0", "webfiori/ui":"v2.6.1", From e989a6af1c39461f831825251610938d9ccb25a9 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 00:46:48 +0300 Subject: [PATCH 15/22] Update CreateWebServiceTest.php --- tests/webfiori/framework/test/cli/CreateWebServiceTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php index d42026dd..ae3fd474 100644 --- a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php +++ b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php @@ -200,9 +200,9 @@ public function test01() { ParamOption::DESCRIPTION => 'Random\'s desc', ParamOption::DEFAULT => null, ParamOption::EMPTY => false, - ParamOption::MAX => PHP_FLOAT_MAX, + ParamOption::MAX => defined('PHP_FLOAT_MAX') ? PHP_FLOAT_MAX : 1.7976931348623E+308, ParamOption::MAX_LENGTH => null, - ParamOption::MIN => PHP_FLOAT_MIN, + ParamOption::MIN => defined('PHP_FLOAT_MIN') ? PHP_FLOAT_MIN : 2.2250738585072E-308, ParamOption::MIN_LENGTH => null, ParamOption::OPTIONAL => false, ]); From 10bcf7b24a4573032e63aeff66c684e72b1ac229 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 00:46:56 +0300 Subject: [PATCH 16/22] Update WebServiceWriter.php --- webfiori/framework/writers/WebServiceWriter.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 1568ad94..56c20802 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -49,12 +49,7 @@ class WebServiceWriter extends ClassWriter { */ public function __construct($webServicesObj = null) { parent::__construct('NewWebService', APP_PATH.'apis', APP_DIR.'\\apis'); - if (!defined('PHP_FLOAT_MAX')) { - define('PHP_FLOAT_MAX', 1.7976931348623E+308); - } - if (!defined('PHP_FLOAT_MIN')) { - define('PHP_FLOAT_MIN', 2.2250738585072E-308); - } + $this->setSuffix('Service'); $this->addUseStatement(EAbstractWebService::class); $this->addUseStatement(ParamType::class); From 100e3720385ec106971ee6ac33bc54c807471505 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 00:58:15 +0300 Subject: [PATCH 17/22] Update WebServiceWriter.php --- webfiori/framework/writers/WebServiceWriter.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 56c20802..5bc189ed 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -219,10 +219,14 @@ private function appendParam($param) { } if ($param->getType() == ParamType::INT || $param->getType() == ParamType::DOUBLE) { - if ($param->getMinValue() !== null && ($param->getMinValue() != PHP_INT_MIN && $param->getMinValue() != PHP_FLOAT_MIN)) { + $minFloat = defined('PHP_FLOAT_MIN') ? PHP_FLOAT_MIN : 2.2250738585072E-308; + $maxFloat = defined('PHP_FLOAT_MAX') ? PHP_FLOAT_MAX : 1.7976931348623E+308; + if ($param->getMinValue() !== null && ($param->getMinValue() != $minFloat && $param->getMinValue() != $maxFloat)) { $this->append("ParamOption::MIN => ".$param->getMinValue().",", 4); } - if ($param->getMaxValue() !== null && ($param->getMaxValue() != PHP_INT_MAX && $param->getMaxValue() != PHP_FLOAT_MAX)) { + $maxInt = PHP_INT_MAX; + $minInt = defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX; + if ($param->getMaxValue() !== null && ($param->getMaxValue() != $maxInt && $param->getMinValue() != $minInt)) { $this->append("ParamOption::MAX => ".$param->getMinValue().",", 4); } } From 9d26f5345bafbc42418f4322b34c0c528263498a Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 01:02:15 +0300 Subject: [PATCH 18/22] Update CreateWebServiceTest.php --- .../test/cli/CreateWebServiceTest.php | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php index ae3fd474..cbea6455 100644 --- a/tests/webfiori/framework/test/cli/CreateWebServiceTest.php +++ b/tests/webfiori/framework/test/cli/CreateWebServiceTest.php @@ -207,6 +207,103 @@ public function test01() { ParamOption::OPTIONAL => false, ]); } + /** + * @test + */ + public function test02() { + $runner = $runner = App::getRunner(); + $runner->setInputs([ + 'NewWeb3', + '', + 'get-hello-3', + 'Service\'s Desc', + '', + 'y', + '5', + 'n', + 'y', + 'a-number', + '4', + 'Random\'s desc', + 'n', + '', + '', + ]); + $runner->setArgsVector([ + 'webfiori', + 'create', + '--c' => 'web-service' + ]); + $result = $runner->start(); + //$this->assertEquals(0, $result); + $this->assertEquals([ + "Enter a name for the new class:\n", + "Enter an optional namespace for the class: Enter = 'app\apis'\n", + "Enter a name for the new web service:\n", + "Description:\n", + "Request method:\n", + "0: CONNECT\n", + "1: DELETE\n", + "2: GET <--\n", + "3: HEAD\n", + "4: OPTIONS\n", + "5: POST\n", + "6: PUT\n", + "7: TRACE\n", + "Would you like to add another request method?(y/N)\n", + "Request method:\n", + "0: CONNECT\n", + "1: DELETE\n", + "2: GET <--\n", + "3: HEAD\n", + "4: OPTIONS\n", + "5: POST\n", + "6: PUT\n", + "7: TRACE\n", + "Would you like to add another request method?(y/N)\n", + "Would you like to add request parameters to the service?(y/N)\n", + "Enter a name for the request parameter:\n", + "Choose parameter type:\n", + "0: array <--\n", + "1: boolean\n", + "2: email\n", + "3: double\n", + "4: integer\n", + "5: json-obj\n", + "6: string\n", + "7: url\n", + "Description:\n", + "Is this parameter optional?(Y/n)\n", + "Would you like to set minimum and maximum limites?(y/N)\n", + "Success: New parameter added.\n", + "Would you like to add another parameter?(y/N)\n", + "Creating the class...\n", + 'Info: New class was created at "'.ROOT_PATH.DS.'app'.DS."apis\".\n", + "Info: Don't forget to add the service to a services manager.\n", + ], $runner->getOutput()); + $clazz = '\\app\\apis\\NewWeb3Service'; + $this->assertTrue(class_exists($clazz)); + $this->removeClass($clazz); + $instance = new $clazz(); + $instance instanceof AbstractWebService; + $this->assertEquals('get-hello-3', $instance->getName()); + $this->assertEquals(1, count($instance->getParameters())); + $this->assertEquals('Service\'s Desc', $instance->getDescription()); + $this->assertEquals([RequestMethod::GET, RequestMethod::POST], $instance->getRequestMethods()); + $param00 = $instance->getParameters()[0]; + $this->assertRequestParameter($param00, [ + ParamOption::NAME => 'a-number', + ParamOption::TYPE => ParamType::INT, + ParamOption::DESCRIPTION => 'Random\'s desc', + ParamOption::DEFAULT => null, + ParamOption::EMPTY => false, + ParamOption::MAX => PHP_INT_MAX, + ParamOption::MAX_LENGTH => null, + ParamOption::MIN => defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX, + ParamOption::MIN_LENGTH => null, + ParamOption::OPTIONAL => false, + ]); + } private function assertRequestParameter(RequestParameter $param, array $expected) { $this->assertEquals($expected[ParamOption::NAME], $param->getName()); $this->assertEquals($expected[ParamOption::TYPE], $param->getType()); From 2bc30bc8e0e37d0d50b6275d7e5ce904a3bfc2ba Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 01:09:04 +0300 Subject: [PATCH 19/22] Update App.php --- webfiori/framework/App.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index d008d476..90d2e972 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -540,7 +540,7 @@ private function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_VERSION', '3.0.0-RC19'); + define('WF_VERSION', '3.0.0-Beta 1'); /** * A constant that tells the type of framework version. * @@ -548,7 +548,7 @@ private function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_VERSION_TYPE', 'Release Candidate'); + define('WF_VERSION_TYPE', 'Beta'); /** * The date at which the framework version was released. * @@ -556,7 +556,7 @@ private function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_RELEASE_DATE', '2023-11-07'); + define('WF_RELEASE_DATE', '2024-01-17'); } /** @@ -569,7 +569,7 @@ private function initMiddleware() { }); if (!class_exists(APP_DIR.'\ini\InitMiddleware')) { - Ini::get()->createIniClass('InitMiddleware', 'Register middleware which are created outside the folder \'app/middleware\'.'); + Ini::get()->createIniClass('InitMiddleware', 'Register middleware which are created outside the folder \'[APP_DIR]/middleware\'.'); } call_user_func(APP_DIR.'\ini\InitMiddleware::init'); } From 66ba3394c05228a230ccfdb7c987c6cd89b30f4c Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 01:13:47 +0300 Subject: [PATCH 20/22] Run CS Fixer --- ecs.php | 13 +- .../framework/test/cli/CreateTableTest.php | 5 +- .../framework/test/cli/RunSQLCommandTest.php | 7 +- .../test/cli/SchedulerCommandTest.php | 1 + .../framework/test/router/RouterTest.php | 84 +++--- webfiori/framework/App.php | 58 ++-- webfiori/framework/DB.php | 2 +- webfiori/framework/EmailMessage.php | 5 +- webfiori/framework/Ini.php | 49 ++-- webfiori/framework/ThemeLoader.php | 4 +- .../cli/helpers/CreateWebService.php | 70 ++--- webfiori/framework/config/ClassDriver.php | 58 ++-- .../framework/config/ConfigurationDriver.php | 12 +- webfiori/framework/config/JsonDriver.php | 125 +++++---- webfiori/framework/router/RouteOption.php | 49 ++-- webfiori/framework/router/Router.php | 20 +- webfiori/framework/scheduler/TasksManager.php | 203 +++++++------- webfiori/framework/session/Session.php | 2 +- webfiori/framework/ui/WebPage.php | 31 ++- .../framework/writers/TableClassWriter.php | 263 +++++++++--------- .../framework/writers/WebServiceWriter.php | 143 +++++----- 21 files changed, 611 insertions(+), 593 deletions(-) diff --git a/ecs.php b/ecs.php index 3ae60220..ff05957f 100644 --- a/ecs.php +++ b/ecs.php @@ -6,13 +6,14 @@ use Symplify\EasyCodingStandard\Config\ECSConfig; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; -return function (ECSConfig $ecsConfig): void { +return function (ECSConfig $ecsConfig): void +{ $ecsConfig->paths([ - __DIR__ . '/app', - __DIR__ . '/public', - __DIR__ . '/tests', - __DIR__ . '/themes', - __DIR__ . '/webfiori', + __DIR__.'/app', + __DIR__.'/public', + __DIR__.'/tests', + __DIR__.'/themes', + __DIR__.'/webfiori', ]); // this way you add a single rule diff --git a/tests/webfiori/framework/test/cli/CreateTableTest.php b/tests/webfiori/framework/test/cli/CreateTableTest.php index 27ca07bf..357aa397 100644 --- a/tests/webfiori/framework/test/cli/CreateTableTest.php +++ b/tests/webfiori/framework/test/cli/CreateTableTest.php @@ -496,6 +496,7 @@ public function testCreateTable05() { $this->assertEquals(DataType::INT, $idCol->getDatatype()); $this->assertTrue($idCol->isPrimary()); $this->assertTrue($idCol->isUnique()); + return $clazz; } /** @@ -546,7 +547,7 @@ public function testCreateTable06($refTable) { $output = $runner->getOutput(); $clazz = '\\app\\database\\Cool06Table'; $this->assertTrue(class_exists($clazz)); - + $this->removeClass($clazz); $this->assertEquals(array_merge([ "Database type:\n", @@ -597,7 +598,7 @@ public function testCreateTable06($refTable) { "Would you like to create an entity class that maps to the database table?(y/N)\n", 'Info: New class was created at "'.ROOT_PATH.DS.'app'.DS."database\".\n", ]), $output); - + $instance = new $clazz(); $instance instanceof Table; $this->assertEquals(1, $instance->getForeignKeysCount()); diff --git a/tests/webfiori/framework/test/cli/RunSQLCommandTest.php b/tests/webfiori/framework/test/cli/RunSQLCommandTest.php index 349f3136..f7b4396a 100644 --- a/tests/webfiori/framework/test/cli/RunSQLCommandTest.php +++ b/tests/webfiori/framework/test/cli/RunSQLCommandTest.php @@ -88,7 +88,6 @@ public function testCLIQuery01() { * @test */ public function testCLIQuery02() { - JsonDriver::setConfigFileName('run-sql-test'); App::setConfigDriver(JsonDriver::class); @@ -97,11 +96,11 @@ public function testCLIQuery02() { App::getConfig()->addOrUpdateDBConnection($conn); $driver = new JsonDriver(); $driver->setConfigFileName('run-sql-test'); - + Controller::setDriver($driver); - + $this->assertTrue(get_class(App::getConfig()) == JsonDriver::class); - + $runner = App::getRunner(); $runner->setArgsVector([ 'webfiori', diff --git a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php index a0b436d1..be96bea3 100644 --- a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php +++ b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php @@ -184,6 +184,7 @@ public function test05() { "Skip"]; $actual = $runner->getOutput(); $idx = 0; + foreach ($expected as $item) { if ($item == 'Skip') { break; diff --git a/tests/webfiori/framework/test/router/RouterTest.php b/tests/webfiori/framework/test/router/RouterTest.php index 97d8b122..ceab4fe2 100644 --- a/tests/webfiori/framework/test/router/RouterTest.php +++ b/tests/webfiori/framework/test/router/RouterTest.php @@ -25,13 +25,13 @@ public function test00() { */ public function testAddAPIRoute00() { $this->assertTrue(Router::api([ - RouteOption::PATH => '/call-api-00', - RouteOption::TO => '/my-api.php'])); + RouteOption::PATH => '/call-api-00', + RouteOption::TO => '/my-api.php'])); $this->assertFalse(Router::page([ - RouteOption::PATH => '/call-api-00', - RouteOption::TO => '/my-other-api.php'])); + RouteOption::PATH => '/call-api-00', + RouteOption::TO => '/my-other-api.php'])); $this->assertTrue(Router::page([ - RouteOption::PATH => '/call-api-01', + RouteOption::PATH => '/call-api-01', RouteOption::TO => '/my-api.php'])); } /** @@ -45,20 +45,20 @@ public function testAddClosureRoute00() { { }; $this->assertTrue(Router::closure([ - RouteOption::PATH => '/call', - RouteOption::TO => $c1 + RouteOption::PATH => '/call', + RouteOption::TO => $c1 ])); $this->assertFalse(Router::closure([ - RouteOption::PATH => '/call', - RouteOption::TO => $c2 + RouteOption::PATH => '/call', + RouteOption::TO => $c2 ])); $this->assertTrue(Router::closure([ - RouteOption::PATH => '/call-2', - RouteOption::TO => $c1 + RouteOption::PATH => '/call-2', + RouteOption::TO => $c1 ])); $this->assertFalse(Router::closure([ - RouteOption::PATH => '/call', - RouteOption::TO => 'Not Func' + RouteOption::PATH => '/call', + RouteOption::TO => 'Not Func' ])); } /** @@ -66,14 +66,14 @@ public function testAddClosureRoute00() { */ public function testAddViewRoute00() { $this->assertTrue(Router::page([ - RouteOption::PATH => '/view-something', - RouteOption::TO => 'my-view.php'])); + RouteOption::PATH => '/view-something', + RouteOption::TO => 'my-view.php'])); $this->assertFalse(Router::page([ - RouteOption::PATH => '/view-something', - RouteOption::TO => '/my-other-view.php'])); + RouteOption::PATH => '/view-something', + RouteOption::TO => '/my-other-view.php'])); $this->assertTrue(Router::page([ - RouteOption::PATH => '/view-something-2', - RouteOption::TO => '/my-view.php'])); + RouteOption::PATH => '/view-something-2', + RouteOption::TO => '/my-view.php'])); } /** * @test @@ -84,8 +84,8 @@ public function testOptionalParam00() { { }); Router::closure([ - RouteOption::PATH => '{var-1}/{var-2?}', - RouteOption::TO => function() + RouteOption::PATH => '{var-1}/{var-2?}', + RouteOption::TO => function() { }, RouteOption::VALUES => [ @@ -110,8 +110,8 @@ public function testOptionalParam01() { Router::removeAll(); Router::closure([ - RouteOption::PATH => '{var-1}/{var-2?}', - RouteOption::TO => function() + RouteOption::PATH => '{var-1}/{var-2?}', + RouteOption::TO => function() { } ]); @@ -132,8 +132,8 @@ public function testRoute00() { { }); Router::closure([ - RouteOption::PATH => '{var-1}/{var-2}', - RouteOption::TO => function() + RouteOption::PATH => '{var-1}/{var-2}', + RouteOption::TO => function() { } ]); @@ -155,8 +155,8 @@ public function testRoute01() { { }); Router::closure([ - RouteOption::PATH => '{var-1}/{var-2}/{var-1}', - RouteOption::TO => function() + RouteOption::PATH => '{var-1}/{var-2}/{var-1}', + RouteOption::TO => function() { } ]); @@ -172,37 +172,37 @@ public function testRoute01() { public function testRoutesGroup00() { Router::removeAll(); Router::page([ - RouteOption::PATH => 'users', + RouteOption::PATH => 'users', RouteOption::CASE_SENSITIVE => false, RouteOption::MIDDLEWARE => 'M1', RouteOption::LANGS => ['EN'], RouteOption::REQUEST_METHODS => RequestMethod::POST, - RouteOption::SUB_ROUTES => [ + RouteOption::SUB_ROUTES => [ [ - RouteOption::PATH => 'view-user/{user-id}', - RouteOption::TO => 'ViewUserPage.php', - RouteOption::LANGS => ['AR'] + RouteOption::PATH => 'view-user/{user-id}', + RouteOption::TO => 'ViewUserPage.php', + RouteOption::LANGS => ['AR'] ], [ - RouteOption::PATH => 'get-users', - RouteOption::LANGS => ['AR'], + RouteOption::PATH => 'get-users', + RouteOption::LANGS => ['AR'], RouteOption::CASE_SENSITIVE => true, - RouteOption::SUB_ROUTES => [ + RouteOption::SUB_ROUTES => [ [ - RouteOption::PATH => 'by-name', - RouteOption::TO => 'GetUserByName.php', - RouteOption::LANGS => ['FR'], + RouteOption::PATH => 'by-name', + RouteOption::TO => 'GetUserByName.php', + RouteOption::LANGS => ['FR'], RouteOption::CASE_SENSITIVE => false, ], [ - RouteOption::PATH => 'by-email', - RouteOption::TO => 'GetUserByEmail.php' + RouteOption::PATH => 'by-email', + RouteOption::TO => 'GetUserByEmail.php' ] ], ], [ - RouteOption::PATH => '/', - RouteOption::TO => 'ListUsers.php', + RouteOption::PATH => '/', + RouteOption::TO => 'ListUsers.php', RouteOption::CASE_SENSITIVE => true, RouteOption::REQUEST_METHODS => [RequestMethod::OPTIONS, RequestMethod::GET] ] diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index 90d2e972..c6c33018 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -45,31 +45,6 @@ * */ class App { - /** - * Sets the class that will be used as configuration driver. - * - * This method must be used before calling the method 'App::start()' in order - * to set proper configuration driver. - * - * @param string $clazz The full name of the class including namespace. - */ - public static function setConfigDriver(string $clazz) { - self::$ConfigDriver = $clazz; - } - /** - * Returns the class that represents configuration driver. - * - * @return string The full name of the class including namespace. - */ - public static function getConfigDriver() : string { - return self::$ConfigDriver; - } - /** - * A string which points to the class that represents configuration driver. - * - * @var string - */ - private static $ConfigDriver = 'webfiori\\framework\\config\\ClassDriver'; /** * A constant that indicates that the status of the class is 'initialized'. * @@ -106,6 +81,12 @@ public static function getConfigDriver() : string { * @var Runner */ private static $CliRunner; + /** + * A string which points to the class that represents configuration driver. + * + * @var string + */ + private static $ConfigDriver = 'webfiori\\framework\\config\\ClassDriver'; /** * A single instance of the class. * @@ -295,14 +276,23 @@ public static function getClassStatus() { */ public static function getConfig(): ConfigurationDriver { $driver = Controller::getDriver(); - + if (get_class($driver) != self::$ConfigDriver) { Controller::setDriver(new self::$ConfigDriver()); Controller::get()->updateEnv(); $driver = Controller::getDriver(); } + return $driver; } + /** + * Returns the class that represents configuration driver. + * + * @return string The full name of the class including namespace. + */ + public static function getConfigDriver() : string { + return self::$ConfigDriver; + } /** * Returns an instance which represents the class that is used to run the @@ -368,6 +358,17 @@ public static function getRunner() : Runner { return self::$CliRunner; } + /** + * Sets the class that will be used as configuration driver. + * + * This method must be used before calling the method 'App::start()' in order + * to set proper configuration driver. + * + * @param string $clazz The full name of the class including namespace. + */ + public static function setConfigDriver(string $clazz) { + self::$ConfigDriver = $clazz; + } /** * Start your WebFiori application. @@ -420,7 +421,7 @@ private function checkAppDir() { /** * Directory separator. */ - define('DS', DIRECTORY_SEPARATOR); + define('DS', DIRECTORY_SEPARATOR); } if (!defined('APP_DIR')) { @@ -437,13 +438,14 @@ private function checkAppDir() { http_response_code(500); die('Error: Unable to initialize the application. Invalid application directory name: "'.APP_DIR.'".'); } + if (!defined('APP_PATH')) { /** * The absolute path to application directory. * * @var string */ - define('APP_PATH', ROOT_PATH.DIRECTORY_SEPARATOR.APP_DIR.DIRECTORY_SEPARATOR); + define('APP_PATH', ROOT_PATH.DIRECTORY_SEPARATOR.APP_DIR.DIRECTORY_SEPARATOR); } } diff --git a/webfiori/framework/DB.php b/webfiori/framework/DB.php index 56c8f5c2..caa6a3ed 100644 --- a/webfiori/framework/DB.php +++ b/webfiori/framework/DB.php @@ -62,7 +62,7 @@ public function __construct($connName) { } if (!($conn instanceof ConnectionInfo)) { - throw new DatabaseException("No connection was found which has the name '$connName'. Driver: ". get_class(App::getConfig()).'.'); + throw new DatabaseException("No connection was found which has the name '$connName'. Driver: ".get_class(App::getConfig()).'.'); } } parent::__construct($conn); diff --git a/webfiori/framework/EmailMessage.php b/webfiori/framework/EmailMessage.php index 859f5585..978fc46e 100644 --- a/webfiori/framework/EmailMessage.php +++ b/webfiori/framework/EmailMessage.php @@ -10,10 +10,10 @@ */ namespace webfiori\framework; +use webfiori\email\Email; use webfiori\email\exceptions\SMTPException; use webfiori\email\SMTPAccount; use webfiori\framework\exceptions\MissingLangException; -use webfiori\email\Email; /** * A class that can be used to write HTML formatted Email messages. * @@ -82,13 +82,12 @@ public function getTranslation() { * * @return Email The method will return same instance at which the method is * called on. - * + * * @throws MissingLangException */ public function setLang(string $lang = 'EN') : Email { if (parent::setLang($lang)) { $this->usingLanguage(); - } return $this; diff --git a/webfiori/framework/Ini.php b/webfiori/framework/Ini.php index e79a5c95..8ead27dc 100644 --- a/webfiori/framework/Ini.php +++ b/webfiori/framework/Ini.php @@ -34,10 +34,32 @@ class Ini { */ private static $singleton; private function __construct() { - } - + + /** + * Creates all directories at which the application needs to run. + */ + public static function createAppDirs() { + $DS = DIRECTORY_SEPARATOR; + self::mkdir(ROOT_PATH.$DS.APP_DIR); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'ini'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'ini'.$DS.'routes'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'pages'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'commands'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'tasks'); + self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'middleware'); + 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.'public'); + } + + /** * Creates initialization class. @@ -126,29 +148,6 @@ public static function get(): Ini { return self::$singleton; } - - - /** - * Creates all directories at which the application needs to run. - */ - public static function createAppDirs() { - $DS = DIRECTORY_SEPARATOR; - self::mkdir(ROOT_PATH.$DS.APP_DIR); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'ini'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'ini'.$DS.'routes'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'pages'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'commands'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'tasks'); - self::mkdir(ROOT_PATH.$DS.APP_DIR.$DS.'middleware'); - 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.'public'); - } public static function mkdir($dir) { if (!is_dir($dir)) { set_error_handler(function (int $errno, string $errstr) diff --git a/webfiori/framework/ThemeLoader.php b/webfiori/framework/ThemeLoader.php index 2b1b1ed1..9056e06c 100644 --- a/webfiori/framework/ThemeLoader.php +++ b/webfiori/framework/ThemeLoader.php @@ -55,7 +55,7 @@ private function __construct() { * * This method will return an associative array. The key is the theme * name and the value is an object of type Theme that contains theme info. - * + * * @param bool $updateCache If set to true, cached data of descovered themes * will reset and search will be performed again. * @@ -251,7 +251,7 @@ private static function scanDir($filesInDir, $pathToScan, $dirName) { $ns = require_once $pathToScan.DS.$fileName; $aNs = gettype($ns) == 'string' ? $ns.'\\' : '\\'; $aCName = $aNs.$cName; - + if (!class_exists($aCName)) { $aCName = '\\'.self::THEMES_DIR.'\\'.$dirName.'\\'.$cName; } diff --git a/webfiori/framework/cli/helpers/CreateWebService.php b/webfiori/framework/cli/helpers/CreateWebService.php index fd501530..9cca0a82 100644 --- a/webfiori/framework/cli/helpers/CreateWebService.php +++ b/webfiori/framework/cli/helpers/CreateWebService.php @@ -33,6 +33,21 @@ public function __construct(CreateCommand $command) { $this->serviceObj = new ServiceHolder(); parent::__construct($command, new WebServiceWriter($this->serviceObj)); } + public function addRequestMethods() { + $toSelect = RequestMethod::getAll(); + $addOne = true; + + while ($addOne) { + array_multisort($toSelect); + $this->serviceObj->addRequestMethod($this->select('Request method:', $toSelect, 2)); + + if (count($toSelect) > 1) { + $addOne = $this->confirm('Would you like to add another request method?', false); + } else { + $addOne = false; + } + } + } public function readClassInfo() { $this->setClassInfo(APP_DIR.'\\apis', 'Service'); @@ -48,21 +63,6 @@ public function readClassInfo() { $this->writeClass(); $this->info('Don\'t forget to add the service to a services manager.'); } - public function addRequestMethods() { - $toSelect = RequestMethod::getAll(); - $addOne = true; - - while ($addOne) { - - array_multisort($toSelect); - $this->serviceObj->addRequestMethod($this->select('Request method:', $toSelect, 2)); - if (count($toSelect) > 1) { - $addOne = $this->confirm('Would you like to add another request method?', false); - } else { - $addOne = false; - } - } - } private function addParamsToService() { do { $paramObj = new RequestParameter('h'); @@ -71,14 +71,16 @@ private function addParamsToService() { $paramObj->setDescription($this->getInput('Description:')); $added = $this->serviceObj->addParameter($paramObj); $paramObj->setIsOptional($this->confirm('Is this parameter optional?', true)); + if ($paramObj->getType() == ParamType::STRING || $paramObj->getType() == ParamType::URL || $paramObj->getType() == ParamType::EMAIL) { $paramObj->setIsEmptyStringAllowed($this->confirm('Are empty values allowed?', false)); $this->setMinAndMaxLength($paramObj); } + if ($paramObj->getType() == ParamType::INT || $paramObj->getType() == ParamType::DOUBLE) { $this->setMinAndMax($paramObj); } - + if ($added) { $this->success('New parameter added.'); } else { @@ -87,41 +89,43 @@ private function addParamsToService() { $addMore = $this->confirm('Would you like to add another parameter?', false); } while ($addMore); } - private function setMinAndMaxLength(RequestParameter $param) { - $setMinMax = $this->confirm('Would you like to set minimum and maximum length?', false); - + private function setMinAndMax(RequestParameter $param) { + $setMinMax = $this->confirm('Would you like to set minimum and maximum limites?', false); + if (!$setMinMax) { return; } $isValid = false; - + $method = $param->getType() == ParamType::INT ? 'readInteger' : 'readFloat'; + while (!$isValid) { - $min = $this->getCommand()->readInteger('Minimum length:'); - $max = $this->getCommand()->readInteger('Maximum length:'); + $min = $this->getCommand()->$method('Minimum value:'); + $max = $this->getCommand()->$method('Maximum value:'); + if ($min < $max) { - $param->setMinLength($min); - $param->setMaxLength($max); + $param->setMinValue($min); + $param->setMaxValue($max); $isValid = true; } else { $this->error('Minimum and maximum should not overlap.'); } } } - private function setMinAndMax(RequestParameter $param) { - $setMinMax = $this->confirm('Would you like to set minimum and maximum limites?', false); - + private function setMinAndMaxLength(RequestParameter $param) { + $setMinMax = $this->confirm('Would you like to set minimum and maximum length?', false); + if (!$setMinMax) { return; } $isValid = false; - $method = $param->getType() == ParamType::INT ? 'readInteger' : 'readFloat'; - + while (!$isValid) { - $min = $this->getCommand()->$method('Minimum value:'); - $max = $this->getCommand()->$method('Maximum value:'); + $min = $this->getCommand()->readInteger('Minimum length:'); + $max = $this->getCommand()->readInteger('Maximum length:'); + if ($min < $max) { - $param->setMinValue($min); - $param->setMaxValue($max); + $param->setMinLength($min); + $param->setMaxLength($max); $isValid = true; } else { $this->error('Minimum and maximum should not overlap.'); diff --git a/webfiori/framework/config/ClassDriver.php b/webfiori/framework/config/ClassDriver.php index 90236dc7..43b3209d 100644 --- a/webfiori/framework/config/ClassDriver.php +++ b/webfiori/framework/config/ClassDriver.php @@ -33,6 +33,26 @@ public function __construct() { $this->docEmptyLine = " * "; $this->initDefaultConfig(); } + public static function a($file, $str, $tabSize = 0) { + $isResource = is_resource($file); + $tabStr = $tabSize > 0 ? ' ' : ''; + + if (gettype($str) == 'array') { + foreach ($str as $subStr) { + if ($isResource) { + fwrite($file, str_repeat($tabStr, $tabSize).$subStr.self::NL); + } else { + $file->append(str_repeat($tabStr, $tabSize).$subStr.self::NL); + } + } + } else { + if ($isResource) { + fwrite($file, str_repeat($tabStr, $tabSize).$str.self::NL); + } else { + $file->append(str_repeat($tabStr, $tabSize).$str.self::NL); + } + } + } /** * Adds application environment variable to the configuration. * @@ -54,15 +74,6 @@ public function addEnvVar(string $name, $value, string $description = null) { ]; $this->writeAppConfig(); } - /** - * Removes specific application environment variable given its name. - * - * @param string $name The name of the variable. - */ - public function removeEnvVar(string $name) { - unset($this->configVars['env-vars'][$name]); - $this->writeAppConfig(); - } /** * Adds new database connections information or update existing connection. * @@ -362,6 +373,15 @@ public function removeDBConnection(string $connectionName) { $this->configVars['database-connections'] = $updated; $this->writeAppConfig(); } + /** + * Removes specific application environment variable given its name. + * + * @param string $name The name of the variable. + */ + public function removeEnvVar(string $name) { + unset($this->configVars['env-vars'][$name]); + $this->writeAppConfig(); + } public function removeSMTPAccount(string $accountName) { if (isset($this->configVars['smtp-connections'][$accountName])) { @@ -861,26 +881,6 @@ public function writeAppConfig() { self::a($cFile, "}"); $cFile->write(false, true); } - public static function a($file, $str, $tabSize = 0) { - $isResource = is_resource($file); - $tabStr = $tabSize > 0 ? ' ' : ''; - - if (gettype($str) == 'array') { - foreach ($str as $subStr) { - if ($isResource) { - fwrite($file, str_repeat($tabStr, $tabSize).$subStr.self::NL); - } else { - $file->append(str_repeat($tabStr, $tabSize).$subStr.self::NL); - } - } - } else { - if ($isResource) { - fwrite($file, str_repeat($tabStr, $tabSize).$str.self::NL); - } else { - $file->append(str_repeat($tabStr, $tabSize).$str.self::NL); - } - } - } private function initDefaultConfig() { $this->configVars = [ 'smtp-connections' => [], diff --git a/webfiori/framework/config/ConfigurationDriver.php b/webfiori/framework/config/ConfigurationDriver.php index 40fcd411..367eade9 100644 --- a/webfiori/framework/config/ConfigurationDriver.php +++ b/webfiori/framework/config/ConfigurationDriver.php @@ -27,12 +27,6 @@ interface ConfigurationDriver { * of the constant. */ public function addEnvVar(string $name, $value, string $description = null); - /** - * Removes specific application environment variable given its name. - * - * @param string $name The name of the variable. - */ - public function removeEnvVar(string $name); /** * Adds new database connections information or update existing connections. * @@ -257,6 +251,12 @@ public function removeAllDBConnections(); * */ public function removeDBConnection(string $connectionName); + /** + * Removes specific application environment variable given its name. + * + * @param string $name The name of the variable. + */ + public function removeEnvVar(string $name); /** * Removes SMTP account if it exists. * diff --git a/webfiori/framework/config/JsonDriver.php b/webfiori/framework/config/JsonDriver.php index e457b380..4fd096fc 100644 --- a/webfiori/framework/config/JsonDriver.php +++ b/webfiori/framework/config/JsonDriver.php @@ -19,10 +19,6 @@ * @author Ibrahim */ class JsonDriver implements ConfigurationDriver { - /** - * The name of JSON configuration file. - */ - private static $configFileName = 'app-config'; /** * The location at which the configuration file will be kept at. * @@ -30,29 +26,9 @@ class JsonDriver implements ConfigurationDriver { */ const JSON_CONFIG_FILE_PATH = APP_PATH.'config'.DIRECTORY_SEPARATOR; /** - * Sets the name of the file that configuration values will be taken from. - * - * The file must exist on the directory [APP_PATH]/config/ . - * - * @param string $name - */ - public static function setConfigFileName(string $name) { - $split = explode('.', trim($name)); - if (count($split) == 2) { - self::$configFileName = $split[0]; - } else if (count($split) == 1) { - self::$configFileName = trim($name); - } - } - /** - * Returns the name of the file at which the application is using to - * read configuration. - * - * @return string + * The name of JSON configuration file. */ - public static function getConfigFileName() : string { - return self::$configFileName; - } + private static $configFileName = 'app-config'; private $json; /** * Creates new instance of the class. @@ -118,15 +94,6 @@ public function addEnvVar(string $name, $value, string $description = null) { ])); $this->writeJson(); } - /** - * Removes specific application environment variable given its name. - * - * @param string $name The name of the variable. - */ - public function removeEnvVar(string $name) { - $this->json->get('env-vars')->remove($name); - $this->writeJson(); - } public function addOrUpdateDBConnection(ConnectionInfo $dbConnectionsInfo) { $connectionJAsJson = new Json([ 'type' => $dbConnectionsInfo->getDatabaseType(), @@ -190,19 +157,30 @@ public function getAppVersionType() : string { } /** * Returns the base URL of the application. - * + * * Note that if the base is set so 'DYNAMIC' in the configuration, it will * be auto-generated at run time. - * + * * @return string A string such as 'http://example.com:8989'. */ public function getBaseURL(): string { $val = $this->json->get('base-url'); + if ($val == '' || $val == 'DYNAMIC') { return Uri::getBaseURL(); } + return $val; } + /** + * Returns the name of the file at which the application is using to + * read configuration. + * + * @return string + */ + public static function getConfigFileName() : string { + return self::$configFileName; + } public function getDBConnection(string $conName) { $jsonObj = $this->json->get('database-connections')->get($conName); @@ -210,11 +188,13 @@ public function getDBConnection(string $conName) { if ($jsonObj !== null) { $extras = $jsonObj->get('extras'); $extrasArr = []; + if ($extras instanceof Json) { foreach ($extras->getProperties() as $prop) { $extrasArr[$prop->getName()] = $prop->getValue(); } } + return new ConnectionInfo( $jsonObj->get('type'), $jsonObj->get('username'), @@ -239,15 +219,15 @@ public function getDBConnections(): array { $name = $propObj->getName(); $jsonObj = $propObj->getValue(); $acc = new ConnectionInfo( - $this->getProp($jsonObj, 'type', $name), - $this->getProp($jsonObj, 'username', $name), - $this->getProp($jsonObj, 'password', $name), - $this->getProp($jsonObj, 'database', $name)); + $this->getProp($jsonObj, 'type', $name), + $this->getProp($jsonObj, 'username', $name), + $this->getProp($jsonObj, 'password', $name), + $this->getProp($jsonObj, 'database', $name)); $extrasObj = $jsonObj->get('extras'); - + if ($extrasObj !== null && $extrasObj instanceof Json) { $extrasArr = []; - + foreach ($extrasObj->getProperties() as $prop) { $extrasArr[$prop->getName()] = $prop->getValue(); } @@ -261,13 +241,6 @@ public function getDBConnections(): array { return $retVal; } - private function getProp(Json $j, $name, string $connName) { - $val = $j->get($name); - if ($val === null) { - throw new InitializationException('The property "'.$name.'" of the connection "'.$connName.'" is missing.'); - } - return $val; - } public function getDescription(string $langCode) { return $this->json->get('app-descriptions')->get(strtoupper(trim($langCode))); @@ -305,17 +278,19 @@ public function getEnvVars(): array { } /** * Returns a string that represent the home page of the application. - * + * * Note that if home page is set to 'BASE_URL' in configuration, the * method will use the base URL as the home page. - * + * * @return string */ public function getHomePage() : string { $home = $this->json->get('home-page') ?? ''; + if ($home == 'BASE_URL') { return $this->getBaseURL(); } + return $home; } @@ -367,7 +342,7 @@ public function getSMTPConnection(string $name) { } /** * Returns an array that contains all added SMTP accounts. - * + * * @return array An array that contains all added SMTP accounts. */ public function getSMTPConnections(): array { @@ -406,11 +381,11 @@ public function getTheme(): string { public function getTitle(string $lang) : string { $titles = $this->json->get('titles'); $langU = strtoupper(trim($lang)); - + if (strlen($langU) == 0) { return ''; } - + foreach ($titles->getProperties() as $prob) { if ($prob->getName() == $langU) { return $prob->getValue(); @@ -465,6 +440,15 @@ public function removeAllDBConnections() { public function removeDBConnection(string $connectionName) { } + /** + * Removes specific application environment variable given its name. + * + * @param string $name The name of the variable. + */ + public function removeEnvVar(string $name) { + $this->json->get('env-vars')->remove($name); + $this->writeJson(); + } public function removeSMTPAccount(string $accountName) { $this->json->add('smtp-connections', new Json()); @@ -509,6 +493,22 @@ public function setAppVersion(string $vNum, string $vType, string $releaseDate) public function setBaseURL(string $url) { } + /** + * Sets the name of the file that configuration values will be taken from. + * + * The file must exist on the directory [APP_PATH]/config/ . + * + * @param string $name + */ + public static function setConfigFileName(string $name) { + $split = explode('.', trim($name)); + + if (count($split) == 2) { + self::$configFileName = $split[0]; + } else if (count($split) == 1) { + self::$configFileName = trim($name); + } + } /** * Sets or update default description of the application that will be used * by web pages. @@ -595,7 +595,7 @@ public function setTitle(string $title, string $langCode) { return; } $trimmedTitle = trim($title); - + if (strlen($trimmedTitle) == 0) { return; } @@ -612,7 +612,7 @@ public function setTitle(string $title, string $langCode) { */ public function setTitleSeparator(string $separator) { $trimmed = trim($separator); - + if (strlen($trimmed) != 0) { $this->json->add('name-separator', $separator); $this->writeJson(); @@ -621,6 +621,15 @@ public function setTitleSeparator(string $separator) { public function toJSON() : Json { return $this->json; } + private function getProp(Json $j, $name, string $connName) { + $val = $j->get($name); + + if ($val === null) { + throw new InitializationException('The property "'.$name.'" of the connection "'.$connName.'" is missing.'); + } + + return $val; + } private function isValidLangCode($langCode) { $code = strtoupper(trim($langCode)); @@ -638,6 +647,4 @@ private function writeJson() { $file->setRawData($json.''); $file->write(false, true); } - - } diff --git a/webfiori/framework/router/RouteOption.php b/webfiori/framework/router/RouteOption.php index c97bc121..0a3827d7 100644 --- a/webfiori/framework/router/RouteOption.php +++ b/webfiori/framework/router/RouteOption.php @@ -8,7 +8,6 @@ * https://github.com/WebFiori/.github/blob/main/LICENSE * */ - namespace webfiori\framework\router; /** @@ -18,55 +17,55 @@ */ class RouteOption { /** - * An option that represents the path part of the URI. - */ - const PATH = 'path'; - /** - * An option which is used to set the resource at which the route will point to. + * An option which is used to set the name of controller action that will be invoked (MVC). */ - const TO = 'route-to'; + const ACTION = 'action'; /** - * An option which is used to set route type. + * An option which is used to treat the route as an API call. */ - const TYPE = 'type'; + const API = 'as-api'; /** * An option which is used to indicate if path is case sensitive or not. */ const CASE_SENSITIVE = 'case-sensitive'; /** - * An option which is used to tell if the route should be part of auto-generated sitemap or not. + * An option which is used to set an array as closure parameters (applies to routes of type closure only) */ - const SITEMAP = 'in-sitemap'; + const CLOSURE_PARAMS = 'closure-params'; /** - * An option which is used to treat the route as an API call. + * An option which is used to set the languages at which the route will be available at (used in building sitemap). */ - const API = 'as-api'; + const LANGS = 'languages'; /** - * An option which is used to set an array as closure parameters (applies to routes of type closure only) + * An option which is used to set the middleware that will be applied to the route. */ - const CLOSURE_PARAMS = 'closure-params'; + const MIDDLEWARE = 'middleware'; /** - * An option which is used to set the name of controller action that will be invoked (MVC). + * An option that represents the path part of the URI. */ - const ACTION = 'action'; + const PATH = 'path'; /** * An option which is used to set an array of allowed request methods. */ const REQUEST_METHODS = 'methods'; /** - * An option which is used to set the languages at which the route will be available at (used in building sitemap). + * An option which is used to tell if the route should be part of auto-generated sitemap or not. */ - const LANGS = 'languages'; + const SITEMAP = 'in-sitemap'; /** - * An option which is used to set an array of allowed values to route parameters. + * An option which is used to set sub-routes. */ - const VALUES = 'vars-values'; + const SUB_ROUTES = 'routes'; /** - * An option which is used to set the middleware that will be applied to the route. + * An option which is used to set the resource at which the route will point to. */ - const MIDDLEWARE = 'middleware'; + const TO = 'route-to'; /** - * An option which is used to set sub-routes. + * An option which is used to set route type. */ - const SUB_ROUTES = 'routes'; + const TYPE = 'type'; + /** + * An option which is used to set an array of allowed values to route parameters. + */ + const VALUES = 'vars-values'; } diff --git a/webfiori/framework/router/Router.php b/webfiori/framework/router/Router.php index 795eed6c..3285bf05 100644 --- a/webfiori/framework/router/Router.php +++ b/webfiori/framework/router/Router.php @@ -1103,6 +1103,16 @@ private function fixUriPath(string $path): string { return $path; } + private function getFileDirAndName($absDir): array { + $explode = explode(DS, $absDir); + $fileName = $explode[count($explode) - 1]; + $dir = substr($absDir, 0, strlen($absDir) - strlen($fileName)); + + return [ + 'name' => $fileName, + 'dir' => $dir + ]; + } /** * Creates and Returns a single instance of the router. * @@ -1118,16 +1128,6 @@ private static function getInstance(): Router { return self::$router; } - private function getFileDirAndName($absDir): array { - $explode = explode(DS, $absDir); - $fileName = $explode[count($explode) - 1]; - $dir = substr($absDir, 0, strlen($absDir) - strlen($fileName)); - - return [ - 'name' => $fileName, - 'dir' => $dir - ]; - } /** * Returns an array that holds allowed request methods for fetching the * specified resource. diff --git a/webfiori/framework/scheduler/TasksManager.php b/webfiori/framework/scheduler/TasksManager.php index 142ea81f..932a0f4a 100644 --- a/webfiori/framework/scheduler/TasksManager.php +++ b/webfiori/framework/scheduler/TasksManager.php @@ -281,6 +281,20 @@ public static function get(): TasksManager { return self::$tasksManager; } + /** + * Returns the number of current hour in the day as integer. + * + * This method is used by the class 'AbstractTask' to validate task + * execution time. The method will always return a value between 0 and 23 + * inclusive. + * + * @return int An integer that represents current hour number in + * the day. + * @since 1.0.2 + */ + public static function getHour() : int { + return self::get()->timestamp['hour']; + } /** * Returns the array that contains logged messages. * @@ -294,6 +308,48 @@ public static function get(): TasksManager { public static function getLogArray() : array { return self::get()->logsArray; } + /** + * Returns the number of current minute in the current hour as integer. + * + * This method is used by the class 'AbstractTask' to validate task + * execution time. The method will always return a value between 0 and 59 + * inclusive. + * + * @return int An integer that represents current minute number in + * the current hour. + * + * @since 1.0.2 + */ + public static function getMinute() : int { + return self::get()->timestamp['minute']; + } + /** + * Returns the number of current month as integer. + * + * This method is used by the class 'AbstracTask' to validate task + * execution time. The method will always return a value between 1 and 12 + * inclusive. + * + * @return int An integer that represents current month's number. + * @since 1.0.2 + */ + public static function getMonth() : int { + return self::get()->timestamp['month']; + } + /** + * Gets the password that is used to protect tasks execution. + * + * The password is used to prevent unauthorized access to execute tasks. + * The provided password must be 'sha256' hashed string. It is recommended + * to hash the password externally then use the hash inside your code. + * + * @return string If the password is set, the method will return it. + * If not set, the method will return the string 'NO_PASSWORD'. + * + */ + public static function getPassword() : string { + return self::get()->getPasswordHelper(); + } /** * Returns a task given its name. * @@ -338,18 +394,42 @@ public static function getTasksNames() : array { return self::get()->tasksNamesArr; } /** - * Returns the number of current hour in the day as integer. + * Returns the time at which tasks check was initialized. * - * This method is used by the class 'AbstractTask' to validate task - * execution time. The method will always return a value between 0 and 23 - * inclusive. + * @return string The method will return a time string in the format + * 'YY-DD HH:MM' where: + *
    + *
  • 'YY' is month number.
  • + *
  • 'MM' is day number in the current month.
  • + *
  • 'HH' is the hour.
  • + *
  • 'MM' is the minute.
  • + *
* - * @return int An integer that represents current hour number in - * the day. - * @since 1.0.2 + * @since 1.0.7 */ - public static function getHour() : int { - return self::get()->timestamp['hour']; + public static function getTimestamp() : string { + $month = self::getMonth(); + + if ($month < 10) { + $month = '0'.$month; + } + $day = self::dayOfMonth(); + + if ($day < 10) { + $day = '0'.$day; + } + $hour = self::getHour(); + + if ($hour < 10) { + $hour = '0'.$hour; + } + $minute = self::getMinute(); + + if ($minute < 10) { + $minute = '0'.$minute; + } + + return $month.'-'.$day.' '.$hour.':'.$minute; } /** * Creates routes to tasks web interface pages. @@ -409,34 +489,6 @@ public static function log(string $message) { self::get()->command->println("%s", $message); } } - /** - * Returns the number of current minute in the current hour as integer. - * - * This method is used by the class 'AbstractTask' to validate task - * execution time. The method will always return a value between 0 and 59 - * inclusive. - * - * @return int An integer that represents current minute number in - * the current hour. - * - * @since 1.0.2 - */ - public static function getMinute() : int { - return self::get()->timestamp['minute']; - } - /** - * Returns the number of current month as integer. - * - * This method is used by the class 'AbstracTask' to validate task - * execution time. The method will always return a value between 1 and 12 - * inclusive. - * - * @return int An integer that represents current month's number. - * @since 1.0.2 - */ - public static function getMonth() : int { - return self::get()->timestamp['month']; - } /** * Create a task that will be executed once every month. * @@ -479,33 +531,6 @@ public static function monthlyTask(int $dayNumber, string $time, string $name, c return false; } - /** - * Gets the password that is used to protect tasks execution. - * - * The password is used to prevent unauthorized access to execute tasks. - * The provided password must be 'sha256' hashed string. It is recommended - * to hash the password externally then use the hash inside your code. - * - * @return string If the password is set, the method will return it. - * If not set, the method will return the string 'NO_PASSWORD'. - * - */ - public static function getPassword() : string { - - return self::get()->getPasswordHelper(); - } - /** - * Sets the password that is used to protect tasks execution. - * - * The password is used to prevent unauthorized access to execute tasks. - * The provided password must be 'sha256' hashed string. It is recommended - * to hash the password externally then use the hash inside your code. - * - * @param string $pass The password that will be used. - */ - public static function setPassword(string $pass) { - self::get()->setPasswordHelper($pass); - } /** * Register any task which exist in the folder 'tasks' of the application. * @@ -719,52 +744,26 @@ public static function setMonth(int $month) { } } /** - * Returns a queue of all queued tasks. + * Sets the password that is used to protect tasks execution. * - * @return Queue An object of type 'Queue' which contains all queued tasks. + * The password is used to prevent unauthorized access to execute tasks. + * The provided password must be 'sha256' hashed string. It is recommended + * to hash the password externally then use the hash inside your code. * - * @since 1.0 + * @param string $pass The password that will be used. */ - public static function tasksQueue() : Queue { - return self::get()->getQueueHelper(); + public static function setPassword(string $pass) { + self::get()->setPasswordHelper($pass); } /** - * Returns the time at which tasks check was initialized. + * Returns a queue of all queued tasks. * - * @return string The method will return a time string in the format - * 'YY-DD HH:MM' where: - *
    - *
  • 'YY' is month number.
  • - *
  • 'MM' is day number in the current month.
  • - *
  • 'HH' is the hour.
  • - *
  • 'MM' is the minute.
  • - *
+ * @return Queue An object of type 'Queue' which contains all queued tasks. * - * @since 1.0.7 + * @since 1.0 */ - public static function getTimestamp() : string { - $month = self::getMonth(); - - if ($month < 10) { - $month = '0'.$month; - } - $day = self::dayOfMonth(); - - if ($day < 10) { - $day = '0'.$day; - } - $hour = self::getHour(); - - if ($hour < 10) { - $hour = '0'.$hour; - } - $minute = self::getMinute(); - - if ($minute < 10) { - $minute = '0'.$minute; - } - - return $month.'-'.$day.' '.$hour.':'.$minute; + public static function tasksQueue() : Queue { + return self::get()->getQueueHelper(); } /** * Creates a task that will be executed on specific time weekly. diff --git a/webfiori/framework/session/Session.php b/webfiori/framework/session/Session.php index cbd2d8bc..91f70bc8 100644 --- a/webfiori/framework/session/Session.php +++ b/webfiori/framework/session/Session.php @@ -591,7 +591,7 @@ public function serialize() : string { // Serialize => Encode => [Encrypt] $serializedSession = base64_encode(trim(serialize($this))); $len = strlen($serializedSession); - + $cipherMeth = 'aes-256-ctr'; if (in_array($cipherMeth, openssl_get_cipher_methods())) { diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index add4271b..c4baf81f 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -767,21 +767,6 @@ public function render(bool $formatted = false, bool $returnResult = false) { return $this->getDocument(); } - private function invokeBeforeRender(int $current = 0) { - $currentCount = count($this->beforeRenderCallbacks); - - if ($currentCount == 0 || $currentCount == $current) { - return; - } - $this->beforeRenderCallbacks->get($current)->call($this); - $newCount = count($this->beforeRenderCallbacks); - if ($newCount != $currentCount) { - $this->beforeRenderCallbacks->insertionSort(false); - $this->invokeBeforeRender(); - } else { - $this->invokeBeforeRender($current + 1); - } - } /** * Resets page attributes to default values. * @@ -1223,6 +1208,22 @@ private function getHead() { return $headNode; } + private function invokeBeforeRender(int $current = 0) { + $currentCount = count($this->beforeRenderCallbacks); + + if ($currentCount == 0 || $currentCount == $current) { + return; + } + $this->beforeRenderCallbacks->get($current)->call($this); + $newCount = count($this->beforeRenderCallbacks); + + if ($newCount != $currentCount) { + $this->beforeRenderCallbacks->insertionSort(false); + $this->invokeBeforeRender(); + } else { + $this->invokeBeforeRender($current + 1); + } + } private function resetBeforeLoaded() { $this->beforeRenderCallbacks = new LinkedList(); $this->addBeforeRender(function (WebPage $page) diff --git a/webfiori/framework/writers/TableClassWriter.php b/webfiori/framework/writers/TableClassWriter.php index 1e9e57b2..c9fa28c9 100644 --- a/webfiori/framework/writers/TableClassWriter.php +++ b/webfiori/framework/writers/TableClassWriter.php @@ -10,6 +10,8 @@ */ namespace webfiori\framework\writers; +use const APP_DIR; +use const APP_PATH; use webfiori\database\ColOption; use webfiori\database\Column; use webfiori\database\DataType; @@ -20,8 +22,6 @@ use webfiori\database\mysql\MySQLColumn; use webfiori\database\mysql\MySQLTable; use webfiori\database\Table; -use const APP_DIR; -use const APP_PATH; /** * A class which is used to write database table classes. @@ -250,11 +250,41 @@ private function addColsHelper() { } $this->append(']);', 2); } + private function addFKOption(Column $colObj) { + $fks = $this->getTable()->getForeignKeys(); + + foreach ($fks as $fk) { + $fk instanceof FK; + $sourceCols = array_values($fk->getOwnerCols()); + + if (count($sourceCols) == 1 && $sourceCols[0]->getNormalName() == $colObj->getNormalName()) { + $this->addFKOptionHelper($colObj, $fk); + } + } + } + private function addFKOptionHelper(Column $col, FK $fk) { + $refTableNs = get_class($fk->getSource()); + $cName = $this->getNamespace().'\\'.$this->getName(); + $refTableClassName = '$this'; + + if ($cName != $refTableNs) { + $nsSplit = explode('\\', $refTableNs); + $refTableClassName = 'new '.$nsSplit[count($nsSplit) - 1].'()'; + } + $keyName = $fk->getKeyName(); + $sourceCol = array_keys($fk->getSourceCols())[0]; + $this->append("ColOption::FK => [", 4); + $this->append("ColOption::FK_NAME => '".$keyName."',", 5); + $this->append("ColOption::FK_TABLE => ".$refTableClassName.",", 5); + $this->append("ColOption::FK_COL => '".$sourceCol."',", 5); + $this->append("ColOption::FK_ON_UPDATE => ".$this->getFkCond($fk->getOnUpdate()).",", 5); + $this->append("ColOption::FK_ON_DELETE => ".$this->getFkCond($fk->getOnDelete()).",", 5); + $this->append("],", 4); + } private function addFksHelper() { $fks = $this->tableObj->getForeignKeys(); foreach ($fks as $fkObj) { - if (count($fkObj->getSourceCols()) == 1) { continue; } @@ -280,6 +310,7 @@ private function addFksHelper() { private function addFksUseTables() { if ($this->tableObj !== null) { $fks = $this->tableObj->getForeignKeys(); + if (count($fks) != 0) { $this->addUseStatement(FK::class); } @@ -295,91 +326,6 @@ private function addFksUseTables() { } } } - private function getType(string $dataType) { - switch ($dataType) { - case 'bigint' : { - return 'DataType::BIGINT'; - } - case 'binary' : { - return 'DataType::BINARY'; - } - case 'bit' : { - return 'DataType::BIT'; - } - case 'blob' : { - return 'DataType::BLOB'; - } - case 'longblob' : { - return 'DataType::BLOB_LONG'; - } - case 'mediumblob' : { - return 'DataType::BLOB_MEDIUM'; - } - case 'tinyblob' : { - return 'DataType::BLOB_TINY'; - } - case 'bool' : { - return 'DataType::BOOL'; - } - case 'boolean' : { - return 'DataType::BOOL'; - } - case 'char' : { - return 'DataType::CHAR'; - } - case 'date' : { - return 'DataType::DATE'; - } - case 'datetime' : { - return 'DataType::DATETIME'; - } - case 'datetime2' : { - return 'DataType::DATETIME2'; - } - case 'decimal' : { - return 'DataType::DECIMAL'; - } - case 'double' : { - return 'DataType::DOUBLE'; - } - case 'float' : { - return 'DataType::FLOAT'; - } - case 'int' : { - return 'DataType::INT'; - } - case 'money' : { - return 'DataType::MONEY'; - } - case 'nchar' : { - return 'DataType::NCHAR'; - } - case 'nvarchar' : { - return 'DataType::NVARCHAR'; - } - case 'text' : { - return 'DataType::TEXT'; - } - case 'medumtext' : { - return 'DataType::TEXT_MEDIUM'; - } - case 'time' : { - return 'DataType::TIME'; - } - case 'timestamp' : { - return 'DataType::TIMESTAMP'; - } - case 'varbinary' : { - return 'DataType::VARBINARY'; - } - case 'varchar' : { - return 'DataType::VARCHAR'; - } - default : { - return "'mixed'"; - } - } - } /** * * @param MySQLColumn $colObj @@ -399,7 +345,6 @@ private function appendColObj($key, $colObj) { || $dataType == 'char' || $dataType == 'nchar' || $dataType == 'nvarchar') { - $this->append("ColOption::SIZE => '".$colObj->getSize()."',", 4); if ($dataType == 'decimal') { @@ -444,34 +389,23 @@ private function appendColObj($key, $colObj) { $this->addFKOption($colObj); $this->append("],", 3); } - private function addFKOption(Column $colObj) { - $fks = $this->getTable()->getForeignKeys(); - foreach ($fks as $fk) { - $fk instanceof FK; - $sourceCols = array_values($fk->getOwnerCols()); - if (count($sourceCols) == 1 && $sourceCols[0]->getNormalName() == $colObj->getNormalName()) { - $this->addFKOptionHelper($colObj, $fk); - } - } - } - private function addFKOptionHelper(Column $col, FK $fk) { - $refTableNs = get_class($fk->getSource()); - $cName = $this->getNamespace().'\\'.$this->getName(); - $refTableClassName = '$this'; + /** + * Extract and return the name of table class based on associated table object. + * + */ + private function extractAndSetTableClassName() { + $clazz = get_class($this->getTable()); - if ($cName != $refTableNs) { - $nsSplit = explode('\\', $refTableNs); - $refTableClassName = 'new '.$nsSplit[count($nsSplit) - 1].'()'; + $split = explode('\\', $clazz); + $count = count($split); + + if ($count > 1) { + $this->setClassName($split[$count - 1]); + array_pop($split); + $this->setNamespace(implode('\\', $split)); + } else { + $this->setClassName($split[0]); } - $keyName = $fk->getKeyName(); - $sourceCol = array_keys($fk->getSourceCols())[0]; - $this->append("ColOption::FK => [", 4); - $this->append("ColOption::FK_NAME => '".$keyName."',", 5); - $this->append("ColOption::FK_TABLE => ".$refTableClassName.",", 5); - $this->append("ColOption::FK_COL => '".$sourceCol."',", 5); - $this->append("ColOption::FK_ON_UPDATE => ".$this->getFkCond($fk->getOnUpdate()).",", 5); - $this->append("ColOption::FK_ON_DELETE => ".$this->getFkCond($fk->getOnDelete()).",", 5); - $this->append("],", 4); } private function getFkCond(string $txt) { switch ($txt) { @@ -492,22 +426,89 @@ private function getFkCond(string $txt) { } } } - /** - * Extract and return the name of table class based on associated table object. - * - */ - private function extractAndSetTableClassName() { - $clazz = get_class($this->getTable()); - - $split = explode('\\', $clazz); - $count = count($split); - - if ($count > 1) { - $this->setClassName($split[$count - 1]); - array_pop($split); - $this->setNamespace(implode('\\', $split)); - } else { - $this->setClassName($split[0]); + private function getType(string $dataType) { + switch ($dataType) { + case 'bigint' : { + return 'DataType::BIGINT'; + } + case 'binary' : { + return 'DataType::BINARY'; + } + case 'bit' : { + return 'DataType::BIT'; + } + case 'blob' : { + return 'DataType::BLOB'; + } + case 'longblob' : { + return 'DataType::BLOB_LONG'; + } + case 'mediumblob' : { + return 'DataType::BLOB_MEDIUM'; + } + case 'tinyblob' : { + return 'DataType::BLOB_TINY'; + } + case 'bool' : { + return 'DataType::BOOL'; + } + case 'boolean' : { + return 'DataType::BOOL'; + } + case 'char' : { + return 'DataType::CHAR'; + } + case 'date' : { + return 'DataType::DATE'; + } + case 'datetime' : { + return 'DataType::DATETIME'; + } + case 'datetime2' : { + return 'DataType::DATETIME2'; + } + case 'decimal' : { + return 'DataType::DECIMAL'; + } + case 'double' : { + return 'DataType::DOUBLE'; + } + case 'float' : { + return 'DataType::FLOAT'; + } + case 'int' : { + return 'DataType::INT'; + } + case 'money' : { + return 'DataType::MONEY'; + } + case 'nchar' : { + return 'DataType::NCHAR'; + } + case 'nvarchar' : { + return 'DataType::NVARCHAR'; + } + case 'text' : { + return 'DataType::TEXT'; + } + case 'medumtext' : { + return 'DataType::TEXT_MEDIUM'; + } + case 'time' : { + return 'DataType::TIME'; + } + case 'timestamp' : { + return 'DataType::TIMESTAMP'; + } + case 'varbinary' : { + return 'DataType::VARBINARY'; + } + case 'varchar' : { + return 'DataType::VARCHAR'; + } + default : { + return "'mixed'"; + } } } private function writeConstructor() { diff --git a/webfiori/framework/writers/WebServiceWriter.php b/webfiori/framework/writers/WebServiceWriter.php index 5bc189ed..d322499c 100644 --- a/webfiori/framework/writers/WebServiceWriter.php +++ b/webfiori/framework/writers/WebServiceWriter.php @@ -148,53 +148,19 @@ public function writeClassComment() { public function writeClassDeclaration() { $this->append('class '.$this->getName().' extends EAbstractWebService {'); } - private function getType(string $type) { - switch ($type) { - case 'int': { - return 'ParamType::INT'; - } - case 'integer': { - return 'ParamType::INT'; - } - case 'string': { - return 'ParamType::STRING'; - } - case 'array': { - return 'ParamType::ARR'; - } - case 'bool': { - return 'ParamType::BOOL'; - } - case 'boolean': { - return 'ParamType::BOOL'; - } - case 'double': { - return 'ParamType::DOUBLE'; - } - case 'email': { - return 'ParamType::EMAIL'; - } - case 'json-obj': { - return 'ParamType::JSON_OBJ'; - } - case 'url': { - return 'ParamType::URL'; - } - } - } /** * * @param RequestParameter $param */ private function appendParam($param) { $this->append("'".$param->getName()."' => [", 3); - + $this->append("ParamOption::TYPE => ".$this->getType($param->getType()).",", 4); if ($param->isOptional()) { $this->append("ParamOption::OPTIONAL => true,", 4); } - + if ($param->getDefault() !== null) { $toAppend = "ParamOption::DEFAULT => ".$param->getDefault().","; @@ -210,22 +176,26 @@ private function appendParam($param) { if ($param->isEmptyStringAllowed()) { $this->append("ParamOption::EMPTY => true,", 4); } + if ($param->getMinLength() !== null) { $this->append("ParamOption::MIN_LENGTH => ".$param->getMinLength().",", 4); } + if ($param->getMaxLength() !== null) { $this->append("ParamOption::MAX_LENGTH => ".$param->getMaxLength().",", 4); } } - + if ($param->getType() == ParamType::INT || $param->getType() == ParamType::DOUBLE) { $minFloat = defined('PHP_FLOAT_MIN') ? PHP_FLOAT_MIN : 2.2250738585072E-308; $maxFloat = defined('PHP_FLOAT_MAX') ? PHP_FLOAT_MAX : 1.7976931348623E+308; + if ($param->getMinValue() !== null && ($param->getMinValue() != $minFloat && $param->getMinValue() != $maxFloat)) { $this->append("ParamOption::MIN => ".$param->getMinValue().",", 4); } $maxInt = PHP_INT_MAX; $minInt = defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX; + if ($param->getMaxValue() !== null && ($param->getMaxValue() != $maxInt && $param->getMinValue() != $minInt)) { $this->append("ParamOption::MAX => ".$param->getMinValue().",", 4); } @@ -246,6 +216,71 @@ private function appendParams($paramsArray) { $this->append(']);', 2); } } + private function getMethod($method) { + switch ($method) { + case RequestMethod::CONNECT:{ + return "RequestMethod::CONNECT"; + } + case RequestMethod::DELETE:{ + return "RequestMethod::DELETE"; + } + case RequestMethod::GET:{ + return "RequestMethod::GET"; + } + case RequestMethod::HEAD:{ + return "RequestMethod::HEAD"; + } + case RequestMethod::OPTIONS:{ + return "RequestMethod::OPTIONS"; + } + case RequestMethod::PATCH:{ + return "RequestMethod::PATCH"; + } + case RequestMethod::POST:{ + return "RequestMethod::POST"; + } + case RequestMethod::PUT:{ + return "RequestMethod::PUT"; + } + case RequestMethod::TRACE:{ + return "RequestMethod::TRACE"; + } + } + } + private function getType(string $type) { + switch ($type) { + case 'int': { + return 'ParamType::INT'; + } + case 'integer': { + return 'ParamType::INT'; + } + case 'string': { + return 'ParamType::STRING'; + } + case 'array': { + return 'ParamType::ARR'; + } + case 'bool': { + return 'ParamType::BOOL'; + } + case 'boolean': { + return 'ParamType::BOOL'; + } + case 'double': { + return 'ParamType::DOUBLE'; + } + case 'email': { + return 'ParamType::EMAIL'; + } + case 'json-obj': { + return 'ParamType::JSON_OBJ'; + } + case 'url': { + return 'ParamType::URL'; + } + } + } private function implementMethods() { $name = $this->servicesObj->getName(); $this->append([ @@ -290,8 +325,9 @@ private function writeConstructor() { $this->f('__construct'), ], 1); $this->append('parent::__construct(\''.$this->servicesObj->getName().'\');', 2); - $this->append('$this->setDescription(\''. str_replace("'", "\\'", $this->servicesObj->getDescription()).'\');', 2); + $this->append('$this->setDescription(\''.str_replace("'", "\\'", $this->servicesObj->getDescription()).'\');', 2); $this->append('$this->setRequestMethods([', 2); + foreach ($this->servicesObj->getRequestMethods() as $method) { $this->append($this->getMethod($method).',', 3); } @@ -299,37 +335,6 @@ private function writeConstructor() { $this->appendParams($this->servicesObj->getParameters()); $this->append('}', 1); } - private function getMethod($method) { - switch ($method) { - case RequestMethod::CONNECT:{ - return "RequestMethod::CONNECT"; - } - case RequestMethod::DELETE:{ - return "RequestMethod::DELETE"; - } - case RequestMethod::GET:{ - return "RequestMethod::GET"; - } - case RequestMethod::HEAD:{ - return "RequestMethod::HEAD"; - } - case RequestMethod::OPTIONS:{ - return "RequestMethod::OPTIONS"; - } - case RequestMethod::PATCH:{ - return "RequestMethod::PATCH"; - } - case RequestMethod::POST:{ - return "RequestMethod::POST"; - } - case RequestMethod::PUT:{ - return "RequestMethod::PUT"; - } - case RequestMethod::TRACE:{ - return "RequestMethod::TRACE"; - } - } - } private function writeServiceDoc($service) { $docArr = []; From a65a587d52af485feaeeadbeaf4e48e3270e8fd0 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 01:19:11 +0300 Subject: [PATCH 21/22] 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 be96bea3..8c906348 100644 --- a/tests/webfiori/framework/test/cli/SchedulerCommandTest.php +++ b/tests/webfiori/framework/test/cli/SchedulerCommandTest.php @@ -172,8 +172,8 @@ public function test05() { "Stack Trace:\n", "#0 At class app\\tasks\Fail2TestTask line 1083\n", "#1 At class webfiori\\framework\scheduler\AbstractTask line 406\n", - "#2 At class webfiori\\framework\scheduler\AbstractTask line 904\n", - "#3 At class webfiori\\framework\scheduler\TasksManager line 600\n", + "#2 At class webfiori\\framework\scheduler\AbstractTask line 903\n", + "#3 At class webfiori\\framework\scheduler\TasksManager line 625\n", "#4 At class webfiori\\framework\scheduler\TasksManager line 135\n", "#5 At class webfiori\\framework\cli\commands\SchedulerCommand line 86\n", "#6 At class webfiori\\framework\cli\commands\SchedulerCommand line 328\n", From eca77ce67870665446f047adc380c437cd98e8d9 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Wed, 17 Jan 2024 01:20:05 +0300 Subject: [PATCH 22/22] Update App.php --- webfiori/framework/App.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index c6c33018..0f2886a8 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -542,7 +542,7 @@ private function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_VERSION', '3.0.0-Beta 1'); + define('WF_VERSION', '3.0.0-Beta.1'); /** * A constant that tells the type of framework version. *