fix php 8.1 deprecations (#3648)

* show session maxlifetime in doctor
* rephrase compatibility in release notes
* bump version
This commit is contained in:
Kevin Papst
2022-11-24 13:12:18 +01:00
committed by GitHub
parent 2aa5eccc7c
commit 94a6b99c57
20 changed files with 125 additions and 57 deletions

View File

@@ -36,8 +36,11 @@ template: |
[Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://tobybatch.github.io/kimai2/) [Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://tobybatch.github.io/kimai2/)
**PHP Version compatibility:** **PHP Version compatibility:**
- PHP 7.3 is [end-of-life](https://www.php.net/supported-versions.php): please update now - PHP 7.3 is [end-of-life](https://www.php.net/supported-versions.php)
- PHP 7.4 is almost EOL, PHP 8 and PHP 8.1 are supported - PHP 7.4 is [end-of-life](https://www.php.net/supported-versions.php) in a few days!
- PHP 8.0 and PHP 8.1 are supported
**Next release will be PHP 8 ONLY, as [announced one year ago](https://www.kimai.org/blog/2021/sunsetting-php-7/).**
$CHANGES $CHANGES

View File

@@ -17,11 +17,11 @@ class Constants
/** /**
* The current release version * The current release version
*/ */
public const VERSION = '1.29.0'; public const VERSION = '1.29.1';
/** /**
* The current release: major * 10000 + minor * 100 + patch * The current release: major * 10000 + minor * 100 + patch
*/ */
public const VERSION_ID = 12900; public const VERSION_ID = 12901;
/** /**
* The current release status, either "stable" or "dev" * The current release status, either "stable" or "dev"
*/ */

View File

@@ -268,6 +268,7 @@ class DoctorController extends AbstractController
'post_max_size', 'post_max_size',
'sys_temp_dir', 'sys_temp_dir',
'date.timezone', 'date.timezone',
'session.gc_maxlifetime',
]; ];
$settings = []; $settings = [];

View File

@@ -35,7 +35,12 @@ final class InvoiceDocument
public function getFilename(): string public function getFilename(): string
{ {
return $this->file->getRealPath(); $path = $this->file->getRealPath();
if ($path === false) {
throw new \Exception('Invoice template got deleted from filesystem');
}
return $path;
} }
public function getFileExtension(): string public function getFileExtension(): string
@@ -45,6 +50,11 @@ final class InvoiceDocument
public function getLastChange(): int public function getLastChange(): int
{ {
return $this->file->getMTime(); $modified = $this->file->getMTime();
if ($modified === false) {
throw new \Exception('Invoice template got deleted from filesystem');
}
return $modified;
} }
} }

View File

@@ -53,7 +53,8 @@ class RedirectToLocaleSubscriber implements EventSubscriberInterface
// Ignore requests from referrers with the same HTTP host in order to prevent // Ignore requests from referrers with the same HTTP host in order to prevent
// changing language for users who possibly already selected it for this application. // changing language for users who possibly already selected it for this application.
if (0 === stripos($request->headers->get('referer'), $request->getSchemeAndHttpHost())) { $referer = $request->headers->get('referer');
if ($referer !== null && 0 === stripos($referer, $request->getSchemeAndHttpHost())) {
return; return;
} }

View File

@@ -68,6 +68,9 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
foreach ($row->getCellIterator() as $cell) { foreach ($row->getCellIterator() as $cell) {
$value = $cell->getValue(); $value = $cell->getValue();
$replacer = null; $replacer = null;
if ($value === null) {
continue;
}
$firstReplacerPos = stripos($value, '${'); $firstReplacerPos = stripos($value, '${');
if ($firstReplacerPos === false) { if ($firstReplacerPos === false) {
continue; continue;
@@ -134,7 +137,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer
$cellCounter = 0; $cellCounter = 0;
foreach ($row->getCellIterator() as $cell) { foreach ($row->getCellIterator() as $cell) {
$value = $cell->getValue(); $value = $cell->getValue();
if (stripos($value, '${entry.') !== false) { if ($value !== null && stripos($value, '${entry.') !== false) {
$startRow = $row->getRowIndex(); $startRow = $row->getRowIndex();
$worksheet->insertNewRowBefore($startRow + 1, $invoiceItemCount - 1); $worksheet->insertNewRowBefore($startRow + 1, $invoiceItemCount - 1);
break 2; break 2;

View File

@@ -11,6 +11,7 @@ namespace App\Invoice;
use App\Configuration\LanguageFormattings; use App\Configuration\LanguageFormattings;
use App\Constants; use App\Constants;
use App\Entity\Customer;
use App\Entity\Invoice; use App\Entity\Invoice;
use App\Entity\InvoiceDocument; use App\Entity\InvoiceDocument;
use App\Event\InvoiceCreatedEvent; use App\Event\InvoiceCreatedEvent;
@@ -580,7 +581,22 @@ final class ServiceInvoice
} }
uasort($customerEntries, function ($a, $b) { uasort($customerEntries, function ($a, $b) {
return strcmp($a['customer']->getName(), $b['customer']->getName()); $customerA = $a['customer'] ?? null;
$customerB = $b['customer'] ?? null;
$nameA = ($customerA instanceof Customer) ? $customerA->getName() : null;
$nameB = ($customerB instanceof Customer) ? $customerB->getName() : null;
if ($nameA === null && $nameB === null) {
$result = 0;
} elseif ($nameA === null && $nameB !== null) {
$result = 1;
} elseif ($nameA !== null && $nameB === null) {
$result = -1;
} else {
$result = strcmp($nameA, $nameB);
}
return $result;
}); });
foreach ($customerEntries as $id => $settings) { foreach ($customerEntries as $id => $settings) {

View File

@@ -296,6 +296,7 @@ class TimesheetCountedStatistic implements \JsonSerializable
$this->recordRateExported = $recordRate; $this->recordRateExported = $recordRate;
} }
#[\ReturnTypeWillChange]
public function jsonSerialize() public function jsonSerialize()
{ {
return [ return [

View File

@@ -45,33 +45,45 @@ final class LocaleHelper
/** /**
* Transforms seconds into a decimal formatted duration string. * Transforms seconds into a decimal formatted duration string.
* *
* @param int $seconds * @param int|null $seconds
* @return string * @return string
*/ */
public function durationDecimal(int $seconds) public function durationDecimal(?int $seconds): string
{ {
if ($seconds === null) {
$seconds = 0;
}
$value = round($seconds / 3600, 2); $value = round($seconds / 3600, 2);
return $this->getDurationFormatter()->format((float) $value); return $this->getDurationFormatter()->format($value);
} }
/** /**
* Only used in twig filter |amount and invoice templates * Only used in twig filter |amount and invoice templates
* *
* @param string|float $amount * @param string|float|null $amount
* @return bool|false|string * @return bool|false|string
*/ */
public function amount($amount) public function amount($amount)
{ {
if ($amount === null) {
$amount = 0.00;
}
return $this->getNumberFormatter()->format($amount); return $this->getNumberFormatter()->format($amount);
} }
/** /**
* @param string $currency * @param string|null $currency
* @return string * @return string
*/ */
public function currency($currency) public function currency(?string $currency)
{ {
if ($currency === null) {
return '';
}
try { try {
return Currencies::getSymbol(strtoupper($currency), $this->locale); return Currencies::getSymbol(strtoupper($currency), $this->locale);
} catch (\Exception $ex) { } catch (\Exception $ex) {
@@ -109,7 +121,7 @@ final class LocaleHelper
} }
/** /**
* @param int|float $amount * @param int|float|null $amount
* @param string|null $currency * @param string|null $currency
* @param bool $withCurrency * @param bool $withCurrency
* @return string * @return string
@@ -120,6 +132,10 @@ final class LocaleHelper
$withCurrency = false; $withCurrency = false;
} }
if ($amount === null) {
$amount = 0;
}
if (false === $withCurrency) { if (false === $withCurrency) {
return $this->getMoneyFormatter($withCurrency)->format($amount, NumberFormatter::TYPE_DEFAULT); return $this->getMoneyFormatter($withCurrency)->format($amount, NumberFormatter::TYPE_DEFAULT);
} }

View File

@@ -25,12 +25,20 @@ class DateTimeFormatValidator extends ConstraintValidator
throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\DateTimeFormat'); throw new UnexpectedTypeException($constraint, __NAMESPACE__ . '\DateTimeFormat');
} }
if ($value === null) {
return;
}
$valid = true; $valid = true;
try { if (!\is_string($value)) {
$test = new \DateTime($value);
} catch (\Exception $ex) {
$valid = false; $valid = false;
} else {
try {
$test = new \DateTime($value);
} catch (\Exception $ex) {
$valid = false;
}
} }
if (false === $valid) { if (false === $valid) {

View File

@@ -28,4 +28,27 @@ class InvoiceDocumentTest extends TestCase
self::assertEquals('default.html.twig', $sut->getName()); self::assertEquals('default.html.twig', $sut->getName());
self::assertIsInt($sut->getLastChange()); self::assertIsInt($sut->getLastChange());
} }
public function testThrowsOnDeletedFile()
{
$catchedException = false;
$dir = realpath(__DIR__ . '/../../templates/invoice/renderer');
$file = tempnam($dir, 'invoice-renderer');
if ($file !== false) {
touch($file);
$sut = new InvoiceDocument(new \SplFileInfo($file));
unlink($file);
try {
// names are cached by SplFileInfo, so we need to trigger a function that does access the file
$sut->getLastChange();
} catch (\Exception $exception) {
$catchedException = true;
}
}
self::assertTrue($catchedException, 'Invoice document did not throw exception');
}
} }

View File

@@ -27,13 +27,11 @@ class DocxRendererTest extends TestCase
$sut = $this->getAbstractRenderer(DocxRenderer::class); $sut = $this->getAbstractRenderer(DocxRenderer::class);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
} }
public function testRender() public function testRender()

View File

@@ -31,13 +31,10 @@ class JsonRendererTest extends KernelTestCase
$sut = new JsonRenderer($env); $sut = new JsonRenderer($env);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('text.txt.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('text.txt.twig')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('javascript.json.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('javascript.json.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('xml.xml.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('xml.xml.twig')));

View File

@@ -29,12 +29,10 @@ class OdsRendererTest extends TestCase
$sut = $this->getAbstractRenderer(OdsRenderer::class); $sut = $this->getAbstractRenderer(OdsRenderer::class);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true))); $this->assertTrue($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
} }

View File

@@ -48,11 +48,9 @@ class PdfRendererTest extends KernelTestCase
$this->assertTrue($sut->supports($this->getInvoiceDocument('default.pdf.twig', true))); $this->assertTrue($sut->supports($this->getInvoiceDocument('default.pdf.twig', true)));
$this->assertTrue($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
} }
public function testRender() public function testRender()

View File

@@ -31,13 +31,11 @@ class TextRendererTest extends KernelTestCase
$sut = new TextRenderer($env); $sut = new TextRenderer($env);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('text.txt.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('text.txt.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('xml.xml.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('xml.xml.twig')));

View File

@@ -33,11 +33,9 @@ class TwigRendererTest extends KernelTestCase
$this->assertTrue($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
} }
public function testRender() public function testRender()

View File

@@ -30,13 +30,11 @@ class XlsxRendererTest extends TestCase
$sut = $this->getAbstractRenderer(XlsxRenderer::class); $sut = $this->getAbstractRenderer(XlsxRenderer::class);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true))); $this->assertTrue($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
} }
public function getTestModel() public function getTestModel()

View File

@@ -31,14 +31,12 @@ class XmlRendererTest extends KernelTestCase
$sut = new XmlRenderer($env); $sut = new XmlRenderer($env);
$this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('default.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('freelancer.pdf.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('timesheet.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('foo.html.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('company.docx')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('export.csv'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('spreadsheet.xlsx'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods', true)));
$this->assertFalse($sut->supports($this->getInvoiceDocument('open-spreadsheet.ods')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('text.txt.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('text.txt.twig')));
$this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig'))); $this->assertFalse($sut->supports($this->getInvoiceDocument('javascript.json.twig')));
$this->assertTrue($sut->supports($this->getInvoiceDocument('xml.xml.twig'))); $this->assertTrue($sut->supports($this->getInvoiceDocument('xml.xml.twig')));

View File

@@ -69,7 +69,10 @@ class DurationValidatorTest extends ConstraintValidatorTestCase
{ {
$constraint = new Duration(); $constraint = new Duration();
$this->validator->validate($input, $constraint); $this->validator->validate($input, $constraint);
$this->validator->validate(strtoupper($input), $constraint); if ($input !== null) {
$input = strtoupper($input);
}
$this->validator->validate($input, $constraint);
$this->assertNoViolation(); $this->assertNoViolation();
} }