Release 2.56 (#5909)

This commit is contained in:
Kevin Papst
2026-04-25 09:58:43 +02:00
committed by GitHub
parent 01cb14a3da
commit 087350ab72
14 changed files with 477 additions and 162 deletions

View File

@@ -55,8 +55,8 @@ body:
options:
- "8.5"
- "8.4"
- "8.2"
- "8.3"
- "8.2"
- "8.1"
- Unknown
- Other (please mention below)

View File

@@ -22,7 +22,7 @@ version-resolver:
- 'translation'
default: patch
template: |
**Compatible with PHP 8.1 to 8.5**
**Compatible with PHP 8.2 to 8.5**
$CHANGES

View File

@@ -19,7 +19,7 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
matrix:
php: ['8.1', '8.2', '8.3', '8.4', '8.5']
php: ['8.2', '8.3', '8.4', '8.5']
name: Integration (${{ matrix.php }})
steps:

View File

@@ -29,7 +29,7 @@ and so much more.
### Requirements
- PHP 8.1.3 minimum with support for 8.2, 8.3, 8.4, 8.5
- PHP 8.2 minimum with support for 8.3, 8.4, 8.5
- MariaDB / MySQL: oldest maintained LTS release (MariaDB >= [10.6](https://endoflife.date/mariadb) or MySQL >= [8.4](https://endoflife.date/mysql)) or newer
- A webserver and subdomain (subdirectory is not supported)
- PHP extensions: `gd`, `intl`, `json`, `mbstring`, `pdo`, `tokenizer`, `xml`, `xsl`, `zip`

View File

@@ -8,6 +8,15 @@ you can upgrade your Kimai installation to the latest stable release.
Check below if there are more version specific steps required, which need to be executed after the normal update process.
Perform EACH version specific task between your version and the new one, otherwise you risk data inconsistency or a broken installation.
## [2.56.0](https://github.com/kimai/kimai/releases/tag/2.56.0)
The required minimum PHP version is now 8.2, read https://www.php.net/supported-versions.php
If you are still using PHP 8.1, please be aware it is EOL and does not receive any security updates.
If you have to upgrade to a newer version, do yourself the favor and upgrade directly to PHP 8.5.
The requirement for 8.2 is an intermediate solution for the near future, and the requirement will be raised to 8.5 rather sooner than later.
## [2.0.30](https://github.com/kimai/kimai/releases/tag/2.0.30)
The `DATABASE_URL` in your environment settings (e.g. [.env](https://github.com/kimai/kimai/issues/4246), [docker-compose.yaml](https://github.com/tobybatch/kimai2/issues/531) or webserver config)

View File

@@ -14,7 +14,7 @@
}
],
"require": {
"php": "8.1.*||8.2.*||8.3.*||8.4.*||8.5.*",
"php": "8.2.*||8.3.*||8.4.*||8.5.*",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",
@@ -117,7 +117,7 @@
},
"optimize-autoloader": true,
"platform": {
"php": "8.1.3"
"php": "8.2"
},
"preferred-install": {
"*": "dist"

396
composer.lock generated
View File

@@ -4,25 +4,25 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "81e3162783fcc7f01c10601aeb09b48f",
"content-hash": "9408273b8cfa9c509c6e483245818e8e",
"packages": [
{
"name": "azuyalabs/yasumi",
"version": "2.9.0",
"version": "2.11.0",
"source": {
"type": "git",
"url": "https://github.com/azuyalabs/yasumi.git",
"reference": "36005a0796b64bc07257f39c11c42f147fe5320f"
"reference": "e24bc345d2dd0b9425ff19abf7f11574bbb1d7be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/36005a0796b64bc07257f39c11c42f147fe5320f",
"reference": "36005a0796b64bc07257f39c11c42f147fe5320f",
"url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/e24bc345d2dd0b9425ff19abf7f11574bbb1d7be",
"reference": "e24bc345d2dd0b9425ff19abf7f11574bbb1d7be",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=8.1"
"php": ">=8.2"
},
"require-dev": {
"azuyalabs/php-cs-fixer-config": "^0.3",
@@ -30,7 +30,7 @@
"mikey179/vfsstream": "^1.6",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpunit/phpunit": "^8.5 || ^9.6"
"phpunit/phpunit": "^11.5"
},
"suggest": {
"ext-calendar": "For calculating the date of Easter"
@@ -77,7 +77,7 @@
"type": "other"
}
],
"time": "2025-12-29T16:13:54+00:00"
"time": "2026-03-30T13:35:38+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -1663,22 +1663,22 @@
},
{
"name": "easybill/zugferd-php",
"version": "v2.1.1",
"version": "v2.3.1",
"source": {
"type": "git",
"url": "https://github.com/easybill/zugferd-php.git",
"reference": "376749138f5ecd216b73b5cdd5d4473f2a2d3252"
"reference": "fba2a504feb58ae2b3a19da5a0c051ce8fe13895"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/easybill/zugferd-php/zipball/376749138f5ecd216b73b5cdd5d4473f2a2d3252",
"reference": "376749138f5ecd216b73b5cdd5d4473f2a2d3252",
"url": "https://api.github.com/repos/easybill/zugferd-php/zipball/fba2a504feb58ae2b3a19da5a0c051ce8fe13895",
"reference": "fba2a504feb58ae2b3a19da5a0c051ce8fe13895",
"shasum": ""
},
"require": {
"ext-dom": "*",
"jms/serializer": "^3.16",
"php": "^8.1"
"php": "^8.2"
},
"require-dev": {
"ext-libxml": "*",
@@ -1721,9 +1721,9 @@
],
"support": {
"issues": "https://github.com/easybill/zugferd-php/issues",
"source": "https://github.com/easybill/zugferd-php/tree/v2.1.1"
"source": "https://github.com/easybill/zugferd-php/tree/v2.3.1"
},
"time": "2024-11-07T08:28:44+00:00"
"time": "2025-09-13T13:57:46+00:00"
},
{
"name": "egulias/email-validator",
@@ -2947,31 +2947,32 @@
},
{
"name": "maennchen/zipstream-php",
"version": "3.1.1",
"version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/maennchen/ZipStream-PHP.git",
"reference": "6187e9cc4493da94b9b63eb2315821552015fca9"
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/6187e9cc4493da94b9b63eb2315821552015fca9",
"reference": "6187e9cc4493da94b9b63eb2315821552015fca9",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f",
"reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-zlib": "*",
"php-64bit": "^8.1"
"php-64bit": "^8.2"
},
"require-dev": {
"brianium/paratest": "^7.7",
"ext-zip": "*",
"friendsofphp/php-cs-fixer": "^3.16",
"guzzlehttp/guzzle": "^7.5",
"mikey179/vfsstream": "^1.6",
"php-coveralls/php-coveralls": "^2.5",
"phpunit/phpunit": "^10.0",
"vimeo/psalm": "^5.0"
"phpunit/phpunit": "^11.0",
"vimeo/psalm": "^6.0"
},
"suggest": {
"guzzlehttp/psr7": "^2.4",
@@ -3012,7 +3013,7 @@
],
"support": {
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.1"
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2"
},
"funding": [
{
@@ -3020,7 +3021,7 @@
"type": "github"
}
],
"time": "2024-10-10T12:33:01+00:00"
"time": "2025-01-27T12:07:53+00:00"
},
{
"name": "markbaker/complex",
@@ -3468,42 +3469,45 @@
},
{
"name": "nelmio/api-doc-bundle",
"version": "v5.7.1",
"version": "v5.10.0",
"source": {
"type": "git",
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
"reference": "772d26a9d940c9a8d173f75f0fdbaab47c1c87f2"
"reference": "212d490a85589ddce0f2dc683bef89bc62485340"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/772d26a9d940c9a8d173f75f0fdbaab47c1c87f2",
"reference": "772d26a9d940c9a8d173f75f0fdbaab47c1c87f2",
"url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/212d490a85589ddce0f2dc683bef89bc62485340",
"reference": "212d490a85589ddce0f2dc683bef89bc62485340",
"shasum": ""
},
"require": {
"php": ">=8.1",
"phpdocumentor/reflection-docblock": "^5.0",
"phpdocumentor/type-resolver": "^1.8.2",
"php": ">=8.2",
"phpdocumentor/reflection-docblock": "^5.0 || ^6.0",
"phpdocumentor/type-resolver": "^1.8.2 || ^2.0",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/container": "^1.0 || ^2.0",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"symfony/config": "^6.4 || ^7.1",
"symfony/console": "^6.4 || ^7.1",
"symfony/dependency-injection": "^6.4 || ^7.1",
"symfony/config": "^6.4 || ^7.2 || ^8.0",
"symfony/console": "^6.4 || ^7.2 || ^8.0",
"symfony/dependency-injection": "^6.4 || ^7.2 || ^8.0",
"symfony/deprecation-contracts": "^2.1 || ^3",
"symfony/framework-bundle": "^6.4 || ^7.1",
"symfony/http-foundation": "^6.4 || ^7.1",
"symfony/http-kernel": "^6.4 || ^7.1",
"symfony/options-resolver": "^6.4 || ^7.1",
"symfony/property-info": "^6.4 || ^7.1",
"symfony/routing": "^6.4 || ^7.1",
"zircote/swagger-php": "^4.11.1 || ^5.0"
"symfony/framework-bundle": "^6.4 || ^7.2 || ^8.0",
"symfony/http-foundation": "^6.4 || ^7.2 || ^8.0",
"symfony/http-kernel": "^6.4 || ^7.2 || ^8.0",
"symfony/options-resolver": "^6.4 || ^7.2 || ^8.0",
"symfony/property-info": "^6.4 || ^7.2 || ^8.0",
"symfony/routing": "^6.4 || ^7.2 || ^8.0",
"symfony/type-info": "^7.2 || ^8.0",
"zircote/swagger-php": "^4.11.1 || ^5.0 || ^6.0"
},
"conflict": {
"symfony/property-info": "6.4.32 || 7.3.10 || 7.4.4 || 8.0.4",
"zircote/swagger-php": "4.8.7 || 5.5.0"
},
"require-dev": {
"api-platform/core": "^3.2",
"api-platform/core": "^3.2 || ^4.0",
"doctrine/inflector": "^2.0",
"friendsofphp/php-cs-fixer": "^3.52",
"friendsofsymfony/rest-bundle": "^3.2.0",
"jms/serializer": "^3.32",
@@ -3513,25 +3517,22 @@
"phpstan/phpstan-strict-rules": "^2.0",
"phpstan/phpstan-symfony": "^2.0",
"phpunit/phpunit": "^10.5",
"symfony/asset": "^6.4 || ^7.1",
"symfony/browser-kit": "^6.4 || ^7.1",
"symfony/cache": "^6.4 || ^7.1",
"symfony/dom-crawler": "^6.4 || ^7.1",
"symfony/expression-language": "^6.4 || ^7.1",
"symfony/finder": "^6.4 || ^7.1",
"symfony/form": "^6.4 || ^7.1",
"symfony/phpunit-bridge": "^6.4 || ^7.1",
"symfony/property-access": "^6.4 || ^7.1",
"symfony/security-csrf": "^6.4 || ^7.1",
"symfony/security-http": "^6.4 || ^7.1",
"symfony/serializer": "^6.4 || ^7.1",
"symfony/stopwatch": "^6.4 || ^7.1",
"symfony/templating": "^6.4 || ^7.1",
"symfony/translation": "^6.4 || ^7.1",
"symfony/twig-bundle": "^6.4 || ^7.1",
"symfony/uid": "^6.4 || ^7.1",
"symfony/validator": "^6.4 || ^7.1",
"willdurand/hateoas-bundle": "^2.7",
"symfony/asset": "^6.4 || ^7.2 || ^8.0",
"symfony/browser-kit": "^6.4 || ^7.2 || ^8.0",
"symfony/cache": "^6.4 || ^7.2 || ^8.0",
"symfony/dom-crawler": "^6.4 || ^7.2 || ^8.0",
"symfony/expression-language": "^6.4 || ^7.2 || ^8.0",
"symfony/finder": "^6.4 || ^7.2 || ^8.0",
"symfony/form": "^6.4 || ^7.2 || ^8.0",
"symfony/phpunit-bridge": "^6.4 || ^7.2 || ^8.0",
"symfony/security-csrf": "^6.4 || ^7.2 || ^8.0",
"symfony/security-http": "^6.4 || ^7.2 || ^8.0",
"symfony/serializer": "^6.4 || ^7.2 || ^8.0",
"symfony/translation": "^6.4 || ^7.2 || ^8.0",
"symfony/twig-bundle": "^6.4 || ^7.2 || ^8.0",
"symfony/uid": "^6.4 || ^7.2 || ^8.0",
"symfony/validator": "^6.4 || ^7.2 || ^8.0",
"willdurand/hateoas-bundle": "^2.7 || ^3.0",
"willdurand/negotiation": "^3.0"
},
"suggest": {
@@ -3579,7 +3580,7 @@
],
"support": {
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.7.1"
"source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.10.0"
},
"funding": [
{
@@ -3587,7 +3588,7 @@
"type": "github"
}
],
"time": "2025-11-13T10:32:15+00:00"
"time": "2026-04-10T14:26:01+00:00"
},
{
"name": "nelmio/cors-bundle",
@@ -3778,16 +3779,16 @@
},
{
"name": "openspout/openspout",
"version": "v4.25.0",
"version": "v4.28.5",
"source": {
"type": "git",
"url": "https://github.com/openspout/openspout.git",
"reference": "519affe730d92e1598720a6467227fc28550f0e6"
"reference": "ab05a09fe6fce57c90338f83280648a9786ce36b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/openspout/openspout/zipball/519affe730d92e1598720a6467227fc28550f0e6",
"reference": "519affe730d92e1598720a6467227fc28550f0e6",
"url": "https://api.github.com/repos/openspout/openspout/zipball/ab05a09fe6fce57c90338f83280648a9786ce36b",
"reference": "ab05a09fe6fce57c90338f83280648a9786ce36b",
"shasum": ""
},
"require": {
@@ -3797,17 +3798,17 @@
"ext-libxml": "*",
"ext-xmlreader": "*",
"ext-zip": "*",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
"php": "~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"ext-zlib": "*",
"friendsofphp/php-cs-fixer": "^3.64.0",
"infection/infection": "^0.29.6",
"phpbench/phpbench": "^1.3.1",
"phpstan/phpstan": "^1.12.4",
"phpstan/phpstan-phpunit": "^1.4.0",
"phpstan/phpstan-strict-rules": "^1.6.1",
"phpunit/phpunit": "^10.5.20 || ^11.3.6"
"friendsofphp/php-cs-fixer": "^3.68.3",
"infection/infection": "^0.29.10",
"phpbench/phpbench": "^1.4.0",
"phpstan/phpstan": "^2.1.2",
"phpstan/phpstan-phpunit": "^2.0.4",
"phpstan/phpstan-strict-rules": "^2",
"phpunit/phpunit": "^11.5.4"
},
"suggest": {
"ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)",
@@ -3855,7 +3856,7 @@
],
"support": {
"issues": "https://github.com/openspout/openspout/issues",
"source": "https://github.com/openspout/openspout/tree/v4.25.0"
"source": "https://github.com/openspout/openspout/tree/v4.28.5"
},
"funding": [
{
@@ -3867,7 +3868,7 @@
"type": "github"
}
],
"time": "2024-09-24T09:03:42+00:00"
"time": "2025-01-30T13:51:11+00:00"
},
{
"name": "pagerfanta/pagerfanta",
@@ -4316,16 +4317,16 @@
},
{
"name": "phpoffice/phpspreadsheet",
"version": "2.4.4",
"version": "2.4.5",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
"reference": "78bf6f0b4945ab31f1935741324ef3f0bf59a6fe"
"reference": "ec7815be350e03df90f3e2ace92653fa6cb4327c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/78bf6f0b4945ab31f1935741324ef3f0bf59a6fe",
"reference": "78bf6f0b4945ab31f1935741324ef3f0bf59a6fe",
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/ec7815be350e03df90f3e2ace92653fa6cb4327c",
"reference": "ec7815be350e03df90f3e2ace92653fa6cb4327c",
"shasum": ""
},
"require": {
@@ -4416,9 +4417,9 @@
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.4.4"
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.4.5"
},
"time": "2026-04-10T03:20:38+00:00"
"time": "2026-04-19T05:48:49+00:00"
},
{
"name": "phpoffice/phpword",
@@ -4929,6 +4930,68 @@
},
"time": "2021-10-29T13:26:27+00:00"
},
{
"name": "radebatz/type-info-extras",
"version": "1.0.7",
"source": {
"type": "git",
"url": "https://github.com/DerManoMann/type-info-extras.git",
"reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DerManoMann/type-info-extras/zipball/95a524a74a61648b44e355cb33d38db4b17ef5ce",
"reference": "95a524a74a61648b44e355cb33d38db4b17ef5ce",
"shasum": ""
},
"require": {
"php": ">=8.2",
"phpstan/phpdoc-parser": "^2.0",
"symfony/type-info": "^7.3.8 || ^7.4.1 || ^8.0 || ^8.1-@dev"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.70",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Radebatz\\TypeInfoExtras\\": "src"
},
"exclude-from-classmap": [
"/tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Martin Rademacher",
"email": "mano@radebatz.org"
}
],
"description": "Extras for symfony/type-info",
"homepage": "http://radebatz.net/mano/",
"keywords": [
"component",
"symfony",
"type-info",
"types"
],
"support": {
"issues": "https://github.com/DerManoMann/type-info-extras/issues",
"source": "https://github.com/DerManoMann/type-info-extras/tree/1.0.7"
},
"time": "2026-03-06T22:40:29+00:00"
},
{
"name": "robrichards/xmlseclibs",
"version": "3.1.5",
@@ -5259,16 +5322,16 @@
},
{
"name": "smalot/pdfparser",
"version": "v2.12.4",
"version": "v2.12.5",
"source": {
"type": "git",
"url": "https://github.com/smalot/pdfparser.git",
"reference": "028d7cc0ceff323bc001d763caa2bbdf611866c4"
"reference": "2cfa0d92bd557875c9f52a75fde0e8392302a354"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smalot/pdfparser/zipball/028d7cc0ceff323bc001d763caa2bbdf611866c4",
"reference": "028d7cc0ceff323bc001d763caa2bbdf611866c4",
"url": "https://api.github.com/repos/smalot/pdfparser/zipball/2cfa0d92bd557875c9f52a75fde0e8392302a354",
"reference": "2cfa0d92bd557875c9f52a75fde0e8392302a354",
"shasum": ""
},
"require": {
@@ -5304,9 +5367,9 @@
],
"support": {
"issues": "https://github.com/smalot/pdfparser/issues",
"source": "https://github.com/smalot/pdfparser/tree/v2.12.4"
"source": "https://github.com/smalot/pdfparser/tree/v2.12.5"
},
"time": "2026-03-10T15:39:47+00:00"
"time": "2026-04-17T11:37:58+00:00"
},
{
"name": "spomky-labs/otphp",
@@ -9747,6 +9810,89 @@
],
"time": "2026-01-05T12:44:39+00:00"
},
{
"name": "symfony/type-info",
"version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/type-info.git",
"reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/type-info/zipball/6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd",
"reference": "6bf34da885ff5143a3dfd8f1b863bb8ab95f50bd",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/container": "^1.1|^2.0",
"symfony/deprecation-contracts": "^2.5|^3"
},
"conflict": {
"phpstan/phpdoc-parser": "<1.30"
},
"require-dev": {
"phpstan/phpdoc-parser": "^1.30|^2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\TypeInfo\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mathias Arlaud",
"email": "mathias.arlaud@gmail.com"
},
{
"name": "Baptiste LEDUC",
"email": "baptiste.leduc@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Extracts PHP types information.",
"homepage": "https://symfony.com",
"keywords": [
"PHPStan",
"phpdoc",
"symfony",
"type"
],
"support": {
"source": "https://github.com/symfony/type-info/tree/v7.4.8"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/validator",
"version": "v6.4.36",
@@ -10650,23 +10796,23 @@
},
{
"name": "webmozart/assert",
"version": "1.12.1",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
"reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4",
"reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-date": "*",
"ext-filter": "*",
"php": "^7.2 || ^8.0"
"php": "^8.2"
},
"suggest": {
"ext-intl": "",
@@ -10676,7 +10822,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
"dev-feature/2-0": "2.0-dev"
}
},
"autoload": {
@@ -10692,6 +10838,10 @@
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com"
}
],
"description": "Assertions to validate method input/output with nice error messages.",
@@ -10702,9 +10852,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.12.1"
"source": "https://github.com/webmozarts/assert/tree/2.3.0"
},
"time": "2025-10-29T15:56:20+00:00"
"time": "2026-04-11T10:33:05+00:00"
},
{
"name": "willdurand/jsonp-callback-validator",
@@ -10807,24 +10957,25 @@
},
{
"name": "zircote/swagger-php",
"version": "5.8.3",
"version": "6.0.6",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "098223019f764a16715f64089a58606096719c98"
"reference": "9447c1f45b5ae93185caea9a0c8e298399188a25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/098223019f764a16715f64089a58606096719c98",
"reference": "098223019f764a16715f64089a58606096719c98",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/9447c1f45b5ae93185caea9a0c8e298399188a25",
"reference": "9447c1f45b5ae93185caea9a0c8e298399188a25",
"shasum": ""
},
"require": {
"ext-json": "*",
"nikic/php-parser": "^4.19 || ^5.0",
"php": ">=7.4",
"php": ">=8.2",
"phpstan/phpdoc-parser": "^2.0",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"radebatz/type-info-extras": "^1.0.2",
"symfony/deprecation-contracts": "^2 || ^3",
"symfony/finder": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0"
@@ -10836,14 +10987,9 @@
"composer/package-versions-deprecated": "^1.11",
"doctrine/annotations": "^2.0",
"friendsofphp/php-cs-fixer": "^3.62.0",
"phpstan/phpstan": "^1.6 || ^2.0",
"phpunit/phpunit": "^9.0",
"rector/rector": "^1.0 || ^2.3.1",
"vimeo/psalm": "^4.30 || ^5.0"
},
"suggest": {
"doctrine/annotations": "^2.0",
"radebatz/type-info-extras": "^1.0.2"
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^11.5",
"rector/rector": "^2.3.1"
},
"bin": [
"bin/openapi"
@@ -10851,7 +10997,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
"dev-master": "6.x-dev"
}
},
"autoload": {
@@ -10889,7 +11035,7 @@
],
"support": {
"issues": "https://github.com/zircote/swagger-php/issues",
"source": "https://github.com/zircote/swagger-php/tree/5.8.3"
"source": "https://github.com/zircote/swagger-php/tree/6.0.6"
},
"funding": [
{
@@ -10897,7 +11043,7 @@
"type": "github"
}
],
"time": "2026-03-02T00:47:18+00:00"
"time": "2026-02-28T06:42:58+00:00"
}
],
"packages-dev": [
@@ -11804,11 +11950,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.48",
"version": "2.1.51",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/231397213efb7c0a066ee024b5c3c87f2d3adfa0",
"reference": "231397213efb7c0a066ee024b5c3c87f2d3adfa0",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59",
"reference": "dc3b523c45e714c70de2ac5113b958223b55dc59",
"shasum": ""
},
"require": {
@@ -11853,7 +11999,7 @@
"type": "github"
}
],
"time": "2026-04-15T20:24:19+00:00"
"time": "2026-04-21T18:22:01+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@@ -11907,16 +12053,16 @@
},
{
"name": "phpstan/phpstan-doctrine",
"version": "2.0.20",
"version": "2.0.21",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-doctrine.git",
"reference": "72f4f7a02d6c98d9101e8616e0488bc0a785196d"
"reference": "81dac0ee4363c2359128aec844df31efb215dddc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/72f4f7a02d6c98d9101e8616e0488bc0a785196d",
"reference": "72f4f7a02d6c98d9101e8616e0488bc0a785196d",
"url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/81dac0ee4363c2359128aec844df31efb215dddc",
"reference": "81dac0ee4363c2359128aec844df31efb215dddc",
"shasum": ""
},
"require": {
@@ -11977,9 +12123,9 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan-doctrine/issues",
"source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.20"
"source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.21"
},
"time": "2026-03-13T13:44:51+00:00"
"time": "2026-04-17T13:00:39+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
@@ -14524,7 +14670,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "8.1.*||8.2.*||8.3.*||8.4.*||8.5.*",
"php": "8.2.*||8.3.*||8.4.*||8.5.*",
"ext-gd": "*",
"ext-intl": "*",
"ext-json": "*",
@@ -14540,7 +14686,7 @@
"ext-simplexml": "*"
},
"platform-overrides": {
"php": "8.1.3"
"php": "8.2"
},
"plugin-api-version": "2.9.0"
}

View File

@@ -67,5 +67,7 @@ nelmio_api_doc:
swagger_ui_config: []
# https://redocly.com/docs/redoc/config/
redocly_config: []
# https://scalar.com/products/api-references/configuration
scalar_config: []
# https://docs.stoplight.io/docs/elements/b074dc47b2826-elements-configuration-options
stoplight_config: { basePath: '/api/doc', router: 'memory', logo: '/touch-icon-192x192.png', hideInternal: true }

180
kimai.sh
View File

@@ -6,13 +6,106 @@
# To improve this script across platforms I need your feedback!
# --------------------------------------------------------------------------------
function verbose() {
if [[ "${IS_VERBOSE}" == "1" ]]; then
echo "$@"
fi
}
function flush_cache() {
rm -rf var/cache/* 2>&1
}
function reload_cache() {
flush_cache
bin/console cache:warmup
}
function composer_is_phar() {
[[ "${KIMAI_COMPOSER}" == *.phar ]]
}
function find_command_path() {
which "$1" 2>/dev/null
}
function resolve_executable() {
local executable="$1"
if [[ -z "${executable}" || "${executable}" == */* || "${executable}" == *.phar ]]; then
echo "${executable}"
return
fi
local resolved
resolved="$(find_command_path "${executable}")"
if [[ -n "${resolved}" ]]; then
echo "${resolved}"
else
echo "${executable}"
fi
}
function composer_exists() {
if composer_is_phar; then
[[ -f "${KIMAI_COMPOSER}" ]]
elif [[ "${KIMAI_COMPOSER}" == */* ]]; then
[[ -x "${KIMAI_COMPOSER}" ]]
else
command -v "${KIMAI_COMPOSER}" >/dev/null 2>&1
fi
}
function run_composer() {
#export COMPOSER_ALLOW_SUPERUSER=1
if composer_is_phar; then
"$KIMAI_PHP" "$KIMAI_COMPOSER" "$@"
else
"$KIMAI_COMPOSER" "$@"
fi
}
function confirm_update() {
local version="$1"
local answer
echo "About to update Kimai to version ${version}."
read -r -p "Continue? [y/N] " answer
if [[ ! "${answer}" =~ ^[Yy]([Ee][Ss])?$ ]]; then
echo "Update cancelled."
exit 1
fi
}
function update_kimai() {
if [[ "$1" =~ ^([0-9]+\.){2,3}[0-9]+$ ]]; then
if [[ "$1" == "latest" ]]; then
if ! command -v sort >/dev/null 2>&1 || ! command -v tail >/dev/null 2>&1; then
echo "we could not detect the latest kimai version due to missing commands: sort, tail"
exit 1
fi
git fetch --tags
export VERSION="$(git tag --list | sort -V | tail -n 1)"
if [ -z "$VERSION" ]; then
echo "Failed loading Kimai version"
exit 1
fi
elif [[ "$1" =~ ^([0-9]+\.){2,3}[0-9]+$ ]]; then
export VERSION=$1
else
echo "You need to supply a full Kimai version like: \"2.24.0\""
exit 1
fi
git fetch --tags
if ! git rev-parse --verify --quiet "refs/tags/$VERSION" >/dev/null; then
echo "Requested Kimai version does not exist: $VERSION"
exit 1
fi
confirm_update "$VERSION"
git checkout -- composer.json
git checkout -- composer.lock
git checkout -- symfony.lock
@@ -23,10 +116,10 @@ function update_kimai() {
fi
rm -rf var/sessions/ 2>&1
rm -rf var/cache/* 2>&1
git fetch --tags
flush_cache
git checkout "$VERSION"
$KIMAI_PHP "$KIMAI_COMPOSER" install --no-dev --optimize-autoloader
run_composer install --no-dev --optimize-autoloader || exit 1
$KIMAI_PHP bin/console kimai:install
@@ -39,11 +132,22 @@ function update_kimai() {
function install_plugins() {
# detect if there are additional plugins that we need to install
packages="$($PHP bin/console kimai:plugin --composer)"
export PACKAGES=$packages
if [ -n "$PACKAGES" ]; then
$KIMAI_PHP "$KIMAI_COMPOSER" require "$PACKAGES"
$KIMAI_PHP bin/console kimai:plugins --install
local packages_output
local -a packages
if ! packages_output="$($KIMAI_PHP bin/console kimai:plugin --composer)"; then
echo "Failed loading plugin list from kimai:plugin --composer"
exit 1
fi
read -r -a packages <<< "$packages_output"
if [[ ${#packages[@]} -gt 0 ]]; then
verbose "Installing Composer plugins: ${packages[*]}"
run_composer require "${packages[@]} --update-no-dev --optimize-autoloader" || exit 1
$KIMAI_PHP bin/console kimai:plugins --install || exit 1
else
verbose "No Composer plugins detected."
fi
}
@@ -53,6 +157,21 @@ function set_permission() {
chmod -R g+rw var/
}
# ---------------------------------------------------------
# runtime logic below
export IS_VERBOSE=0
POSITIONAL_ARGS=()
for arg in "$@"; do
if [[ "${arg}" == "-v" ]]; then
export IS_VERBOSE=1
else
POSITIONAL_ARGS+=("${arg}")
fi
done
set -- "${POSITIONAL_ARGS[@]}"
if [[ -z "${KIMAI_USER}" ]]; then
export KIMAI_USER=""
fi
@@ -62,19 +181,36 @@ if [[ -z "${KIMAI_GROUP}" ]]; then
fi
if [[ -z "${KIMAI_PHP}" ]]; then
export KIMAI_PHP="php"
php_path="$(find_command_path php)"
if [[ -n "${php_path}" ]]; then
export KIMAI_PHP="${php_path}"
else
export KIMAI_PHP="php"
fi
else
export KIMAI_PHP="$(resolve_executable "${KIMAI_PHP}")"
fi
if [[ -z "${KIMAI_COMPOSER}" ]]; then
export KIMAI_COMPOSER="composer"
composer_path="$(find_command_path composer)"
if [[ -n "${composer_path}" ]]; then
export KIMAI_COMPOSER="${composer_path}"
else
export KIMAI_COMPOSER="composer"
fi
else
export KIMAI_COMPOSER="$(resolve_executable "${KIMAI_COMPOSER}")"
fi
cd "$(dirname "$0")" || { echo "Cannot change working directory."; exit 1; }
# we need a few commands installed in order for this script to complete
command -v $KIMAI_COMPOSER >/dev/null 2>&1 || { echo >&2 "Update requires 'composer' but it's not installed."; exit 1; }
composer_exists || { echo >&2 "Update requires 'composer' but it's not installed or not executable."; exit 1; }
command -v git >/dev/null 2>&1 || { echo >&2 "Update requires 'git' but it's not installed."; exit 1; }
command -v $KIMAI_PHP >/dev/null 2>&1 || { echo >&2 "Update requires 'php' but it's not installed."; exit 1; }
command -v "$KIMAI_PHP" >/dev/null 2>&1 || { echo >&2 "Update requires 'php' but it's not installed."; exit 1; }
verbose "Using PHP: $KIMAI_PHP"
verbose "Using Composer: $KIMAI_COMPOSER"
if [[ -n $1 ]]; then
if [ "$1" == 'update' ]; then
@@ -83,9 +219,12 @@ if [[ -n $1 ]]; then
elif [ "$1" == 'permission' ]; then
set_permission
exit
elif [ "$1" == 'plugins' ]; then
elif [ "$1" == 'plugins' ] || [ "$1" == 'plugin' ]; then
install_plugins
exit
elif [ "$1" == 'cache' ]; then
reload_cache
exit
else
echo ""
echo ">> Unknown command: $1"
@@ -95,9 +234,13 @@ fi
echo ""
echo "This script has the following sub-commands:"
echo ""
echo "$0 update <version> - Install Kimai version <version>"
echo "$0 update - Update Kimai to the latest version"
echo "$0 update <version> - Update Kimai to the given version <version>"
echo "$0 permission - Fix file permissions"
echo "$0 plugins - Install plugins from var/packages/*.zip"
echo "$0 plugin[s] - Install available plugins from var/packages/*.zip"
echo "$0 cache - Clear application cache"
echo ""
echo "Append -v anywhere in the command to enable verbose output"
echo ""
echo "Use the following environment variables to customize the runtime:"
echo ""
@@ -109,8 +252,9 @@ echo "KIMAI_NO_PERMS - Skip changing permissions"
echo ""
echo "Examples:"
echo ""
echo "$0 2.24.0"
echo "KIMAI_PHP=/usr/bin/php8.3 $0 2.24.0"
echo "$0 -v update"
echo "$0 update 2.54.0 -v"
echo "KIMAI_PHP=/usr/bin/php8.3 $0 2.54.0"
echo "KIMAI_PHP=/usr/bin/php8.3 KIMAI_COMPOSER=/tmp/composer.phar $0 2.24.0"
echo "KIMAI_PHP=php8.3 KIMAI_GROUP=httpd $0 2.24.0"
echo "KIMAI_NO_PERMS=1 KIMAI_PHP=/usr/bin/php8.3 $0 2.24.0"

View File

@@ -17,11 +17,11 @@ final class Constants
/**
* The current release version
*/
public const VERSION = '2.55.0';
public const VERSION = '2.56.0';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 25500;
public const VERSION_ID = 25600;
/**
* The software name
*/

View File

@@ -36,7 +36,6 @@ class SpoutSpreadsheet implements SpreadsheetPackage
private readonly ?string $locale = null,
)
{
$this->writer->setCreator(Constants::SOFTWARE);
}
/**
@@ -138,6 +137,10 @@ class SpoutSpreadsheet implements SpreadsheetPackage
public function save(): void
{
if ($this->writer instanceof XLSXWriter) {
$this->writer->setCreator(Constants::SOFTWARE);
}
$this->writer->close();
}
}

View File

@@ -1,3 +1,23 @@
{%- macro duration(timesheet, options) -%}
<span
{%- if options and options.title is defined %} data-replacer="duration" data-title="true"{% endif -%}
{%- if not timesheet.end %} data-since="{{ timesheet.begin is null ? '' : timesheet.begin.format(constant('DATE_ISO8601')) }}"{% endif -%}
{# TODO calculate break and add as data-duration #}
class="duration {% if options and options.class is defined %} {{ options.class }}{% endif %}">
{{- timesheet.begin is null ? 0|duration : timesheet|duration -}}
</span>
{% endmacro %}
{%- macro duration_seconds(seconds, options) -%}
<span
{%- if options and options.title is defined %} data-replacer="duration" data-title="true" {% endif -%}
{%- if options and options.active is defined and options.active %} data-since="{{ create_date('- '~seconds~' seconds').format(constant('DATE_ISO8601')) }}" {% endif -%}
{# TODO calculate break and add as data-duration #}
class="duration {% if options and options.class is defined %} {{ options.class }}{% endif %}">
{{- seconds|duration -}}
</span>
{% endmacro %}
{%- macro search_filter(route, options) -%}
{% set opts = {'performSearch': 1} %}
{% if options.begin is defined and options.end is defined %}

View File

@@ -105,11 +105,7 @@
&dash;
{% endif %}
{% elseif column == 'duration' %}
{% if entry.end %}
{{ entry.duration|duration }}
{% else %}
<i data-since="{{ entry.begin.format(constant('DATE_ISO8601')) }}">{{ entry|duration }}</i>
{% endif %}
{{ widgets.duration(entry) }}
{% elseif column == 'break' %}
{{ entry.break|duration }}
{% elseif column == 'hourlyRate' %}

View File

@@ -2506,11 +2506,6 @@ parameters:
count: 1
path: Widget/Type/MoreTest.php
-
message: "#^Cannot access offset 'financialBegin' on mixed\\.$#"
count: 2
path: Widget/Type/PaginatedWorkingTimeChartTest.php
-
message: "#^Parameter \\#2 \\$array of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertArrayHasKey\\(\\) expects array\\|ArrayAccess, mixed given\\.$#"
count: 2