diff --git a/app/apis/RoutingTestClass.php b/app/apis/RoutingTestClass.php index 93bab031..6b9f7822 100644 --- a/app/apis/RoutingTestClass.php +++ b/app/apis/RoutingTestClass.php @@ -1,5 +1,4 @@ @@ -28,12 +28,6 @@ public function __construct() { 'two-group', ]); } - /** - * Execute a set of instructions before accessing the application. - */ - public function before(Request $request, Response $response) { - //TODO: Implement the action to perform before processing the request. - } /** * Execute a set of instructions after processing the request and before sending back the response. */ @@ -46,4 +40,10 @@ public function after(Request $request, Response $response) { public function afterSend(Request $request, Response $response) { //TODO: Implement the action to perform after sending the request. } + /** + * Execute a set of instructions before accessing the application. + */ + public function before(Request $request, Response $response) { + //TODO: Implement the action to perform before processing the request. + } } diff --git a/app/tasks/Fail1TestTask.php b/app/tasks/Fail1TestTask.php index ab1869dd..dd0b326a 100644 --- a/app/tasks/Fail1TestTask.php +++ b/app/tasks/Fail1TestTask.php @@ -42,6 +42,7 @@ public function afterExec() { */ public function execute() { TasksManager::logInfo('Task '.$this->getTaskName().' Is executing...'); + return false; } /** diff --git a/php_cs.php.dist b/php_cs.php.dist index 346f35e1..3c2e444c 100644 --- a/php_cs.php.dist +++ b/php_cs.php.dist @@ -24,7 +24,6 @@ return $config->setRules([ ], 'indentation_type' => true, 'method_chaining_indentation' => true, - 'statement_indentation' => true, 'no_trailing_whitespace' => true, 'no_trailing_whitespace_in_comment' => true, 'blank_line_after_opening_tag' => true, diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index 28e1aed9..a024dbd2 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -417,6 +417,15 @@ private static function autoRegisterHelper($options) { } catch (Error $ex) { } } + private static function call($func) { + try { + call_user_func($func); + } catch (Exception $ex) { + if (self::getRunner()->isCLI()) { + printf("WARNING: ".$ex->getMessage().' at '.$ex->getFile().':'.$ex->getLine()."\n"); + } + } + } private function checkAppDir() { if (!defined('DS')) { /** @@ -577,15 +586,6 @@ private function initMiddleware() { } self::call(APP_DIR.'\ini\InitMiddleware::init'); } - private static function call($func) { - try { - call_user_func($func); - } catch (Exception $ex) { - if (self::getRunner()->isCLI()) { - printf("WARNING: ".$ex->getMessage().' at '.$ex->getFile().':'.$ex->getLine()."\n"); - } - } - } /** * @throws FileException */ diff --git a/webfiori/framework/ThemeLoader.php b/webfiori/framework/ThemeLoader.php index e78e519d..7ae18ffd 100644 --- a/webfiori/framework/ThemeLoader.php +++ b/webfiori/framework/ThemeLoader.php @@ -10,16 +10,16 @@ */ namespace webfiori\framework; +use const DS; use Error; use Exception; +use const ROOT_PATH; +use const THEMES_PATH; use webfiori\file\File; use webfiori\framework\exceptions\InitializationException; use webfiori\framework\exceptions\NoSuchThemeException; use webfiori\framework\router\Router; use webfiori\http\Response; -use const DS; -use const ROOT_PATH; -use const THEMES_PATH; /** diff --git a/webfiori/framework/autoload/ClassInfo.php b/webfiori/framework/autoload/ClassInfo.php index 394d3671..a54da9ff 100644 --- a/webfiori/framework/autoload/ClassInfo.php +++ b/webfiori/framework/autoload/ClassInfo.php @@ -9,11 +9,16 @@ * */ namespace webfiori\framework\autoload; + /** * A class that contains the names of indices that are used by loaded class info array. * */ class ClassInfo { + /** + * A constant that represents the value 'cached' of class. + */ + const CACHED = 'loaded-from-cache'; /** * A constant that represents the value 'name' of class. */ @@ -26,8 +31,4 @@ class ClassInfo { * A constant that represents the value 'path' of class. */ const PATH = 'path'; - /** - * A constant that represents the value 'cached' of class. - */ - const CACHED = 'loaded-from-cache'; } diff --git a/webfiori/framework/autoload/ClassLoader.php b/webfiori/framework/autoload/ClassLoader.php index ab51106a..3a55cc04 100644 --- a/webfiori/framework/autoload/ClassLoader.php +++ b/webfiori/framework/autoload/ClassLoader.php @@ -16,23 +16,17 @@ * * The class aims to provide all needed utilities to autoload any classes * which are within the scope of the framework. In addition, the developer - * can add his own custom folders to the autoloader. More to that, the autoloader - * will load any class which exist in the folder 'vendor' if composer - * is used to collect the dependencies. To activate this feature, the constant - * 'LOAD_COMPOSER_PACKAGES' must be defined and set to true. The class can be used independent of - * any other component to load classes. + * can add his own custom folders to the autoloader. * * @author Ibrahim * - * @version 1.1.7 */ class ClassLoader { /** * The name of the file that represents autoloader's cache. * @var string - * @since 1.1.6 */ - const CACHE_NAME = 'autoload.cache'; + const CACHE_NAME = 'class-loader.cache'; /** * An array that contains the possible things that can be performed * if a class has failed to load. @@ -41,7 +35,6 @@ class ClassLoader { *
  • throw-exception
  • , *
  • do-nothing
  • * - * @since 1.1.6 */ const ON_FAIL_ACTIONS = [ 'throw-exception', @@ -54,7 +47,6 @@ class ClassLoader { * * @var array * - * @since 1.1.6 */ private $cacheArr; /** @@ -62,7 +54,6 @@ class ClassLoader { * * @var array * - * @since 1.1.4 */ private $loadedClasses; /** @@ -70,7 +61,6 @@ class ClassLoader { * * @var ClassLoader * - * @since 1.0 */ private static $loader; /** @@ -79,7 +69,6 @@ class ClassLoader { * * @var string|callable * - * @since 1.1.3 */ private $onFail; /** @@ -87,7 +76,6 @@ class ClassLoader { * * @var string * - * @since 1.0 */ private $rootDir; /** @@ -95,7 +83,6 @@ class ClassLoader { * * @var array * - * @since 1.0 */ private $searchFolders; @@ -107,7 +94,6 @@ class ClassLoader { * @param string $onFail * @throws ClassLoaderException * @throws Exception - * @since 1.0 */ private function __construct(string $root = '', array $searchFolders = [], bool $defineRoot = false, string $onFail = self::ON_FAIL_ACTIONS[0]) { $this->searchFolders = []; @@ -115,6 +101,7 @@ private function __construct(string $root = '', array $searchFolders = [], bool $this->loadedClasses = []; require_once 'ClassLoaderException.php'; require_once 'ClassInfo.php'; + if (defined('ROOT_PATH')) { $this->rootDir = ROOT_PATH; } else if (strlen($root) != 0 && is_dir($root)) { @@ -169,6 +156,43 @@ private function __construct(string $root = '', array $searchFolders = [], bool ClassInfo::CACHED => false ]; } + /** + * Load a class using its specified path. + * + * This method can be used in case the class that the user tries to load does + * not comply with PSR-4 standard for placing classes in correct folder + * with correct namespace. Once loaded, it will be added to the cache. + * + * @param string $className The name of the class that will be loaded. + * + * @param string $classWithNs The full name of the class including its namespace. + * + * @param string $path The path to PHP file that has class implementation. + * + * @return bool If file is exist and class is loaded, true is returned. False + * otherwise. + */ + public function addClassMap(string $className, string $classWithNs, string $path) : bool { + $ns = count(explode('\\', $classWithNs)) == 1 ? '\\' : substr($classWithNs, 0, strlen($classWithNs) - strlen($className) - 1); + + if ($this->loadFromCache($ns, $className)) { + return true; + } + + if (!file_exists($path)) { + return false; + } + require_once $path; + + $this->loadedClasses[] = [ + ClassInfo::NAME => $className, + ClassInfo::NS => $ns, + ClassInfo::PATH => $path, + ClassInfo::CACHED => false + ]; + + return true; + } /** * Returns a single instance of the class 'ClassLoader'. * @@ -247,7 +271,6 @@ public static function get(array $options = [ return self::$loader; } - /** * Returns an array that contains all cached classes information. * @@ -257,7 +280,6 @@ public static function get(array $options = [ * * @return array An array that contains all cached classes information. * @throws Exception - * @since 1.1.7 */ public static function getCacheArray(): array { return self::get()->cacheArr; @@ -295,7 +317,6 @@ public static function getCachePath() : string { * @throws Exception * loaded. * - * @since 1.0 */ public static function getClassPath(string $className, string $namespace = null, bool $load = false): array { $retVal = []; @@ -328,7 +349,6 @@ public static function getClassPath(string $className, string $namespace = null, * @return array An array of all added search folders. * * @throws Exception - * @since 1.1.1 */ public static function getFolders(): array { $folders = []; @@ -358,7 +378,6 @@ public static function getFolders(): array { * @return array An associative array that contains loaded classes info. * * @throws Exception - * @since 1.1.4 */ public static function getLoadedClasses(): array { return self::get()->loadedClasses; @@ -377,7 +396,6 @@ public static function getLoadedClasses(): array { * Else, it will return false. * * @throws Exception - * @since 1.1.5 */ public static function isLoaded(string $class, string $ns = null): bool { foreach (self::getLoadedClasses() as $classArr) { @@ -393,6 +411,73 @@ public static function isLoaded(string $class, string $ns = null): bool { return false; } + /** + * Checks if provided string represents a valid namespace or not. + * + * @param string $ns A string to be validated. + * + * @return bool If the provided string represents a valid namespace, the + * method will return true. False if it does not represent a valid namespace. + */ + public static function isValidNamespace(string $ns) { + if ($ns == '\\') { + return true; + } + $split = explode('\\', $ns); + + foreach ($split as $subNs) { + $len = strlen($subNs); + + for ($x = 0 ; $x < $len ; $x++) { + $char = $subNs[$x]; + + if ($x == 0 && $char >= '0' && $char <= '9') { + return false; + } + + if (!(($char <= 'Z' && $char >= 'A') || ($char <= 'z' && $char >= 'a') || ($char >= '0' && $char <= '9') || $char == '_')) { + return false; + } + } + } + + return true; + } + /** + * Load a class using its specified path. + * + * This method can be used in case the class that the user tries to load does + * not comply with PSR-4 standard for placing classes in correct folder + * with correct namespace. Once loaded, it will be added to the cache. + * + * @param string $className The name of the class that will be loaded. + * + * @param string $classWithNs The full name of the class including its namespace. + * + * @param string $filePath The path to PHP file that has class implementation. + * + * @return bool If file is exist and class is loaded, true is returned. False + * otherwise. + */ + public static function map(string $className, string $classWithNs, string $filePath) { + self::get()->addClassMap($className, $classWithNs, $filePath); + } + /** + * Load multiple classes from same path which belongs to same namespace. + * + * This helper method can be used to autoload classes which are non-PSR-4 compliant. + * + * @param string $ns The namespace at which classes belongs to. + * + * @param string $path The location at which all classes stored at. + * + * @param array $classes An array that holds the names of the classes. + */ + public static function mapAll(string $ns, string $path, array $classes) { + foreach ($classes as $className) { + self::map($className, $ns.'\\'.$className, $path.DIRECTORY_SEPARATOR.$className.'.php'); + } + } /** * Adds new folder to the set folder at which the autoloader will try to search @@ -405,7 +490,6 @@ public static function isLoaded(string $class, string $ns = null): bool { * are inside the given directory will be included in the search. * * @throws Exception - * @since 1.1.2 */ public static function newSearchFolder(string $dir, bool $incSubFolders = true) { self::get()->addSearchDirectory($dir,$incSubFolders); @@ -417,7 +501,6 @@ public static function newSearchFolder(string $dir, bool $incSubFolders = true) * @return string The root directory that is used to search inside. * * @throws Exception - * @since 1.1.5 */ public static function root(): string { return self::get()->getRoot(); @@ -434,7 +517,6 @@ public static function root(): string { * * * @throws Exception - * @since 1.1.5 */ public static function setOnFail($onFail) { if (is_callable($onFail)) { @@ -459,7 +541,6 @@ public static function setOnFail($onFail) { * @param string $appendRoot If set to true, Root directory of the search will * be added as a prefix to the path. * - * @since 1.0 * * @deprecated since version 1.1.2 */ @@ -530,13 +611,44 @@ private static function checkComposer() { self::$loader->addSearchDirectory($vendorFolder, true, false); } } + private function createNSFromPath(string $filePath, string $className) { + $split = explode(DIRECTORY_SEPARATOR, $filePath); + $nsArr = ['\\'.$className]; + $currentNs = ''; + + foreach ($split as $str) { + if (self::isValidNamespace($str)) { + if (strlen($currentNs) == 0) { + $currentNs = '\\'.$str; + } else { + $currentNs = $currentNs.'\\'.$str; + } + $nsArr[] = $currentNs.'\\'.$className; + } + } + $currentNs = ''; + + for ($x = count($split) - 1 ; $x > -1 ; $x--) { + $str = $split[$x]; + + if (self::isValidNamespace($str)) { + if (strlen($currentNs) == 0) { + $currentNs = '\\'.$str; + } else { + $currentNs = '\\'.$str.$currentNs; + } + $nsArr[] = $currentNs.'\\'.$className; + } + } + + return $nsArr; + } /** * Returns an array string that contains all possible paths for the folder * 'vendor'. * * @return array * - * @since 1.1.6 */ private static function getComposerVendorDirs(): array { $DS = DIRECTORY_SEPARATOR; @@ -570,7 +682,6 @@ private static function getComposerVendorDirs(): array { * * @return string The root directory that is used to search inside. * - * @since 1.0 */ private function getRoot(): string { return $this->rootDir; @@ -583,7 +694,6 @@ private function getRoot(): string { * * @throws ClassLoaderException * @throws Exception - * @since 1.0 */ private function loadClass(string $classWithNs) { $cArr = explode('\\', $classWithNs); @@ -642,7 +752,7 @@ private function loadClassHelper(string $className, string $classWithNs, string if (!$isFileLoaded && file_exists($f)) { $nsFromPath = $this->createNSFromPath($f, $className); - + if (in_array('\\'.$classWithNs, $nsFromPath)) { require_once $f; $ns = count(explode('\\', $classWithNs)) == 1 ? '\\' : substr($classWithNs, 0, strlen($classWithNs) - strlen($className) - 1); @@ -658,68 +768,6 @@ private function loadClassHelper(string $className, string $classWithNs, string return $loaded; } - /** - * Checks if provided string represents a valid namespace or not. - * - * @param string $ns A string to be validated. - * - * @return bool If the provided string represents a valid namespace, the - * method will return true. False if it does not represent a valid namespace. - */ - public static function isValidNamespace(string $ns) { - if ($ns == '\\') { - return true; - } - $split = explode('\\', $ns); - - foreach ($split as $subNs) { - $len = strlen($subNs); - - for ($x = 0 ; $x < $len ; $x++) { - $char = $subNs[$x]; - - if ($x == 0 && $char >= '0' && $char <= '9') { - return false; - } - - if (!(($char <= 'Z' && $char >= 'A') || ($char <= 'z' && $char >= 'a') || ($char >= '0' && $char <= '9') || $char == '_')) { - return false; - } - } - } - - return true; - } - private function createNSFromPath(string $filePath, string $className) { - $split = explode(DIRECTORY_SEPARATOR, $filePath); - $nsArr = ['\\'.$className]; - $currentNs = ''; - foreach ($split as $str) { - if (self::isValidNamespace($str)) { - if (strlen($currentNs) == 0) { - $currentNs = '\\'.$str; - } else { - $currentNs = $currentNs.'\\'.$str; - } - $nsArr[] = $currentNs.'\\'.$className; - } - - } - $currentNs = ''; - for ($x = count($split) - 1 ; $x > -1 ; $x--) { - $str = $split[$x]; - if (self::isValidNamespace($str)) { - if (strlen($currentNs) == 0) { - $currentNs = '\\'.$str; - } else { - $currentNs = '\\'.$str.$currentNs; - } - $nsArr[] = $currentNs.'\\'.$className; - } - - } - return $nsArr; - } private function loadFromCache($classNS, $className): bool { $loaded = false; @@ -766,7 +814,6 @@ private function parseCacheString($str) { /** * Read the file which contains autoloader cached content. * - * @since 1.1.6 */ private function readCache() { $autoloadCachePath = $this->getRoot().DIRECTORY_SEPARATOR.APP_DIR.DIRECTORY_SEPARATOR.'sto'; @@ -786,7 +833,6 @@ private function readCache() { * This method is called every time a new class is loaded to update the cache. * * @throws Exception - * @since 1.1.6 */ private function updateCacheHelper() { $autoloadCache = self::getCachePath(); diff --git a/webfiori/framework/cli/commands/ListThemesCommand.php b/webfiori/framework/cli/commands/ListThemesCommand.php index 53e42386..72c8376d 100644 --- a/webfiori/framework/cli/commands/ListThemesCommand.php +++ b/webfiori/framework/cli/commands/ListThemesCommand.php @@ -10,8 +10,8 @@ */ namespace webfiori\framework\cli\commands; -use webfiori\cli\CLICommand; use webfiori\cli\Argument; +use webfiori\cli\CLICommand; use webfiori\framework\ThemeLoader; /** diff --git a/webfiori/framework/cli/commands/RunSQLQueryCommand.php b/webfiori/framework/cli/commands/RunSQLQueryCommand.php index 34810332..59a9321a 100644 --- a/webfiori/framework/cli/commands/RunSQLQueryCommand.php +++ b/webfiori/framework/cli/commands/RunSQLQueryCommand.php @@ -10,8 +10,8 @@ */ namespace webfiori\framework\cli\commands; -use webfiori\cli\CLICommand; use webfiori\cli\Argument; +use webfiori\cli\CLICommand; use webfiori\database\Database; use webfiori\database\DatabaseException; use webfiori\database\Table; @@ -122,15 +122,15 @@ private function connectionBased($dbConnections) : int { if ($file !== null) { $fileObj = new File(ROOT_PATH.DS.$file); - + if (!$fileObj->isExist()) { $fileObj = new File($file); } - + if ($fileObj->isExist()) { $fileObj->read(); $mime = $fileObj->getMIME(); - + if ($mime == 'application/sql' || $mime == 'application/x-sql') { return $this->runFileQuery($schema, $fileObj); } else { @@ -211,7 +211,7 @@ private function queryFromFile($schema) { while (!File::isFileExist($filePath)) { $filePath = $this->getInput('File path:'); $modified = ROOT_PATH.DS.$filePath; - + if (File::isFileExist($modified)) { $filePath = $modified; } @@ -233,7 +233,7 @@ private function queryFromFile($schema) { return $this->runFileQuery($schema, $file); } - + private function queryOnSchema(DB $schema) { if ($this->isArgProvided('--create')) { $schema->createTables(); diff --git a/webfiori/framework/cli/commands/SchedulerCommand.php b/webfiori/framework/cli/commands/SchedulerCommand.php index cd8291d5..e62e5df7 100644 --- a/webfiori/framework/cli/commands/SchedulerCommand.php +++ b/webfiori/framework/cli/commands/SchedulerCommand.php @@ -10,8 +10,8 @@ */ namespace webfiori\framework\cli\commands; -use webfiori\cli\CLICommand; use webfiori\cli\Argument; +use webfiori\cli\CLICommand; use webfiori\framework\scheduler\AbstractTask; use webfiori\framework\scheduler\TasksManager; /** @@ -111,7 +111,7 @@ public function listTasks() { } private function checkTaskArgs($taskName) { $task = TasksManager::getTask($taskName); - + if ($task === null) { return; } diff --git a/webfiori/framework/cli/commands/UpdateSettingsCommand.php b/webfiori/framework/cli/commands/UpdateSettingsCommand.php index dde9ee0b..be850b02 100644 --- a/webfiori/framework/cli/commands/UpdateSettingsCommand.php +++ b/webfiori/framework/cli/commands/UpdateSettingsCommand.php @@ -11,8 +11,8 @@ namespace webfiori\framework\cli\commands; use Throwable; -use webfiori\cli\CLICommand; use webfiori\cli\Argument; +use webfiori\cli\CLICommand; use webfiori\cli\InputValidator; use webfiori\framework\App; use webfiori\framework\config\Controller; @@ -104,6 +104,7 @@ private function getThemeNs() { if ($instance instanceof Theme) { return true; } + return false; } catch (Throwable $exc) { return false; diff --git a/webfiori/framework/cli/commands/UpdateTableCommand.php b/webfiori/framework/cli/commands/UpdateTableCommand.php index 50d8f392..8bfb30a7 100644 --- a/webfiori/framework/cli/commands/UpdateTableCommand.php +++ b/webfiori/framework/cli/commands/UpdateTableCommand.php @@ -10,8 +10,8 @@ */ namespace webfiori\framework\cli\commands; -use webfiori\cli\CLICommand; use webfiori\cli\Argument; +use webfiori\cli\CLICommand; use webfiori\framework\cli\CLIUtils; use webfiori\framework\cli\helpers\CreateTableObj; use webfiori\framework\cli\helpers\TableObjHelper; diff --git a/webfiori/framework/cli/helpers/CreateAPITestCase.php b/webfiori/framework/cli/helpers/CreateAPITestCase.php index 7a83e604..1fea484b 100644 --- a/webfiori/framework/cli/helpers/CreateAPITestCase.php +++ b/webfiori/framework/cli/helpers/CreateAPITestCase.php @@ -42,16 +42,16 @@ public function readClassInfo() { array_pop($nsArr); $ns = implode('\\', $nsArr); $this->setClassName($this->writer->getServiceName().'Test'); - $this->setNamespace('tests\\'. $ns); - $this->setPath(ROOT_PATH.DS.'tests'.DS. implode(DS, $nsArr)); - + $this->setNamespace('tests\\'.$ns); + $this->setPath(ROOT_PATH.DS.'tests'.DS.implode(DS, $nsArr)); + if ($this->getCommand()->isArgProvided('--defaults')) { $this->writeClass(); } else { - $this->checkPlace($ns); } + return true; } private function checkPlace($ns) { @@ -60,7 +60,7 @@ private function checkPlace($ns) { $this->println("Name: ".$this->getWriter()->getName(true)); $this->println("Path: ".$this->getWriter()->getPath()); $confrm = $this->confirm('Would you like to use default parameters?', true); - + if ($confrm) { $this->writeClass(); } else { @@ -69,57 +69,64 @@ private function checkPlace($ns) { $this->writeClass(); } } - private function readServiceInfo() { - $selected = $this->getCommand()->getArgValue('--service'); - $services = $this->writer->getServicesManager()->getServices(); - if ($selected !== null) { - if (!isset($services[$selected])) { - $this->info('Selected services manager has no service with name \''.$selected.'\'.'); - } else { - $this->writer->setService($services[$selected]); - return; - } - } - if (count($services) == 0) { - $this->info('Provided services manager has 0 registered services.'); - return; - } - $selected = $this->select('Which service you would like to have a test case for?', array_keys($services)); - $this->writer->setService($services[$selected]); - } private function readManagerInfo() { $m = $this->getCommand()->getArgValue('--manager'); $instance = null; - + if ($m !== null) { if (class_exists($m)) { $instance = new $m(); if ($instance instanceof WebServicesManager) { $this->writer->setServicesManager($instance); + return true; } else { $this->error("The argument --manager has invalid value."); + return false; } } else { $this->error("The argument --manager has invalid value."); + return false; } } + if ($instance === null) { while (!($instance instanceof WebServicesManager)) { $instance = $this->getCommand()->readInstance('Please enter services manager information:'); - + if (!($instance instanceof WebServicesManager)) { $this->error('Provided class is not an instance of '.WebServicesManager::class); } else { $this->writer->setServicesManager($instance); + return true; } } - } } + private function readServiceInfo() { + $selected = $this->getCommand()->getArgValue('--service'); + $services = $this->writer->getServicesManager()->getServices(); + + if ($selected !== null) { + if (!isset($services[$selected])) { + $this->info('Selected services manager has no service with name \''.$selected.'\'.'); + } else { + $this->writer->setService($services[$selected]); + return; + } + } + + if (count($services) == 0) { + $this->info('Provided services manager has 0 registered services.'); + + return; + } + $selected = $this->select('Which service you would like to have a test case for?', array_keys($services)); + $this->writer->setService($services[$selected]); + } } diff --git a/webfiori/framework/cli/helpers/CreateCLIClassHelper.php b/webfiori/framework/cli/helpers/CreateCLIClassHelper.php index 72a9caa4..a6f83e1f 100644 --- a/webfiori/framework/cli/helpers/CreateCLIClassHelper.php +++ b/webfiori/framework/cli/helpers/CreateCLIClassHelper.php @@ -63,6 +63,7 @@ private function getArgs() : array { continue; } $argObj->setDescription($this->getInput('Describe this argument and how to use it:', '')); + foreach ($this->getFixedValues() as $v) { $argObj->addAllowedValue($v); } diff --git a/webfiori/framework/cli/helpers/CreateClassHelper.php b/webfiori/framework/cli/helpers/CreateClassHelper.php index 71e0613a..aad7e229 100644 --- a/webfiori/framework/cli/helpers/CreateClassHelper.php +++ b/webfiori/framework/cli/helpers/CreateClassHelper.php @@ -204,7 +204,7 @@ public function select($prompt, $choices, $defaultIndex = -1) { public function setClassInfo(string $ns, string $suffix) { $classInfo = $this->getClassInfo($ns, $suffix); $this->setNamespace($classInfo['namespace']); - + if ($suffix != $classInfo['name']) { $this->setClassName($classInfo['name']); } @@ -232,14 +232,14 @@ public function setClassName(string $name) : bool { public function setNamespace($ns) : bool { return $this->getWriter()->setNamespace($ns); } - /** - * Sets the location at which the class will be created on. - * - * @param string $path A string that represents folder path. - * - * @return boolean If the path is successfully set, the method will return true. - * Other than that, false is returned. - */ + /** + * Sets the location at which the class will be created on. + * + * @param string $path A string that represents folder path. + * + * @return boolean If the path is successfully set, the method will return true. + * Other than that, false is returned. + */ public function setPath(string $path): bool { return $this->getWriter()->setPath($path); } diff --git a/webfiori/framework/config/Controller.php b/webfiori/framework/config/Controller.php index 4b5dcaaf..843e81c5 100644 --- a/webfiori/framework/config/Controller.php +++ b/webfiori/framework/config/Controller.php @@ -14,7 +14,7 @@ class Controller { const NL = "\n"; /** - * + * * @var ConfigurationDriver */ private $driver; @@ -27,20 +27,13 @@ public function __construct() { $this->driver = new $driverClazz(); $this->init($this->driver); } - private static function init(ConfigurationDriver $driver, bool $reCreate = false) { - try { - $driver->initialize($reCreate); - } catch (Exception $ex) { - throw new InitializationException('Unable to initialize configuration driver due to an error: '.$ex->getMessage(), $ex->getCode(), $ex); - } - } /** * Adds new environment variable to the configuration of the app. - * + * * @param string $name The name of the variable such as 'MY_VAR'. - * + * * @param mixed $value The value of the variable. - * + * * @param string|null $description An optional text that describes the variable. */ public function addEnvVar(string $name, $value, string $description = null) { @@ -77,7 +70,6 @@ public function copy(ConfigurationDriver $new) { } foreach ($current->getEnvVars() as $name => $probs) { - $new->addEnvVar($name, $probs['value'], $probs['description']); } $new->setPrimaryLanguage($current->getPrimaryLanguage()); @@ -136,4 +128,11 @@ public static function updateEnv() { } } } + private static function init(ConfigurationDriver $driver, bool $reCreate = false) { + try { + $driver->initialize($reCreate); + } catch (Exception $ex) { + throw new InitializationException('Unable to initialize configuration driver due to an error: '.$ex->getMessage(), $ex->getCode(), $ex); + } + } } diff --git a/webfiori/framework/config/JsonDriver.php b/webfiori/framework/config/JsonDriver.php index 6b908147..bd56c043 100644 --- a/webfiori/framework/config/JsonDriver.php +++ b/webfiori/framework/config/JsonDriver.php @@ -123,9 +123,9 @@ public function addOrUpdateSMTPAccount(SMTPAccount $emailAccount) { } /** * Returns the name of the application in specific display language. - * + * * @param string $langCode Language code such as 'AR'. - * + * * @return string|null Application name or null if language code does not * exist. */ @@ -332,12 +332,11 @@ public function getPrimaryLanguage(): string { */ public function getSchedulerPassword(): string { $pass = $this->json->get('scheduler-password') ?? 'NO_PASSWORD'; - + if (strlen($pass.'') == 0 || $pass == 'NO_PASSWORD') { - return 'NO_PASSWORD'; } - + return $pass; } /** @@ -383,7 +382,7 @@ public function getSMTPConnections(): array { $jsonObj = $prop->getValue(); $acc = new SMTPAccount(); $acc->setAccountName($name); - + $acc->setAddress($this->getProp($jsonObj, 'address', $name)); $acc->setPassword($this->getProp($jsonObj, 'password', $name)); $acc->setPort($this->getProp($jsonObj, 'port', $name)); @@ -398,7 +397,7 @@ public function getSMTPConnections(): array { /** * Returns the name or the namespace of default theme that the application * will use in case a page does not have specific theme. - * + * * @return string */ public function getTheme(): string { @@ -459,10 +458,10 @@ public function getTitleSeparator(): string { } /** * Creates application configuration file. - * + * * This method will attempt to create a JSON configuration file in the folder * 'config' of the application. - * + * * @param bool $reCreate If this parameter is set to true and there already a configuration * file with same name, it will be overriden. */ @@ -476,7 +475,7 @@ public function initialize(bool $reCreate = false) { } /** * Deletes configuration file. - * + * * Note that in order to remove specific configuration file, its name must * be set using the method JsonDriver::setConfigFileName() */ @@ -488,20 +487,26 @@ public function removeAllDBConnections() { $this->json->add('database-connections', new Json([], 'none', 'same')); $this->writeJson(); } + /** + * Removes all added SMTP connections. + */ + public function removeAllSMTPAccounts() { + $this->json->add('smtp-connections', new Json([], 'none', 'same')); + $this->writeJson(); + } public function removeDBConnection(string $connectionName) { $connections = $this->getDBConnections(); $accountNameTrimmed = trim($connectionName); $toAdd = []; - + foreach ($connections as $connection) { - if ($connection->getName() != $accountNameTrimmed) { $toAdd[] = $connection; } } $this->removeAllDBConnections(); - + foreach ($toAdd as $account) { $this->addOrUpdateDBConnection($account); } @@ -515,31 +520,23 @@ public function removeEnvVar(string $name) { $this->json->get('env-vars')->remove($name); $this->writeJson(); } - /** - * Removes all added SMTP connections. - */ - public function removeAllSMTPAccounts() { - $this->json->add('smtp-connections', new Json([], 'none', 'same')); - $this->writeJson(); - } /** * Removes specific SMTP connection from the configuration given its name. - * + * * @param string $accountName The name of the connection. */ public function removeSMTPAccount(string $accountName) { $connections = $this->getSMTPConnections(); $accountNameTrimmed = trim($accountName); $toAdd = []; - + foreach ($connections as $connection) { - if ($connection->getAccountName() != $accountNameTrimmed) { $toAdd[] = $connection; } } $this->removeAllSMTPAccounts(); - + foreach ($toAdd as $account) { $this->addOrUpdateSMTPAccount($account); } @@ -582,14 +579,14 @@ public function setAppVersion(string $vNum, string $vType, string $releaseDate) } /** * Sets the base URL of the application. - * + * * This is usually used in fetching resources. - * + * * @param string $url */ public function setBaseURL(string $url) { $trim = trim($url); - + if (strlen($trim) == 0) { $this->json->add('base-url', 'DYNAMIC'); } else { @@ -640,9 +637,9 @@ public function setDescription(string $description, string $langCode) { */ public function setHomePage(string $url) { $trim = trim($url); - + if (strlen($trim) == 0) { - $this->json->add('home-page', 'BASE_URL'); + $this->json->add('home-page', 'BASE_URL'); } else { $this->json->add('home-page', $trim); } diff --git a/webfiori/framework/handlers/APICallErrHandler.php b/webfiori/framework/handlers/APICallErrHandler.php index 852f53c7..0acd6b41 100644 --- a/webfiori/framework/handlers/APICallErrHandler.php +++ b/webfiori/framework/handlers/APICallErrHandler.php @@ -33,7 +33,6 @@ class APICallErrHandler extends AbstractHandler { public function __construct() { parent::__construct(); $this->setName('API Call Errors Handler'); - } /** * Handles the exception diff --git a/webfiori/framework/handlers/CLIErrHandler.php b/webfiori/framework/handlers/CLIErrHandler.php index 96e01ef7..a98de53f 100644 --- a/webfiori/framework/handlers/CLIErrHandler.php +++ b/webfiori/framework/handlers/CLIErrHandler.php @@ -17,8 +17,8 @@ use webfiori\framework\scheduler\TasksManager; /** * Exceptions handler which is used to handle exceptions in case of running - * CLI applications. - * + * CLI applications. + * * The priority of the handler * is set to 0 which indicates that it will be executed last. * diff --git a/webfiori/framework/handlers/HTTPErrHandler.php b/webfiori/framework/handlers/HTTPErrHandler.php index 8f1a95eb..c080e379 100644 --- a/webfiori/framework/handlers/HTTPErrHandler.php +++ b/webfiori/framework/handlers/HTTPErrHandler.php @@ -18,8 +18,8 @@ use webfiori\http\Response; /** * Errors and exceptions handler which is used to handle errors in case of - * HTTP request. - * + * HTTP request. + * * The priority of the handler * is set to 0 which indicates that it will be executed last. * diff --git a/webfiori/framework/router/Router.php b/webfiori/framework/router/Router.php index 3ce28e03..b8f6a435 100644 --- a/webfiori/framework/router/Router.php +++ b/webfiori/framework/router/Router.php @@ -604,6 +604,7 @@ public static function redirect(string $path, string $to, int $code = 301) { } Response::addHeader('location', $to); Response::setCode($httpCode); + if (!Runner::isCLI()) { Response::send(); } @@ -639,6 +640,7 @@ public static function removeRoute(string $path) : bool { $pathFix = self::getInstance()->fixUriPath($path); $retVal = false; $routes = &self::getInstance()->routes; + if (isset($routes['static'][$pathFix])) { unset($routes['static'][$pathFix]); diff --git a/webfiori/framework/scheduler/AbstractTask.php b/webfiori/framework/scheduler/AbstractTask.php index c548cb20..72f1e394 100644 --- a/webfiori/framework/scheduler/AbstractTask.php +++ b/webfiori/framework/scheduler/AbstractTask.php @@ -183,7 +183,7 @@ public function __construct(string $taskName = '', string $when = '* * * * *', s $this->taskDetails['days-of-month'] = []; $this->taskDetails['months'] = []; $this->taskDetails['days-of-week'] = []; - + if (!$this->cron($when)) { @@ -206,7 +206,7 @@ public function __construct(string $taskName = '', string $when = '* * * * *', s * * @param string|TaskArgument $nameOrObj The name of the argument. This also can be an * object of type TaskArgument. - * + * * @throws InvalidArgumentException If provided argument is not a string or an object of type * 'TaskArgument'. * @@ -353,33 +353,6 @@ public function dailyAt(int $hour = 0, int $minute = 0) : bool { public function everyHour() { $this->cron('0 * * * *'); } - /** - * Schedules a task to run weekly at specific week day and time. - * - * @param int $dayNameOrNum A 3 letter day name (such as 'sun' or 'tue') or a day number from 0 to 6. - * 0 for sunday. Default is 0. - * - * @param string $time A time in the form 'HH:MM'. HH can have any value - * between 0 and 23 inclusive. MM can have any value between 0 and 59 inclusive. - * default is '00:00'. - * - * @return bool If the time for the task is set, the method will - * return true. If not, it will return false. - */ - public function everyWeek($dayNameOrNum = 0, string $time = '00:00') : bool { - return $this->weeklyOn($dayNameOrNum, $time); - } - /** - * Schedule a task to run every specific number of hours. - * - * The expression that will be used is "At minute 0 past every X hour" where - * x is the number of hours. - * - * @param int $xHour The number of hours at which the job will be executed. - */ - public function everyXHour(int $xHour) { - $this->cron('0 */'.$xHour.' * * *'); - } /** * Schedules a task to run every month on specific day and time. * @@ -409,12 +382,39 @@ public function everyMonthOn(int $dayNum = 1, string $time = '00:00') : bool { return false; } + /** + * Schedules a task to run weekly at specific week day and time. + * + * @param int $dayNameOrNum A 3 letter day name (such as 'sun' or 'tue') or a day number from 0 to 6. + * 0 for sunday. Default is 0. + * + * @param string $time A time in the form 'HH:MM'. HH can have any value + * between 0 and 23 inclusive. MM can have any value between 0 and 59 inclusive. + * default is '00:00'. + * + * @return bool If the time for the task is set, the method will + * return true. If not, it will return false. + */ + public function everyWeek($dayNameOrNum = 0, string $time = '00:00') : bool { + return $this->weeklyOn($dayNameOrNum, $time); + } + /** + * Schedule a task to run every specific number of hours. + * + * The expression that will be used is "At minute 0 past every X hour" where + * x is the number of hours. + * + * @param int $xHour The number of hours at which the job will be executed. + */ + public function everyXHour(int $xHour) { + $this->cron('0 */'.$xHour.' * * *'); + } /** * Schedule a task to run every specific number of minutes. - * + * * Assuming that 5 is supplied as a value, this means that the job will be * executed every 5 minutes within an hour. - * + * * @param int $step The number of minutes that a job will be executed after. */ public function everyXMinuts(int $step) { @@ -1433,7 +1433,7 @@ private function getArgValFromTerminal($name) { */ private function getSubExprType(string $expr): string { $retVal = self::ANY_VAL; - + if ($expr != '*') { $split0 = explode('/', $expr); $count = count($split0); @@ -1463,6 +1463,7 @@ private function getSubExprType(string $expr): string { if (!(strlen($split0[0]) != 0 && strlen($split0[1]) != 0)) { return self::INV_VAL; } + return self::STEP_VAL; } } @@ -1541,7 +1542,7 @@ private function logExeException(Throwable $ex, string $meth = '') { $e = new TraceEntry($firstEntry); TasksManager::log('#'.$index.' '.$e); $index++; - + foreach ($trace as $traceEntry) { $e = new TraceEntry($traceEntry); TasksManager::log('#'.$index.' '.$e); diff --git a/webfiori/framework/scheduler/TasksManager.php b/webfiori/framework/scheduler/TasksManager.php index 3d93fed3..181b0db5 100644 --- a/webfiori/framework/scheduler/TasksManager.php +++ b/webfiori/framework/scheduler/TasksManager.php @@ -479,7 +479,7 @@ public static function initRoutes() { * * @param string $message A string that act as a log message. It will be * appended as passed without any changes. - * + * * @param string $type The type of the message that will be logged. It can * be one of the following values: * * Default is 'none'. - * + * * @since 1.0.8 */ public static function log(string $message, string $type = 'none') { @@ -509,17 +509,17 @@ public static function log(string $message, string $type = 'none') { } /** * Appends a message to the array that contains logged messages. - * + * * @param string $msg A string that act as a log message. It will be * appended as passed without any changes. Note that if running in CLI, - * this will appear as a success message + * this will appear as a error message */ - public static function logSuccess(string $msg) { - self::log($msg, 'success'); + public static function logErr(string $msg) { + self::log($msg, 'error'); } /** * Appends a message to the array that contains logged messages. - * + * * @param string $msg A string that act as a log message. It will be * appended as passed without any changes. Note that if running in CLI, * this will appear as a info message @@ -529,13 +529,13 @@ public static function logInfo(string $msg) { } /** * Appends a message to the array that contains logged messages. - * + * * @param string $msg A string that act as a log message. It will be * appended as passed without any changes. Note that if running in CLI, - * this will appear as a error message + * this will appear as a success message */ - public static function logErr(string $msg) { - self::log($msg, 'error'); + public static function logSuccess(string $msg) { + self::log($msg, 'success'); } /** * Create a task that will be executed once every month. diff --git a/webfiori/framework/session/SessionDB.php b/webfiori/framework/session/SessionDB.php index 556a63ee..e1860622 100644 --- a/webfiori/framework/session/SessionDB.php +++ b/webfiori/framework/session/SessionDB.php @@ -36,20 +36,6 @@ public function __construct() { $this->addTable(new MSSQLSessionDataTable()); $this->addTable(new MySQLSessionDataTable()); } - /** - * Removes database tables which are used to store session information. - */ - public function removeTables() { - - $this->transaction(function (DB $db) { - try { - $db->table('session_data')->drop()->execute(); - $db->table('sessions')->drop()->execute(); - } catch (DatabaseException $ex) { - return; - } - }); - } /** * Clears the sessions which are older than the constant 'SESSION_GC' or * older than 30 days if the constant is not defined. @@ -168,6 +154,20 @@ public function removeSession(string $sId) { $this->table('session_data')->delete()->where('s-id', $sId)->execute(); $this->table('sessions')->delete()->where('s-id', $sId)->execute(); } + /** + * Removes database tables which are used to store session information. + */ + public function removeTables() { + $this->transaction(function (DB $db) + { + try { + $db->table('session_data')->drop()->execute(); + $db->table('sessions')->drop()->execute(); + } catch (DatabaseException $ex) { + return; + } + }); + } /** * Store session state. diff --git a/webfiori/framework/session/SessionOption.php b/webfiori/framework/session/SessionOption.php index aea91495..a7806534 100644 --- a/webfiori/framework/session/SessionOption.php +++ b/webfiori/framework/session/SessionOption.php @@ -8,7 +8,6 @@ * https://github.com/WebFiori/.github/blob/main/LICENSE * */ - namespace webfiori\framework\session; /** @@ -22,16 +21,16 @@ class SessionOption { * An option which is used to set session duration (minutes) */ const DURATION = 'duration'; - /** - * An option which is used to set if session timeout will be refreshed on - * each request or not (bool) - */ - const REFRESH = 'refresh'; /** * An option which is used to set the name of the session. The name will be * same as session cookie. */ const NAME = 'name'; + /** + * An option which is used to set if session timeout will be refreshed on + * each request or not (bool) + */ + const REFRESH = 'refresh'; /** * An option which is used to set the ID of the session. */ diff --git a/webfiori/framework/session/SessionStatus.php b/webfiori/framework/session/SessionStatus.php index 9fca10c0..46003718 100644 --- a/webfiori/framework/session/SessionStatus.php +++ b/webfiori/framework/session/SessionStatus.php @@ -9,6 +9,7 @@ * */ namespace webfiori\framework\session; + /** * A class which is used to hold constants which represents different statuses * of a session. diff --git a/webfiori/framework/session/SessionsManager.php b/webfiori/framework/session/SessionsManager.php index 51925ddd..1f96c854 100644 --- a/webfiori/framework/session/SessionsManager.php +++ b/webfiori/framework/session/SessionsManager.php @@ -104,30 +104,6 @@ public static function destroy() { self::getInstance()->activeSession = null; } } - /** - * Returns a date string that represents the time at which all sessions - * that was created before it will be cleared. - * - * This method will try to use the environment variable 'SESSION_GC' to - * decide on the time. If this environment variable does not exist, - * it will use the value 30 days to create the date time string which - * indicates that any session created 30 days ago will be cleared. - * - * @return string A date string in the format 'YYYY-MM-DD HH:MM:SS'. - */ - public static function getGCTime() : string { - $olderThan = time() - 60 * 60 * 24 * 30; - $fromEnv = getenv('SESSION_GC') !== false ? intval(getenv('SESSION_GC')) : 0; - $fromConst = defined('SESSION_GC') && intval(SESSION_GC) > 0 ? intval(SESSION_GC) : 0; - - if ($fromEnv != 0) { - $olderThan = $fromEnv; - } else if ($fromConst != 0) { - $olderThan = $fromConst; - } - - return date('Y-m-d H:i:s', $olderThan); - } /** * Returns the value of a session variable. * @@ -192,6 +168,32 @@ public static function getCookiesHeaders() : array { return $retVal; } + /** + * Returns a date string that represents the time at which all sessions + * that was created before it will be cleared. + * + * This method will try to use the environment variable 'SESSION_GC' to + * decide on the time. If this environment variable does not exist, + * it will use the value 30 days to create the date time string which + * indicates that any session created 30 days ago will be cleared. + * + * @return string A date string in the format 'YYYY-MM-DD HH:MM:SS'. + */ + public static function getGCTime() : string { + $olderThan = time() - 60 * 60 * 24 * 30; + $fromEnv = getenv('SESSION_GC') !== false ? intval(getenv('SESSION_GC')) : 0; + $fromConst = defined('SESSION_GC') && intval(SESSION_GC) > 0 ? intval(SESSION_GC) : 0; + + if ($fromEnv != 0) { + $olderThan = $fromEnv; + } else { + if ($fromConst != 0) { + $olderThan = $fromConst; + } + } + + return date('Y-m-d H:i:s', $olderThan); + } /** * Returns the ID of a session from a cookie given its name. * @@ -259,7 +261,7 @@ public static function getStorage() : SessionStorage { * Checks if the active session has a cookie or not. * * Note that in command line, the method will always return false. - * + * * @return bool true if The active session has a cookie. False if not. If no * session is active, false is returned. */ diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index 531934b9..c1eb7f33 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -574,11 +574,10 @@ public function getTitleSep() : string { * */ public function getTranslation() : Lang { - if (!$this->skipLangCheck && $this->tr === null) { $this->usingLanguage(); } - + return $this->tr; } /** @@ -774,13 +773,6 @@ public function render(bool $formatted = false, bool $returnResult = false) { return $this->getDocument(); } - private function getConfigVar(string $meth, string $default = null, array $params = []) { - try{ - return call_user_func_array([App::getConfig(), $meth], $params); - } catch (InitializationException $ex) { - return $default; - } - } /** * Resets page attributes to default values. * @@ -792,7 +784,7 @@ public function reset() { $this->checkLang(); $this->usingLanguage(); - + $this->setWebsiteName($this->getConfigVar('getAppName', 'WebFiori App', [$this->getLangCode()])); $this->setDescription($this->getConfigVar('getDescription', '', [$this->getLangCode()])); $this->setTitle($this->getConfigVar('getTitle', 'Hello World', [$this->getLangCode()])); @@ -802,10 +794,10 @@ public function reset() { $langObj = $this->getTranslation(); $this->contentDir = $langObj->getWritingDir(); } catch (TypeError $ex) { - $this->contentDir = 'ltr'; + $this->contentDir = 'ltr'; } - + $this->incFooter = true; $this->incHeader = true; @@ -1015,7 +1007,7 @@ public function setTheme($themeNameOrClass = null) { return; } $xthemeName = $themeNameOrClass; - + if (strlen(trim($themeNameOrClass.'')) == 0) { $xthemeName = $this->getConfigVar('getTheme', $themeNameOrClass); } @@ -1196,13 +1188,20 @@ private function checkLang() { } $this->setLang($langCodeFromSession); } + private function getConfigVar(string $meth, string $default = null, array $params = []) { + try { + return call_user_func_array([App::getConfig(), $meth], $params); + } catch (InitializationException $ex) { + return $default; + } + } private function getHead() { $loadedTheme = $this->getTheme(); $defaultBase = Util::getBaseURL(); $base = $this->getConfigVar('getBaseURL', $defaultBase); - + $headNode = new HeadNode( $this->getTitle().$this->getTitleSep().$this->getWebsiteName(), $this->getCanonical(), @@ -1333,6 +1332,7 @@ private function usingLanguage() { if (!$this->skipLangCheck) { throw new MissingLangException($ex->getMessage()); } + return; } $pageLang = $this->getTranslation(); diff --git a/webfiori/framework/writers/APITestCaseWriter.php b/webfiori/framework/writers/APITestCaseWriter.php index b9eee6c9..d921df55 100644 --- a/webfiori/framework/writers/APITestCaseWriter.php +++ b/webfiori/framework/writers/APITestCaseWriter.php @@ -1,5 +1,4 @@ phpunitV = $num; - } - /** - * Returns PHPUnit version number. - * - * This is used to check if annotations or attributes should be used in test case - * method declaration. Starting with PHPUnit 10, attributes are used. - * - * @return int - */ - public function getPhpUnitVersion() : int { - return $this->phpunitV; - } /** * Creates new instance of the class. - * + * * @param WebServicesManager|null $manager Web services manager instance. - * + * * @param AbstractWebService $service The web service at which the test case * will be based on. */ public function __construct(WebServicesManager $manager = null, $service = null) { parent::__construct('WebService', ROOT_PATH.'\\tests\\apis', ROOT_PATH.'tests\\apis'); $this->setSuffix('Test'); + if ($manager !== null) { $this->setServicesManager($manager); } $this->setPhpUnitVersion(9); + if (!($service instanceof AbstractWebService)) { if ($service !== null && class_exists($service)) { $s = new $service(); - + if ($s instanceof AbstractWebService) { $this->setService($s); } @@ -71,33 +50,60 @@ public function __construct(WebServicesManager $manager = null, $service = null) } else { $this->setService($service); } - + } + /** + * Returns PHPUnit version number. + * + * This is used to check if annotations or attributes should be used in test case + * method declaration. Starting with PHPUnit 10, attributes are used. + * + * @return int + */ + public function getPhpUnitVersion() : int { + return $this->phpunitV; + } + /** + * Returns the web service object which was associated with the writer. + * + * @return AbstractWebService + */ + public function getService() : AbstractWebService { + return $this->serviceObj; + } + /** + * Returns the name of the class of the web service. + * + * @return string + */ + public function getServiceName() : string { + return $this->serviceObjName; } /** * Returns the associated web services manager that will be used by the test case. - * + * * @return WebServicesManager */ public function getServicesManager() : WebServicesManager { return $this->servicesManager; } /** - * Sets the associated web services manager that will be used by the test case. - * - * @param WebServicesManager $m + * Returns the name of web services manager class. + * + * @return string */ - public function setServicesManager(WebServicesManager $m) { - $this->servicesManager = $m; - $clazzExp = explode('\\', get_class($m)); - $this->servicesManagerName = $clazzExp[count($clazzExp) - 1]; + public function getServicesManagerName() : string { + return $this->servicesManagerName; } /** - * Returns the web service object which was associated with the writer. + * Sets PHPUnit version number. * - * @return AbstractWebService + * This is used to check if annotations or attributes should be used in test case + * method declaration. Starting with PHPUnit 10, attributes are used. + * + * @param int $num */ - public function getService() : AbstractWebService { - return $this->serviceObj; + public function setPhpUnitVersion(int $num) { + $this->phpunitV = $num; } /** @@ -110,6 +116,16 @@ public function setService(AbstractWebService $service) { $clazzExp = explode('\\', get_class($service)); $this->serviceObjName = $clazzExp[count($clazzExp) - 1]; } + /** + * Sets the associated web services manager that will be used by the test case. + * + * @param WebServicesManager $m + */ + public function setServicesManager(WebServicesManager $m) { + $this->servicesManager = $m; + $clazzExp = explode('\\', get_class($m)); + $this->servicesManagerName = $clazzExp[count($clazzExp) - 1]; + } /** * Write the test case class. @@ -119,22 +135,6 @@ public function writeClass() { $this->addAllUse(); parent::writeClass(); } - /** - * Returns the name of the class of the web service. - * - * @return string - */ - public function getServiceName() : string { - return $this->serviceObjName; - } - /** - * Returns the name of web services manager class. - * - * @return string - */ - public function getServicesManagerName() : string { - return $this->servicesManagerName; - } public function writeClassBody() { $this->writeNotAllowedRequestMethodTestCases(); $this->writeRequiredParametersTestCases(); @@ -152,130 +152,139 @@ public function writeClassComment() { public function writeClassDeclaration() { $this->append('class '.$this->getName().' extends APITestCase {'); } + private function addAllUse() { + $this->addUseStatement(APITestCase::class); + $this->addUseStatement(RequestMethod::class); + $this->addUseStatement(get_class($this->getService())); + $this->addUseStatement(get_class($this->getServicesManager())); + $this->addUseStatement('PHPUnit\Framework\Attributes\Test'); + } + private function addTestAnnotation() { + if ($this->phpunitV >= 10) { + $this->append('#[Test]', 1); + } else { + $this->append('/**', 1); + $this->append(' * @test', 1); + $this->append(' */', 1); + } + } + private function getMethName($reqMeth) { + if ($reqMeth == RequestMethod::GET) { + return 'getRequest'; + } else { + if ($reqMeth == RequestMethod::PUT) { + return 'putRequest'; + } else { + if ($reqMeth == RequestMethod::POST) { + return 'postRequest'; + } else { + if ($reqMeth == RequestMethod::DELETE) { + return 'getRequest'; + } else { + return 'callEndpoint'; + } + } + } + } + } + private function writeNotAllowedRequestMethodTestCases() { + $methods = $this->getService()->getRequestMethods(); + $testCasesCount = 0; + + foreach (RequestMethod::getAll() as $method) { + if (!in_array($method, $methods)) { + $this->addTestAnnotation(); + $this->append('public function testRequestMethodNotAllowed'.($testCasesCount < 10 ? '0'.$testCasesCount : $testCasesCount).'() {', 1); + $methodName = $this->getMethName($method); + + if ($methodName == 'callEndpoint') { + $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'.strtoupper($method).', '.$this->getServiceName().'::class, []);', 2); + } else { + $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), '.$this->getServiceName().'::class, []);', 2); + } + $this->append("\$this->assertEquals('{'.self::NL", 2); + $this->append(". ' \"message\":\"Method Not Allowed.\",'.self::NL", 2); + $this->append(". ' \"type\":\"error\",'.self::NL", 2); + $this->append(". ' \"http_code\":405'.self::NL", 2); + $this->append(". '}', \$output);", 2); + $this->append('}', 1); + $testCasesCount++; + } + } + } private function writeRequiredParametersTestCases() { $params = $this->getService()->getParameters(); $responseMessage = ResponseMessage::get('404-2'); $missingArr = []; + foreach ($params as $param) { - if (!$param->isOptional()) { $missingArr[] = $param->getName(); } } + if (count($missingArr) !== 0) { $requestMethod = $this->getService()->getRequestMethods()[0]; $this->addTestAnnotation(); $this->append('public function testRequiredParameters() {', 1); - $this->append('$output = $this->callEndpoint(new '.$this->getServicesManagerName().'(), RequestMethod::'. strtoupper($requestMethod).', '.$this->getServiceName().'::class, []);', 2); + $this->append('$output = $this->callEndpoint(new '.$this->getServicesManagerName().'(), RequestMethod::'.strtoupper($requestMethod).', '.$this->getServiceName().'::class, []);', 2); $this->append("\$this->assertEquals('{'.self::NL", 2); - $this->append(". ' \"message\":\"$responseMessage\'". implode("\',", $missingArr)."\'.\",'.self::NL", 2); - $this->append(". ' \"type\":\"error\",'.self::NL", 2); - $this->append(". ' \"http_code\":404,'.self::NL", 2); - $this->append(". ' \"more_info\":{'.self::NL", 2); - $this->append(". ' \"missing\":['.self::NL", 2); - for ($x = 0 ; $x < count($missingArr) ; $x++) { - $item = $missingArr[$x]; - if ($x + 1 == count($missingArr)) { - $this->append(". ' \"$item\"'.self::NL", 2); - } else { - $this->append(". ' \"$item\"',.self::NL", 2); - } + $this->append(". ' \"message\":\"$responseMessage\'".implode("\',", $missingArr)."\'.\",'.self::NL", 2); + $this->append(". ' \"type\":\"error\",'.self::NL", 2); + $this->append(". ' \"http_code\":404,'.self::NL", 2); + $this->append(". ' \"more_info\":{'.self::NL", 2); + $this->append(". ' \"missing\":['.self::NL", 2); + + for ($x = 0 ; $x < count($missingArr) ; $x++) { + $item = $missingArr[$x]; + + if ($x + 1 == count($missingArr)) { + $this->append(". ' \"$item\"'.self::NL", 2); + } else { + $this->append(". ' \"$item\"',.self::NL", 2); } - $this->append(". ' ]'.self::NL", 2); - $this->append(". ' }'.self::NL", 2); - $this->append(". '}', \$output);", 2); + } + $this->append(". ' ]'.self::NL", 2); + $this->append(". ' }'.self::NL", 2); + $this->append(". '}', \$output);", 2); $this->append('}', 1); } } private function writeTestCases() { $methods = $this->getService()->getRequestMethods(); $testCasesCount = 0; - + foreach (RequestMethod::getAll() as $method) { if (in_array($method, $methods)) { $this->addTestAnnotation(); $this->append('public function test'.$method.'Request00() {', 1); $this->append("//TODO: Write test case for $method request.", 2); $methodName = $this->getMethName($method); - + if (count($this->getService()->getParameters()) == 0) { if ($methodName == 'callEndpoint') { - $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'. strtoupper($method).', '.$this->getServiceName().'::class, []);', 2); + $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'.strtoupper($method).', '.$this->getServiceName().'::class, []);', 2); } else { $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), '.$this->getServiceName().'::class, []);', 2); } } else { if ($methodName == 'callEndpoint') { - $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'. strtoupper($method).', '.$this->getServiceName().'::class, [', 2); + $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'.strtoupper($method).', '.$this->getServiceName().'::class, [', 2); } else { $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), '.$this->getServiceName().'::class, [', 2); } + foreach ($this->getService()->getParameters() as $reqParam) { $this->append("'".$reqParam->getName()."' => null,", 3); } $this->append(']);', 2); } - - $this->append("\$this->assertEquals('{'.self::NL", 2); - $this->append(". '}', \$output);", 2); - $this->append('}', 1); - $testCasesCount++; - } - } - } - private function addTestAnnotation() { - if ($this->phpunitV >= 10) { - $this->append('#[Test]', 1); - } else { - $this->append('/**', 1); - $this->append(' * @test', 1); - $this->append(' */', 1); - } - } - private function writeNotAllowedRequestMethodTestCases() { - $methods = $this->getService()->getRequestMethods(); - $testCasesCount = 0; - - foreach (RequestMethod::getAll() as $method) { - if (!in_array($method, $methods)) { - $this->addTestAnnotation(); - $this->append('public function testRequestMethodNotAllowed'.($testCasesCount < 10 ? '0'.$testCasesCount : $testCasesCount).'() {', 1); - $methodName = $this->getMethName($method); - - if ($methodName == 'callEndpoint') { - $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), RequestMethod::'. strtoupper($method).', '.$this->getServiceName().'::class, []);', 2); - } else { - $this->append('$output = $this->'.$methodName.'(new '.$this->getServicesManagerName().'(), '.$this->getServiceName().'::class, []);', 2); - } + $this->append("\$this->assertEquals('{'.self::NL", 2); - $this->append(". ' \"message\":\"Method Not Allowed.\",'.self::NL", 2); - $this->append(". ' \"type\":\"error\",'.self::NL", 2); - $this->append(". ' \"http_code\":405'.self::NL", 2); $this->append(". '}', \$output);", 2); $this->append('}', 1); $testCasesCount++; } } } - private function getMethName($reqMeth) { - if ($reqMeth == RequestMethod::GET) { - return 'getRequest'; - } else if ($reqMeth == RequestMethod::PUT) { - return 'putRequest'; - } else if ($reqMeth == RequestMethod::POST) { - return 'postRequest'; - } else if ($reqMeth == RequestMethod::DELETE) { - return 'getRequest'; - } else { - return 'callEndpoint'; - } - } - private function addAllUse() { - $this->addUseStatement(APITestCase::class); - $this->addUseStatement(RequestMethod::class); - $this->addUseStatement(get_class($this->getService())); - $this->addUseStatement(get_class($this->getServicesManager())); - $this->addUseStatement('PHPUnit\Framework\Attributes\Test'); - } - } diff --git a/webfiori/framework/writers/CLICommandClassWriter.php b/webfiori/framework/writers/CLICommandClassWriter.php index d233cfe4..5b2f73a6 100644 --- a/webfiori/framework/writers/CLICommandClassWriter.php +++ b/webfiori/framework/writers/CLICommandClassWriter.php @@ -181,9 +181,8 @@ private function writeConstructor() { $this->append(["parent::__construct('$this->name', ["], 2); foreach ($this->args as $argObj) { - $this->append("'".$argObj->getName()."' => [", 3); - + if (strlen($argObj->getDescription()) != 0) { $this->append("'description' => '".str_replace("'", "\'", $argObj->getDescription())."',", 4); } diff --git a/webfiori/framework/writers/SchedulerTaskClassWriter.php b/webfiori/framework/writers/SchedulerTaskClassWriter.php index b603875f..acc01579 100644 --- a/webfiori/framework/writers/SchedulerTaskClassWriter.php +++ b/webfiori/framework/writers/SchedulerTaskClassWriter.php @@ -27,7 +27,7 @@ class SchedulerTaskClassWriter extends ClassWriter { * * @param string $className The name of the class that will represent the * task. - * + * * @param string $taskName The name of the task. * * @param string $taskDesc A short description that description what does the diff --git a/webfiori/framework/writers/TableClassWriter.php b/webfiori/framework/writers/TableClassWriter.php index ea029874..9f40c52a 100644 --- a/webfiori/framework/writers/TableClassWriter.php +++ b/webfiori/framework/writers/TableClassWriter.php @@ -254,7 +254,6 @@ private function addFKOption(Column $colObj) { $fks = $this->getTable()->getForeignKeys(); foreach ($fks as $fk) { - $sourceCols = array_values($fk->getOwnerCols()); if (count($sourceCols) == 1 && $sourceCols[0]->getNormalName() == $colObj->getNormalName()) {