Use DatetimeImmutable instead of DateTime wherever possible

This commit is contained in:
Jan Böhmer 2024-06-22 17:36:54 +02:00
parent eebc373734
commit 235d572f8c
39 changed files with 222 additions and 112 deletions

View file

@ -9,10 +9,17 @@ doctrine:
# either here or in the DATABASE_URL env var (see .env file)
types:
# UTC datetimes
datetime:
class: App\Doctrine\Types\UTCDateTimeType
date:
class: App\Doctrine\Types\UTCDateTimeType
datetime_immutable:
class: App\Doctrine\Types\UTCDateTimeImmutableType
date_immutable:
class: App\Doctrine\Types\UTCDateTimeImmutableType
big_decimal:
class: App\Doctrine\Types\BigDecimalType
tinyint:

View file

@ -75,7 +75,7 @@ class APITokenFixtures extends Fixture implements DependentFixtureInterface
$expired_token->setUser($admin_user);
$expired_token->setLevel(ApiTokenLevel::FULL);
$expired_token->setName('expired');
$expired_token->setValidUntil(new \DateTime('-1 day'));
$expired_token->setValidUntil(new \DateTimeImmutable('-1 day'));
$this->setTokenSecret($expired_token, self::TOKEN_EXPIRED);
$manager->persist($expired_token);

View file

@ -99,7 +99,7 @@ class PartFixtures extends Fixture implements DependentFixtureInterface
$part->addPartLot($partLot1);
$partLot2 = new PartLot();
$partLot2->setExpirationDate(new DateTime());
$partLot2->setExpirationDate(new \DateTimeImmutable());
$partLot2->setComment('Test');
$partLot2->setNeedsRefill(true);
$partLot2->setStorageLocation($manager->find(StorageLocation::class, 3));

View file

@ -47,7 +47,7 @@ class LocaleDateTimeColumn extends AbstractColumn
}
if (!$value instanceof DateTimeInterface) {
$value = new DateTime((string) $value);
$value = new \DateTimeImmutable((string) $value);
}
$formatValues = [

View file

@ -0,0 +1,97 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Doctrine\Types;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\DateTimeImmutableType;
use Doctrine\DBAL\Types\DateTimeType;
use Doctrine\DBAL\Types\Exception\InvalidFormat;
/**
* This DateTimeImmutableType all dates to UTC, so it can be later used with the timezones.
* Taken (and adapted) from here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/working-with-datetime.html.
*/
class UTCDateTimeImmutableType extends DateTimeImmutableType
{
private static ?DateTimeZone $utc_timezone = null;
/**
* {@inheritdoc}
*
* @param T $value
*
* @return (T is null ? null : string)
*
* @template T
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
if (!self::$utc_timezone instanceof \DateTimeZone) {
self::$utc_timezone = new DateTimeZone('UTC');
}
if ($value instanceof \DateTimeImmutable) {
$value = $value->setTimezone(self::$utc_timezone);
}
return parent::convertToDatabaseValue($value, $platform);
}
/**
* {@inheritDoc}
*
* @param T $value
*
* @template T
*/
public function convertToPHPValue($value, AbstractPlatform $platform): ?\DateTimeImmutable
{
if (!self::$utc_timezone instanceof \DateTimeZone) {
self::$utc_timezone = new DateTimeZone('UTC');
}
if (null === $value || $value instanceof \DateTimeImmutable) {
return $value;
}
$converted = \DateTimeImmutable::createFromFormat(
$platform->getDateTimeFormatString(),
$value,
self::$utc_timezone
);
if (!$converted) {
throw InvalidFormat::new(
$value,
static::class,
$platform->getDateTimeFormatString(),
);
}
return $converted;
}
}

View file

@ -185,9 +185,9 @@ abstract class Attachment extends AbstractNamedDBElement
protected ?AttachmentType $attachment_type = null;
#[Groups(['attachment:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['attachment:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
public function __construct()

View file

@ -135,9 +135,9 @@ class AttachmentType extends AbstractStructuralDBElement
protected Collection $attachments_with_type;
#[Groups(['attachment_type:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['attachment_type:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
public function __construct()

View file

@ -41,9 +41,9 @@ use Symfony\Component\Validator\Constraints as Assert;
abstract class AbstractCompany extends AbstractPartsContainingDBElement
{
#[Groups(['company:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['company:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
/**
* @var string The address of the company

View file

@ -34,28 +34,28 @@ use Symfony\Component\Serializer\Annotation\Groups;
trait TimestampTrait
{
/**
* @var \DateTime|null the date when this element was modified the last time
* @var \DateTimeImmutable|null the date when this element was modified the last time
*/
#[Groups(['extended', 'full'])]
#[ApiProperty(writable: false)]
#[ORM\Column(name: 'last_modified', type: Types::DATETIME_MUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])]
protected ?\DateTime $lastModified = null;
#[ORM\Column(name: 'last_modified', type: Types::DATETIME_IMMUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])]
protected ?\DateTimeImmutable $lastModified = null;
/**
* @var \DateTime|null the date when this element was created
* @var \DateTimeImmutable|null the date when this element was created
*/
#[Groups(['extended', 'full'])]
#[ApiProperty(writable: false)]
#[ORM\Column(name: 'datetime_added', type: Types::DATETIME_MUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])]
protected ?\DateTime $addedDate = null;
#[ORM\Column(name: 'datetime_added', type: Types::DATETIME_IMMUTABLE, options: ['default' => 'CURRENT_TIMESTAMP'])]
protected ?\DateTimeImmutable $addedDate = null;
/**
* Returns the last time when the element was modified.
* Returns null if the element was not yet saved to DB yet.
*
* @return \DateTimeInterface|null the time of the last edit
* @return \DateTimeImmutable|null the time of the last edit
*/
public function getLastModified(): ?\DateTimeInterface
public function getLastModified(): ?\DateTimeImmutable
{
return $this->lastModified;
}
@ -64,9 +64,9 @@ trait TimestampTrait
* Returns the date/time when the element was created.
* Returns null if the element was not yet saved to DB yet.
*
* @return \DateTimeInterface|null the creation time of the part
* @return \DateTimeImmutable|null the creation time of the part
*/
public function getAddedDate(): ?\DateTimeInterface
public function getAddedDate(): ?\DateTimeImmutable
{
return $this->addedDate;
}
@ -78,9 +78,9 @@ trait TimestampTrait
#[ORM\PreUpdate]
public function updateTimestamps(): void
{
$this->lastModified = new DateTime('now');
$this->lastModified = new \DateTimeImmutable('now');
if (null === $this->addedDate) {
$this->addedDate = new DateTime('now');
$this->addedDate = new \DateTimeImmutable('now');
}
}
}

View file

@ -25,7 +25,7 @@ namespace App\Entity\LogSystem;
use Doctrine\DBAL\Types\Types;
use App\Entity\Base\AbstractDBElement;
use App\Entity\UserSystem\User;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\LogEntryRepository;
@ -56,10 +56,10 @@ abstract class AbstractLogEntry extends AbstractDBElement
protected string $username = '';
/**
* @var \DateTimeInterface The datetime the event associated with this log entry has occured
* @var \DateTimeImmutable The datetime the event associated with this log entry has occured
*/
#[ORM\Column(name: 'datetime', type: Types::DATETIME_MUTABLE)]
protected \DateTimeInterface $timestamp;
#[ORM\Column(name: 'datetime', type: Types::DATETIME_IMMUTABLE)]
protected \DateTimeImmutable $timestamp;
/**
* @var LogLevel The priority level of the associated level. 0 is highest, 7 lowest
@ -90,7 +90,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
public function __construct()
{
$this->timestamp = new DateTime();
$this->timestamp = new \DateTimeImmutable();
}
/**
@ -165,7 +165,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
/**
* Returns the timestamp when the event that caused this log entry happened.
*/
public function getTimestamp(): \DateTimeInterface
public function getTimestamp(): \DateTimeImmutable
{
return $this->timestamp;
}
@ -175,7 +175,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
*
* @return $this
*/
public function setTimestamp(\DateTime $timestamp): self
public function setTimestamp(\DateTimeImmutable $timestamp): self
{
$this->timestamp = $timestamp;

View file

@ -92,7 +92,7 @@ class OAuthToken extends AbstractNamedDBElement implements AccessTokenInterface
return $this->token;
}
public function getExpirationDate(): ?\DateTimeInterface
public function getExpirationDate(): ?\DateTimeImmutable
{
return $this->expires_at;
}

View file

@ -183,9 +183,9 @@ class Category extends AbstractPartsContainingDBElement
protected Collection $parameters;
#[Groups(['category:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['category:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
#[Assert\Valid]
#[ORM\Embedded(class: EDACategoryInfo::class)]

View file

@ -134,9 +134,9 @@ class Footprint extends AbstractPartsContainingDBElement
protected Collection $parameters;
#[Groups(['footprint:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['footprint:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
#[Assert\Valid]
#[ORM\Embedded(class: EDAFootprintInfo::class)]

View file

@ -54,9 +54,9 @@ class InfoProviderReference
#[Groups(['provider_reference:read'])]
private ?string $provider_url = null;
#[Column(type: Types::DATETIME_MUTABLE, nullable: true, options: ['default' => null])]
#[Column(type: Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
#[Groups(['provider_reference:read'])]
private ?\DateTime $last_updated = null;
private ?\DateTimeImmutable $last_updated = null;
/**
* Constructing is forbidden from outside.
@ -95,9 +95,8 @@ class InfoProviderReference
/**
* Gets the time, when the part was last time updated by the provider.
* @return \DateTimeInterface|null
*/
public function getLastUpdated(): ?\DateTimeInterface
public function getLastUpdated(): ?\DateTimeImmutable
{
return $this->last_updated;
}
@ -140,7 +139,7 @@ class InfoProviderReference
$ref->provider_key = $provider_key;
$ref->provider_id = $provider_id;
$ref->provider_url = $provider_url;
$ref->last_updated = new \DateTime();
$ref->last_updated = new \DateTimeImmutable();
return $ref;
}
@ -155,7 +154,7 @@ class InfoProviderReference
$ref->provider_key = $dto->provider_key;
$ref->provider_id = $dto->provider_id;
$ref->provider_url = $dto->provider_url;
$ref->last_updated = new \DateTime();
$ref->last_updated = new \DateTimeImmutable();
return $ref;
}
}

View file

@ -156,9 +156,9 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
protected Collection $parameters;
#[Groups(['measurement_unit:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['measurement_unit:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
/**

View file

@ -154,9 +154,9 @@ class Part extends AttachmentContainingDBElement
protected ?Attachment $master_picture_attachment = null;
#[Groups(['part:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['part:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
public function __construct()

View file

@ -105,13 +105,13 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
protected string $comment = '';
/**
* @var \DateTime|null Set a time until when the lot must be used.
* @var \DateTimeImmutable|null Set a time until when the lot must be used.
* Set to null, if the lot can be used indefinitely.
*/
#[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(name: 'expiration_date', type: Types::DATETIME_MUTABLE, nullable: true)]
#[ORM\Column(name: 'expiration_date', type: Types::DATETIME_IMMUTABLE, nullable: true)]
#[Year2038BugWorkaround]
protected ?\DateTime $expiration_date = null;
protected ?\DateTimeImmutable $expiration_date = null;
/**
* @var StorageLocation|null The storelocation of this lot
@ -194,7 +194,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
}
//Check if the expiration date is bigger then current time
return $this->expiration_date < new DateTime('now');
return $this->expiration_date < new \DateTimeImmutable('now');
}
/**
@ -236,7 +236,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
/**
* Gets the expiration date for the part lot. Returns null, if no expiration date was set.
*/
public function getExpirationDate(): ?\DateTimeInterface
public function getExpirationDate(): ?\DateTimeImmutable
{
return $this->expiration_date;
}
@ -246,7 +246,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
*
*
*/
public function setExpirationDate(?\DateTime $expiration_date): self
public function setExpirationDate(?\DateTimeImmutable $expiration_date): self
{
$this->expiration_date = $expiration_date;

View file

@ -170,9 +170,9 @@ class StorageLocation extends AbstractPartsContainingDBElement
protected ?Attachment $master_picture_attachment = null;
#[Groups(['location:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['location:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
/********************************************************************************

View file

@ -156,9 +156,9 @@ class Currency extends AbstractStructuralDBElement
protected Collection $pricedetails;
#[Groups(['currency:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['currency:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
public function __construct()

View file

@ -46,7 +46,7 @@ use App\Entity\Contracts\NamedElementInterface;
use App\Entity\Contracts\TimeStampableInterface;
use App\Entity\Parts\Part;
use App\Entity\Parts\Supplier;
use DateTime;
use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@ -173,9 +173,9 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N
#[ORM\PreUpdate]
public function updateTimestamps(): void
{
$this->lastModified = new DateTime('now');
$this->lastModified = new DateTimeImmutable('now');
if (!$this->addedDate instanceof \DateTimeInterface) {
$this->addedDate = new DateTime('now');
$this->addedDate = new DateTimeImmutable('now');
}
if ($this->part instanceof Part) {

View file

@ -38,7 +38,7 @@ use App\Validator\Constraints\BigDecimal\BigDecimalPositive;
use App\Validator\Constraints\Selectable;
use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;
@ -141,9 +141,9 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
#[ORM\PreUpdate]
public function updateTimestamps(): void
{
$this->lastModified = new DateTime('now');
$this->lastModified = new DateTimeImmutable('now');
if (!$this->addedDate instanceof \DateTimeInterface) {
$this->addedDate = new DateTime('now');
$this->addedDate = new DateTimeImmutable('now');
}
if ($this->orderdetail instanceof Orderdetail) {

View file

@ -158,9 +158,9 @@ class Project extends AbstractStructuralDBElement
protected Collection $parameters;
#[Groups(['project:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['project:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
/********************************************************************************

View file

@ -75,10 +75,10 @@ class ApiToken implements TimeStampableInterface
#[Groups('token:read')]
private ?User $user = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
#[Groups('token:read')]
#[Year2038BugWorkaround]
private ?\DateTime $valid_until;
private ?\DateTimeImmutable $valid_until;
#[ORM\Column(length: 68, unique: true)]
private string $token;
@ -87,9 +87,9 @@ class ApiToken implements TimeStampableInterface
#[Groups('token:read')]
private ApiTokenLevel $level = ApiTokenLevel::READ_ONLY;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
#[Groups('token:read')]
private ?\DateTime $last_time_used = null;
private ?\DateTimeImmutable $last_time_used = null;
public function __construct(ApiTokenType $tokenType = ApiTokenType::PERSONAL_ACCESS_TOKEN)
{
@ -97,7 +97,7 @@ class ApiToken implements TimeStampableInterface
$this->token = $tokenType->getTokenPrefix() . bin2hex(random_bytes(32));
//By default, tokens are valid for 1 year.
$this->valid_until = new \DateTime('+1 year');
$this->valid_until = new \DateTimeImmutable('+1 year');
}
public function getTokenType(): ApiTokenType
@ -116,7 +116,7 @@ class ApiToken implements TimeStampableInterface
return $this;
}
public function getValidUntil(): ?\DateTimeInterface
public function getValidUntil(): ?\DateTimeImmutable
{
return $this->valid_until;
}
@ -127,10 +127,10 @@ class ApiToken implements TimeStampableInterface
*/
public function isValid(): bool
{
return $this->valid_until === null || $this->valid_until > new \DateTime();
return $this->valid_until === null || $this->valid_until > new \DateTimeImmutable();
}
public function setValidUntil(?\DateTime $valid_until): ApiToken
public function setValidUntil(?\DateTimeImmutable $valid_until): ApiToken
{
$this->valid_until = $valid_until;
return $this;
@ -159,19 +159,17 @@ class ApiToken implements TimeStampableInterface
/**
* Gets the last time the token was used to authenticate or null if it was never used.
* @return \DateTimeInterface|null
*/
public function getLastTimeUsed(): ?\DateTimeInterface
public function getLastTimeUsed(): ?\DateTimeImmutable
{
return $this->last_time_used;
}
/**
* Sets the last time the token was used to authenticate.
* @param \DateTime|null $last_time_used
* @return ApiToken
*/
public function setLastTimeUsed(?\DateTime $last_time_used): ApiToken
public function setLastTimeUsed(?\DateTimeImmutable $last_time_used): ApiToken
{
$this->last_time_used = $last_time_used;
return $this;

View file

@ -117,10 +117,10 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
protected ?int $id = null;
#[Groups(['user:read'])]
protected ?\DateTime $lastModified = null;
protected ?\DateTimeImmutable $lastModified = null;
#[Groups(['user:read'])]
protected ?\DateTime $addedDate = null;
protected ?\DateTimeImmutable $addedDate = null;
/**
* @var bool Determines if the user is disabled (user can not log in)
@ -277,11 +277,11 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
#[Groups(['user:read', 'user:write'])]
protected ?Attachment $master_picture_attachment = null;
/** @var \DateTimeInterface|null The time when the backup codes were generated
/** @var \DateTimeImmutable|null The time when the backup codes were generated
*/
#[Groups(['full'])]
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
protected ?\DateTimeInterface $backupCodesGenerationDate = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
protected ?\DateTimeImmutable $backupCodesGenerationDate = null;
/** @var Collection<int, LegacyU2FKeyInterface>
*/
@ -318,10 +318,10 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
protected ?PermissionData $permissions = null;
/**
* @var \DateTime|null the time until the password reset token is valid
* @var \DateTimeImmutable|null the time until the password reset token is valid
*/
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
protected ?\DateTime $pw_reset_expires = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
protected ?\DateTimeImmutable $pw_reset_expires = null;
/**
* @var bool True if the user was created by a SAML provider (and therefore cannot change its password)
@ -528,7 +528,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
/**
* Gets the datetime when the password reset token expires.
*/
public function getPwResetExpires(): \DateTimeInterface|null
public function getPwResetExpires(): \DateTimeImmutable|null
{
return $this->pw_reset_expires;
}
@ -536,7 +536,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
/**
* Sets the datetime when the password reset token expires.
*/
public function setPwResetExpires(\DateTime $pw_reset_expires): self
public function setPwResetExpires(\DateTimeImmutable $pw_reset_expires): self
{
$this->pw_reset_expires = $pw_reset_expires;
@ -897,7 +897,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
public function setBackupCodes(array $codes): self
{
$this->backupCodes = $codes;
$this->backupCodesGenerationDate = $codes === [] ? null : new DateTime();
$this->backupCodesGenerationDate = $codes === [] ? null : new \DateTimeImmutable();
return $this;
}
@ -905,7 +905,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
/**
* Return the date when the backup codes were generated.
*/
public function getBackupCodesGenerationDate(): ?\DateTimeInterface
public function getBackupCodesGenerationDate(): ?\DateTimeImmutable
{
return $this->backupCodesGenerationDate;
}

View file

@ -82,9 +82,8 @@ class WebauthnKey extends BasePublicKeyCredentialSource implements TimeStampable
/**
* Retrieve the last time when the key was used.
* @return \DateTimeInterface|null
*/
public function getLastTimeUsed(): ?\DateTimeInterface
public function getLastTimeUsed(): ?\DateTimeImmutable
{
return $this->last_time_used;
}

View file

@ -84,7 +84,7 @@ class LabelResponse extends Response
*/
public function setAutoLastModified(): LabelResponse
{
$this->setLastModified(new DateTime());
$this->setLastModified(new \DateTimeImmutable());
return $this;
}

View file

@ -75,7 +75,7 @@ class ApiTokenAuthenticator implements AuthenticatorInterface
$old_time = $token->getLastTimeUsed();
//Set the last used date of the token
$token->setLastTimeUsed(new \DateTime());
$token->setLastTimeUsed(new \DateTimeImmutable());
//Only flush the token if the last used date change is more than 10 minutes
//For performance reasons we don't want to flush the token every time it is used, but only if it is used more than 10 minutes after the last time it was used
//If a flush is later in the code we don't want to flush the token again

View file

@ -225,7 +225,7 @@ trait PKImportHelperTrait
protected function setCreationDate(TimeStampableInterface $entity, ?string $datetime_str): void
{
if ($datetime_str !== null && $datetime_str !== '' && $datetime_str !== '0000-00-00 00:00:00') {
$date = new \DateTime($datetime_str);
$date = new \DateTimeImmutable($datetime_str);
} else {
$date = null; //Null means "now" at persist time
}

View file

@ -97,7 +97,7 @@ final class LabelExampleElementsGenerator
$lot->setDescription('Example Lot');
$lot->setComment('Lot comment');
$lot->setExpirationDate(new DateTime('+1 days'));
$lot->setExpirationDate(new \DateTimeImmutable('+1 day'));
$lot->setStorageLocation($this->getStructuralData(StorageLocation::class));
$lot->setAmount(123);
$lot->setOwner($this->getUser());

View file

@ -81,7 +81,7 @@ final class GlobalProviders implements PlaceholderProviderInterface
return 'anonymous';
}
$now = new DateTime();
$now = new \DateTimeImmutable();
if ('[[DATETIME]]' === $placeholder) {
$formatter = IntlDateFormatter::create(

View file

@ -42,7 +42,6 @@ declare(strict_types=1);
namespace App\Services\LabelSystem\PlaceholderProviders;
use App\Entity\Contracts\TimeStampableInterface;
use DateTime;
use IntlDateFormatter;
use Locale;
@ -57,11 +56,11 @@ final class TimestampableElementProvider implements PlaceholderProviderInterface
$formatter = new IntlDateFormatter(Locale::getDefault(), IntlDateFormatter::SHORT, IntlDateFormatter::SHORT);
if ('[[LAST_MODIFIED]]' === $placeholder) {
return $formatter->format($label_target->getLastModified() ?? new DateTime());
return $formatter->format($label_target->getLastModified() ?? new \DateTimeImmutable());
}
if ('[[CREATION_DATE]]' === $placeholder) {
return $formatter->format($label_target->getAddedDate() ?? new DateTime());
return $formatter->format($label_target->getAddedDate() ?? new \DateTimeImmutable());
}
}

View file

@ -136,7 +136,7 @@ class LogDataFormatter
}
try {
$dateTime = new \DateTime($date, new \DateTimeZone($timezone));
$dateTime = new \DateTimeImmutable($date, new \DateTimeZone($timezone));
} catch (\Exception) {
return '<i>unknown DateTime format</i>';
}

View file

@ -34,11 +34,13 @@ use App\Repository\LogEntryRepository;
use Brick\Math\BigDecimal;
use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Exception;
use InvalidArgumentException;
use PHPUnit\Util\Type;
use ReflectionClass;
class TimeTravel
@ -171,17 +173,26 @@ class TimeTravel
/**
* This function decodes the array which is created during the json_encode of a datetime object and returns a DateTime object.
* @param array $input
* @return DateTime
* @return \DateTimeInterface
* @throws Exception
*/
private function dateTimeDecode(?array $input): ?\DateTime
private function dateTimeDecode(?array $input, string $doctrineType): ?\DateTimeInterface
{
//Allow null values
if ($input === null) {
return null;
}
return new \DateTime($input['date'], new \DateTimeZone($input['timezone']));
//Mutable types
if (in_array($doctrineType, [Types::DATETIME_MUTABLE, Types::DATE_MUTABLE], true)) {
return new \DateTime($input['date'], new \DateTimeZone($input['timezone']));
}
//Immutable types
if (in_array($doctrineType, [Types::DATETIME_IMMUTABLE, Types::DATE_IMMUTABLE], true)) {
return new \DateTimeImmutable($input['date'], new \DateTimeZone($input['timezone']));
}
throw new InvalidArgumentException('The given doctrine type is not a datetime type!');
}
/**
@ -208,8 +219,10 @@ class TimeTravel
$data = BigDecimal::of($data);
}
if (!$data instanceof DateTime && ('datetime' === $metadata->getFieldMapping($field)['type'])) {
$data = $this->dateTimeDecode($data);
if (!$data instanceof \DateTimeInterface
&& (in_array($metadata->getFieldMapping($field)['type'],
[Types::DATETIME_IMMUTABLE, Types::DATETIME_IMMUTABLE, Types::DATE_MUTABLE, Types::DATETIME_IMMUTABLE], true))) {
$data = $this->dateTimeDecode($data, $metadata->getFieldMapping($field)['type']);
}
$this->setField($element, $field, $data);

View file

@ -49,7 +49,7 @@ final class SidebarTreeUpdater
//This tag and therfore this whole cache gets cleared by TreeCacheInvalidationListener when a structural element is changed
$item->tag('sidebar_tree_update');
return new \DateTime();
return new \DateTimeImmutable();
});
}
}

View file

@ -59,8 +59,7 @@ class PasswordResetManager
$user->setPwResetToken($this->passwordEncoder->hash($unencrypted_token));
//Determine the expiration datetime of
$expiration_date = new DateTime();
$expiration_date->add(date_interval_create_from_date_string('1 day'));
$expiration_date = new \DateTimeImmutable("+1 day");
$user->setPwResetExpires($expiration_date);
if ($user->getEmail() !== null && $user->getEmail() !== '') {
@ -105,7 +104,7 @@ class PasswordResetManager
}
//Check if token is expired yet
if ($user->getPwResetExpires() < new DateTime()) {
if ($user->getPwResetExpires() < new \DateTimeImmutable()) {
return false;
}
@ -119,7 +118,7 @@ class PasswordResetManager
//Remove token
$user->setPwResetToken(null);
$user->setPwResetExpires(new DateTime());
$user->setPwResetExpires(new \DateTimeImmutable());
//Save to DB
$this->em->flush();

View file

@ -33,12 +33,11 @@ class PartLotTest extends TestCase
$lot = new PartLot();
$this->assertNull($lot->isExpired(), 'Lot must be return null when no Expiration date is set!');
$datetime = new DateTime();
$lot->setExpirationDate($datetime->setTimestamp(strtotime('now +1 hour')));
$lot->setExpirationDate(new \DateTimeImmutable('+1 hour'));
$this->assertFalse($lot->isExpired(), 'Lot with expiration date in the future must not be expired!');
$lot->setExpirationDate($datetime->setTimestamp(strtotime('now -1 hour')));
$lot->setExpirationDate(new \DateTimeImmutable('-1 hour'));
$this->assertTrue($lot->isExpired(), 'Lot with expiration date in the past must be expired!');
}
}

View file

@ -95,7 +95,7 @@ class PartTest extends TestCase
$part->addPartLot(
(new PartLot())
->setAmount(6)
->setExpirationDate($datetime->setTimestamp(strtotime('now -1 hour')))
->setExpirationDate(new \DateTimeImmutable('-1 hour'))
);
$this->assertEqualsWithDelta(13.0, $part->getAmountSum(), PHP_FLOAT_EPSILON);

View file

@ -73,7 +73,7 @@ class UserTest extends TestCase
$codes = ['test', 'invalid', 'test'];
$user->setBackupCodes($codes);
// Backup Codes generation date must be changed!
$this->assertInstanceOf(\DateTime::class, $user->getBackupCodesGenerationDate());
$this->assertNotNull($user->getBackupCodesGenerationDate());
$this->assertSame($codes, $user->getBackupCodes());
//Test what happens if we delete the backup keys

View file

@ -65,7 +65,7 @@ class PartLotProviderTest extends WebTestCase
$this->target = new PartLot();
$this->target->setDescription('Lot description');
$this->target->setComment('Lot comment');
$this->target->setExpirationDate(new \DateTime('1999-04-13'));
$this->target->setExpirationDate(new \DateTimeImmutable('1999-04-13'));
$this->target->setInstockUnknown(true);
$location = new StorageLocation();