diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index b16e2c35..f18cd8cd 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -10,7 +10,6 @@ build:
tests:
override:
- js-scrutinizer-run
- - php-scrutinizer-run
tests:
tests:
override:
diff --git a/README.md b/README.md
index 09b52a35..62d1b87a 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@ Kimai - the open source time-tracker application with a mobile-first approach (r
[](https://packagist.org/packages/kevinpapst/kimai2)
[](https://packagist.org/packages/kevinpapst/kimai2)
[](https://travis-ci.org/kevinpapst/kimai2)
-[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master)
[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master)
[](https://scrutinizer-ci.com/g/kevinpapst/kimai2/build-status/master)
diff --git a/composer.json b/composer.json
index 48bc26d3..66359be6 100644
--- a/composer.json
+++ b/composer.json
@@ -65,6 +65,8 @@
"dama/doctrine-test-bundle": "^5.0",
"friendsofphp/php-cs-fixer": "^2.10",
"phpstan/phpstan": "^0.11.7",
+ "phpstan/phpstan-doctrine": "^0.11.4",
+ "phpstan/phpstan-symfony": "^0.11.6",
"phpunit/phpunit": "^7.0",
"symfony/browser-kit": "^4.0",
"symfony/css-selector": "^4.0",
@@ -117,7 +119,7 @@
"tests": "vendor/bin/phpunit tests/",
"tests-unit": "vendor/bin/phpunit --exclude-group integration tests/",
"tests-integration": "vendor/bin/phpunit --group integration tests/",
- "phpstan": "vendor/bin/phpstan analyse src --level=1",
+ "phpstan": "vendor/bin/phpstan analyse src --level=3",
"codestyle": "vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none",
"codestyle-fix": "vendor/bin/php-cs-fixer fix"
},
diff --git a/composer.lock b/composer.lock
index f977d85f..2a744fe6 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4ea6464ccdb8d43c48a4c1188ba2c4d5",
+ "content-hash": "640489c19c632dec49629bd3f2509930",
"packages": [
{
"name": "beberlei/DoctrineExtensions",
@@ -9947,6 +9947,139 @@
"description": "PHPStan - PHP Static Analysis Tool",
"time": "2019-05-19T17:36:42+00:00"
},
+ {
+ "name": "phpstan/phpstan-doctrine",
+ "version": "0.11.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-doctrine.git",
+ "reference": "d625bfba7a15c9224568ccedc5955a0730cb5d1d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/d625bfba7a15c9224568ccedc5955a0730cb5d1d",
+ "reference": "d625bfba7a15c9224568ccedc5955a0730cb5d1d",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^4.0",
+ "php": "~7.1",
+ "phpstan/phpdoc-parser": "^0.3",
+ "phpstan/phpstan": "^0.11.7"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.0",
+ "doctrine/common": "<2.7",
+ "doctrine/mongodb-odm": "<1.2",
+ "doctrine/orm": "<2.5"
+ },
+ "require-dev": {
+ "consistence/coding-standard": "^3.8",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
+ "doctrine/collections": "^1.0",
+ "doctrine/common": "^2.7",
+ "doctrine/mongodb-odm": "^1.2",
+ "doctrine/orm": "^2.5",
+ "jakub-onderka/php-parallel-lint": "^1.0",
+ "phing/phing": "^2.16.0",
+ "phpstan/phpstan-phpunit": "^0.11",
+ "phpstan/phpstan-strict-rules": "^0.11",
+ "phpunit/phpunit": "^7.0",
+ "slevomat/coding-standard": "^5.0.4"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.11-dev"
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Doctrine extensions for PHPStan",
+ "time": "2019-05-21T10:47:03+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-symfony",
+ "version": "0.11.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-symfony.git",
+ "reference": "c7be3054c21fd472a52b1c38eb129c3f93776084"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/c7be3054c21fd472a52b1c38eb129c3f93776084",
+ "reference": "c7be3054c21fd472a52b1c38eb129c3f93776084",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "nikic/php-parser": "^4.0",
+ "php": "^7.1",
+ "phpstan/phpstan": "^0.11.7"
+ },
+ "conflict": {
+ "symfony/framework-bundle": "<3.0"
+ },
+ "require-dev": {
+ "consistence/coding-standard": "^3.0.1",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
+ "jakub-onderka/php-parallel-lint": "^1.0",
+ "nette/di": "^3.0-stable",
+ "phing/phing": "^2.16.0",
+ "phpstan/phpstan-phpunit": "^0.11",
+ "phpstan/phpstan-strict-rules": "^0.11",
+ "phpunit/phpunit": "^7.0",
+ "slevomat/coding-standard": "^4.5.2",
+ "squizlabs/php_codesniffer": "^3.3.2",
+ "symfony/console": "^3.0 || ^4.0",
+ "symfony/framework-bundle": "^3.0 || ^4.0",
+ "symfony/messenger": "^4.2",
+ "symfony/serializer": "^3.0 || ^4.0"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.11-dev"
+ },
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Lukáš Unger",
+ "email": "looky.msc@gmail.com",
+ "homepage": "https://lookyman.net"
+ }
+ ],
+ "description": "Symfony Framework extensions and rules for PHPStan",
+ "time": "2019-05-19T17:40:25+00:00"
+ },
{
"name": "phpunit/php-code-coverage",
"version": "6.1.4",
@@ -11207,7 +11340,8 @@
"ext-intl": "*",
"ext-mbstring": "*",
"ext-pdo": "*",
- "ext-zip": "*"
+ "ext-zip": "*",
+ "ext-json": "*"
},
"platform-dev": []
}
diff --git a/phpstan.neon b/phpstan.neon
index 2df7a8e9..fdb6dab8 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,4 +1,20 @@
+includes:
+ - vendor/phpstan/phpstan-symfony/extension.neon
+ - vendor/phpstan/phpstan-doctrine/extension.neon
+
parameters:
autoload_directories:
- %rootDir%/../../../src/Migrations
-
+# symfony:
+# container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml'
+ ignoreErrors:
+ - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::scalarNode\(\).#'
+ - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#'
+ - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::useAttributeAsKey\(\).#'
+ - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::addDefaultsIfNotSet\(\).#'
+ - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::requiresAtLeastOneElement\(\).#'
+ - '#Access to an undefined property Faker\\Generator::\$stateAbbr.#'
+ - '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
+ - '#Access to an undefined property Faker\\Generator::\$bs.#'
+ excludes_analyse:
+ - %rootDir%/../../../src/Command/KimaiImporterCommand.php
diff --git a/src/API/TagController.php b/src/API/TagController.php
index e78cd361..cc69e182 100644
--- a/src/API/TagController.php
+++ b/src/API/TagController.php
@@ -47,6 +47,8 @@ class TagController extends BaseApiController
}
/**
+ * Fetch all existing tags
+ *
* @SWG\Response(
* response=200,
* description="Returns the collection of all existing tags as string array",
@@ -65,9 +67,7 @@ class TagController extends BaseApiController
$filter = $paramFetcher->get('name');
$data = $this->repository->findAllTagNames($filter);
- if (null === $data) {
- $data = [];
- }
+
$view = new View($data, 200);
$view->getContext()->setGroups(['Default', 'Collection']);
@@ -75,7 +75,7 @@ class TagController extends BaseApiController
}
/**
- * Delete an existing tag
+ * Delete a tag
*
* @SWG\Delete(
* @SWG\Response(
diff --git a/src/Command/ConvertTimezoneCommand.php b/src/Command/ConvertTimezoneCommand.php
index c67e4415..fe019db9 100644
--- a/src/Command/ConvertTimezoneCommand.php
+++ b/src/Command/ConvertTimezoneCommand.php
@@ -125,7 +125,7 @@ class ConvertTimezoneCommand extends Command
if ('y' !== $answer) {
$io->text('Aborting.');
- return;
+ return 1;
}
$utc = new \DateTimeZone('UTC');
@@ -145,5 +145,7 @@ class ConvertTimezoneCommand extends Command
$io->writeln('. (' . $i . '/' . $amount . ')');
$io->writeln('');
+
+ return 0;
}
}
diff --git a/src/Command/CreateUserCommand.php b/src/Command/CreateUserCommand.php
index 2a843111..e197afce 100644
--- a/src/Command/CreateUserCommand.php
+++ b/src/Command/CreateUserCommand.php
@@ -10,7 +10,6 @@
namespace App\Command;
use App\Entity\User;
-use Doctrine\Bundle\DoctrineBundle\Registry;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
@@ -19,7 +18,6 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -29,11 +27,11 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
class CreateUserCommand extends Command
{
/**
- * @var UserPasswordEncoder
+ * @var UserPasswordEncoderInterface
*/
protected $encoder;
/**
- * @var Registry
+ * @var RegistryInterface
*/
protected $doctrine;
/**
@@ -124,7 +122,7 @@ class CreateUserCommand extends Command
);
}
- return;
+ return 1;
}
try {
@@ -132,10 +130,14 @@ class CreateUserCommand extends Command
$entityManager->persist($user);
$entityManager->flush();
$io->success('Success! Created user: ' . $user->getUsername());
+
+ return 0;
} catch (\Exception $ex) {
$io->error('Failed to create user: ' . $user->getUsername());
$io->error('Reason: ' . $ex->getMessage());
}
+
+ return 2;
}
/**
diff --git a/src/Command/KimaiImporterCommand.php b/src/Command/KimaiImporterCommand.php
index 9b19f4e3..49916218 100644
--- a/src/Command/KimaiImporterCommand.php
+++ b/src/Command/KimaiImporterCommand.php
@@ -29,7 +29,6 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -48,7 +47,7 @@ class KimaiImporterCommand extends Command
/**
* Create the user default passwords
- * @var UserPasswordEncoder
+ * @var UserPasswordEncoderInterface
*/
protected $encoder;
/**
@@ -389,7 +388,7 @@ class KimaiImporterCommand extends Command
/**
* Thanks to "xelozz -at- gmail.com", see http://php.net/manual/en/function.memory-get-usage.php#96280
- * @param $size
+ * @param int $size
* @return string
*/
protected function bytesHumanReadable($size)
@@ -402,7 +401,7 @@ class KimaiImporterCommand extends Command
}
/**
- * @param $table
+ * @param string $table
* @param array $where
* @return array
*/
@@ -429,7 +428,7 @@ class KimaiImporterCommand extends Command
/**
* @param SymfonyStyle $io
- * @param $object
+ * @param object $object
* @return bool
*/
protected function validateImport(SymfonyStyle $io, $object)
diff --git a/src/Command/ResetCommand.php b/src/Command/ResetCommand.php
index 43465fa8..716a7890 100644
--- a/src/Command/ResetCommand.php
+++ b/src/Command/ResetCommand.php
@@ -10,6 +10,7 @@
namespace App\Command;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -129,6 +130,7 @@ EOT
return true;
}
+ /** @var QuestionHelper $questionHelper */
$questionHelper = $this->getHelperSet()->get('question');
$question = new ConfirmationQuestion('' . $question . '', $default);
diff --git a/src/Command/VersionCommand.php b/src/Command/VersionCommand.php
index 3bdb4b40..f2311ae3 100644
--- a/src/Command/VersionCommand.php
+++ b/src/Command/VersionCommand.php
@@ -47,27 +47,29 @@ class VersionCommand extends Command
if ($input->getOption('semver')) {
$io->writeln(Constants::VERSION . '-' . Constants::STATUS);
- return;
+ return 0;
}
if ($input->getOption('short')) {
$io->writeln(Constants::VERSION);
- return;
+ return 0;
}
if ($input->getOption('name')) {
$io->writeln(Constants::NAME);
- return;
+ return 0;
}
if ($input->getOption('candidate')) {
$io->writeln(Constants::STATUS);
- return;
+ return 0;
}
$io->writeln('Kimai 2 - ' . Constants::VERSION . ' ' . Constants::STATUS . ' (' . Constants::NAME . ') by Kevin Papst and contributors.');
+
+ return 0;
}
}
diff --git a/src/Controller/ActivityController.php b/src/Controller/ActivityController.php
index 941606c9..d307e23b 100644
--- a/src/Controller/ActivityController.php
+++ b/src/Controller/ActivityController.php
@@ -31,11 +31,18 @@ use Symfony\Component\Routing\Annotation\Route;
class ActivityController extends AbstractController
{
/**
- * @return \App\Repository\ActivityRepository
+ * @var ActivityRepository
*/
- protected function getRepository()
+ private $repository;
+
+ public function __construct(ActivityRepository $repository)
{
- return $this->getDoctrine()->getRepository(Activity::class);
+ $this->repository = $repository;
+ }
+
+ protected function getRepository(): ActivityRepository
+ {
+ return $this->repository;
}
/**
diff --git a/src/Controller/InvoiceController.php b/src/Controller/InvoiceController.php
index 395d5118..bee386bf 100644
--- a/src/Controller/InvoiceController.php
+++ b/src/Controller/InvoiceController.php
@@ -45,12 +45,11 @@ class InvoiceController extends AbstractController
* @var TimesheetRepository
*/
protected $timesheetRepository;
+ /**
+ * @var UserDateTimeFactory
+ */
protected $dateTimeFactory;
- /**
- * @param ServiceInvoice $service
- * @param InvoiceTemplateRepository $invoice
- */
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, UserDateTimeFactory $dateTimeFactory)
{
$this->service = $service;
@@ -222,7 +221,7 @@ class InvoiceController extends AbstractController
* @Route(path="/template/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_invoice_template_paginated", methods={"GET", "POST"})
* @Security("is_granted('view_invoice_template')")
*
- * @param $page
+ * @param int $page
* @return \Symfony\Component\HttpFoundation\Response
*/
public function listTemplateAction($page)
diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php
index a71ffc66..a8b871ae 100644
--- a/src/Controller/ProfileController.php
+++ b/src/Controller/ProfileController.php
@@ -9,7 +9,6 @@
namespace App\Controller;
-use App\Entity\Timesheet;
use App\Entity\User;
use App\Event\PrepareUserEvent;
use App\Form\UserApiTokenType;
@@ -67,9 +66,19 @@ class ProfileController extends AbstractController
* @Route(path="/{username}", name="user_profile", methods={"GET"})
* @Security("is_granted('view', profile)")
*/
- public function indexAction(User $profile)
+ public function indexAction(User $profile, TimesheetRepository $repository)
{
- return $this->renderProfileView($profile, 'charts', 'user/stats.html.twig', []);
+ $userStats = $repository->getUserStatistics($profile);
+ $monthlyStats = $repository->getMonthlyStats($profile);
+
+ $viewVars = [
+ 'tab' => 'charts',
+ 'user' => $profile,
+ 'stats' => $userStats,
+ 'years' => $monthlyStats,
+ ];
+
+ return $this->render('user/stats.html.twig', $viewVars);
}
/**
@@ -271,23 +280,6 @@ class ProfileController extends AbstractController
]);
}
- protected function renderProfileView(User $user, string $tab, string $template, array $vars)
- {
- /* @var $timesheetRepo TimesheetRepository */
- $timesheetRepo = $this->getDoctrine()->getRepository(Timesheet::class);
- $userStats = $timesheetRepo->getUserStatistics($user);
- $monthlyStats = $timesheetRepo->getMonthlyStats($user);
-
- $viewVars = [
- 'tab' => $tab,
- 'user' => $user,
- 'stats' => $userStats,
- 'years' => $monthlyStats,
- ];
-
- return $this->render($template, array_merge($viewVars, $vars));
- }
-
/**
* @param User $user
* @return \Symfony\Component\Form\FormInterface
diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php
index 004c1d0c..dc961632 100644
--- a/src/Controller/ProjectController.php
+++ b/src/Controller/ProjectController.php
@@ -31,11 +31,18 @@ use Symfony\Component\Routing\Annotation\Route;
class ProjectController extends AbstractController
{
/**
- * @return \App\Repository\ProjectRepository
+ * @var ProjectRepository
*/
- protected function getRepository()
+ private $repository;
+
+ public function __construct(ProjectRepository $repository)
{
- return $this->getDoctrine()->getRepository(Project::class);
+ $this->repository = $repository;
+ }
+
+ protected function getRepository(): ProjectRepository
+ {
+ return $this->repository;
}
/**
@@ -64,7 +71,7 @@ class ProjectController extends AbstractController
}
/* @var $entries Pagerfanta */
- $entries = $this->getDoctrine()->getRepository(Project::class)->findByQuery($query);
+ $entries = $this->getRepository()->findByQuery($query);
return $this->render('project/index.html.twig', [
'entries' => $entries,
diff --git a/src/Controller/TimesheetAbstractController.php b/src/Controller/TimesheetAbstractController.php
index 31974c48..3897c6a3 100644
--- a/src/Controller/TimesheetAbstractController.php
+++ b/src/Controller/TimesheetAbstractController.php
@@ -17,6 +17,7 @@ use App\Form\Toolbar\TimesheetToolbarForm;
use App\Repository\ActivityRepository;
use App\Repository\ProjectRepository;
use App\Repository\Query\TimesheetQuery;
+use App\Repository\TagRepository;
use App\Repository\TimesheetRepository;
use App\Timesheet\UserDateTimeFactory;
use Doctrine\Common\Collections\ArrayCollection;
@@ -35,15 +36,16 @@ abstract class TimesheetAbstractController extends AbstractController
* @var TimesheetConfiguration
*/
protected $configuration;
-
/**
- * @param UserDateTimeFactory $dateTime
- * @param TimesheetConfiguration $configuration
+ * @var TimesheetRepository
*/
- public function __construct(UserDateTimeFactory $dateTime, TimesheetConfiguration $configuration)
+ protected $repository;
+
+ public function __construct(UserDateTimeFactory $dateTime, TimesheetConfiguration $configuration, TimesheetRepository $repository)
{
$this->dateTime = $dateTime;
$this->configuration = $configuration;
+ $this->repository = $repository;
}
/**
@@ -59,7 +61,7 @@ abstract class TimesheetAbstractController extends AbstractController
*/
protected function getRepository()
{
- return $this->getDoctrine()->getRepository(Timesheet::class);
+ return $this->repository;
}
protected function index($page, Request $request, string $renderTemplate)
@@ -86,9 +88,11 @@ abstract class TimesheetAbstractController extends AbstractController
$tags = $query->getTags(true);
if (!empty($tags)) {
+ /** @var TagRepository $tagRepo */
+ $tagRepo = $this->getDoctrine()->getRepository(Tag::class);
$query->setTags(
new ArrayCollection(
- $this->getDoctrine()->getRepository(Tag::class)->findIdsByTagNameList(implode(',', $tags))
+ $tagRepo->findIdsByTagNameList(implode(',', $tags))
)
);
}
diff --git a/src/Controller/TimesheetTeamController.php b/src/Controller/TimesheetTeamController.php
index 654620ff..2776edda 100644
--- a/src/Controller/TimesheetTeamController.php
+++ b/src/Controller/TimesheetTeamController.php
@@ -28,7 +28,7 @@ class TimesheetTeamController extends TimesheetAbstractController
* @Route(path="/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_timesheet_paginated", methods={"GET"})
* @Security("is_granted('view_other_timesheet')")
*
- * @param $page
+ * @param int $page
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php
index ec8a243f..b764299c 100644
--- a/src/Controller/UserController.php
+++ b/src/Controller/UserController.php
@@ -9,11 +9,12 @@
namespace App\Controller;
-use App\Entity\Timesheet;
use App\Entity\User;
use App\Form\Toolbar\UserToolbarForm;
use App\Form\UserCreateType;
use App\Repository\Query\UserQuery;
+use App\Repository\TimesheetRepository;
+use App\Repository\UserRepository;
use App\Security\RolePermissionManager;
use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
@@ -33,21 +34,23 @@ class UserController extends AbstractController
* @var UserPasswordEncoderInterface
*/
protected $encoder;
-
/**
- * @param UserPasswordEncoderInterface $encoder
+ * @var UserRepository
*/
- public function __construct(UserPasswordEncoderInterface $encoder)
+ protected $repository;
+
+ public function __construct(UserPasswordEncoderInterface $encoder, UserRepository $repository)
{
$this->encoder = $encoder;
+ $this->repository = $repository;
}
/**
- * @return \App\Repository\UserRepository
+ * @return UserRepository
*/
protected function getRepository()
{
- return $this->getDoctrine()->getRepository(User::class);
+ return $this->repository;
}
/**
@@ -133,13 +136,14 @@ class UserController extends AbstractController
*
* @param User $userToDelete
* @param Request $request
+ * @param TimesheetRepository $repository
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws \Doctrine\ORM\NonUniqueResultException
*/
- public function deleteAction(User $userToDelete, Request $request)
+ public function deleteAction(User $userToDelete, Request $request, TimesheetRepository $repository)
{
// $userToDelete MUST not be called $user, as $user is always the current user!
- $stats = $this->getDoctrine()->getRepository(Timesheet::class)->getUserStatistics($userToDelete);
+ $stats = $repository->getUserStatistics($userToDelete);
$deleteForm = $this->createFormBuilder(null, [
'attr' => [
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index ca9e7ed7..ea25790d 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -12,6 +12,7 @@ namespace App\DependencyInjection;
use App\Model\DashboardSection;
use App\Model\Widget;
use App\Timesheet\Rounding\RoundingInterface;
+use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
@@ -28,6 +29,7 @@ class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('kimai');
+ /** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();
$rootNode
@@ -69,6 +71,7 @@ class Configuration implements ConfigurationInterface
protected function getTimesheetNode()
{
$builder = new TreeBuilder('timesheet');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -195,6 +198,7 @@ class Configuration implements ConfigurationInterface
protected function getInvoiceNode()
{
$builder = new TreeBuilder('invoice');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -217,6 +221,7 @@ class Configuration implements ConfigurationInterface
protected function getLanguagesNode()
{
$builder = new TreeBuilder('languages');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -240,6 +245,7 @@ class Configuration implements ConfigurationInterface
protected function getCalendarNode()
{
$builder = new TreeBuilder('calendar');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -292,6 +298,7 @@ class Configuration implements ConfigurationInterface
protected function getThemeNode()
{
$builder = new TreeBuilder('theme');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -320,6 +327,7 @@ class Configuration implements ConfigurationInterface
protected function getUserNode()
{
$builder = new TreeBuilder('user');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -340,6 +348,7 @@ class Configuration implements ConfigurationInterface
protected function getWidgetsNode()
{
$builder = new TreeBuilder('widgets');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -371,6 +380,7 @@ class Configuration implements ConfigurationInterface
protected function getDashboardNode()
{
$builder = new TreeBuilder('dashboard');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -404,6 +414,7 @@ class Configuration implements ConfigurationInterface
protected function getDefaultsNode()
{
$builder = new TreeBuilder('defaults');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
@@ -426,6 +437,7 @@ class Configuration implements ConfigurationInterface
protected function getPermissionsNode()
{
$builder = new TreeBuilder('permissions');
+ /** @var ArrayNodeDefinition $rootNode */
$node = $builder->getRootNode();
$node
diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php
index 1827d822..4fd7d827 100644
--- a/src/Entity/Timesheet.php
+++ b/src/Entity/Timesheet.php
@@ -124,7 +124,7 @@ class Timesheet
private $exported = false;
/**
- * @var \App\Entity\Tag[]
+ * @var Tag[]|ArrayCollection
*
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="timesheets", cascade={"persist"})
* @ORM\JoinTable(
@@ -201,7 +201,7 @@ class Timesheet
}
/**
- * @return \DateTime
+ * @return \DateTime|null
*/
public function getEnd()
{
diff --git a/src/Event/ThemeEvent.php b/src/Event/ThemeEvent.php
index 3aaa2335..834d2ad1 100644
--- a/src/Event/ThemeEvent.php
+++ b/src/Event/ThemeEvent.php
@@ -35,9 +35,6 @@ class ThemeEvent extends Event
*/
protected $payload = null;
- /**
- * @param string $name
- */
public function __construct(User $user, $payload = null)
{
$this->user = $user;
diff --git a/src/EventSubscriber/RegistrationSubscriber.php b/src/EventSubscriber/RegistrationSubscriber.php
index d4d9d9a5..db441e6b 100644
--- a/src/EventSubscriber/RegistrationSubscriber.php
+++ b/src/EventSubscriber/RegistrationSubscriber.php
@@ -49,7 +49,7 @@ class RegistrationSubscriber implements EventSubscriberInterface
*/
public function onRegistrationSuccess(FormEvent $event)
{
- /** @var $user \FOS\UserBundle\Model\UserInterface */
+ /** @var \FOS\UserBundle\Model\UserInterface $user */
$user = $event->getForm()->getData();
$roles = [User::ROLE_USER];
diff --git a/src/EventSubscriber/TimezoneSubscriber.php b/src/EventSubscriber/TimezoneSubscriber.php
index 7df32a43..bf7909a4 100644
--- a/src/EventSubscriber/TimezoneSubscriber.php
+++ b/src/EventSubscriber/TimezoneSubscriber.php
@@ -10,7 +10,6 @@
namespace App\EventSubscriber;
use App\Entity\User;
-use KevinPapst\AdminLTEBundle\Event\ShowUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
@@ -23,17 +22,11 @@ class TimezoneSubscriber implements EventSubscriberInterface
*/
protected $storage;
- /**
- * @param TokenStorageInterface $tokenStorage
- */
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->storage = $tokenStorage;
}
- /**
- * @return array
- */
public static function getSubscribedEvents(): array
{
return [
@@ -41,24 +34,18 @@ class TimezoneSubscriber implements EventSubscriberInterface
];
}
- /**
- * @param ShowUserEvent $event
- */
public function setTimezone(GetResponseEvent $event)
{
if (!$this->canHandleEvent()) {
return;
}
- /* @var $user User */
+ /** @var User $user */
$user = $this->storage->getToken()->getUser();
$timezone = $user->getPreferenceValue('timezone', date_default_timezone_get());
date_default_timezone_set($timezone);
}
- /**
- * @return bool
- */
protected function canHandleEvent(): bool
{
if (null === $this->storage->getToken()) {
diff --git a/src/Export/Renderer/AbstractSpreadsheetRenderer.php b/src/Export/Renderer/AbstractSpreadsheetRenderer.php
index e8a99493..4fa652dd 100644
--- a/src/Export/Renderer/AbstractSpreadsheetRenderer.php
+++ b/src/Export/Renderer/AbstractSpreadsheetRenderer.php
@@ -17,7 +17,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
-use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
abstract class AbstractSpreadsheetRenderer
{
@@ -58,7 +58,7 @@ abstract class AbstractSpreadsheetRenderer
}
/**
- * @param $amount
+ * @param int $amount
* @return mixed
*/
protected function getFormattedMoney($amount, $currency)
@@ -80,7 +80,7 @@ abstract class AbstractSpreadsheetRenderer
}
/**
- * @param $seconds
+ * @param int $seconds
* @return mixed
*/
protected function getFormattedDuration($seconds)
@@ -209,7 +209,7 @@ abstract class AbstractSpreadsheetRenderer
/**
* @param Spreadsheet $spreadsheet
* @return string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @throws \Exception
*/
abstract protected function saveSpreadsheet(Spreadsheet $spreadsheet): string;
}
diff --git a/src/Export/Renderer/CsvRenderer.php b/src/Export/Renderer/CsvRenderer.php
index 9b551b31..a6156c97 100644
--- a/src/Export/Renderer/CsvRenderer.php
+++ b/src/Export/Renderer/CsvRenderer.php
@@ -33,12 +33,16 @@ class CsvRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-csv');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Csv');
$writer->save($filename);
diff --git a/src/Export/Renderer/OdsRenderer.php b/src/Export/Renderer/OdsRenderer.php
index 08d920be..798aadcd 100644
--- a/src/Export/Renderer/OdsRenderer.php
+++ b/src/Export/Renderer/OdsRenderer.php
@@ -33,12 +33,16 @@ class OdsRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-ods');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Ods');
$writer->save($filename);
diff --git a/src/Export/Renderer/XlsxRenderer.php b/src/Export/Renderer/XlsxRenderer.php
index 11e53b95..1228d83b 100644
--- a/src/Export/Renderer/XlsxRenderer.php
+++ b/src/Export/Renderer/XlsxRenderer.php
@@ -33,12 +33,16 @@ class XlsxRenderer extends AbstractSpreadsheetRenderer implements RendererInterf
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet): string
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-export-xlsx');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save($filename);
diff --git a/src/Form/Extension/EnhancedChoiceTypeExtension.php b/src/Form/Extension/EnhancedChoiceTypeExtension.php
index dd86bc68..0c1221d1 100644
--- a/src/Form/Extension/EnhancedChoiceTypeExtension.php
+++ b/src/Form/Extension/EnhancedChoiceTypeExtension.php
@@ -28,9 +28,6 @@ class EnhancedChoiceTypeExtension extends AbstractTypeExtension
*/
protected $type = null;
- /**
- * @param null|string $type
- */
public function __construct(ThemeConfiguration $configuration)
{
$this->type = $configuration->getSelectPicker();
diff --git a/src/Form/Toolbar/AbstractToolbarForm.php b/src/Form/Toolbar/AbstractToolbarForm.php
index 2b7b22fa..eacfaf45 100644
--- a/src/Form/Toolbar/AbstractToolbarForm.php
+++ b/src/Form/Toolbar/AbstractToolbarForm.php
@@ -79,7 +79,7 @@ abstract class AbstractToolbarForm extends AbstractType
/**
* @param FormBuilderInterface $builder
- * @param null|string $label
+ * @param string $label
*/
protected function addVisibilityChoice(FormBuilderInterface $builder, string $label = 'label.visible')
{
diff --git a/src/Form/Type/ActivityType.php b/src/Form/Type/ActivityType.php
index 759f1375..f566621e 100644
--- a/src/Form/Type/ActivityType.php
+++ b/src/Form/Type/ActivityType.php
@@ -42,8 +42,8 @@ class ActivityType extends AbstractType
/**
* @param Activity $choiceValue
- * @param $key
- * @param $value
+ * @param string $key
+ * @param mixed $value
* @return array
*/
public function choiceAttr($choiceValue, $key, $value)
diff --git a/src/Form/Type/DateRangeType.php b/src/Form/Type/DateRangeType.php
index 198d14ae..64e225df 100644
--- a/src/Form/Type/DateRangeType.php
+++ b/src/Form/Type/DateRangeType.php
@@ -78,7 +78,7 @@ class DateRangeType extends AbstractType
* A better way would be to use the Intl NumberFormatter, but if that is not available
* and the Symfony polyfill is used, this method would not work properly.
*
- * @param $string
+ * @param string $string
* @return string
*/
protected function convertArabicPersian($string)
diff --git a/src/Form/Type/ProjectType.php b/src/Form/Type/ProjectType.php
index 40d07105..b2c9ec5e 100644
--- a/src/Form/Type/ProjectType.php
+++ b/src/Form/Type/ProjectType.php
@@ -24,8 +24,8 @@ class ProjectType extends AbstractType
{
/**
* @param Project $choiceValue
- * @param $key
- * @param $value
+ * @param string $key
+ * @param mixed $value
* @return array
*/
public function choiceAttr($choiceValue, $key, $value)
diff --git a/src/Form/Type/UserRoleType.php b/src/Form/Type/UserRoleType.php
index 624f811e..1bd3d44f 100644
--- a/src/Form/Type/UserRoleType.php
+++ b/src/Form/Type/UserRoleType.php
@@ -24,7 +24,6 @@ class UserRoleType extends AbstractType
protected $roles = [];
/**
- * UserRolesType constructor.
* @param string[] $roles
*/
public function __construct(array $roles = [])
@@ -38,11 +37,12 @@ class UserRoleType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$roles = [];
- /* @var string[] $value */
foreach ($this->roles as $key => $value) {
$roles[$key] = $key;
- foreach ($value as $value2) {
- $roles[$value2] = $value2;
+ if (is_array($value)) {
+ foreach ($value as $value2) {
+ $roles[$value2] = $value2;
+ }
}
}
diff --git a/src/Invoice/Renderer/AbstractRenderer.php b/src/Invoice/Renderer/AbstractRenderer.php
index dbc0bd28..a41e5753 100644
--- a/src/Invoice/Renderer/AbstractRenderer.php
+++ b/src/Invoice/Renderer/AbstractRenderer.php
@@ -11,7 +11,7 @@ namespace App\Invoice\Renderer;
use App\Twig\DateExtensions;
use App\Twig\Extensions;
-use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
abstract class AbstractRenderer
{
@@ -63,8 +63,8 @@ abstract class AbstractRenderer
}
/**
- * @param $amount
- * @param $currency
+ * @param int $amount
+ * @param string $currency
* @return string
*/
protected function getFormattedMoney($amount, $currency)
@@ -82,7 +82,7 @@ abstract class AbstractRenderer
}
/**
- * @param $seconds
+ * @param int $seconds
* @return mixed
*/
protected function getFormattedDuration($seconds)
diff --git a/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php b/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
index 99cd1100..3d772194 100644
--- a/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
+++ b/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php
@@ -19,9 +19,11 @@ use Symfony\Component\HttpFoundation\Response;
abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
{
/**
+ * Saves the Spreadhseet and returns the filename.
+ *
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
abstract protected function saveSpreadsheet(Spreadsheet $spreadsheet);
@@ -31,6 +33,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
* @param InvoiceDocument $document
* @param InvoiceModel $model
* @return Response
+ * @throws \Exception
*/
public function render(InvoiceDocument $document, InvoiceModel $model): Response
{
diff --git a/src/Invoice/Renderer/CsvRenderer.php b/src/Invoice/Renderer/CsvRenderer.php
index b3089bb9..cc4a7d6e 100644
--- a/src/Invoice/Renderer/CsvRenderer.php
+++ b/src/Invoice/Renderer/CsvRenderer.php
@@ -33,12 +33,16 @@ class CsvRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-csv');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Csv');
$writer->save($filename);
diff --git a/src/Invoice/Renderer/DocxRenderer.php b/src/Invoice/Renderer/DocxRenderer.php
index 92c07582..e4a2571c 100644
--- a/src/Invoice/Renderer/DocxRenderer.php
+++ b/src/Invoice/Renderer/DocxRenderer.php
@@ -14,6 +14,7 @@ use App\Invoice\RendererInterface;
use App\Model\InvoiceModel;
use PhpOffice\PhpWord\Escaper\Xml;
use PhpOffice\PhpWord\Exception\Exception as OfficeException;
+use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\TemplateProcessor;
use Symfony\Component\HttpFoundation\File\Stream;
use Symfony\Component\HttpFoundation\Response;
@@ -27,7 +28,7 @@ class DocxRenderer extends AbstractRenderer implements RendererInterface
*/
public function render(InvoiceDocument $document, InvoiceModel $model): Response
{
- \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(false);
+ Settings::setOutputEscapingEnabled(false);
$filename = basename($document->getFilename());
diff --git a/src/Invoice/Renderer/OdsRenderer.php b/src/Invoice/Renderer/OdsRenderer.php
index 230ff15b..3dde4dbd 100644
--- a/src/Invoice/Renderer/OdsRenderer.php
+++ b/src/Invoice/Renderer/OdsRenderer.php
@@ -33,12 +33,16 @@ class OdsRenderer extends AbstractSpreadsheetRenderer implements RendererInterfa
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-ods');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Ods');
$writer->save($filename);
diff --git a/src/Invoice/Renderer/RendererTrait.php b/src/Invoice/Renderer/RendererTrait.php
index 1f4fe5ea..bfeee5fe 100644
--- a/src/Invoice/Renderer/RendererTrait.php
+++ b/src/Invoice/Renderer/RendererTrait.php
@@ -56,7 +56,8 @@ trait RendererTrait
abstract protected function getFormattedTime(\DateTime $date);
/**
- * @param $amount
+ * @param int $amount
+ * @param string $currency
* @return mixed
*/
abstract protected function getFormattedMoney($amount, $currency);
@@ -68,7 +69,7 @@ trait RendererTrait
abstract protected function getFormattedMonthName(\DateTime $date);
/**
- * @param $seconds
+ * @param int $seconds
* @return mixed
*/
abstract protected function getFormattedDuration($seconds);
diff --git a/src/Invoice/Renderer/XlsxRenderer.php b/src/Invoice/Renderer/XlsxRenderer.php
index 0da8da3d..be2cc8c4 100644
--- a/src/Invoice/Renderer/XlsxRenderer.php
+++ b/src/Invoice/Renderer/XlsxRenderer.php
@@ -33,12 +33,16 @@ class XlsxRenderer extends AbstractSpreadsheetRenderer implements RendererInterf
/**
* @param Spreadsheet $spreadsheet
- * @return bool|string
- * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ * @return string
+ * @throws \Exception
*/
protected function saveSpreadsheet(Spreadsheet $spreadsheet)
{
$filename = tempnam(sys_get_temp_dir(), 'kimai-invoice-xlsx');
+ if (false === $filename) {
+ throw new \Exception('Could not open temporary file');
+ }
+
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save($filename);
diff --git a/src/Migrations/Version20180715160326.php b/src/Migrations/Version20180715160326.php
index 6b2834b8..7e7a4755 100644
--- a/src/Migrations/Version20180715160326.php
+++ b/src/Migrations/Version20180715160326.php
@@ -31,7 +31,6 @@ final class Version20180715160326 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function up(Schema $schema): void
@@ -82,7 +81,7 @@ final class Version20180715160326 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
+ * @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema): void
{
diff --git a/src/Migrations/Version20180730044139.php b/src/Migrations/Version20180730044139.php
index 774cb293..c43ebb47 100644
--- a/src/Migrations/Version20180730044139.php
+++ b/src/Migrations/Version20180730044139.php
@@ -30,7 +30,6 @@ final class Version20180730044139 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
*/
public function up(Schema $schema): void
{
@@ -63,7 +62,6 @@ final class Version20180730044139 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
*/
public function down(Schema $schema): void
{
diff --git a/src/Migrations/Version20180805183527.php b/src/Migrations/Version20180805183527.php
index 8860b8f8..7ac05502 100644
--- a/src/Migrations/Version20180805183527.php
+++ b/src/Migrations/Version20180805183527.php
@@ -22,7 +22,6 @@ final class Version20180805183527 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
*/
public function up(Schema $schema): void
{
@@ -44,7 +43,6 @@ final class Version20180805183527 extends AbstractMigration
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
*/
public function down(Schema $schema): void
{
diff --git a/src/Model/Statistic/Month.php b/src/Model/Statistic/Month.php
index e7a0f479..2837b88f 100644
--- a/src/Model/Statistic/Month.php
+++ b/src/Model/Statistic/Month.php
@@ -56,7 +56,7 @@ class Month
}
/**
- * @param $totalDuration
+ * @param int $totalDuration
* @return $this
*/
public function setTotalDuration($totalDuration)
@@ -75,7 +75,7 @@ class Month
}
/**
- * @param $totalRate
+ * @param int $totalRate
* @return $this
*/
public function setTotalRate($totalRate)
diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php
index 4aea6bd4..ba7165d0 100644
--- a/src/Repository/ActivityRepository.php
+++ b/src/Repository/ActivityRepository.php
@@ -22,7 +22,7 @@ use Pagerfanta\Pagerfanta;
class ActivityRepository extends AbstractRepository
{
/**
- * @param $id
+ * @param int $id
* @return null|Activity
*/
public function getById($id)
diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php
index 5ee6e960..bfd3341f 100644
--- a/src/Repository/CustomerRepository.php
+++ b/src/Repository/CustomerRepository.php
@@ -23,7 +23,7 @@ use Pagerfanta\Pagerfanta;
class CustomerRepository extends AbstractRepository
{
/**
- * @param $id
+ * @param int $id
* @return null|Customer
*/
public function getById($id)
diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php
index 58669a5c..a630df7e 100644
--- a/src/Repository/ProjectRepository.php
+++ b/src/Repository/ProjectRepository.php
@@ -26,7 +26,7 @@ use Pagerfanta\Pagerfanta;
class ProjectRepository extends AbstractRepository
{
/**
- * @param $id
+ * @param int $id
* @return null|Project
*/
public function getById($id)
diff --git a/src/Repository/Query/BaseQuery.php b/src/Repository/Query/BaseQuery.php
index 5826ec10..3c21cf75 100644
--- a/src/Repository/Query/BaseQuery.php
+++ b/src/Repository/Query/BaseQuery.php
@@ -140,7 +140,7 @@ class BaseQuery
}
/**
- * @param $resultType
+ * @param string $resultType
* @return $this
* @throws \InvalidArgumentException
*/
diff --git a/src/Repository/Query/TimesheetQuery.php b/src/Repository/Query/TimesheetQuery.php
index 48383efd..f3e41529 100644
--- a/src/Repository/Query/TimesheetQuery.php
+++ b/src/Repository/Query/TimesheetQuery.php
@@ -58,7 +58,7 @@ class TimesheetQuery extends ActivityQuery
/**
* @var iterable
*/
- protected $tags;
+ protected $tags = [];
public function __construct()
{
diff --git a/src/Repository/TimesheetRepository.php b/src/Repository/TimesheetRepository.php
index a0dbce98..8a1a0bf1 100644
--- a/src/Repository/TimesheetRepository.php
+++ b/src/Repository/TimesheetRepository.php
@@ -118,7 +118,7 @@ class TimesheetRepository extends AbstractRepository
}
/**
- * @param $select
+ * @param string $select
* @param User $user
* @return int
* @throws \Doctrine\ORM\NonUniqueResultException
@@ -290,7 +290,7 @@ class TimesheetRepository extends AbstractRepository
/**
* @param User $user
- * @param int $limit
+ * @param int $hardLimit
* @return int
* @throws RepositoryException
* @throws \Doctrine\ORM\ORMException
diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php
index ed3b8a15..8f5b90bc 100644
--- a/src/Repository/UserRepository.php
+++ b/src/Repository/UserRepository.php
@@ -16,7 +16,7 @@ use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
class UserRepository extends AbstractRepository implements UserLoaderInterface
{
/**
- * @param $id
+ * @param int $id
* @return null|User
*/
public function getById($id)
diff --git a/src/Timesheet/Rounding/RoundingInterface.php b/src/Timesheet/Rounding/RoundingInterface.php
index eebcffec..0be213f6 100644
--- a/src/Timesheet/Rounding/RoundingInterface.php
+++ b/src/Timesheet/Rounding/RoundingInterface.php
@@ -30,7 +30,7 @@ interface RoundingInterface
/**
* @param Timesheet $record
- * @param $minutes
+ * @param int $minutes
*/
public function roundDuration(Timesheet $record, $minutes);
}
diff --git a/src/Twig/Extensions.php b/src/Twig/Extensions.php
index 91cb807c..4bef727c 100644
--- a/src/Twig/Extensions.php
+++ b/src/Twig/Extensions.php
@@ -134,7 +134,7 @@ class Extensions extends AbstractExtension
}
/**
- * @param $object
+ * @param object $object
* @return null|string
*/
public function getClassName($object)
@@ -155,14 +155,24 @@ class Extensions extends AbstractExtension
*/
public function duration($duration, $format = null)
{
- $seconds = $duration;
+ if (null === $duration) {
+ $duration = 0;
+ }
+
if ($duration instanceof Timesheet) {
$seconds = $duration->getDuration();
if (null === $duration->getEnd()) {
$seconds = time() - $duration->getBegin()->getTimestamp();
}
+
+ $duration = $seconds;
}
+ return $this->formatDuration((int) $duration, $format);
+ }
+
+ protected function formatDuration(int $seconds, $format = null): string
+ {
if ($seconds < 0) {
return '?';
}
diff --git a/src/Utils/Duration.php b/src/Utils/Duration.php
index 59609d15..3bd0d6c8 100644
--- a/src/Utils/Duration.php
+++ b/src/Utils/Duration.php
@@ -24,9 +24,9 @@ class Duration
/**
* Transforms seconds into a duration string.
*
- * @param $seconds
+ * @param int $seconds
* @param string $format
- * @return string
+ * @return string|null
*/
public function format($seconds, $format = self::FORMAT_NO_SECONDS)
{
@@ -51,9 +51,9 @@ class Duration
/**
* @param string $duration
- * @return string
+ * @return int
*/
- public function parseDurationString($duration)
+ public function parseDurationString($duration): int
{
if (false !== stripos($duration, ':')) {
return $this->parseDuration($duration, self::FORMAT_COLON);
@@ -72,7 +72,7 @@ class Duration
* @return int
* @throws \InvalidArgumentException
*/
- public function parseDuration(string $duration, string $mode)
+ public function parseDuration(string $duration, string $mode): int
{
if (empty($duration)) {
return 0;
@@ -88,10 +88,10 @@ class Duration
}
$seconds = 0;
if (3 == count($parts)) {
- $seconds += array_pop($parts);
+ $seconds += (int) array_pop($parts);
}
- $seconds += $parts[1] * 60;
- $seconds += $parts[0] * 3600;
+ $seconds += (int) $parts[1] * 60;
+ $seconds += (int) $parts[0] * 3600;
break;
case self::FORMAT_NATURAL:
diff --git a/src/Utils/LocaleSettings.php b/src/Utils/LocaleSettings.php
index 1fb2dec9..0c96e01a 100644
--- a/src/Utils/LocaleSettings.php
+++ b/src/Utils/LocaleSettings.php
@@ -18,7 +18,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
class LocaleSettings
{
/**
- * @var array
+ * @var LanguageFormattings
*/
protected $formats;
diff --git a/src/Utils/ParsedownExtension.php b/src/Utils/ParsedownExtension.php
index 7793357a..cbf4a4da 100644
--- a/src/Utils/ParsedownExtension.php
+++ b/src/Utils/ParsedownExtension.php
@@ -39,7 +39,7 @@ class ParsedownExtension extends \Parsedown
* - changes any space to a hyphen.
* - If that is not unique, add "-1", "-2", "-3",... to make it unique
*
- * @param $text
+ * @param string $text
* @return string
*/
private function getIDfromText($text)
diff --git a/symfony.lock b/symfony.lock
index e9958f31..9978a44a 100644
--- a/symfony.lock
+++ b/symfony.lock
@@ -324,6 +324,12 @@
"phpstan/phpstan": {
"version": "0.11.7"
},
+ "phpstan/phpstan-doctrine": {
+ "version": "0.11.4"
+ },
+ "phpstan/phpstan-symfony": {
+ "version": "0.11.6"
+ },
"phpunit/php-code-coverage": {
"version": "5.3.0"
},
diff --git a/templates/export/index.html.twig b/templates/export/index.html.twig
index cca23c8e..6910431f 100644
--- a/templates/export/index.html.twig
+++ b/templates/export/index.html.twig
@@ -46,7 +46,7 @@
{{ tables.data_table_header(tableName, columns) }}
{% for entry in entries %}
{% set currency = entry.project.customer.currency %}
- {% set duration = entry.duration|duration() %}
+ {% set duration = entry.duration|duration %}
{% if entry.fixedRate is not null %}
{% set rate = entry.fixedRate %}
{% set duration = 1 %}
diff --git a/templates/invoice/index.html.twig b/templates/invoice/index.html.twig
index ae383ad4..a5571f4b 100644
--- a/templates/invoice/index.html.twig
+++ b/templates/invoice/index.html.twig
@@ -42,7 +42,7 @@
{{ tables.data_table_header(tableName, columns) }}
{% for entry in entries %}
- {% set duration = entry.duration|duration() %}
+ {% set duration = entry.duration|duration %}
{% if entry.fixedRate is not null %}
{% set rate = entry.fixedRate %}
{% set duration = 1 %}
diff --git a/templates/invoice/renderer/default.html.twig b/templates/invoice/renderer/default.html.twig
index c5f402c2..4f9fcb07 100644
--- a/templates/invoice/renderer/default.html.twig
+++ b/templates/invoice/renderer/default.html.twig
@@ -64,7 +64,7 @@
{% for entry in model.calculator.entries %}
- {% set duration = entry.duration|duration() %}
+ {% set duration = entry.duration|duration %}
{% if entry.fixedRate is not null %}
{% set rate = entry.fixedRate %}
{% set duration = 1 %}
diff --git a/templates/invoice/renderer/freelancer.html.twig b/templates/invoice/renderer/freelancer.html.twig
index 44d50fca..096c3344 100644
--- a/templates/invoice/renderer/freelancer.html.twig
+++ b/templates/invoice/renderer/freelancer.html.twig
@@ -77,7 +77,7 @@
{% for entry in model.calculator.entries %}
- {% set duration = entry.duration|duration() %}
+ {% set duration = entry.duration|duration %}
{% if entry.fixedRate is not null %}
{% set rate = entry.fixedRate %}
{% set duration = 1 %}
diff --git a/templates/macros/actions.html.twig b/templates/macros/actions.html.twig
index 9a456dc7..cb970159 100644
--- a/templates/macros/actions.html.twig
+++ b/templates/macros/actions.html.twig
@@ -247,7 +247,7 @@
{% endmacro %}
{% macro timesheet(timesheet, view) %}
- {%- filter spaceless -%}
+ {%- apply spaceless -%}
{% import "macros/widgets.html.twig" as widgets %}
{% set actions = {} %}
@@ -283,7 +283,7 @@
{% else %}
{{ widgets.entity_actions(event.payload.actions) }}
{% endif %}
- {%- endfilter -%}
+ {%- endapply -%}
{% endmacro %}
{% macro timesheets_team(view) %}
diff --git a/templates/macros/datatables.html.twig b/templates/macros/datatables.html.twig
index 2e2afd54..1768254f 100644
--- a/templates/macros/datatables.html.twig
+++ b/templates/macros/datatables.html.twig
@@ -31,7 +31,7 @@
{% endmacro %}
{% macro data_table_column_class(name, columns, column) %}
-{% spaceless %}
+{% apply spaceless %}
{% set class = '' %}
{% set always = false %}
@@ -67,7 +67,7 @@
{% if not class is empty %}
{{ class }}
{% endif %}
-{% endspaceless %}
+{% endapply %}
{% endmacro %}
{% macro data_table_header_options(name, columns, options) %}
diff --git a/templates/macros/widgets.html.twig b/templates/macros/widgets.html.twig
index 8adfb8b7..b2064732 100644
--- a/templates/macros/widgets.html.twig
+++ b/templates/macros/widgets.html.twig
@@ -180,7 +180,7 @@
{% endif %}
@@ -205,7 +205,7 @@
{% macro action_button(icon, values, type) %}
{%- import _self as macro -%}
- {%- filter spaceless -%}
+ {%- apply spaceless -%}
{% set id = null %}
{% set onclick = null %}
{% set modal = null %}
@@ -278,17 +278,17 @@
{%- endfor -%}
{%- endif -%}
>{% if title is not null %}{{ title }}{% else %}{{ macro.icon(icon) }}{% endif %}
- {% endfilter %}
+ {% endapply %}
{% endmacro %}
{% macro button_group(actions, type) %}
{%- import _self as macro -%}
- {%- filter spaceless -%}
+ {%- apply spaceless -%}
{%- for icon,values in actions %}
{{ macro.action_button(icon, values, type) }}
{% endfor -%}
- {% endfilter %}
+ {% endapply %}
{% endmacro %}
diff --git a/templates/user/index.html.twig b/templates/user/index.html.twig
index 17850e62..3fd0a31f 100644
--- a/templates/user/index.html.twig
+++ b/templates/user/index.html.twig
@@ -30,7 +30,7 @@
{% if entries.count == 0 %}
{{ widgets.callout('warning', 'error.no_entries_found') }}
{% else %}
- {{ tables.data_table_header(tableName, columns) }}
+ {{ tables.data_table_header(tableName, columns, false, 'kimai.userUpdate') }}
{% for entry in entries %}
diff --git a/templates/user/stats.html.twig b/templates/user/stats.html.twig
index 2b819f8e..661982dd 100644
--- a/templates/user/stats.html.twig
+++ b/templates/user/stats.html.twig
@@ -64,7 +64,6 @@
var userProfileChartCanvas{{ year }} = $("#userProfileChart{{ year }}").get(0).getContext("2d");
- {% if tab == 'charts' %}
var userProfileChart{{ year }} = new Chart(
userProfileChartCanvas{{ year }}, {
type: 'bar',
@@ -72,17 +71,6 @@
options: userProfileChartOptions()
}
);
- {% endif %}
-
- $('a[href="#charts"]').on('shown.bs.tab', function(event){
- var userProfileChart{{ year }} = new Chart(
- userProfileChartCanvas{{ year }}, {
- type: 'bar',
- data: userProfileChartData{{ year }},
- options: userProfileChartOptions()
- }
- );
- });
});
{% endblock %}
diff --git a/tests/Export/Renderer/AbstractRendererTest.php b/tests/Export/Renderer/AbstractRendererTest.php
index 3c6a48fd..17694372 100644
--- a/tests/Export/Renderer/AbstractRendererTest.php
+++ b/tests/Export/Renderer/AbstractRendererTest.php
@@ -23,7 +23,7 @@ use App\Utils\LocaleSettings;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
abstract class AbstractRendererTest extends KernelTestCase
{
diff --git a/tests/Invoice/Renderer/AbstractRendererTest.php b/tests/Invoice/Renderer/AbstractRendererTest.php
index 7232e287..bcbf348b 100644
--- a/tests/Invoice/Renderer/AbstractRendererTest.php
+++ b/tests/Invoice/Renderer/AbstractRendererTest.php
@@ -28,7 +28,7 @@ use App\Utils\LocaleSettings;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
abstract class AbstractRendererTest extends KernelTestCase
{
diff --git a/tests/Twig/ExtensionsTest.php b/tests/Twig/ExtensionsTest.php
index 366d77e8..917cda1a 100644
--- a/tests/Twig/ExtensionsTest.php
+++ b/tests/Twig/ExtensionsTest.php
@@ -196,7 +196,8 @@ class ExtensionsTest extends TestCase
$this->assertEquals('00:00 h', $sut->duration('0'));
$sut = $this->getSut($this->localeEn, 'en');
- $this->assertNull($sut->duration(null));
+
+ $this->assertEquals('00:00 h', $sut->duration(null));
}
protected function getTimesheet($seconds)