diff --git a/README.md b/README.md index 57c3b40..b8fa568 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Introduction -This developer guides intention is to provide useful information for developers to help you to understand the structure of the application, important api functions, workflows and standards for code quality as well as code style. The intention is to provide enough information to get a basic understanding of these key elements for module developers, frontend developers or developers working on the core application. +This developer guides intention is to provide useful information for developers to help you to understand the structure of the application, important API functions, workflows and standards for code quality as well as code style. The intention is to provide enough information to get a basic understanding of these key elements for module developers, frontend developers or developers working on the core application. -The guide is **not** explaining in detail how to use all of the api, for this you can find the automatically generated code documentation or even better use the test cases. All the provided information are very important to ensure the quality of the published code and often are mandatory. Not following these guides can cause security issues, worsen the user experience or even cause malfunction as well as make it difficult for other developers to understand the code. \ No newline at end of file +The guide is **not** explaining in detail how to use all of the API, for this you can find the automatically generated code documentation or even better use the test cases. All the provided information are very important to ensure the quality of the published code and often are mandatory. Not following these guides can cause security issues, worsen the user experience or even cause malfunction as well as make it difficult for other developers to understand the code. \ No newline at end of file diff --git a/SUMMARY.md b/SUMMARY.md index a564a71..083dd0a 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -16,6 +16,9 @@ * [Modules]({%}?page=example_module/module) * [Packages]({%}?page=example_module/packages) +## Application Specific +* [Language Files]({%}?page=basics/language_files) + ## API ### Message * [Uri]({%}?page=services/uri) diff --git a/basics/dispatching.md b/basics/dispatching.md index 7990628..f86464e 100644 --- a/basics/dispatching.md +++ b/basics/dispatching.md @@ -14,15 +14,28 @@ The `dispatch()` function accepts additionally a variable amount of parameters w #### Module -The string representation for a module method is almost the same as the static function representation. The only difference is that a module method has only one colon `:` between the function name and the namespace. +The module function can be called by providing the namespace followed by the function name concatonated with a colon `:` between the function name and the namespace. ```php $dispatcher->dispatch('\My\Namespace:methodToCall', $methodToCallPara1, $methodToCallPara2, ...); ``` +In order to allow the dispatcher to automatically create a a controller instance. The class constructore for the controller `\my\Namespace` needs to extend `ModuleAbstract` and have the format: + +```php +public function __construct(ApplicationAbstract $app) {} +``` + +Alternatively you can also add a controller manually to the disptacher. In this case you may construct the controller as you see fit. However, the controller must extend `ModuleAbstract`. + +```php +$testController = new MyController(); +$dispatcher->set($testController, MyController::class) +``` + #### Static -The string representation for a module method is almost the same as the static function representation. The only difference is that a module method has two colons `::` between the function name and the namespace. +A static function can be called by providing the namespace followed by the function name concatonated with two colons `::` between the function name and the namespace. ```php $dispatcher->dispatch('\My\Namespace::staticToCall', $staticToCallPara1, $staticToCallPara2, ...); @@ -44,7 +57,7 @@ The dispatcher accepts the results from the `route()` method of the router which $dispatcher->dispatch($router->route($request->uri->getRoute())); ``` -Based on the function definition returned by the router it's possible to pass more parameters to the function such e.g. request and response objects. +Based on the function definition returned by the router it's possible to pass additional parameters to the function such e.g. request and response objects. ```php $dispatcher->dispatch($router->route($request->uri->getRoute()), $request, $response); diff --git a/basics/language_files.md b/basics/language_files.md index e69de29..24a18eb 100644 --- a/basics/language_files.md +++ b/basics/language_files.md @@ -0,0 +1,47 @@ +# Language Files + +Language files are used for static/custom translations. Depending on the request and response localization the correct language files are loaded and used for the translations invoked by `getHtml()` in the views. + +## Naming + +The naming convention of the files is `language.lang.php` e.g. `en.lang.php`. In some cases it may be necessary to provide language files for other modules in such a case the name of the language file is `module_name.language.lang.php` e.g. `Navigation.en.lang.php`. + +## Content + +The content of the language files is very simple. All you need is the module name, a key/identifier for the string and the translation. + +```php + [ + 'Audit' => 'Prüfung', + 'Auditor' => 'Prüfer', + 'Audits' => 'Prüfungen', + 'By' => 'Von', +]]; +``` + +It is recommended to create the translations in a spreadsheet software and export it as csv, as it is much easier to create all the translations for the different languages. You can install the **demo application**, install **your module** and then go to the Exchange module. In here you can navigate to the **OMS** exchange (exporter) and export the language/translations. This will provide a csv file for all modules and all templates with all the different supported languages. Simply search for your module and create the missing translations. + +Afterwards you can import the modified csv file in the OMS exchange which will create the language files based on this file. + +> Please note that the csv must be `;` deliminated and `"` escaped. + +## Import/Export + +In order to create translations more easily you may use the OMS language exporter. This exporter generates a csv including all module langauge files. You can use this to easily check which localized string are not implemented in the language file of the respective language. + +After you modified the csv file you can import it with the OMS language importer. + +Since the import/export is so simple it is actually recommended to use `$this->getHtml('...')` in the `.tpl.php` files without manually generating a language file and only do it in the generated csv file. This option also allows you to use automatic translation tools. diff --git a/basics/requests.md b/basics/requests.md index 6e2ea8c..f7dc4bb 100644 --- a/basics/requests.md +++ b/basics/requests.md @@ -7,29 +7,39 @@ Requests can be either incoming requests such as http requests on the server sid A http request in its' most basic form only consists of a uri. ```php -$request = new Request(new HttpUri('http://your_request.com/url?to=call')); +$request = new HttpRequest(new HttpUri('http://your_request.com/url?to=call')); ``` +### Localization + +The request localization can be defined witht he second parameter + +```php +$request = new HttpRequest(new HttpUri('http://your_request.com/url?to=call'), new Localization()); +``` + +In all web applications (e.g. backend, api, etc.) the localization and uri are automatically propagated during the initialization of the application. + ## Rest Requests With the rest implementation it's possible to make http requests and read the response. ```php -$request = new Request(new HttpUri('http://your_request.com/url?to=call')); -$result = Rest::request($request); +$request = new HttpRequest(new HttpUri('http://your_request.com/url?to=call')); +$result = Rest::Httprequest($request); ``` Alternatively it's also possible to make a rest request directly from the Http request object. ```php -$request = new Request(new HttpUri('http://your_request.com/url?to=call')); +$request = new HttpRequest(new HttpUri('http://your_request.com/url?to=call')); $result = $request->rest(); ``` The result contains the raw response text. By defining the request method it's also possible to make other requests such as `PUT` requests. ```php -$request = new Request(new HttpUri('http://your_request.com/url?to=call')); +$request = new HttpRequest(new HttpUri('http://your_request.com/url?to=call')); $request->setMethod(RequestMethod::PUT); $result = Rest::request($request); ``` diff --git a/basics/responses.md b/basics/responses.md index 498840d..1a80d67 100644 --- a/basics/responses.md +++ b/basics/responses.md @@ -1,9 +1,93 @@ # Responses -Responses can be either outgoing responses such as http or socket responses on the server side or responses generated on the client side for socket/websocket requests. Responses usually get generated based on a request. +Responses can be either outgoing responses such as http or socket responses on the server side or responses generated on the client side for socket/websocket requests. ## Http Response +A response can be defined with the `set()` function and the body of the response can be generated/rendered with the `getBody()` function. + +```php +$response->set('response_id', {stringifiable_element}); +``` + +The `response_id` is a unique internal id which allows you to create multiple response elements. All response elements will get combined (usually concatenated) during the rendering process. + +The `{stringifiable_element}` accepts all supported types from the `StringUtils::stringify` function. e.g. + +* \JsonSerializable +* array +* \Serializable +* string +* int +* float +* bool +* null +* \DateTimeInterface +* RenderableInterface +* \__toString() + +All other types will be rendered as null during `getBody()`. + +### Localization + +The response localization can be defined witht he second parameter + +```php +$response = new HttpResponse(new Localization()); +``` + +In all web applications (e.g. backend, api, etc.) the localization is automatically propagated during the initialization of the application. This means all templates, views and controller endpoints may use the localization in order to generate localized responses. + +### Default Headers + +The default headers for all web applications are: + +* 'content-type' => 'text/html; charset=utf-8' +* 'x-xss-protection' => '1; mode=block' +* 'x-content-type-options' => 'nosniff' +* 'x-frame-options' => 'SAMEORIGIN' +* 'referrer-policy' => 'same-origin' + +In order to change the response type the header `content-type` must be changed. + +```php +$response->header->set('content-type', '....', true); +``` + +The boolean flag at the end forces an overwrite of the previous response type. If this flag is not set the response type will not be overwritten. The reason for this flag is to protect header elements from accidentally getting overwritten. + +### Sending Headers + +> After the headers are sent it is not possible to change the headers or re-send them, the header is locked!. Usually you don't have to worry about this since the headers are automatically sent just before the content gets rendered. + +### Text/Html Response + +In most cases the text/html response is created based on a `View` which has a template assigned which then gets rendered during the response rendering process. The `View` implements `RenderableInterface` (see above). In case of frontend web applications only a view needs to be created in the controller endpoints which is then returned from the controller endpoint. + +```php +public function controllerEndpoint(HttpRequest $request, HttpResponse $response, array $data = []) : RenderableInterface +{ + $view = new View($this->app->l11nManager, $request, $response); + $view->setTemplate('....'); + $view->setData('data_id', ....); + + return $view; +} +``` + +### JSON Response + +A json response can be created by defining the correct header and adding `\JsonSerializable` elements to the response. + +```php +$response->header->set('Content-Type', MimeType::M_JSON . '; charset=utf-8', true); +$response->set('response_id' {array or \JsonSerializable}; +``` + +### Other + +Other response types besides the above described ones are all handled in the same way. You must set the response type/header and provide an element which can be converted by `StringUtils::stringify()`. + ## Socket Response ## Websocket Response diff --git a/basics/routing.md b/basics/routing.md index 2897e25..16cb9ef 100644 --- a/basics/routing.md +++ b/basics/routing.md @@ -1,7 +1,6 @@ # Routing -Routing allows to bind a string representation to a function. This is required in order to execute request specific code segments. -One very common scenario for routing is to take the url of a http(s) request and assign it to a function located in a controller. +Routing allows to bind a string representation to a function. This is required in order to execute request specific code segments. One very common scenario for routing is to take the URL of a http(s) request and assign it to a function located in a controller. ## Routes @@ -45,7 +44,7 @@ Instead of defining closures it's possible to define a string representation of $router->add('foo/bar', '\foo\controller:barFunction'); ``` -Static functions can be defined in the following fashion: +Static functions can be defined in the following way: ```php $router->add('foo/bar', '\foo\controller::barFunction'); @@ -65,12 +64,12 @@ The routing file must have the following structure: [ [ - 'dest' => {CLOSURE/REFERENCE_STRING}, - 'verb' => {VERB_1 | VERB_2}, + 'dest' => CLOSURE/REFERENCE_STRING, + 'verb' => VERB_1 | VERB_2, ], [ - 'dest' => {CLOSURE/REFERENCE_STRING}, - 'verb' => {VERB_3}, + 'dest' => CLOSURE/REFERENCE_STRING, + 'verb' => VERB_3, ], ], '{ANOTHER_ROUTE_STRING}' => [ ... ], @@ -83,7 +82,7 @@ In this schematic the first route has different destinations depending on the ve With request verbs it's possible to use the same request path and assign different endpoints to them. This is helpful when you want to have the same path for retrieving data and changing data. This is a normal situation in web development e.g. -Let's assume we have the url `https://yoururl.com/user/1/name` if we make a `GET` request we could return the name for the user `1` and if we make a `POST` request we could update the name for the user `1` and use the same url. +Let's assume we have the URL `https://yoururl.com/user/1/name` if we make a `GET` request we could return the name for the user `1` and if we make a `POST` request we could update the name for the user `1` and use the same url. Allowed request verbs are: @@ -111,17 +110,17 @@ $router->route('foo/bar', null, RouteVerb::GET, 'APP_NAME', ORG_ID, ACCOUNT); [ [ - 'dest' => {CLOSURE/REFERENCE_STRING}, - 'verb' => {VERB_1 | VERB_2}, + 'dest' => CLOSURE/REFERENCE_STRING, + 'verb' => VERB_1 | VERB_2, 'permission' => [ - 'module' => {MODULE_NAME}, - 'type' => {CREATE | READ | UPDATE | DELETE | PERMISSION}, - 'state' => {MODULE_SPECIFIC_IDENTIFIER_FOR_THE_PERMISSION}, + 'module' => MODULE_NAME, + 'type' => CREATE | READ | UPDATE | DELETE | PERMISSION, + 'state' => MODULE_SPECIFIC_IDENTIFIER_FOR_THE_PERMISSION, ], ], [ - 'dest' => {CLOSURE/REFERENCE_STRING}, - 'verb' => {VERB_3}, + 'dest' => CLOSURE/REFERENCE_STRING, + 'verb' => VERB_3, ], ], '{ANOTHER_ROUTE_STRING}' => [ ... ], @@ -144,7 +143,7 @@ The state allows a module to have different permissions. E.g. a news module has ## CSRF Protection -Often you would like to enable `CSRF` protection for certain urls or routing paths. The router can check if a CSRF protection is needed for a certain path and if it is needed it will check if a CSRF token is provided. If no CSRF protection is required it will ignore the CSRF token, but if it is necessary the router will check if it is available. +Often you would like to enable `CSRF` protection for certain urls or routing paths. The router can check if a CSRF protection is needed for a certain path and if it is needed it will check if the correct CSRF token is provided. If no CSRF protection is required it will ignore the CSRF token. ```php $router->add('foo/bar', function() { diff --git a/basics/views.md b/basics/views.md index 52375a3..ddfeed1 100644 --- a/basics/views.md +++ b/basics/views.md @@ -14,7 +14,22 @@ The base view class contains the request as well as the response objects hence i In the template you can simply use `$this->getText({TEXT_ID})` for localized text. All other localization elements can be accessed in a similar way e.g. `$this->l11n->getTemperature()`. -In html templates it's recommended to use `$this->getHtml({TEXT_ID})` as this will savely escape defined strings for html output. In case you would like to escape none pre-defined language strings use `$this->printHtml('string to escape')`. +In html templates it's recommended to use `$this->getHtml({TEXT_ID})` as this will safely escape static/defined strings (see language files) for html output. By default the language file of the current module is used for the translation, if you would like to use a translation from another module you have to specify the module name and the template name e.g. `getHtml('Word', 'ModuleName', 'TemplateName'); ?>`. This only works if the specified module is available and therefore it is only recommended to use this if the current module has a dependency defined on the specified module which ensures its availability. + +Furthermore there is also a global translation file for common translations, this can be accessed as follows `getHtml('Word', '0', '0'); ?>`. Common translations are for example `ID`, `Submit`, `Send`, `Cancel` etc. + +In case you would like to escape none pre-defined language strings use `$this->printHtml('string to escape')`. + +Other important functions are: + +* getNumeric() +* getPrecentage() +* getCurrency() +* getDateTime() + +## HTML Encoding + +The `printHtml()` function already escapes the localized output. However, in some cases no static localization is available and a simple string needs to be encoded (e.g. a string from a model like the title of a news article). In this case the member function `printHtml()` or the static function `html()` can be called to correctly escape whatever input string is provided. ## Templates diff --git a/datastorage/cache.md b/datastorage/cache.md index ab01c3e..3860df3 100644 --- a/datastorage/cache.md +++ b/datastorage/cache.md @@ -1,8 +1,8 @@ -## Cache +# Cache For caching the `CacheManager` provides access to the caching systems in place. Out of the box the CacheManager supports and automatically initializes either Redis or Memcached depending on the client configuration. The caching is not mandatory and therefor shouldn't be misused as in-memory database. It is not necessary to check if Redis or Memcached are available the CacheManager automatically handles the caching based on their existence. -### HTTP Cache +## HTTP Cache By default only stylesheets, javascript and layout images as well as module images are cached. Everything else is considered volatile and not cached. If a response specific response should be cached feel free to use the response header: @@ -18,4 +18,31 @@ Example usage: ```php $head->addAsset(AssetType::JS, $request->uri->getBase() . 'Modules/Media/Controller.js?v=' . self::MODULE_VERSION); -``` \ No newline at end of file +``` + +## System Cache + +All cache systems implement very similar functionality. It is only due to different features of the underlying technology that some minor details are different between the different cache solutions. + +The available cache solutions are: + +* File Cache +* Redis Cache +* MemCached + +The available features are as follows: + +* set - Sets a cache value or overwrites it (in some cache implementations with expiration date) +* add - Adds a cache value if the key doesn't exists (in some cache implementations with expiration date) +* replace - Replaces a cache value if the key exists +* get - Gets the cache value by key +* delete - Deletes a cache key/value +* exists - Checks if a key/value exists +* increment - Increments a numeric value by a given amount +* decrement - Decrements a numeric value by a given amount +* rename - Renames a key +* getLike - Finds keys/values based on a regex pattern +* deleteLike - Deletes keys/values based on a regex pattern +* updateExpire - Updates the expire date (only in some cache implementations possible) +* flush - Empties the cache by expiration +* flushAll - Empties the complete cache \ No newline at end of file diff --git a/example_app/app.md b/example_app/app.md index 6bea8f1..26487b1 100644 --- a/example_app/app.md +++ b/example_app/app.md @@ -4,7 +4,7 @@ The following application is a minimal sample in order to show how it's possible ```txt # .htaccess -# enable url rewriting +# enable URL rewriting RewriteEngine On RewriteBase / @@ -20,9 +20,10 @@ The following application is a minimal sample in order to show how it's possible ``` ```php +run(); // outputs the application response ``` ```php +set('Content', $pageView); - /* get data from url endpoints defined by the routes */ + /* get data from URL endpoints defined by the routes */ $dispatch = $this->dispatcher->dispatch( $this->router->route( $request->uri->getRoute(), @@ -103,7 +105,7 @@ class Application extends ApplicationAbstract // initialize request from the superglobals which are automatically populated $request = Request::createFromSuperglobals(); - // optional: will transform the url path and sub-paths to hashs + // optional: will transform the URL path and sub-paths to hashs $request->createRequestHashs(0); // if your application is located in a web-subfolder for easier handling @@ -142,9 +144,10 @@ class Application extends ApplicationAbstract ``` ```php + getData('dispatch'); // get data bound to the view with the key "dispatch" - foreach($dispatch as $view) echo $view->render(); ?> // in this case it has a 'render()' method which is called + foreach($dispatch as $view) echo $view->render(); // in this case it has a 'render()' method which is called + ?> ``` ```php + - -accountaccount_idPK...groupgroup_idPK...profile_accountprofile_account_idPKprofile_account_accountFK1...languagelanguage_idPKCustomerIdFK1...modulemodule_idPK...l11nl11n_idPKl11n_accountFK1...account_permissionaccount_permission_idPKaccount_permission_accountFK1...module_loadmodule_load_idPKmodule_load_fromFK1...countrycountry_idPK...addressaddress_idPKaddress_countryFK1...account_groupaccount_idPKaccount_group_accountFK1account_group_groupFK2settingssettings_idPKsettings_moduleFK1settings_accountFK2settings_groupFK3....group_permissiongroup_permission_idPKgroup_permission_accountFK1...
Profile Module
Profile Module
Core / Installation
Core / Installation
Admin Module
Admin Module
Address Module
Address Module
\ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + account + + + + + + account_id + + + + + + PK + + + + + + ... + + + + + + + + + group + + + + + + group_id + + + + + + PK + + + + + + ... + + + + + + + + + profile_account + + + + + + profile_account_id + + + + + + PK + + + + + + profile_account_account + + + + + + FK1 + + + + + + ... + + + + + + + + + language + + + + + + language_id + + + + + + PK + + + + + + ... + + + + + + + + + module + + + + + + module_id + + + + + + PK + + + + + + ... + + + + + + + + + l11n + + + + + + l11n_id + + + + + + PK + + + + + + l11n_account + + + + + + FK1 + + + + + + l11n_country + + + + + + FK2 + + + + + + l11n_language + + + + + + FK3 + + + + + + l11n_currency + + + + + + FK4 + + + + + + ... + + + + + + + + + account_permission + + + + + + account_permission_id + + + + + + PK + + + + + + account_permission_account + + + + + + FK1 + + + + + + ... + + + + + + + + + module_load + + + + + + module_load_id + + + + + + PK + + + + + + module_load_from + + + + + + FK1 + + + + + + ... + + + + + + + + + country + + + + + + country_id + + + + + + PK + + + + + + ... + + + + + + + + + address + + + + + + address_id + + + + + + PK + + + + + + address_country + + + + + + FK1 + + + + + + ... + + + + + + + + + + + + account_group + + + + + + account_id + + + + + + PK + + + + + + account_group_account + + + + + + FK1 + + + + + + account_group_group + + + + + + FK2 + + + + + + + + + + + + + + + + + settings + + + + + + settings_id + + + + + + PK + + + + + + settings_module + + + + + + FK1 + + + + + + settings_account + + + + + + FK2 + + + + + + settings_group + + + + + + FK3 + + + + + + .... + + + + + + + + + + + + + + + + + + + + + + + + + + + group_permission + + + + + + group_permission_id + + + + + + PK + + + + + + group_permission_account + + + + + + FK1 + + + + + + ... + + + + + + + + + + + + + + + + +
+
+
+ Profile Module +
+
+
+
+ + Profile Module + +
+
+ + + + +
+
+
+ Core / Installation +
+
+
+
+ + Core / Installation + +
+
+ + + + +
+
+
+ Admin Module +
+
+
+
+ + Admin Module + +
+
+ + + + +
+
+
+ Admin / Localization +
+
+
+
+ + Admin / Localization + +
+
+ + + + + + currency + + + + + + currency_id + + + + + + PK + + + + + + ... + + + + + + + + + + + + +
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/general/setup.md b/general/setup.md index 14baf66..8cf0927 100644 --- a/general/setup.md +++ b/general/setup.md @@ -6,7 +6,7 @@ In order to setup the application for development for the first time please see Make sure your dev-environment or server fulfills the following requirements: -* PHP >= 7.4 +* PHP >= 8.0 * PHP extensions: mbstring, gd, zip, dom, mysql/pgsql/sqlsrv, sqlite, bcmath, imap\*, redis\*, memcached\*, ftp\*, socket\*, curl\*, xml\* * databases: mysql, postgresql, sqlsrv * webserver: apache2 @@ -18,6 +18,12 @@ Extensions marked with `*` are optional. They are only required in special situa Steps which are not explained in this documentation are how to install and setup the above mentioned software and extensions. You also should configure the webserver paths accordingly in order to access the application in the browser. +### Installation Options + +1. Option 1: Full installation, code checks/tests, generating documentation. **Not recomended for quick setup** +2. Option 2: Only installs the application with some tests. Requires you to install the dev tools manually. **Recommended** +3. Option 3: Only installs the application, due to the large amount of data takes some time to execute. **Recommended** + ## Option 1: Linux Shell Script This also installs all required dev tools and sets up the directory structure by itself. Using this method also tears down previous installs for a fresh install perfect for re-installing from the current development version. Furthermore, the use of PHPUnit also makes sure that the application is working as intended. The PHPUnit install also provides lots of dummy data for better integration and functionality testing of your own code/modules. @@ -43,18 +49,18 @@ After the installation you'll have access to the following content: ### Annotation -During this process the database automatically gets dropped (if existing) and re-created. If you don't have `xdebug` installed but `phpdbg` you can replace `php phpunit.phar ...` with `phpdbg -qrr phpunit.phar ...` or use `pcov` for much faster code coverage generation. +During this process the database automatically gets dropped (if it exists) and re-created. If you don't have `xdebug` installed but `phpdbg` you can replace `php phpunit ...` with `phpdbg -qrr phpunit.phar ...` or use `pcov` for much faster code coverage generation in `Build/Inspection/Php/tests.sh` ## Option 2: PHPUnit Test Suits -This wil only setup the application including some dummy data and also perform the code tests but no quality checks. +This will only setup the application including some dummy data and also perform the code tests but no quality checks. ### Steps 1. Go to the directory where you want to install the application 2. Run `git clone -b develop https://github.com/Orange-Management/Orange-Management.git` -3. Run `git submodule update --init --recursive >/dev/null` -4. Run `git submodule foreach git checkout develop >/dev/null` +3. Run `git submodule update --init --recursive` +4. Run `git submodule foreach git checkout develop` 5. Install Composer 6. Run `composer install` inside `Orange-Management` 7. Run `php vendor/bin/phpunit --configuration tests/phpunit_no_coverage.xml` inside `Orange-Management` or open `http://127.0.0.1/Install` @@ -66,7 +72,7 @@ After the installation you'll have access to the following content: ### Annotation -During this process the database automatically gets dropped (if existing) and re-created. If you don't have `xdebug` installed but `phpdbg` you can replace `php phpunit.phar ...` with `phpdbg -qrr phpunit.phar ...` or use `pcov` for much faster code coverage generation. +During this process the database automatically gets dropped (if it exists) and re-created. If you don't have `xdebug` installed but `phpdbg` you can replace `php phpunit ...` with `phpdbg -qrr phpunit.phar ...` or use `pcov` for much faster code coverage generation. ## Git Hooks (Linux only) @@ -88,4 +94,25 @@ The following tools are important to test the application and to ensure the code * phpcs * phpmetrics * documentor -* phpstan \ No newline at end of file +* phpstan + +## Option 3: Demo Application + +This will only setup the application including some dummy data and also perform the code tests but no quality checks. Compared to option 2 this includes much more test data and it doesn't execute a unit test. + +1. Go to the directory where you want to install the application +2. Run `git clone -b develop https://github.com/Orange-Management/Orange-Management.git` +3. Run `git submodule update --init --recursive` +4. Run `git submodule foreach git checkout develop` +5. Install Composer +6. Run `composer install` inside `Orange-Management` +7. Create the database table `oms` +7. Run `php demoSetup/setup.php` inside `Orange-Management` + +After the installation you'll have access to the following content: + +* Application: `http://127.0.0.1` + +### Annotation + +During this process the database automatically gets dropped (if it exists) and re-created. \ No newline at end of file diff --git a/quality/inspections.md b/quality/inspections.md index d2feed9..ce25913 100644 --- a/quality/inspections.md +++ b/quality/inspections.md @@ -1,6 +1,6 @@ # Code Inspections & Tests -Code inspections are very important in order to maintain the same code quality throughout the application. The `Build` repository and package managers such as `composer` and `npm` contain all esential configuration files for the respective inspection tools. The framework and every module will be evaluated based on the defined code and quality standards. Only code that passes all code, quality and test standards are accepted. Updates and bug fixes also must follow the standards. +Code inspections are very important in order to maintain the same code quality throughout the application. The `Build` repository and package managers such as `composer` and `npm` contain all essential configuration files for the respective inspection tools. The framework and every module will be evaluated based on the defined code and quality standards. Only code that passes all code, quality and test standards are accepted. Updates and bug fixes also must follow the standards. ## How and what to test? @@ -20,7 +20,7 @@ When testing it makes sense to test for the happy path/branch of how a method sh ### Unit tests -The smallest/lowest level of testing are the unit tests. Unit tests should be implemented for models and framework methods. Every public function should be covered by at least one unit test. If a method has multiple branches every branch should be covered by a separate unit test. In some cases it might make sense to cover multiple branches in one unit test/test function, such a decision should however be made conciously. +The smallest/lowest level of testing are the unit tests. Unit tests should be implemented for models and framework methods. Every public function should be covered by at least one unit test. If a method has multiple branches every branch should be covered by a separate unit test. In some cases it might make sense to cover multiple branches in one unit test/test function, such a decision should however be made consciously. ### Integration tests @@ -97,7 +97,7 @@ The javascript testing is done with jasmine. The javascript testing directory is ### PHP CS -Besides the code tests and static code analysis the code style is another very imporant inspection to ensure the code quality. +Besides the code tests and static code analysis the code style is another very important inspection to ensure the code quality. ```sh php vendor/bin/phpcs ./ --standard="Build/Config/phpcs.xml" -s --report-junit=Build/test/junit_phpcs.xml diff --git a/services/events.md b/services/events.md index 58a2840..a4b7b8b 100644 --- a/services/events.md +++ b/services/events.md @@ -10,9 +10,11 @@ Every event requires a unique trigger key as well as a `\Closure` which should b $eventManager->attach('eventId', function() { echo 'Hello World'; }); ``` +The eventId can also be a regex in order to let it trigger on multiple occasions (e.g. `$eventManager->attach('/Test[a-z]+/', ...)`) + ### Repeating events -If a event should only be able to be triggered once another boolean parameter has to be edded to the `attach()` function call. +If a event should only be able to be triggered once another boolean parameter has to be added to the `attach()` function call. ```php $eventManager->attach('eventId', function() { echo 'Hello World'; }, true); @@ -38,6 +40,10 @@ An event can be triggered by calling the `trigger()` function. $eventManager->trigger('eventId'); ``` +### Triggering Similar Events + +In some situations you might want to trigger multiple events. In this case you can provide a regex as eventId and/or conditionName (e.g. `$eventManager->trigger('/[a-z]+/')`) + ## Multi Condition Events In some cases it is required that multiple conditions are met before an event is supposed to be triggered. This can be achieved by registering these conditions through the `addGroup()` function. @@ -62,4 +68,4 @@ The order in which these conditions are triggered doesn't mapper. A multi condit ## Frontend vs. Backend -The only key difference between the frontend and backend implementation is that the frontend prevents running the same event in quick succession (less than 500 ms) in order to prevent undesired effects which can happen due to quick UI interaction. +The only key difference between the frontend and backend implementation is that the frontend prevents running the same event in quick succession (less than 300 ms) in order to prevent undesired effects which can happen due to quick UI interaction. diff --git a/services/money.md b/services/money.md index 52b0bbb..d792049 100644 --- a/services/money.md +++ b/services/money.md @@ -10,7 +10,7 @@ The initialization can be done by providing an integer, float or string represen * (float) 1.23 = 1.23 * (string) 1.23 = 1.23 -The max and min money values that can be represented on a 32bit system are `214,748.3647` and `-214,748.3647` which is most of the time not sufficient. On 64bit systems however the max and min values that can be represented ar `922,337,203,685,477.5807` and `-922,337,203,685,477.5807`. +The max and min money values that can be represented on a 32bit system are `214,748.3647` and `-214,748.3647` which is most of the time not sufficient. On 64bit systems however the max and min values that can be represented are `922,337,203,685,477.5807` and `-922,337,203,685,477.5807`. ## Operations diff --git a/services/validation.md b/services/validation.md index e69de29..d1f1105 100644 --- a/services/validation.md +++ b/services/validation.md @@ -0,0 +1,189 @@ +# Validation + +The built in validation allows to quickly validate some basic data formats. + +## Validator + +The `Validator` provides basic validations with: + +### isValid + +With the `isValid` function you can provide a variable which should be validated and one or multiple constraints which should be checked. The return is a boolean. + +```php +public static function isValid(mixed $var, array $constraints = null) : bool; +``` + +Example: + +```php +function functionName(mixed $var, $mySetting1, $mySetting2) : bool +{ + // Do some validation here + return true; +} + +Validator::isValid($testVariable, ['functionName' => $settings]); +``` + +The second parameter (settings parameter) contains an associative array where the key is the function name which should get invoked and the value are the settings/options/parameters passed to the function name. The function must be implemented by the user. + +If multiple constraints are provided the validation only returns true if all validations are successful. + +In order to negate the validation simply add a `Not` to the function name. + +> The negated function name with *Not* must not be separately implemented as it will be negated internally. + +### hasLength + +With `hasLength` you can check if a string has defined length. + +```php +public static function hasLength(string $var, int $min = 0, int $max = \PHP_INT_MAX) : bool; +``` + +### hasLimit + +With `hasLimit` you can check if a numeric value is in a defined range. + +```php +public static function hasLimit(int | float $var, int | float $min = 0, int | float $max = \PHP_INT_MAX) : bool; +``` + +## Base + +### DateTime + +The `DateTime` validator checks if a specified value is a valid datetime. + +```php +DateTime::isValid('2099-01-01'); +``` + +### Json + +The `Json` validator checks if a specified value is a valid json string. + +```php +Json::isValid('[1, 2, 3]'); +``` + +#### Templates + +You can specify a json template which uses regex for validation. The template then is used to validate against a json array. + +The validator checks for completeness, match against the template and possible additional specifications which are not part of the template. + +Example for our `info.json` valdiation + +```json +{ + "name": { + "id": "^[1-9]\\d*", + "internal": "[a-zA-Z0-9]+", + "external": "[a-zA-Z0-9]+" + }, + "category": "[a-zA-Z0-9]+", + "version": "([0-9]+\\.){2}[0-9]+", + "requirements": { + ".*": ".*" + }, + "creator": { + "name": ".+", + "website": ".*" + }, + "description": ".+", + "directory": "[a-zA-Z0-9]+", + "dependencies": { + ".*": ".*" + }, + "providing": { + ".*": ".*" + }, + "load": [ + { + "pid": [ + ".*" + ], + "type": "^[1-9]\\d*", + "for": "^([1-9]\\d*)|([a-zA-Z0-9]+)", + "from": "[a-zA-Z0-9]+", + "file": "[a-zA-Z0-9]*" + } + ] +} + +``` + +The example above shows how to specify optional elements (e.g. "dependencies") which can have as many children in any form possible or optional values with a specific key (e.g. website) which can be omitted. + +## Finance + +### BIC + +A simple BIC validator which only validates the structure not if it actually exists. + +```php +BIC::isValid('DSBACNBXSHA'); +``` + +### Iban + +A simple IBAN validator which only validates the structure not if it actually exists. + +```php +Iban::isValid('DE22 6008 0000 0960 0280 00'); +``` + +### CreditCard + +A simple credit card validator which only validates the structure not if it actually exists. + +```php +CreditCard::isValid('4242424242424242'); +``` + +## Network + +### Email + +A simple email validator which only validates the structure not if it actually exists. + +```php +Email::isValid('test.string@email.com'); +``` + +### Hostname + +A simple hostname validator which only validates the structure not if it actually exists. + +```php +Hostname::isValid('[2001:0db8:85a3:0000:0000:8a2e:0370:7334]'); // true +Hostname::isValid('test.com'); // true + +Hostname::isValid('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // false +Hostname::isValid('http://test.com'); // false +``` + +### Ip + +A simple Ip validator which only validates the structure not if it actually exists. + +```php +IP::isValid('192.168.178.1'); +IP::isValid('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); + +IP::testValidIp4('192.168.178.1'); +IP::testValidIp6('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); +``` + +> Please note that the Ip4 and Ip6 validators only validate their corresponding Ips which means that a Ip4 will not correctly validate as Ip6 and vice versa. + +## Enum + +Enums can be validated by name/key and by value. + +```php +MyEnum::isValidName('ENUM_NAME'); +MyEnum::isValidValue(123); +``` \ No newline at end of file diff --git a/standards/documentation.md b/standards/documentation.md index 5fbc107..4163e64 100644 --- a/standards/documentation.md +++ b/standards/documentation.md @@ -2,7 +2,7 @@ ## Module -Every module must have two types of documentation. One for the end-user, which also includes information for the administrator and module configuration and another documentation for developers. Documentation must be available in at least english language. +Every module must have two types of documentation. One for the end-user, which also includes information for the administrator and module configuration and another documentation for developers. Documentation must be available in at least English language. ### User @@ -26,7 +26,7 @@ The developer documentation should cover the following aspects: ## Database -### Diagramms +### Diagrams Every module that has a database table must implement a UML diagram illustrating the internal table relations as well as the relations with external tables from other modules. @@ -44,11 +44,11 @@ A file documentation MUST be implemented in the following form: * * PHP Version 7.0 * - * @package Package name - * @copyright Orange Management - * @license OMS License 1.0 - * @version 1.0.0 - * @link http://your.url.com + * @package Package name + * @copyright Orange Management + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://your.url.com */ ``` @@ -60,10 +60,10 @@ A class documentation MUST be implemented in the following form: /** * Class description. * - * @package Package name - * @license OMS License 1.0 - * @link http://your.url.com - * @since 1.0.0 + * @package Package name + * @license OMS License 1.0 + * @link http://your.url.com + * @since 1.0.0 */ ``` @@ -101,7 +101,7 @@ A function/method documentation MUST be implemented in the following form: ### Variable -Variable documentation is not mandatory and can be omitted. However it's recommended to use a variable documentation for objects and arrays of objects in templates for ide code completion. +Variable documentation is not mandatory and can be omitted. However it's recommended to use a variable documentation for objects and arrays of objects in templates for IDE code completion. Example: @@ -119,11 +119,11 @@ The javascript documentation is based on JsDoc, therefore only valid JsDoc comme /** * File description. * - * @package Package name - * @copyright Orange Management - * @license OMS License 1.0 - * @version 1.0.0 - * @link http://your.url.com + * @package Package name + * @copyright Orange Management + * @license OMS License 1.0 + * @version 1.0.0 + * @link http://your.url.com */ ``` @@ -135,10 +135,10 @@ A class documentation MUST be implemented in the following form: /** * Class description. * - * @package Package name - * @license OMS License 1.0 - * @link http://your.url.com - * @since 1.0.0 + * @package Package name + * @license OMS License 1.0 + * @link http://your.url.com + * @since 1.0.0 */ ``` diff --git a/standards/general.md b/standards/general.md index e53c33a..2d63707 100644 --- a/standards/general.md +++ b/standards/general.md @@ -128,7 +128,7 @@ $result = condition : very long expr2 which should be written like this; ``` -Multiline `if` conditions should begin with a logical opperator `or`/`and`. +Multiline `if` conditions should begin with a logical operator `or`/`and`. ```php if (isTrue == true @@ -197,7 +197,7 @@ Most issues should be documented in the code as todo and vice versa. ```php /** - * @todo Orange-Management/Repository#IssueNumber [issue:information] + * @todo Orange-Management/Repository#IssueNumber * Below comes the issue/todo description. * This way developers can see todos directly in the code without going to an external source. * Todos must not have empty lines in their descriptions. @@ -208,51 +208,3 @@ Most issues should be documented in the code as todo and vice versa. ``` The issue information can be used to provide additional information such as priority, difficulty and type. - -### Priority - -Structure: - -```php -[p:{PRIORITY}] -``` - -Possible priorities are: - -* high -* low -* medium - -### Difficulty - -Structure: - -```php -[d:{DIFFICULTY}] -``` - -Possible difficulties are: - -* first -* beginner -* expert -* medium - -Difficulties marked with first are perfect for people who would like to contribute to the project for the first time. - -### Type - -Structure: - -```php -[t:{TYPE}] -``` - -Possible types are: - -* feature -* optimization -* performance -* question -* security -* todo (= default) \ No newline at end of file diff --git a/standards/html.md b/standards/html.md index c25fec5..ee91d8d 100644 --- a/standards/html.md +++ b/standards/html.md @@ -31,8 +31,8 @@ The following tags MUST not specify a end tag (\\): ## Accessible Rich Internet Applications -All modules and themes SHOULD be WAI-ARIA 2.0 compliant. For further reading please refere to https://www.w3.org/TR/WCAG20-TECHS/aria. +All modules and themes SHOULD be WAI-ARIA 2.0 compliant. For further reading please refer to https://www.w3.org/TR/WCAG20-TECHS/aria. ## Structured Data (Microdata) -For structured data https://schema.org/ SHOULD be used. \ No newline at end of file +For structured data https://schema.org/ SHOULD be used. \ No newline at end of file diff --git a/standards/php.md b/standards/php.md index d0d9fdc..1cbb919 100644 --- a/standards/php.md +++ b/standards/php.md @@ -26,6 +26,31 @@ This means each class is in a file by itself, and is in a namespace of at least Class names MUST be declared in StudlyCaps. +### Return type hint + +The return type hint must have a whitespace after the closing braces and after the colon. The return type must be on the same line as the closing brace. + +```php +function() : int +{ + +} +``` + +or for multiline function parameters + +```php +function( + $para1, + $para2, + $para3, + $para4 +) : int +{ + +} +``` + ### Default Functions Function calls to php internal function calls must use the root namespace `\`: @@ -37,6 +62,14 @@ Function calls to php internal function calls must use the root namespace `\`: .... ``` +## Type hints + +Type hints are mandatory wherever reasonably possible (member variables, function parameters, return types, ...). + +## Attributes + +Function attributes must not be used! + ## Php in html Php code embedded into template files SHOULD use the alternative syntax for control structures in order to improve the readability: @@ -79,6 +112,30 @@ public CONST_NAME = ...; Instead of using `\file_exists()` the functions `\is_dir()` or `\is_file()` should be used. +## Enum + +Don't use the internal enum implementations of PHP (neither `SplEnum` nor `enum`) + +## Member variables + +Member variables should be public unless there is absolutely no reason directly access them or additional data manipulation upon setting, changing or returning them is required. + +### Getters/Setters + +Getters and setters for member variables should be kept at a minimum and only used when they actually perform additional actions, such as additional type checks, data manipulation, ... + +#### Model IDs + +Model IDs must always be private. They must not have a setter. Therfore most models need/should have a getter function which returns the model ID. + +#### Pseudo enums + +Whenever a scalar coming from the internal enum data type (`\phpOMS\Stdlib\Base\Enum`) is used the variable should be private and a getter and setter should exist for additional type checks. + +```php +private int $myStatus = TestEnum::ACTIVE; // extends \phpOMS\Stdlib\Base\Enum +``` + ## Deprecated functions and variables The following functions and (super-) global variables MUST NOT be used.