phpstan level 3, fixed deprecations, code cleanup (#811)

This commit is contained in:
Kevin Papst
2019-05-28 12:24:21 +02:00
committed by GitHub
parent d9dca96a32
commit 393abe9e62
69 changed files with 381 additions and 189 deletions

View File

@@ -10,7 +10,6 @@ build:
tests:
override:
- js-scrutinizer-run
- php-scrutinizer-run
tests:
tests:
override:

View File

@@ -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)

View File

@@ -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"
},

138
composer.lock generated
View File

@@ -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": []
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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;
}
}

View File

@@ -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;
}
/**

View File

@@ -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)

View File

@@ -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>' . $question . '</question>', $default);

View File

@@ -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;
}
}

View File

@@ -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;
}
/**

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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))
)
);
}

View File

@@ -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
*/

View File

@@ -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' => [

View File

@@ -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

View File

@@ -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()
{

View File

@@ -35,9 +35,6 @@ class ThemeEvent extends Event
*/
protected $payload = null;
/**
* @param string $name
*/
public function __construct(User $user, $payload = null)
{
$this->user = $user;

View File

@@ -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];

View File

@@ -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()) {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -28,9 +28,6 @@ class EnhancedChoiceTypeExtension extends AbstractTypeExtension
*/
protected $type = null;
/**
* @param null|string $type
*/
public function __construct(ThemeConfiguration $configuration)
{
$this->type = $configuration->getSelectPicker();

View File

@@ -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')
{

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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)

View File

@@ -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
{

View File

@@ -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);

View File

@@ -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());

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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)

View File

@@ -22,7 +22,7 @@ use Pagerfanta\Pagerfanta;
class ActivityRepository extends AbstractRepository
{
/**
* @param $id
* @param int $id
* @return null|Activity
*/
public function getById($id)

View File

@@ -23,7 +23,7 @@ use Pagerfanta\Pagerfanta;
class CustomerRepository extends AbstractRepository
{
/**
* @param $id
* @param int $id
* @return null|Customer
*/
public function getById($id)

View File

@@ -26,7 +26,7 @@ use Pagerfanta\Pagerfanta;
class ProjectRepository extends AbstractRepository
{
/**
* @param $id
* @param int $id
* @return null|Project
*/
public function getById($id)

View File

@@ -140,7 +140,7 @@ class BaseQuery
}
/**
* @param $resultType
* @param string $resultType
* @return $this
* @throws \InvalidArgumentException
*/

View File

@@ -58,7 +58,7 @@ class TimesheetQuery extends ActivityQuery
/**
* @var iterable
*/
protected $tags;
protected $tags = [];
public function __construct()
{

View File

@@ -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

View File

@@ -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)

View File

@@ -30,7 +30,7 @@ interface RoundingInterface
/**
* @param Timesheet $record
* @param $minutes
* @param int $minutes
*/
public function roundDuration(Timesheet $record, $minutes);
}

View File

@@ -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 '?';
}

View File

@@ -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:

View File

@@ -18,7 +18,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
class LocaleSettings
{
/**
* @var array
* @var LanguageFormattings
*/
protected $formats;

View File

@@ -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)

View File

@@ -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"
},

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -64,7 +64,7 @@
</thead>
<tbody>
{% 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 %}

View File

@@ -77,7 +77,7 @@
</thead>
<tbody>
{% 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 %}

View File

@@ -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) %}

View File

@@ -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) %}

View File

@@ -180,7 +180,7 @@
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">{{ 'label.actions'|trans }}
<span class="fa fa-caret-down"></span></button>
<ul class="dropdown-menu dropdown-menu-right">
{%- filter spaceless -%}
{%- apply spaceless -%}
{%- for icon,values in actions %}
{% set class = '' %}
{% if icon == 'trash' %}
@@ -197,7 +197,7 @@
{% endif %}
</li>
{% endfor -%}
{% endfilter %}
{% endapply %}
</ul>
</div>
{% 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 %}</a>
{% endfilter %}
{% endapply %}
{% endmacro %}
{% macro button_group(actions, type) %}
{%- import _self as macro -%}
<div class="btn-group">
{%- filter spaceless -%}
{%- apply spaceless -%}
{%- for icon,values in actions %}
{{ macro.action_button(icon, values, type) }}
{% endfor -%}
{% endfilter %}
{% endapply %}
</div>
{% endmacro %}

View File

@@ -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 %}
<tr{% if is_granted('edit', entry) %} class="open-edit alternative-link" data-href="{{ path('user_profile_edit', {'username': entry.username}) }}"{% endif %}>

View File

@@ -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()
}
);
});
});
</script>
{% endblock %}

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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)