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 [![Latest Stable Version](https://poser.pugx.org/kevinpapst/kimai2/v/stable)](https://packagist.org/packages/kevinpapst/kimai2) [![License](https://poser.pugx.org/kevinpapst/kimai2/license)](https://packagist.org/packages/kevinpapst/kimai2) [![Travis Status](https://travis-ci.org/kevinpapst/kimai2.svg?branch=master)](https://travis-ci.org/kevinpapst/kimai2) -[![Code Quality](https://scrutinizer-ci.com/g/kevinpapst/kimai2/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/kevinpapst/kimai2/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/kevinpapst/kimai2/?branch=master) [![Scrutinizer Status](https://scrutinizer-ci.com/g/kevinpapst/kimai2/badges/build.png?b=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)