diff --git a/.cs.php b/.cs.php
index 86a602bf..77cc33ec 100644
--- a/.cs.php
+++ b/.cs.php
@@ -21,7 +21,7 @@
'array_syntax' => ['syntax' => 'short'],
'cast_spaces' => ['space' => 'none'],
'concat_space' => ['spacing' => 'one'],
- 'compact_nullable_typehint' => true,
+ 'compact_nullable_type_declaration' => true,
'nullable_type_declaration' => true,
'nullable_type_declaration_for_default_null_value' => true,
'declare_equal_normalize' => ['space' => 'single'],
diff --git a/README.md b/README.md
index bd7235e7..402c3d39 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ This project showcases a real-world-example of a backend and frontend built usin
[Slim](https://www.slimframework.com/) micro-framework.
The primary goal is to provide a modern codebase with a scalable project structure and
-a range of practical feature implementations.
+the implementation of a range of practical features.
These can serve as learning examples or be adapted for developing new
applications.
@@ -26,18 +26,18 @@ Project components:
* Authentication (login) and authorization (permissions)
* Account verification and password reset via email link and token
* Protection against rapid fire brute force and password spraying attacks (time throttling and
- captcha) - [docs](https://github.com/samuelgfeller/slim-example-project/blob/master/docs/security-concept.md)
+ captcha)
* Localization — English, German and French
* Flash messages
* Request body and input validation
* Template rendering with native PHP syntax
* An intuitive method for editing values in the browser using "contenteditable"
* Dark theme
-* Custom error handler - [docs](https://github.com/samuelgfeller/slim-example-project/blob/master/docs/error-handling.md)
-* Integration testing with fixtures and data providers [docs](https://github.com/samuelgfeller/slim-example-project/blob/master/docs/testing/testing-cheatsheet.md)
-* Database migrations and seeding [docs](https://github.com/samuelgfeller/slim-example-project/blob/master/docs/cheatsheet.md#database-migrations)
+* Custom error handler
+* Integration testing with fixtures and data providers
+* Database migrations and seeding
-Application components demonstrating real-world features as examples:
+Application components demonstrating examples for real-world features:
* Users with 4 different roles and different permissions
* User management for administrators
* User activity history
@@ -95,9 +95,8 @@ The database is reset every half-hour.
**Frontend**
* [Template rendering](https://github.com/samuelgfeller/slim-example-project/wiki/Template-rendering)
-* [Dark mode - (coming soon)]()
-* [JS Modules - (coming soon)]()
-* [Ajax - (coming soon)](https://github.com/samuelgfeller/slim-example-project/wiki/Ajax)
+* [JavaScript - (coming soon)](https://github.com/samuelgfeller/slim-example-project/wiki/JavaScript)
+* [Dark mode](https://github.com/samuelgfeller/slim-example-project/wiki/Dark-Mode)
**Other**
* [Directory structure](https://github.com/samuelgfeller/slim-example-project/wiki/Directory-structure)
@@ -157,6 +156,8 @@ so that it's long living and can be adapted to different needs and preferences.
Basically, this is my take on what a modern and efficient web app could look like with today's
tech.
+## Credits
+
I worked closely with the software architect
[Daniel Opitz](https://odan.github.io/about.html), who also reviewed this project.
I learned a lot during
@@ -165,8 +166,6 @@ and was inspired by his books, articles, tutorials and his slim
[skeleton-project](https://github.com/odan/slim4-skeleton).
I'm grateful for his support and the time he took to help me improve this project.
-## Credits
-
Special thanks to [JetBrains](https://jb.gg/OpenSource) for supporting this project.
## Licence
diff --git a/config/container.php b/config/container.php
index 0f929994..2f08bd90 100644
--- a/config/container.php
+++ b/config/container.php
@@ -60,6 +60,7 @@
$rotatingFileHandler = new RotatingFileHandler($filename, 0, $level, true, 0777);
// The last "true" here tells monolog to remove empty []'s
$rotatingFileHandler->setFormatter(new LineFormatter(null, 'Y-m-d H:i:s', false, true));
+
return $logger->pushHandler($rotatingFileHandler);
},
@@ -153,6 +154,7 @@
SessionInterface::class => function (ContainerInterface $container) {
$options = $container->get('settings')['session'];
+
return new PhpSession($options);
},
diff --git a/config/functions.php b/config/functions.php
index 2343138f..5cfb143e 100644
--- a/config/functions.php
+++ b/config/functions.php
@@ -19,13 +19,13 @@ function html(?string $text = null): string
* If a context is provided, it is used to replace placeholders
* in the translated string.
*
- * @param string $message The message to be translated.
+ * @param string $message the message to be translated
* @param mixed ...$context Optional elements that should be inserted in the string with placeholders.
* The function can be called like this:
* __('The %s contains %d monkeys and %d birds.', 'tree', 5, 3);
* With the argument unpacking operator ...$context, the arguments are accessible within the function as an array.
*
- * @return string The translated string.
+ * @return string the translated string
*/
function __(string $message, ...$context): string
{
diff --git a/public/assets/general/dark-mode/dark-mode.js b/public/assets/general/dark-mode/dark-mode.js
index 07ed5d0f..0455be1b 100644
--- a/public/assets/general/dark-mode/dark-mode.js
+++ b/public/assets/general/dark-mode/dark-mode.js
@@ -1,16 +1,16 @@
-// Get the toggle switch element
import {submitUpdate} from "../ajax/submit-update-data.js?v=0.4.0";
import {displayFlashMessage} from "../page-component/flash-message/flash-message.js?v=0.4.0";
+// Get the toggle switch element
const toggleSwitch = document.querySelector('#dark-mode-toggle-checkbox');
-// Retrieve the current theme from localStorage
-const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
-
if (toggleSwitch) {
// Add event listener to the toggle switch for theme switching
toggleSwitch.addEventListener('change', switchTheme, false);
+ // Retrieve the current theme from localStorage
+ const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
+
// Set the theme based on the stored value from localStorage
if (currentTheme) {
// Set the data-theme attribute on the html element
@@ -45,10 +45,9 @@ function switchTheme(e) {
let userId = document.getElementById('user-id').value;
submitUpdate({theme: theme}, `users/${userId}`, true)
.then(r => {
- })
- .catch(r => {
- displayFlashMessage('error', 'Failed to change the theme in the database.')
- });
+ }).catch(r => {
+ displayFlashMessage('error', 'Failed to change the theme in the database.')
+ });
}
diff --git a/public/assets/general/general-css/default.css b/public/assets/general/general-css/default.css
index 21b43b75..1a8955fa 100644
--- a/public/assets/general/general-css/default.css
+++ b/public/assets/general/general-css/default.css
@@ -10,7 +10,6 @@
--background-accent-3-color: #dcdcdc;
--border-accent-2-color: #d5d5d5;
--accent-color-when-dark: white;
- --translucent-background: rgba(255, 255, 255, 0.8);
/* Text */
--primary-text-color: #2e3e50;
--secondary-text-color: rgba(46, 62, 80, 0.80);
diff --git a/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php b/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php
index b94fbef0..0ee7f2b7 100644
--- a/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php
+++ b/src/Application/Action/Authentication/Ajax/NewPasswordResetSubmitAction.php
@@ -29,9 +29,9 @@ public function __construct(
* @param ServerRequest $request
* @param Response $response
*
- * @return Response
* @throws \Throwable
*
+ * @return Response
*/
public function __invoke(ServerRequest $request, Response $response): Response
{
diff --git a/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php b/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php
index e38b4d62..645d2880 100644
--- a/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php
+++ b/src/Application/Action/Authentication/Ajax/PasswordForgottenEmailSubmitAction.php
@@ -29,9 +29,9 @@ public function __construct(
* @param ServerRequest $request
* @param Response $response
*
- * @return Response
* @throws \Throwable
*
+ * @return Response
*/
public function __invoke(ServerRequest $request, Response $response): Response
{
diff --git a/src/Application/ErrorHandler/DefaultErrorHandler.php b/src/Application/ErrorHandler/DefaultErrorHandler.php
index 65498b39..3e79cfef 100644
--- a/src/Application/ErrorHandler/DefaultErrorHandler.php
+++ b/src/Application/ErrorHandler/DefaultErrorHandler.php
@@ -27,6 +27,12 @@ public function __construct(
}
/**
+ * @param ServerRequestInterface $request
+ * @param Throwable $exception
+ * @param bool $displayErrorDetails
+ * @param bool $logErrors
+ * @param bool $logErrorDetails
+ *
* @throws Throwable
* @throws \ErrorException
*/
@@ -91,6 +97,7 @@ public function __invoke(
// The error-details template does not include the default layout,
// so the base path to the project root folder is required to load assets
$phpRendererAttributes['basePath'] = (new BasePathDetector($request->getServerParams()))->getBasePath();
+
// Render template if the template path fails, the default webserver exception is shown
return $this->phpRenderer->render($response, 'error/error-details.html.php', $phpRendererAttributes);
}
@@ -108,6 +115,7 @@ public function __invoke(
* Determine http status code.
*
* @param Throwable $exception The exception
+ *
* @return int The http code
*/
private function getHttpStatusCode(Throwable $exception): int
@@ -136,6 +144,7 @@ private function getHttpStatusCode(Throwable $exception): int
* Build the attribute array for the detailed error page.
*
* @param Throwable $exception
+ *
* @return array
*/
private function getExceptionDetailsAttributes(Throwable $exception): array
@@ -214,6 +223,7 @@ private function getExceptionDetailsAttributes(Throwable $exception): array
* This function returns the argument as a string.
*
* @param mixed $argument
+ *
* @return string
*/
private function getTraceArgumentAsString(mixed $argument): string
@@ -237,6 +247,7 @@ private function getTraceArgumentAsString(mixed $argument): string
$result[$key] = $value;
}
}
+
// Return the array converted to a string using var_export
return var_export($result, true);
}
@@ -249,8 +260,9 @@ private function getTraceArgumentAsString(mixed $argument): string
* Convert the given argument to a string not longer than 15 chars
* except if it's a file or a class name.
*
- * @param mixed $argument The variable to be converted to a string.
- * @return string The string representation of the variable.
+ * @param mixed $argument the variable to be converted to a string
+ *
+ * @return string the string representation of the variable
*/
private function getTraceArgumentAsTruncatedString(mixed $argument): string
{
@@ -288,6 +300,7 @@ private function getTraceArgumentAsTruncatedString(mixed $argument): string
* If a string is 'App\Domain\Example\Class', this function returns 'Class'.
*
* @param string $string
+ *
* @return string
*/
private function removeEverythingBeforeLastBackslash(string $string): string
diff --git a/src/Application/Middleware/NonFatalErrorHandlerMiddleware.php b/src/Application/Middleware/NonFatalErrorHandlerMiddleware.php
index 57cc3f92..6f5dbb4f 100644
--- a/src/Application/Middleware/NonFatalErrorHandlerMiddleware.php
+++ b/src/Application/Middleware/NonFatalErrorHandlerMiddleware.php
@@ -29,9 +29,9 @@ public function __construct(bool $displayErrorDetails, bool $logErrors, private
* @param ServerRequestInterface $request The request
* @param RequestHandlerInterface $handler The handler
*
- * @return ResponseInterface The response
* @throws ErrorException
*
+ * @return ResponseInterface The response
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
@@ -59,9 +59,11 @@ function ($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
}
}
+
return true;
}
);
+
return $handler->handle($request);
}
}
diff --git a/src/Application/Renderer/RedirectHandler.php b/src/Application/Renderer/RedirectHandler.php
index 550a0445..2bbf57df 100644
--- a/src/Application/Renderer/RedirectHandler.php
+++ b/src/Application/Renderer/RedirectHandler.php
@@ -7,7 +7,6 @@
readonly class RedirectHandler
{
-
public function __construct(private RouteParserInterface $routeParser)
{
}
diff --git a/src/Application/Renderer/TemplateRenderer.php b/src/Application/Renderer/TemplateRenderer.php
index 7cf98131..24a44a8f 100644
--- a/src/Application/Renderer/TemplateRenderer.php
+++ b/src/Application/Renderer/TemplateRenderer.php
@@ -80,9 +80,9 @@ public function renderOnValidationError(
* @param array|null $preloadValues
* @param array $queryParams same query params passed to page to be added again to form after validation error
*
- * @return ResponseInterface
* @throws \Throwable
*
+ * @return ResponseInterface
*/
public function respondWithFormThrottle(
ResponseInterface $response,
diff --git a/src/Domain/Authentication/Repository/UserRoleFinderRepository.php b/src/Domain/Authentication/Repository/UserRoleFinderRepository.php
index 86745663..6c7848c1 100644
--- a/src/Domain/Authentication/Repository/UserRoleFinderRepository.php
+++ b/src/Domain/Authentication/Repository/UserRoleFinderRepository.php
@@ -69,6 +69,7 @@ public function getRoleHierarchyByUserId(int $userId): int
->leftJoin('user_role', ['user.user_role_id = user_role.id'])
->where(['user.deleted_at IS' => null, 'user.id' => $userId]);
$roleResultRow = $query->execute()->fetch('assoc');
+
// If no role found, return highest hierarchy which means lowest privileged role
return (int)($roleResultRow['hierarchy'] ?? 1000);
}
diff --git a/src/Domain/Authentication/Service/LoginVerifier.php b/src/Domain/Authentication/Service/LoginVerifier.php
index 5630792d..cb754110 100644
--- a/src/Domain/Authentication/Service/LoginVerifier.php
+++ b/src/Domain/Authentication/Service/LoginVerifier.php
@@ -28,12 +28,12 @@ public function __construct(
*
* @param array $userLoginValues An associative array containing the user's login credentials.
* Expected keys are 'email' and 'password' and optionally 'g-recaptcha-response'.
- * @param array $queryParams An associative array containing any additional query parameters.
+ * @param array $queryParams an associative array containing any additional query parameters
*
- * @throws TransportExceptionInterface If an error occurs while sending an email to a non-active user.
- * @throws InvalidCredentialsException If the user does not exist or the password is incorrect.
+ * @throws TransportExceptionInterface if an error occurs while sending an email to a non-active user
+ * @throws InvalidCredentialsException if the user does not exist or the password is incorrect
*
- * @return int The ID of the user if the login is successful.
+ * @return int the ID of the user if the login is successful
*/
public function verifyLoginAndGetUserId(array $userLoginValues, array $queryParams = []): int
{
diff --git a/src/Domain/Authorization/Privilege.php b/src/Domain/Authorization/Privilege.php
index c2e2bcae..e3afd314 100644
--- a/src/Domain/Authorization/Privilege.php
+++ b/src/Domain/Authorization/Privilege.php
@@ -23,7 +23,6 @@ enum Privilege
// Allowed to Read, Create, Update and Delete
case CRUD;
-
// Initially, the Privilege Enum was the datatype in result objects that was passed to the PHP templates.
// The case names were the name of the highest privilege (Read, Create, Update, Delete).
// The values were the letters of the associated permissions meaning Delete was 'CRUD', Update was 'CRU' and so on.
diff --git a/src/Domain/Client/Service/Authorization/ClientPermissionVerifier.php b/src/Domain/Client/Service/Authorization/ClientPermissionVerifier.php
index 382ed17b..61e06f8b 100644
--- a/src/Domain/Client/Service/Authorization/ClientPermissionVerifier.php
+++ b/src/Domain/Client/Service/Authorization/ClientPermissionVerifier.php
@@ -41,6 +41,7 @@ public function isGrantedToCreate(?ClientData $client = null): bool
'loggedInUserId not set while isGrantedToCreate authorization check $client: '
. json_encode($client, JSON_PARTIAL_OUTPUT_ON_ERROR)
);
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
@@ -89,6 +90,7 @@ public function isGrantedToAssignUserToClient(
'loggedInUserId not set while isGrantedToAssignUserToClient authorization check $assignedUserId: '
. $assignedUserId
);
+
return false;
}
@@ -136,6 +138,7 @@ public function isGrantedToUpdate(array $clientDataToUpdate, ?int $ownerId, bool
'loggedInUserId not set while isGrantedToUpdate authorization check $clientDataToUpdate: '
. json_encode($clientDataToUpdate, JSON_PARTIAL_OUTPUT_ON_ERROR)
);
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
@@ -217,6 +220,7 @@ public function isGrantedToDelete(?int $ownerId, bool $log = true): bool
{
if (!$this->loggedInUserId) {
$this->logger->error('loggedInUserId not set while isGrantedToDelete authorization check');
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
diff --git a/src/Domain/Client/Service/ClientValidator.php b/src/Domain/Client/Service/ClientValidator.php
index 45e947f1..2e9199ee 100644
--- a/src/Domain/Client/Service/ClientValidator.php
+++ b/src/Domain/Client/Service/ClientValidator.php
@@ -120,4 +120,4 @@ public function validateClientValues(array $clientCreationValues, bool $isCreate
throw new ValidationException($errors);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Domain/Dashboard/Panel/UserFilterChipProvider.php b/src/Domain/Dashboard/Panel/UserFilterChipProvider.php
index c36c8c0a..f5fcd8ff 100644
--- a/src/Domain/Dashboard/Panel/UserFilterChipProvider.php
+++ b/src/Domain/Dashboard/Panel/UserFilterChipProvider.php
@@ -31,7 +31,7 @@ public function getUserFilterChipsHtml(): string
$filters = $this->getActiveAndInactiveUserFilters();
$activeFilterChips = '';
foreach ($filters['active'] as $filterCategory => $filtersInCategory) {
- /** @var \App\Domain\FilterSetting\Data\FilterData $filterData */
+ /** @var FilterData $filterData */
foreach ($filtersInCategory as $filterId => $filterData) {
$activeFilterChips .= "
userActivityLogger->logUserActivity(UserActivity::UPDATED, 'note', $noteId, $updateData);
-
return $updated;
}
diff --git a/src/Domain/Security/Repository/EmailLogFinderRepository.php b/src/Domain/Security/Repository/EmailLogFinderRepository.php
index 89ff1d50..c6ed1150 100644
--- a/src/Domain/Security/Repository/EmailLogFinderRepository.php
+++ b/src/Domain/Security/Repository/EmailLogFinderRepository.php
@@ -19,6 +19,7 @@ public function __construct(
* @param int $seconds
* Throws PersistenceRecordNotFoundException if entry not found
* @param int|null $userId
+ *
* @return int
*/
public function getLoggedEmailCountInTimespan(string $email, int $seconds, ?int $userId): int
diff --git a/src/Domain/Security/Service/EmailRequestFinder.php b/src/Domain/Security/Service/EmailRequestFinder.php
index b3532234..06b02347 100644
--- a/src/Domain/Security/Service/EmailRequestFinder.php
+++ b/src/Domain/Security/Service/EmailRequestFinder.php
@@ -21,6 +21,7 @@ public function __construct(
*
* @param string $email
* @param int|null $userId
+ *
* @return int
*/
public function findEmailAmountInSetTimespan(string $email, ?int $userId): int
diff --git a/src/Domain/User/Service/Authorization/AuthorizedUserRoleFilterer.php b/src/Domain/User/Service/Authorization/AuthorizedUserRoleFilterer.php
index a0fc9227..b598d508 100644
--- a/src/Domain/User/Service/Authorization/AuthorizedUserRoleFilterer.php
+++ b/src/Domain/User/Service/Authorization/AuthorizedUserRoleFilterer.php
@@ -44,4 +44,4 @@ public function filterAuthorizedUserRoles(?int $attributedUserRoleId = null): ar
return $grantedCreateUserRoles;
}
-}
\ No newline at end of file
+}
diff --git a/src/Domain/User/Service/Authorization/UserPermissionVerifier.php b/src/Domain/User/Service/Authorization/UserPermissionVerifier.php
index 3921c5ce..fbc260a1 100644
--- a/src/Domain/User/Service/Authorization/UserPermissionVerifier.php
+++ b/src/Domain/User/Service/Authorization/UserPermissionVerifier.php
@@ -39,6 +39,7 @@ public function isGrantedToCreate(array $userValues): bool
'loggedInUserId not set while authorization check isGrantedToCreate: '
. json_encode($userValues, JSON_PARTIAL_OUTPUT_ON_ERROR)
);
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
@@ -53,11 +54,11 @@ public function isGrantedToCreate(array $userValues): bool
if ($authenticatedUserRoleHierarchy <= $userRoleHierarchies[UserRole::MANAGING_ADVISOR->value]) {
// Managing advisors can do everything with users except setting a role higher than advisor
if ($this->userRoleIsGranted(
- $userValues['user_role_id'] ?? null,
- null,
- $authenticatedUserRoleHierarchy,
- $userRoleHierarchies
- ) === true
+ $userValues['user_role_id'] ?? null,
+ null,
+ $authenticatedUserRoleHierarchy,
+ $userRoleHierarchies
+ ) === true
) {
return true;
}
@@ -99,6 +100,7 @@ public function userRoleIsGranted(
'loggedInUserId not set while authorization check that user role is granted $userRoleIdOfUserToMutate: '
. $userRoleIdOfUserToMutate
);
+
return false;
}
// $authenticatedUserRoleData and $userRoleHierarchies passed as arguments if called inside this class
@@ -157,6 +159,7 @@ public function isGrantedToUpdate(array $userDataToUpdate, string|int $userIdToU
'loggedInUserId not while user update authorization check' .
json_encode($userDataToUpdate, JSON_PARTIAL_OUTPUT_ON_ERROR)
);
+
return false;
}
$grantedUpdateKeys = [];
@@ -222,7 +225,6 @@ public function isGrantedToUpdate(array $userDataToUpdate, string|int $userIdToU
// Owner user (profile edit) is not allowed to change its user role or status
}
-
// Check that the data that the user wanted to update is in $grantedUpdateKeys array
foreach ($userDataToUpdate as $key => $value) {
// If at least one array key doesn't exist in $grantedUpdateKeys it means that user is not permitted
@@ -237,6 +239,7 @@ public function isGrantedToUpdate(array $userDataToUpdate, string|int $userIdToU
return false;
}
}
+
// All keys in $userDataToUpdate are in $grantedUpdateKeys
return true;
}
@@ -258,6 +261,7 @@ public function isGrantedToDelete(
'loggedInUserId not set while authorization check isGrantedToDelete $userIdToDelete: '
. $userIdToDelete
);
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
@@ -278,7 +282,6 @@ public function isGrantedToDelete(
return true;
}
-
if ($log === true) {
$this->logger->notice(
'User ' . $this->loggedInUserId . ' tried to delete user but isn\'t allowed.'
@@ -303,6 +306,7 @@ public function isGrantedToRead(?int $userIdToRead = null, bool $log = true): bo
'loggedInUserId not set while authorization check isGrantedToRead $userIdToRead: '
. $userIdToRead
);
+
return false;
}
$authenticatedUserRoleHierarchy = $this->userRoleFinderRepository->getRoleHierarchyByUserId(
@@ -345,6 +349,7 @@ public function isGrantedToReadUserActivity(
'loggedInUserId not set while authorization check isGrantedToReadUserActivity $userIdToRead: '
. $userIdToRead
);
+
return false;
}
diff --git a/src/Domain/User/Service/UserCreator.php b/src/Domain/User/Service/UserCreator.php
index 6f53d0ff..e500b2cc 100644
--- a/src/Domain/User/Service/UserCreator.php
+++ b/src/Domain/User/Service/UserCreator.php
@@ -36,9 +36,9 @@ public function __construct(
* @param array $userValues
* @param array $queryParams query params that should be added to email verification link (e.g. redirect)
*
- * @return int|bool insert id, false if user already exists
* @throws TransportExceptionInterface|\JsonException|\Exception
*
+ * @return int|bool insert id, false if user already exists
*/
public function createUser(array $userValues, array $queryParams = []): bool|int
{
diff --git a/src/Domain/User/Service/UserFinder.php b/src/Domain/User/Service/UserFinder.php
index 9976b114..b96b9cf7 100644
--- a/src/Domain/User/Service/UserFinder.php
+++ b/src/Domain/User/Service/UserFinder.php
@@ -45,9 +45,9 @@ public function findAllUsersResultDataForList(): array
(int)$userResultData->id,
'status',
);
- // Personal info privilege like first name, email and so on no needed for list
- // $userResultData->generalPrivilege = $this->userPermissionVerifier->getUpdatePrivilegeForUserColumn(
- // 'personal_info', $userResultData->id );
+ // Personal info privilege like first name, email and so on no needed for list
+ // $userResultData->generalPrivilege = $this->userPermissionVerifier->getUpdatePrivilegeForUserColumn(
+ // 'personal_info', $userResultData->id );
} else {
unset($userResultArray[$key]);
}
@@ -72,9 +72,9 @@ public function findUserById(string|int|null $id): UserData
*
* @param int $id
*
- * @return UserResultData
* @throws \Exception
*
+ * @return UserResultData
*/
public function findUserReadResult(int $id): UserResultData
{
diff --git a/src/Infrastructure/Console/SqlSchemaGenerator.php b/src/Infrastructure/Console/SqlSchemaGenerator.php
index 5b88edd1..87e5460b 100644
--- a/src/Infrastructure/Console/SqlSchemaGenerator.php
+++ b/src/Infrastructure/Console/SqlSchemaGenerator.php
@@ -60,9 +60,9 @@ public function generateSqlSchema(): int
*
* @param string $sql The sql
*
- * @return PDOStatement The statement
* @throws UnexpectedValueException
*
+ * @return PDOStatement The statement
*/
private function query(string $sql): PDOStatement
{
diff --git a/src/Infrastructure/Service/LocaleConfigurator.php b/src/Infrastructure/Service/LocaleConfigurator.php
index e5f25f19..b9ca5ffd 100644
--- a/src/Infrastructure/Service/LocaleConfigurator.php
+++ b/src/Infrastructure/Service/LocaleConfigurator.php
@@ -16,13 +16,13 @@ public function __construct(Settings $settings)
/**
* Sets the locale and language settings for the application.
*
- * @param string|null|false $locale The locale or language code (e.g. 'en_US' or 'en').
+ * @param string|false|null $locale The locale or language code (e.g. 'en_US' or 'en').
* If null or false, the default locale from the settings is used.
- * @param string $domain The text domain (default 'messages') for gettext translations.
+ * @param string $domain the text domain (default 'messages') for gettext translations
*
- * @return false|string The new locale string, or false on failure.
+ * @throws \UnexpectedValueException if the locale is not 'en_US' and no translation file exists for the locale
*
- * @throws \UnexpectedValueException If the locale is not 'en_US' and no translation file exists for the locale.
+ * @return false|string the new locale string, or false on failure
*/
public function setLanguage(string|null|false $locale, string $domain = 'messages'): bool|string
{
@@ -81,6 +81,7 @@ public function getLanguageCodeForPath(): string
* if the language is not available.
*
* @param false|string|null $locale
+ *
* @return string
*/
private function getAvailableLocale(null|false|string $locale): string
@@ -104,6 +105,7 @@ private function getAvailableLocale(null|false|string $locale): string
}
// Get the language code from the "target" locale
$localeLanguageCode = $this->getLanguageCodeFromLocale($locale);
+
// Take the locale from the same language if available or the default one
return $localesMappedByLanguage[$localeLanguageCode] ?? $this->localeSettings['default'] ?? 'en_US';
}
@@ -112,6 +114,7 @@ private function getAvailableLocale(null|false|string $locale): string
* Get the language code part of a locale.
*
* @param string|false|null $locale e.g. 'en_US'
+ *
* @return string|null e.g. 'en'
*/
private function getLanguageCodeFromLocale(string|null|false $locale): ?string
@@ -120,6 +123,7 @@ private function getLanguageCodeFromLocale(string|null|false $locale): ?string
if ($locale && str_contains($locale, '-')) {
$locale = str_replace('-', '_', $locale);
}
+
// The language code is the first part of the locale string
return $locale ? explode('_', $locale)[0] : null;
}
diff --git a/src/Infrastructure/Service/Mailer.php b/src/Infrastructure/Service/Mailer.php
index f69d72d5..8eefe800 100644
--- a/src/Infrastructure/Service/Mailer.php
+++ b/src/Infrastructure/Service/Mailer.php
@@ -47,10 +47,11 @@ public function getContentFromTemplate(string $templatePath, array $templateData
}
/**
- * Send and log email
- *
- * @param Email $email
- * @return void
+ * Send and log email.
+ *
+ * @param Email $email
+ *
+ * @return void
*/
public function send(Email $email): void
{
diff --git a/templates/layout.html.php b/templates/layout.html.php
index 0ec6b924..1cfa01fa 100644
--- a/templates/layout.html.php
+++ b/templates/layout.html.php
@@ -54,7 +54,7 @@
const theme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null;
// Get the theme provided from the server via query param (only after login)
const themeParam = new URLSearchParams(window.location.search).get('theme');
- // Finally add the theme to the element
+ // Finally, add the theme to the element
document.documentElement.setAttribute('data-theme', themeParam ?? theme ?? 'light');
// If a theme from the database is provided and not the same with localStorage, replace it
if (themeParam && themeParam !== theme) {
@@ -75,58 +75,8 @@
-
- fetch('layout/navbar.html.php', []);
} ?>
= $content ?>
diff --git a/templates/layout/navbar.html.php b/templates/layout/navbar.html.php
new file mode 100644
index 00000000..f1fa7d7c
--- /dev/null
+++ b/templates/layout/navbar.html.php
@@ -0,0 +1,58 @@
+
+
\ No newline at end of file
diff --git a/templates/user/user-read.html.php b/templates/user/user-read.html.php
index f51b9e0e..f5a164b3 100644
--- a/templates/user/user-read.html.php
+++ b/templates/user/user-read.html.php
@@ -131,7 +131,6 @@ class="contenteditable-edit-icon cursor-pointer"
-
diff --git a/tests/Fixture/FixtureInterface.php b/tests/Fixture/FixtureInterface.php
index 555c1902..2f643050 100644
--- a/tests/Fixture/FixtureInterface.php
+++ b/tests/Fixture/FixtureInterface.php
@@ -4,6 +4,7 @@
/**
* Fixture classes contain the properties $table and $records.
+ *
* @property string $table
* @property array $records
*/
diff --git a/tests/Fixture/NoteFixture.php b/tests/Fixture/NoteFixture.php
index 6ae7dc6c..c3d740dc 100644
--- a/tests/Fixture/NoteFixture.php
+++ b/tests/Fixture/NoteFixture.php
@@ -3,7 +3,7 @@
namespace App\Test\Fixture;
/**
- * Note values that can be inserted into the database
+ * Note values that can be inserted into the database.
*/
class NoteFixture implements FixtureInterface
{
diff --git a/tests/Integration/Authentication/LoginSecurityTest.php b/tests/Integration/Authentication/LoginSecurityTest.php
index 636e339f..2559193a 100644
--- a/tests/Integration/Authentication/LoginSecurityTest.php
+++ b/tests/Integration/Authentication/LoginSecurityTest.php
@@ -28,10 +28,10 @@ class LoginSecurityTest extends TestCase
* Test thresholds and according delays of login failures
* If login request amount exceeds threshold, the user has to wait a certain delay.
*
- * @return void
* @throws NotFoundExceptionInterface
- *
* @throws ContainerExceptionInterface
+ *
+ * @return void
*/
public function testLoginThrottlingWrongCredentials(): void
{
@@ -46,7 +46,7 @@ public function testLoginThrottlingWrongCredentials(): void
$user = $this->insertFixtureWithAttributes(new UserFixture(), [
'email' => $email,
'password_hash' => password_hash($password, PASSWORD_DEFAULT),
- ],);
+ ], );
// Login request body with invalid credentials
$loginRequestBody = ['email' => 'wrong@email.com', 'password' => 'wrong_password'];
diff --git a/tests/Integration/Authentication/PasswordResetSubmitActionTest.php b/tests/Integration/Authentication/PasswordResetSubmitActionTest.php
index ea8a9e9a..011fd4aa 100644
--- a/tests/Integration/Authentication/PasswordResetSubmitActionTest.php
+++ b/tests/Integration/Authentication/PasswordResetSubmitActionTest.php
@@ -42,7 +42,7 @@ public function testResetPasswordSubmit(UserVerificationData $verification, stri
{
$newPassword = 'new password';
// Insert user
- $userRow = $this->insertFixtureWithAttributes(new UserFixture(), ['id' => $verification->userId],);
+ $userRow = $this->insertFixtureWithAttributes(new UserFixture(), ['id' => $verification->userId]);
$this->insertFixture('user_verification', $verification->toArrayForDatabase());
diff --git a/tests/Integration/Client/ClientCreateActionTest.php b/tests/Integration/Client/ClientCreateActionTest.php
index 45b4478d..50883c94 100644
--- a/tests/Integration/Client/ClientCreateActionTest.php
+++ b/tests/Integration/Client/ClientCreateActionTest.php
@@ -45,9 +45,9 @@ class ClientCreateActionTest extends TestCase
* @param array $authenticatedUserRow authenticated user attributes containing the user_role_id
* @param array $expectedResult HTTP status code, bool if db_entry_created and json_response
*
- * @return void
* @throws \JsonException|ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitCreateActionAuthorization(
?array $userLinkedToClientRow,
@@ -156,9 +156,9 @@ public function testClientSubmitCreateActionAuthorization(
* @param array $requestBody
* @param array $jsonResponse
*
- * @return void
* @throws ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitCreateActionInvalid(array $requestBody, array $jsonResponse): void
{
@@ -210,9 +210,9 @@ public function testClientSubmitCreateActionInvalid(array $requestBody, array $j
*
* @param array $requestBody
*
- * @return void
* @throws ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitCreateActionValid(array $requestBody): void
{
diff --git a/tests/Integration/Client/ClientListActionTest.php b/tests/Integration/Client/ClientListActionTest.php
index 2cbce44d..ba3f3c99 100644
--- a/tests/Integration/Client/ClientListActionTest.php
+++ b/tests/Integration/Client/ClientListActionTest.php
@@ -52,10 +52,10 @@ class ClientListActionTest extends TestCase
/**
* Normal page action while having an active session.
*
- * @return void
* @throws NotFoundExceptionInterface
- *
* @throws ContainerExceptionInterface
+ *
+ * @return void
*/
public function testClientListPageActionAuthorization(): void
{
@@ -105,10 +105,10 @@ public function testClientListPageActionUnauthenticated(): void
* @param array $usersToInsert
* @param array $clientStatusesToInsert
*
- * @return void
* @throws NotFoundExceptionInterface
- *
* @throws ContainerExceptionInterface
+ *
+ * @return void
*/
public function testClientListWithFilterAction(
array $filterQueryParamsArr,
@@ -201,10 +201,10 @@ public function testClientListWithFilterAction(
* @param array $queryParams Filter as GET paramets
* @param array $expectedBody Expected response body
*
- * @return void
* @throws NotFoundExceptionInterface
- *
* @throws ContainerExceptionInterface
+ *
+ * @return void
*/
public function testClientListActionInvalidFilters(array $queryParams, array $expectedBody): void
{
diff --git a/tests/Integration/Client/ClientUpdateActionTest.php b/tests/Integration/Client/ClientUpdateActionTest.php
index 676d5824..3646b4b8 100644
--- a/tests/Integration/Client/ClientUpdateActionTest.php
+++ b/tests/Integration/Client/ClientUpdateActionTest.php
@@ -51,9 +51,9 @@ class ClientUpdateActionTest extends TestCase
* @param array $requestData array of data for the request body
* @param array $expectedResult HTTP status code, bool if db_entry_created and json_response
*
- * @return void
* @throws \JsonException|ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitUpdateActionAuthorization(
array $userLinkedToClientRow,
@@ -162,9 +162,9 @@ public function testClientSubmitUpdateActionAuthorization(
* @param array $requestBody
* @param array $jsonResponse
*
- * @return void
* @throws ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitUpdateActionInvalid(array $requestBody, array $jsonResponse): void
{
@@ -228,9 +228,9 @@ public function testClientSubmitUpdateActionUnauthenticated(): void
* Test that if user makes update request but the content has not changed
* compared to what's in the database, the response contains the warning.
*
- * @return void
* @throws ContainerExceptionInterface|NotFoundExceptionInterface
*
+ * @return void
*/
public function testClientSubmitUpdateActionUnchangedContent(): void
{
diff --git a/tests/Integration/User/UserCreateActionTest.php b/tests/Integration/User/UserCreateActionTest.php
index 032b7e6f..495b37d0 100644
--- a/tests/Integration/User/UserCreateActionTest.php
+++ b/tests/Integration/User/UserCreateActionTest.php
@@ -172,7 +172,6 @@ public function testUserSubmitCreateInvalid(array $requestBody, array $jsonRespo
// even if it's an empty string
self::assertSame(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY, $response->getStatusCode());
-
// Database must be unchanged - only one row (authenticated user) expected in user table
$this->assertTableRowCount(1, 'user');
$this->assertJsonData($jsonResponse, $response);
diff --git a/tests/Integration/User/UserListActionTest.php b/tests/Integration/User/UserListActionTest.php
index 32d18a44..ffb1a828 100644
--- a/tests/Integration/User/UserListActionTest.php
+++ b/tests/Integration/User/UserListActionTest.php
@@ -36,9 +36,10 @@ class UserListActionTest extends TestCase
* @param array $authenticatedUserRow authenticated user attributes containing the user_role_id
* @param array $expectedResult HTTP status code and privilege
*
- * @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
+ *
+ * @return void
*/
public function testUserListAuthorization(
array $userRow,
diff --git a/tests/Integration/User/UserUpdateActionTest.php b/tests/Integration/User/UserUpdateActionTest.php
index 8780262d..a6071c5b 100644
--- a/tests/Integration/User/UserUpdateActionTest.php
+++ b/tests/Integration/User/UserUpdateActionTest.php
@@ -43,11 +43,11 @@ class UserUpdateActionTest extends TestCase
* @param array $requestData array of data for the request body
* @param array $expectedResult HTTP status code, bool if db_entry_created and json_response
*
- * @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
- *
* @throws \JsonException
+ *
+ * @return void
*/
public function testUserSubmitUpdateAuthorization(
array $userToChangeRow,
@@ -131,7 +131,7 @@ public function testUserSubmitUpdateInvalid(array $requestBody, array $jsonRespo
{
// Insert user that is allowed to change content (advisor owner)
$userRow = $this->insertFixtureWithAttributes(
- // Replace user_role_id enum case with database id with AuthorizationTestTrait function addUserRoleId()
+ // Replace user_role_id enum case with database id with AuthorizationTestTrait function addUserRoleId()
new UserFixture(),
$this->addUserRoleId(['user_role_id' => UserRole::ADVISOR]),
);
diff --git a/tests/Provider/Client/ClientCreateProvider.php b/tests/Provider/Client/ClientCreateProvider.php
index 49089fa8..9798c231 100644
--- a/tests/Provider/Client/ClientCreateProvider.php
+++ b/tests/Provider/Client/ClientCreateProvider.php
@@ -7,7 +7,6 @@
class ClientCreateProvider
{
-
/**
* Provides test cases for client creation dropdown options.
*
diff --git a/tests/Provider/User/UserUpdateProvider.php b/tests/Provider/User/UserUpdateProvider.php
index d7abc3cf..d0c4748c 100644
--- a/tests/Provider/User/UserUpdateProvider.php
+++ b/tests/Provider/User/UserUpdateProvider.php
@@ -7,7 +7,6 @@
class UserUpdateProvider
{
-
/**
* @return array[]
*/
diff --git a/tests/Traits/FixtureTestTrait.php b/tests/Traits/FixtureTestTrait.php
index 25f46634..efd52540 100644
--- a/tests/Traits/FixtureTestTrait.php
+++ b/tests/Traits/FixtureTestTrait.php
@@ -16,6 +16,7 @@ trait FixtureTestTrait
* @param array $attributes attributes to override in the fixture
* Format: ['field_name' => 'expected_value', 'other_field_name' => 'other expected value', ] -> one insert
* alternatively [['field_name' => 'expected_value'], ['field_name' => 'expected_value'], ] -> two insets
+ *
* @return array inserted row values
*/
protected function insertFixtureWithAttributes(FixtureInterface $fixture, array $attributes = []): array
diff --git a/tests/Unit/Security/SecurityEmailCheckerTest.php b/tests/Unit/Security/SecurityEmailCheckerTest.php
index c7757600..00086918 100644
--- a/tests/Unit/Security/SecurityEmailCheckerTest.php
+++ b/tests/Unit/Security/SecurityEmailCheckerTest.php
@@ -31,11 +31,13 @@ class SecurityEmailCheckerTest extends TestCase
*
* The Data Provider calls this function with all the different variation of email
* request amounts where an exception must be thrown.
+ *
* @dataProvider \App\Test\Provider\Security\EmailRequestProvider::individualEmailThrottlingTestCases()
*
* @param int|string $delay
* @param int $emailLogAmountInTimeSpan
* @param array $securitySettings
+ *
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
@@ -91,6 +93,7 @@ public function testPerformEmailAbuseCheckIndividual(
* @param int $todayEmailAmount too many emails for today
* @param int $thisMonthEmailAmount too many emails for this month
* @param array $securitySettings
+ *
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
diff --git a/tests/Unit/Security/SecurityLoginCheckerTest.php b/tests/Unit/Security/SecurityLoginCheckerTest.php
index 1d88d542..21b915ca 100644
--- a/tests/Unit/Security/SecurityLoginCheckerTest.php
+++ b/tests/Unit/Security/SecurityLoginCheckerTest.php
@@ -50,6 +50,7 @@ class SecurityLoginCheckerTest extends TestCase
* logins_by_ip: array{successes: int, failures: int},
* } $ipAndEmailLogSummary
* @param array $securitySettings
+ *
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/