Звіт у форматі JSON машиночитний
{
"data": {
"repo": {
"topics": [],
"is_fork": false,
"size_kb": 6563,
"has_wiki": false,
"homepage": null,
"languages": {
"PHP": 1428096,
"HTML": 7971
},
"pushed_at": "2026-06-14T12:03:59Z",
"created_at": "2015-08-13T15:04:55Z",
"owner_type": "Organization",
"updated_at": "2026-07-05T12:05:10Z",
"description": "Fluid template rendering engine - Standalone version",
"is_archived": false,
"is_disabled": false,
"license_spdx": "LGPL-3.0",
"default_branch": "main",
"license_spdx_raw": "LGPL-3.0",
"primary_language": "PHP",
"significant_languages": [
"PHP"
]
},
"owner": {
"blog": "https://typo3.org",
"name": "TYPO3 GitHub Department",
"type": "Organization",
"login": "TYPO3",
"company": null,
"location": null,
"followers": 117,
"avatar_url": "https://avatars.githubusercontent.com/u/88698?v=4",
"created_at": "2009-05-26T11:58:25Z",
"is_verified": null,
"public_repos": 23,
"account_age_days": 6274
},
"license": {
"state": "standard",
"spdx_id": "LGPL-3.0",
"raw_spdx": "LGPL-3.0",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "4.6.1",
"kind": "patch",
"published_at": "2026-04-16T17:27:38Z"
},
{
"tag": "5.3.1",
"kind": "patch",
"published_at": "2026-04-16T17:29:34Z"
},
{
"tag": "5.3.0",
"kind": "minor",
"published_at": "2026-04-07T18:05:17Z"
},
{
"tag": "4.6.0",
"kind": "minor",
"published_at": "2026-03-13T10:32:00Z"
},
{
"tag": "5.2.0",
"kind": "minor",
"published_at": "2026-03-11T11:57:45Z"
},
{
"tag": "5.1.1",
"kind": "patch",
"published_at": "2026-02-20T13:39:41Z"
},
{
"tag": "5.1.0",
"kind": "minor",
"published_at": "2026-02-02T18:27:25Z"
},
{
"tag": "4.5.1",
"kind": "patch",
"published_at": "2026-02-04T19:15:03Z"
},
{
"tag": "5.0.3",
"kind": "patch",
"published_at": "2025-12-17T14:29:11Z"
},
{
"tag": "5.0.2",
"kind": "patch",
"published_at": "2025-11-24T09:29:12Z"
},
{
"tag": "5.0.1",
"kind": "patch",
"published_at": "2025-11-18T13:42:50Z"
},
{
"tag": "5.0.0",
"kind": "major",
"published_at": "2025-11-16T15:57:27Z"
},
{
"tag": "4.5.0",
"kind": "minor",
"published_at": "2025-11-15T14:38:21Z"
},
{
"tag": "4.4.3",
"kind": "patch",
"published_at": "2025-08-28T12:05:37Z"
},
{
"tag": "4.4.2",
"kind": "patch",
"published_at": "2025-08-06T10:18:04Z"
},
{
"tag": "4.4.1",
"kind": "patch",
"published_at": "2025-08-06T10:12:06Z"
},
{
"tag": "4.4.0",
"kind": "minor",
"published_at": "2025-07-28T15:14:24Z"
},
{
"tag": "4.3.0",
"kind": "minor",
"published_at": "2025-06-24T13:09:34Z"
},
{
"tag": "4.2.0",
"kind": "minor",
"published_at": "2025-05-19T11:26:58Z"
},
{
"tag": "4.1.2",
"kind": "patch",
"published_at": "2025-04-22T08:33:53Z"
},
{
"tag": "4.1.1",
"kind": "patch",
"published_at": "2025-04-17T15:52:16Z"
},
{
"tag": "4.1.0",
"kind": "minor",
"published_at": "2025-04-09T16:35:12Z"
},
{
"tag": "4.0.3",
"kind": "patch",
"published_at": "2025-01-21T10:02:22Z"
},
{
"tag": "4.0.2",
"kind": "patch",
"published_at": "2024-11-18T21:58:55Z"
},
{
"tag": "4.0.1",
"kind": "patch",
"published_at": "2024-10-03T09:54:14Z"
},
{
"tag": "4.0.0",
"kind": "major",
"published_at": "2024-09-11T08:50:15Z"
},
{
"tag": "2.15.0",
"kind": "minor",
"published_at": "2024-09-01T12:06:14Z"
},
{
"tag": "2.14.1",
"kind": "patch",
"published_at": "2024-07-31T09:10:37Z"
},
{
"tag": "2.14.0",
"kind": "minor",
"published_at": "2024-07-23T16:56:55Z"
},
{
"tag": "2.13.0",
"kind": "minor",
"published_at": "2024-07-12T12:53:56Z"
},
{
"tag": "2.12.0",
"kind": "minor",
"published_at": "2024-06-26T15:26:12Z"
},
{
"tag": "2.11.0",
"kind": "minor",
"published_at": "2024-04-08T07:38:01Z"
},
{
"tag": "2.10.1",
"kind": "patch",
"published_at": "2023-12-11T13:16:47Z"
},
{
"tag": "2.10.0",
"kind": "minor",
"published_at": "2023-11-27T18:18:08Z"
},
{
"tag": "2.9.3",
"kind": "patch",
"published_at": "2023-11-14T16:21:25Z"
},
{
"tag": "2.9.2",
"kind": "patch",
"published_at": "2023-06-19T16:54:15Z"
},
{
"tag": "2.9.1",
"kind": "patch",
"published_at": "2023-06-08T10:15:50Z"
},
{
"tag": "2.9.0",
"kind": "minor",
"published_at": "2023-06-07T14:04:08Z"
}
],
"recent_commits": [
{
"oid": "ef902a525a983f6dde46c37c9f0c1a912da3a7cc",
"body": "See #1378",
"is_bot": false,
"headline": "[TASK] Add test case for stringable component argument (#1379)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-06-14T12:00:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8dca97cbf34875343c73f45eaf319ca13b0f4376",
"body": null,
"is_bot": false,
"headline": "[DOCS] Fix typo in ViewHelper code example (#1376)",
"author_name": "Jonas Werner",
"author_login": "svwerder",
"committed_at": "2026-06-14T11:46:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "28c42c75b171aef196ddbe151b0d1146c290249d",
"body": "symfony/polyfill-php84 1.33.0->1.34.0 adds RoundingMode stub [1].\n\nround() in PHP < 8.4 allows int as third argument only, and\nhas been extended with php 8.4 to allow int|RoundingMode.\n\nThe 'class_exists()' check in RoundViewHelper now suddenly\nreturns true when latest symfony/polyfill-php84 is load\n[…]\n 8.4 callback chain avoiding RoundingMode enum\naltogether, to avoid the symfony/polyfill-php84 influence.\n\n[1] https://github.com/symfony/polyfill-php84/commit/e4e3f1cc81b8b84a7c596f8817c6d186678e2884",
"is_bot": false,
"headline": "[BUGFIX] Fix RoundViewHelper with symfony/polyfill-php84 (#1373)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2026-04-16T17:09:54Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "66da09ba59c857b58851ba667c0c22d10304dc92",
"body": "Currently, the Fluid parser is not ready to require the location\neverywhere, so the `@todo` is clarified.",
"is_bot": false,
"headline": "[TASK] Clarify todo in parser exception (#1369)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-08T09:09:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f1a7aa9759f4cd98f0d598cce73b5ecaec06da26",
"body": null,
"is_bot": false,
"headline": "[TASK] add more test for TemplateParser (#1367)",
"author_name": "Matthias Vogel",
"author_login": "Kanti",
"committed_at": "2026-04-07T17:50:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "96c65692706fefb70108f7bf349f2c748f52c436",
"body": "When removing the `<html xmlns` declarations from the template, line\nbreaks within the `<html>` tags were also removed. This lead to invalid\nline numbers reported by the template parser.\n\nWith this change, the affected `<html>` tag is replaced with the\nmatching number of newlines.",
"is_bot": false,
"headline": "[BUGFIX] Prevent shifting of line numbers (#1358)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-07T17:25:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ae092f1e6e3725e47fb9fc0133a32b540de35b25",
"body": "This patch adds a dedicated CLI command to analyze Fluid templates for\nerrors or deprecations. Templates can be provided either as paths or\nvia STDIN, which enables deeper integration of the fluid CLI tool into\ncode editors and IDEs. Output can be either human-readable or\nmachine-readable as JSON.\n\n\n[…]\ntly, a shortcoming of both the warmup and the analyze command is\nthat no additional global Fluid namespaces can be provided. This needs\nfurther investigation and thus is not addressed with this patch.",
"is_bot": false,
"headline": "[FEATURE] CLI command to analyze Fluid templates (#1359)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-07T15:25:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f40ae370eae8c67839bc7f54f70c10674b69189d",
"body": "Until now, Fluid's exceptions are unaware of the exact location in\nthe original template file that triggered the exception. This patch\nintroduces the necessary data structures and interfaces to accomplish\nthis.\n\nIn a first step, the already available context in the `TemplateParser`\nis now attached t\n[…]\n character numbers currently provided might not\nalways be accurate due to the mechanisms the parser currently uses\ninternally. This change creates the basis to tackle this in a more\nstructured manner.",
"is_bot": false,
"headline": "[TASK] Template location aware exceptions (#1366)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-07T15:20:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "100e885396b0e915c501e72fa5e41fcf8b7982f7",
"body": null,
"is_bot": false,
"headline": "[TASK] Add infection for mutation coverage (#1323)",
"author_name": "Matthias Vogel",
"author_login": "Kanti",
"committed_at": "2026-04-06T10:19:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "951d00e823b476494fb479b08deab0313e3e9567",
"body": "Some new ViewHelpers didn't yet cross-reference to the TYPO3\ndocumentation.",
"is_bot": false,
"headline": "[DOCS] Add missing cross-references to TYPO3 reference (#1363)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-04T10:48:38Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c130f9eacdbbdf045087ea156fe6aaa7bd5c6d95",
"body": "All ViewHelper classes now have a `@see` annotation that points to the\nViewHelper reference documentation. Also, the `@api` annotation has been\nadded where it was missing.",
"is_bot": false,
"headline": "[DOCS] Add links to ViewHelper reference (#1362)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-04T10:41:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a0b26b4cf180c0f774bb826a056187226dc6dced",
"body": "`ViewHelperMetaFactory`, which is used both for CLI commands (such as\ncache warmup and schema generation) and for automatic documentation,\npreviously only collected the last instance of a PhpDoc annotation\non a ViewHelper class:\n\n```\n/**\n * @see link1\n * @see link2\n */\n```\n\nIn that example, the meta\n[…]\nh introduces a new flag that allows collecting all instances of\nannotations. When set, `ViewHelperMetadata` always stores arrays instead\nof strings, even if only one instance of the annotation exists.",
"is_bot": false,
"headline": "[TASK] Option to collect multiple PhpDoc annotations (#1361)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-03T19:24:09Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "79e043dec26f6db37ad31c494c9f514380a5138e",
"body": null,
"is_bot": false,
"headline": "[TASK] Cover multiple @see annotations with test case (#1360)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-04-03T18:54:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "816016e9176d042a7a6e6da4411dbfc1dddfd27a",
"body": "…357)",
"is_bot": false,
"headline": "[TASK] Add test case for undefined variable in ternary expression (#1…",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-31T14:48:59Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "954b6cd74b8cddfdef0dede140bb744f0b2b1490",
"body": "Resolves: #1247",
"is_bot": false,
"headline": "[DOCS] Clarify scope of changelog (#1351)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-17T14:22:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dce7467c2bfd4c8fea3edbb7983664bdc6f51882",
"body": null,
"is_bot": false,
"headline": "[TASK] Add exception codes to ViewHelper exceptions (#1347)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-11T11:10:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "81509636664f1165d4bbacd1750652c911470f84",
"body": "If the path to the current template file is available, exceptions\nfrom ViewHelpers now also contain this information to make debugging\neasier. To achieve that, all ViewHelper exceptions are wrapped in\nan outer exception which extends the original exception message. This\nnew behavior can be disabled by registering a custom error handler\nin the rendering context.",
"is_bot": false,
"headline": "[TASK] Add file path to ViewHelper exception messages (#1346)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-09T17:38:08Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6d3d9c26b02b3cd8c9715bf477d368c6ddb0897b",
"body": "Previously, PHP code within the context of a template (most importantly\nViewHelpers, but also internal Fluid code) wasn't aware of the current\ntemplate context. This meant that exceptions couldn't refer to the\ntemplate file where the exception was actually triggered.\n\nThis change introduces the opti\n[…]\n files.\n\nIn follow-up patches, this path can be used to improve exception\nmessages within Fluid. It also allows users to access these values to\nimprove exception messages in their own implementations.",
"is_bot": false,
"headline": "[TASK] Provide current template path in renderingContext (#1337)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-09T13:24:35Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ec2c8702849d5f8fc3764fd13363dedcd61395a4",
"body": "Several custom exceptions are introduced to indicate invalid ViewHelper\narguments. Their base class `InvalidArgumentException` extends from\n`ViewHelper\\Exception`, which makes it possible to catch them via\nFluid's error handlers. Previously, a lot of ViewHelpers used the\nglobal `\\InvalidArgumentExce\n[…]\n itself and by\nindividual ViewHelpers if a supplied argument value cannot be processed\nby the ViewHelper. We recommend that custom ViewHelpers also use this\nexception if they receive an invalid value.",
"is_bot": false,
"headline": "[TASK] Introduce dedicated exceptions for argument validation (#1344)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-09T13:13:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "cb15a9d7458d5b25073e77b22343d08770874414",
"body": "Use consistent syntax for `\\InvalidArgumentException`.",
"is_bot": false,
"headline": "[TASK] Unify exception usage in TagBuilder (#1345)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-07T16:35:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ffb251a159b7cc58f80eee6ee2740ecd1ee63b39",
"body": "Version 4.4.2 is now officially compatible with Fluid 5.x.\n\n> composer req t3docs/fluid-documentation-generator:\"^4.4.2\" --dev",
"is_bot": false,
"headline": "[TASK] Update fluid-documentation-generator (#1343)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-07T11:02:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c2fd11f2cb85ace3957220c511c1f25944433305",
"body": "In an effort to ease debugging, exceptions thrown by the Fluid parser\nnow contain the full path to the original template file. Previously,\nit could be quite hard to discover the affected template because of\nthe cryptic and internal templateIdentifier. Now, the full template\npath is given as an addit\n[…]\n high-level `TemplateView` class are encouraged to make use of the\nnew argument in `TemplateParser->parse()` and\n`TemplateParse->getOrParseAndStoreTemplate()` to provide the full\npath to the template.",
"is_bot": false,
"headline": "[TASK] Include template path in parser exceptions (#1336)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-07T10:32:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "aadb6ce7869f2b71464cb8aa2054908c440fb41d",
"body": "* Enable a skipped test that seems to work fine now.\n* $this->creatMock() vs. self::createStub(): Mocks that declare\n no expectations are stubs. phpunit is now more picky with\n this, which is good to clarify things. The change switches\n many cases to stubs. Places that mock system-under-test to t\n[…]\n fixture classes extending the abstract and new().\n* expects(self::any()) is deprecated. We switch to once() and\n friends or remove some cases entirely since some of course\n turn out to be obsolete.",
"is_bot": false,
"headline": "[TASK] Have notice and deprecation free phpunit v13 (#1342)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2026-03-06T22:42:45Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "80ebac94351c9c7c12ac5eb4e1f5ea0ecaeb1c0f",
"body": "Do not randomize test order in phpunit.xml since that is\nannoying when actively working with the test code base. Have\na dedicated workflow job instead to add a randomized run.\n\nRaise phpunit.xsd to oldest common denominator and remove\nresolveDependencies attribute which is identical to the\ndefault value.",
"is_bot": false,
"headline": "[TASK] Order tests by default by default (#1341)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2026-03-06T21:23:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b305e7eda33edc631e1c99dbb8c855364e958b51",
"body": "Allows phpunit v13 when PHP version fits.\n\n> composer req --dev friendsofphp/php-cs-fixer:^3.94.2\n> composer req --dev phpstan/phpstan:^2.1.40\n> composer req --dev phpstan/phpstan-phpunit:^2.0.16\n> composer req --dev phpunit/phpunit:\"^11.5.55 || ^12.5.14 || ^13.0.5\"\n> composer req --dev psr/container:^2.0.2\n> composer req --dev t3docs/fluid-documentation-generator:^4.4.1",
"is_bot": false,
"headline": "[TASK] Bump dev dependencies (#1338)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2026-03-06T19:53:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fb31cfe066f9807f818d054f67c74e10304cc31e",
"body": "Resolves: #1244",
"is_bot": false,
"headline": "[DOCS] Add chapter about variable escaping (#1332)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-04T19:00:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "526092c411173c7183aebc1d17a229b8f84a9c78",
"body": "This change aims to simplify debugging by providing improved exception\nmessages in cases where a template file cannot be resolved.\n\nPreviously, these exceptions lacked important context (e. g. which\ntype of template [template, partial, layout] is missing). Also,\nthe messages still suggested to the u\n[…]\nent exception classes and messages are now\nused for templates, partials and layouts, which helps during debugging.\nAlso, the exception messages have been streamlined, which also extends\nto components.",
"is_bot": false,
"headline": "[FEATURE] Clarify exceptions for missing templates (#1335)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-04T11:34:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "66183879e33830822f372801e4e2d83db1a0ae7d",
"body": "Within `TemplatePaths`, a `InvalidTemplateResourceException` is thrown\nif no suitable template can be found based on the provided paths,\ncontroller and action. Previously, this has been part of\n`getTemplateSource()`, right before the template file is read.\n\nThis patch moves the validation one level \n[…]\nluid 6. Otherwise this change would have been breaking.\n\nIn a follow-up patch, this will be improved even further by providing\nthe user with a list of all possible template paths that have been\ntried.",
"is_bot": false,
"headline": "[TASK] Move around template file validation code (#1334)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-03T18:31:52Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "70f6eecafe7372ba762260eb7b4d7db799d880f9",
"body": "A new interface `ComponentListProviderInterface` is added to Fluid,\nwhich allows component collections to not only resolve one specific\ncomponent, but also the reverse: When implemented,\n`getAvailableComponents()` returns a list of all components that are\npart of the collection. This can be useful f\n[…]\nat matches their custom folder structure. This will change in Fluid 6,\nwhere it will be assumed that users define both `resolveTemplateName()`\nand `getAvailableComponents()` consistently if necessary.",
"is_bot": false,
"headline": "[FEATURE] API to generate component listings (#1318)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-03-03T14:30:04Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b3f8a31faabd386a2911101fe19da0a389689796",
"body": "The patch extends Fluid's own XSD schema generator classes to\nbe able to work with component definitions. This allows frameworks\nthat use Fluid components and are aware of all component collections\n(e. g. via DI) to generate XSD schema files for components.",
"is_bot": false,
"headline": "[TASK] Make ViewHelperMetadata compatible with components (#1331)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-26T16:06:54Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4e84db6f2f7824e81aa7c2ea7c5be9a33556c7b0",
"body": "An option is added to `TemplateFinder` and `TemplatePaths` to get all\navailable templates without their file extension. This enables easier\nprocessing later, e. g. in the context of component collections.",
"is_bot": false,
"headline": "[TASK] Option to get available templates without extension (#1330)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-26T15:55:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c61416b4573f6b8d1acca0d01f5ebfe3c07e4087",
"body": "This patch introduces new data structures that represent annotations\nfor ViewHelpers/Components or their individual arguments. The purpose\nis to be able to attach arbitrary information to definitions of\nViewHelpers/Components or arguments, either to pass them along as-is\nor to react to them within F\n[…]\nwritten to Fluid's template\ncache files, each annotation needs to implement the `compile()`\nmethod, which returns the PHP code that re-creates the annotation object\nwhen a template is read from cache.",
"is_bot": false,
"headline": "[FEATURE] Annotations API (#1314)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-24T11:21:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9d563f3b169593548b01f45447f74e7cf454a268",
"body": "Due to an oversight, each component call from a template created its\nown instance of the responsible ViewHelperResolverDelegate. This is\nnot necessary, since resolver delegates are meant to be sharable.\n\nThis patch introduces the new `getResolverDelegate()` method, which\ncentralizes the already exis\n[…]\nrDelegateInstanceFromClassName()` are migrated to the\nnew method.\n\nNote that the performance impact wasn't relevant in TYPO3, since the\nunderlying DI implementation already shared the service objects.",
"is_bot": false,
"headline": "[TASK] Share resolver delegates in cached templates (#1319)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-20T13:34:40Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3d7b9920cc8695aed5c09e5e186beabb8cb8b033",
"body": "… (#1305)\n\nWith #1288, the `UnsafeHTML` interface has been introduced, which prevents\nFluid's default output escaping if implemented by an object. In other Fluid\ninternals, objects that implement that interface are already stringified in\ncases where it increases compatibility with normal strings.\n\nT\n[…]\ntext (variables are not auto-converted if they should be compared\nstrictly).\n\nThis is a general enhancement and should not have consequences to loose\ncomparisons, which are commonly used in templates.",
"is_bot": false,
"headline": "[BUGFIX] Enhance boolean evaluation to check for UnsafeHTML instances…",
"author_name": "Matthias Vogel",
"author_login": "Kanti",
"committed_at": "2026-02-20T13:32:50Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "3f7634a5835a38a677477926549bc5b0f7292b1d",
"body": null,
"is_bot": false,
"headline": "[TASK] Run phpunit tests in random order (#1322)",
"author_name": "Matthias Vogel",
"author_login": "Kanti",
"committed_at": "2026-02-20T10:51:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2e4d7a7ade20427dcc6eb3dee026209906b4d9bb",
"body": "Fluid's `BooleanParser` attempts to cover a specific edge case: If\ntwo objects should be compared, according to the code it should always\nbe a strict comparison. However, the condition that should have\naccomplished this depends on the variable content during the first\nrun. If a template is compiled \n[…]\nither uncached or cached\ntemplates would be correct, but never both. The new version delivers\nthe same results as cached templates before this change, so it should\nbe in line with real-world projects.",
"is_bot": false,
"headline": "[BUGFIX] Address inconsistencies in object comparisons (#1320)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-20T10:41:06Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "65e961e89d6ddefb945118b35b424629014760c8",
"body": null,
"is_bot": false,
"headline": "[TASK] Improve naming of metadata factory tests (#1315)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-18T17:40:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "de298aea884f1a88d029fbdbcff6d1dc33d049a6",
"body": "To simplify the code for further changes, constructor property\npromotion is now used in `ArgumentDefinition`. Also, compilation\nfor Fluid cache file is now handled within `ArgumentDefinition`.\nSelf-explaining comments are removed.",
"is_bot": false,
"headline": "[TASK] Consolidate ArgumentDefinition implementation (#1313)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-17T17:41:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cc132cfff67f98e9311a438efcdd064d34fdac13",
"body": null,
"is_bot": false,
"headline": "[TASK] Improve return type annotation for VH creation (#1310)",
"author_name": "Elias Häußler",
"author_login": "eliashaeussler",
"committed_at": "2026-02-17T15:01:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c16e48bf8043b40f7517f486830376b8067d3a7b",
"body": null,
"is_bot": false,
"headline": "[DOCS] Document reserved variables in Fluid 5 (#1309)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-14T11:22:31Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "83779845a3398e3c9d0edd6a44c358b8bc8acf80",
"body": null,
"is_bot": false,
"headline": "[DOCS] Document version for boolean attributes (#1306)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-14T11:17:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cf489f651e121d963051199c96055f1d822b3cdd",
"body": "… without being escaped (#1288)\n\nFluid escapes variable output by default to prevent XSS. In some cases,\nhowever, values originate from a trusted/sanitized source (e.g. HTML\ngenerated from a sanitizer, CMS RTE output, a Markdown renderer with\na strict allow-list, …) and should be rendered as HTML wi\n[…]\nion unwraps `UnsafeHTML` to a string first,\n so empty HTML values behave like empty strings in conditions\n (e.g. `{content}` is false when it’s `''`).\n\nTests/examples have been adjusted accordingly.",
"is_bot": false,
"headline": "[FEATURE] UnsafeHTML interface to allow passing variables to template…",
"author_name": "Matthias Vogel",
"author_login": "Kanti",
"committed_at": "2026-02-02T18:23:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5badb37e9b00f8ffd45b36a40d5aaa67591b9f08",
"body": "`null` can safely be converted to an empty string, so it's fine to\nallow it as a base string for string replacement.\n\nResolves: #1279",
"is_bot": false,
"headline": "[BUGFIX] Allow null value as input for f:replace (#1282)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-02T18:12:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ee0790808bc83017ff2d745858a138748365d7ec",
"body": "Since Fluid 5, ViewHelpers can assume that the types of their arguments\nare properly enforced by Fluid: Either by automatic casting for simple\ntypes or by throwing an exception. However, this is currently not the\ncase for `string`: Here, `StrictArgumentProcessor` still accepts\nstringable objects as \n[…]\nter:\n\n```\n$this->registerArgument('value', 'string|int|float|Stringable', '');\n```\n\nSince one specified goal of Fluid 5 is type safety within ViewHelpers,\nthis is considered a bugfix.\n\nResolves: #1099",
"is_bot": false,
"headline": "[BUGFIX] Cast stringable objects early for ViewHelper arguments (#1290)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-02T18:11:38Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5f1366e5c1fa8356c81e775b3f68eda3024d7566",
"body": "Previously, duplicate template/partial/layout root paths were removed\nfrom the array to avoid potential duplicate lookups. However, this\nprevented reordering of those paths by adding one path multiple times\nto the same array, which would ideally increase its priority over\nother paths.\n\nThis patch re\n[…]\nbackported to Fluid 4. However,\nthis is highly unlikely, which is why this is not considered a\nbreaking change in Fluid 5.\n\nThis patch also clarifies comments to be able to simplify code with\nFluid 6.",
"is_bot": false,
"headline": "[BUGFIX] Don't remove path duplicates to allow reordering (#1298)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-02-02T18:10:44Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0456eb4b5aa7f1a5ae5a8425cac508ea8c2cd374",
"body": "…296)\n\nResolves: #1284",
"is_bot": false,
"headline": "[TASK] Improve exception message for invalid ViewHelper arguments (#1…",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-01-26T15:40:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "33c5a37b788b0b4cc308f9857dfdb058fb110ca0",
"body": null,
"is_bot": false,
"headline": "[TASK] Apply new cgl rules (#1294)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-01-26T15:36:43Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ed03cf9ad7ecc5603f43e1f2bdbe7dadbf5171d7",
"body": "Documentation is added for the new `*.fluid.*` file extension and the\nassociated fallback chain. File references throughout the documentation\nare adjusted to always use the new extension. Also, links to moved files\nare fixed.\n\nResolves: #1289",
"is_bot": false,
"headline": "[DOCS] Fluid file extension (#1292)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-01-21T17:05:17Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9c1454339cf707a9b4d6920bc602e4423145a339",
"body": null,
"is_bot": false,
"headline": "[TASK] Add stringable tests for StrictArgumentProcessor (#1291)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-01-21T14:28:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2aa9f1d4063d8c9bff7a90927413c148739e39e0",
"body": null,
"is_bot": false,
"headline": "[TASK] Add test case for template name fallbacks (#1287)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2026-01-14T17:40:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "993b77a2a7d9392e024b9980c8462e40c125c9cf",
"body": "Fluid 5 disallows variable names that start with an underscore. While\nthis is already enforced in `StandardVariableProvider` for setting\nsuch variables, this change now also triggers an exception if a\nvariable with an invalid name is accessed.\n\nThis is probably a temporary change to be removed with Fluid 6. For now,\nit allows the `TemplateValidator`, which is used during template\nwarmup, to discover this issue during parse time, and to let users\nknow about affected templates.\n\nResolves: #1263",
"is_bot": false,
"headline": "[TASK] Allow TemplateValidator to discover underscore variables (#1283)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-12-16T17:26:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ae55cbdb8814e031908c9b34bc4a1a2f4dc8eaac",
"body": "Since a Fluid namespace does not necesarily refer to an existing class\n(e. g. \"TYPO3Fluid\\Fluid\\ViewHelpers\" is not a valid PHP class), the\nPHPDoc annotation is removed from `getNamespace()`.\n\nThis enables comparing namespace strings with the result of\n`getNamespace()` without phpstan issues (e. g. \"[...] will always\nevaluate to false\").",
"is_bot": false,
"headline": "[TASK] Loosen PHPDoc type for Fluid namespaces (#1280)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-12-15T19:47:44Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fc82facd9838d6c645de4545dd220ef169240359",
"body": "Due to the way the structure of components is extracted from template\nfiles, it was previously not possible to use a PHP constant or enum\ncase as default value for a component argument.\n\nThis patch adds the `<f:constant>` ViewHelper to the list of allowed\nViewHelpers for that pre-parsing step, which makes it now possible\nto use `<f:constant>` from within `<f:argument>`.",
"is_bot": false,
"headline": "[BUGFIX] Allow constants and enum cases as argument default (#1272)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-12-10T14:49:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1aa517a61646029b4e1029e2ff76aa0109b75c9d",
"body": "…(#1274)",
"is_bot": false,
"headline": "[DOCS] Document `<f:argument>` does not work when rendering sections …",
"author_name": "Daniel Corn",
"author_login": "cundd",
"committed_at": "2025-12-10T11:42:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "aca11557d820b35536455253520ba596b35f1a38",
"body": "The existing `BackedEnumExample` is renamed to `StringBackedEnumExample`\nto also cover int-backed enums with `IntBackedEnumExample`. The\nexisting tests for `<f:constant>` are extended to cover all enum\nvariants.",
"is_bot": false,
"headline": "[TASK] Extend enum test coverage (#1269)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-12-07T16:35:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "03ef568664db2802bbc60e5f1ca81f20340d56f7",
"body": "Due to refactorings in the cache layer of Fluid, which were necessary\nto make cache warmup feasible, the cache identifiers have changed\nwith Fluid 5. This currently leads to parse errors when a template\nname starts with a number because the associated cache file and most\nimportantly the generated PH\n[…]\n \"template_\" to cache\nidentifiers. Other special characters are already dealt with in\n`TemplateCompiler`, so we only need to ensure that the identifier\nstarts with an alpha-character.\n\nResolves: #1266",
"is_bot": false,
"headline": "[BUGFIX] Ensure valid name for cache classes (#1268)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-24T09:27:34Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "18048d9e26375c114cc1a59d47f0ceef186194b2",
"body": "Change AbstractViewHelper declaration\n\n`abstract public function render(): mixed;`\n\nback to\n\n`abstract public function render();`\n\nToo much fallout with too little benefit.\n\nRelated: #1219",
"is_bot": false,
"headline": "[TASK] Avoid mixed return in AbstractViewHelper->render() (#1264)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2025-11-18T13:35:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ee5b89dea88415860da6f7acf2d5f0ca50fcf53f",
"body": "Previous Fluid versions already had a CLI command which could be used\nto warmup the cache for template files, e. g. during deployment.\nHowever, the previous implementation required a lot of additional\ncontext and mixed parsing/compilation and rendering state. The\nprevious warmup implementation has a\n[…]\nhe underlying API,\nnamely `TemplateFinder` for finding template files and\n`TemplateValidator`, which produces a validation report for each\ntemplate, are marked as `@internal` for now.\n\nResolves: #1103",
"is_bot": false,
"headline": "[FEATURE] Overhauled cache warmup command (#1224)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-13T11:05:10Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "31a451f44298bfbcba4ee42eba8a26095cbb2184",
"body": "As a follow-up to #1258, all example templates are now using the new\nFluid file extension. In all places where the file extension isn't\nspecified explicitly, the fallback mechanism allows us to just rename\nthe templates without any other changes. However, for\n`setTemplatePathAndFilename()`, an explicit path is given, which\nneeds to be adjusted. Also, if the file extension is specified\nexplicitly when rendering a partial, this also needs to be adjusted\nmanually.",
"is_bot": false,
"headline": "[TASK] Use Fluid file extension in examples (#1261)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-11T17:16:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "42a07b76a351266d73852bd5475366aa6adfad38",
"body": "Historically, Fluid has always used generic file extensions for\ntemplate files, such as `.html`, `.txt` or `.xml`. While this\nprovides IDE integration out-of-the-box, it makes it hard to\ndistinguish files that are interpreted by Fluid and others that\nare just text or html files.\n\nThis patch introduc\n[…]\nd templates, it also allows us to create better IDE\nintegrations in the future. Also, it enables us to scan a whole project\nfor Fluid templates, which makes cache warmup and linting tasks\nconfig-free.",
"is_bot": false,
"headline": "[FEATURE] Introduce optional .fluid.* file extension (#1258)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-11T16:52:26Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "a83414309826673766bde38daf1f79f1bbef09cd",
"body": "This patch adds a new `<f:contains>` ViewHelper to Fluid, which allows\nto check if a provided string or array contains a specified value.\nDepending on the input, this mimicks PHP's `in_array()` or `str_contains()`.\n\nThe initial version of this patch is intended to be backported to Fluid 4.\nIn a follow-up, the code and argument types can be improved for Fluid 5.\n\nResolves: #1125",
"is_bot": false,
"headline": "[FEATURE] <f:contains> ViewHelper (#1193)",
"author_name": "Michael Staatz",
"author_login": "staatzstreich",
"committed_at": "2025-11-10T13:50:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "91f835138d52d3fc6de51c2f5d6611393feb50d8",
"body": null,
"is_bot": false,
"headline": "[TASK] Cleanup example code (#1260)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-08T15:32:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f926ce229c58f0f13fb0000e43bd456c33641afd",
"body": null,
"is_bot": false,
"headline": "[TASK] Test real-world setup in TemplatePathsTest (#1259)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-08T13:13:09Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8a675bcb65e8a5244cdf1b3c2e3543e0590954db",
"body": "All path handling and resolving in Fluid is performed by the\n`TemplatePaths` class, which is attached to the current rendering\ncontext. While this class has already been simplified in the past,\nthe actual path resolving has largely stayed the same because it\nincludes edge cases that were moved over \n[…]\not be found.\n\nIn a follow-up patch, this will also allow us to introduce a custom\nfile extension for Fluid templates, which will then also be part of that\nfallback chain.\n\nResolves: #371\nRelated: #938",
"is_bot": false,
"headline": "[FEATURE] Overhaul template path resolving (#1243)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-04T16:07:51Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f5377f0fa84adf61bad4fbf222f08cd06b3e8743",
"body": "Fluid has so far removed any CDATA sections from templates. This has\nbeen introduced with 978071f, without any clear indication why this\nwas done. This means that Fluid templates either expect CDATA sections\nto be removed or avoid them altogether since they don't do anything.\nThis gives us the oppor\n[…]\n,\ninstead three curly braces `{{{ }}}` can be used to access Fluid\nvariables, call ViewHelpers and use expressions. The CDATA keyword\nitself is removed from the output.\n\nResolves: #1012\nResolves: #808",
"is_bot": false,
"headline": "[!!!][FEATURE] Alternative Fluid syntax in CDATA (#1246)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-04T16:04:07Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "a8cb13977042aee22f2ccdc04417b0532b2a3d44",
"body": "Quite early in the development of Fluid, the decision has been made\nto remove `<![CDATA[...]]>` sections from templates. The exact reason\nremains unclear, as 978071f8f5484e01882d7c4c6d740064f22d51ef only\nmentions that this has been added, but not why:\n\n> - replace cdata sections with empty lines (in\n[…]\n is now\ndeprecated: From Fluid 5 onwards, CDATA sections will just remain in\nFluid templates untouched. Until then, a deprecation will be emitted\nto let users know that this will change in the future.",
"is_bot": false,
"headline": "[TASK] Deprecate CDATA removal from templates (#1231)",
"author_name": "Michael Staatz",
"author_login": "staatzstreich",
"committed_at": "2025-11-03T18:29:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b09cf355b072aa56e96a3f5dc042445d048db02b",
"body": "In preparation for the possible introduction of more internal\nconstants in Fluid, custom variable names that start with an\nunderscore are no longer allowed.\n\nFor Fluid 4, a deprecation will be introduced in a follow-up patch\nto prepare template authors for the upcoming change.\n\nRelated: #1166\nRelated: #1041",
"is_bot": false,
"headline": "[!!!][TASK] Disallow variables that start with a \"_\" (#1225)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-03T18:25:32Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "640c57cff9f046b59fa530e185ced5200057777e",
"body": "In preparation for the possible introduction of more internal\nconstants in Fluid, custom variable names that start with an\nunderscore will no longer be allowed.\n\nFor Fluid v4, a deprecation is emitted. In a follow-up for Fluid v5,\nthe deprecation will be replaced with an exception.\n\nRelated: #1166\nRelated: #1041",
"is_bot": false,
"headline": "[TASK] Deprecate variables that start with a \"_\" (#1253)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-11-03T18:19:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "8101bb04a438493ff5787cbb5a9f76b9cf817f93",
"body": null,
"is_bot": false,
"headline": "[TASK] Remove a duplicate header comment (#1255)",
"author_name": "Elias Häußler",
"author_login": "eliashaeussler",
"committed_at": "2025-11-01T11:11:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "bd53a073ef487af0d000c94997826270f58a2bea",
"body": null,
"is_bot": false,
"headline": "[TASK] Correct return value comment in `f:max` VH (#1254)",
"author_name": "Elias Häußler",
"author_login": "eliashaeussler",
"committed_at": "2025-11-01T11:09:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ecdcabb67d31b9de164b240192146a289316d666",
"body": "MinViewHelper and MaxViewHelper\n\nExamples\n```\n<f:min value=\"{0: '1', 1: '2', 2: '3'}\" />\n<!-- 1 -->\n\n<f:max value=\"{0: '1', 1: '2', 2: '3'}\" />\n<!-- 3 -->\n```",
"is_bot": false,
"headline": "[FEATURE] Add f:min and f:max ViewHelpers (#1245)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-10-31T16:09:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "58bd87b3dea0ee1c88550f1d350dce4f3c2acaaf",
"body": null,
"is_bot": false,
"headline": "[DOCS] Warn about using f:form.<something> in Components (#1250)",
"author_name": "Lina Wolf",
"author_login": "linawolf",
"committed_at": "2025-10-15T12:56:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "ffc4a8eb80653acf85ce2186a685772d92d6bab4",
"body": null,
"is_bot": false,
"headline": "[TASK] Increase test coverage for template path resolving (#1241)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-21T16:38:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4dc0cc54007d0867ad0bc57578b5c0859ff0966b",
"body": "With the `LenientArgumentProcessor`, which was previously used for\nViewHelper arguments, it was already possible to specify multiple\ntypes for an argument. However, that definition was silently ignored\nduring validation (see `ImageViewHelper` in TYPO3 Core).\n\nWith the introduction of the `StrictArgu\n[…]\nine the desired type automatically. Thus,\nif union types are used, all possible types need to be specified\nexplicitly and need to be dealt manually in the ViewHelper's\nimplementation.\n\nResolves: #1130",
"is_bot": false,
"headline": "[FEATURE] Support union types for ViewHelper and component API (#1226)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-16T14:46:35Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "b4d31333ee61944c97bd4faef0fb018b450d2470",
"body": "Type declarations are added to all ViewHelper methods as well as\nall properties that are considered internal and are most likely not\nredefined directly by a ViewHelper implementation. Three properties\nare left out intentionally, since they are currently part of the\nViewHelper API:\n\n* `AbstractViewHe\n[…]\nHelper::$escapeChildren`\n* `AbstractTagBasedViewHelper::$tagName`\n\nAdding type declarations to those properties would make it impossible\nto create ViewHelpers that are compatible with Fluid v4 and v5.",
"is_bot": false,
"headline": "[!!!][TASK] Add type declarations to ViewHelper classes (#1219)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-16T13:39:15Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ccff4a587599899ee8a43ce7cc9ab5117c9387db",
"body": "RoundViewHelper, CeilViewHelper and FloorViewHelper provide three different ways of rounding to Fluid template.\n\nExamples\n\n```\n<f:round value=\"123.456\" /> <!-- Outputs 123.46 -->\n<f:ceil value=\"123.456\" /> <!-- Outputs 124 -->\n<f:floor value=\"123.456\" /> <!-- Outputs 123 -->\n```\n\nThis continues the work of Pullrequest:\n[[FEATURE] Add f:round, f:ceil and f:floor ViewHelpers #868](https://github.com/TYPO3/Fluid/pull/868)",
"is_bot": false,
"headline": "[FEATURE] Add f:round, f:ceil and f:floor ViewHelpers (#1220)",
"author_name": "Michael Staatz",
"author_login": "staatzstreich",
"committed_at": "2025-09-16T13:17:18Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "856b0811ed113e5b9bf30d4f28d30099155fed92",
"body": "Because of deprecations and other prerequisites in v4, we are now\nable to accumulate all ViewHelper-related validation code in\n`ViewHelperInvoker`.",
"is_bot": false,
"headline": "[!!!][TASK] Accumulate validation code in ViewHelperInvoker (#1216)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-16T12:51:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3205b57fa86c3f045bc0ab7031b106fc3876910c",
"body": "For compatibility reasons, Fluid interpreted tag attributes that were set\nto `null` as empty strings in `TagBuilder`, which resulted in an empty\ntag attribute (`data-attribute=\"\"`). With this patch, that behavior is\nchanged: `null` values now lead to the attribute being omitted from the\ntag, just like `false` does.\n\nBefore:\n\n```xml\n<my:tagBased data-attribute=\"{null}\" />\nResult: <div data-attribute=\"\"></div>\n```\n\nAfter:\n\n```xml\n<my:tagBased data-attribute=\"{null}\" />\nResult: <div></div>\n```",
"is_bot": false,
"headline": "[!!!][TASK] Remove tag attributes if set to null (#1233)",
"author_name": "Bill.大狗",
"author_login": "billdagou",
"committed_at": "2025-09-16T12:14:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3c0131560abf04280f9146f43c23eb6789219e6d",
"body": "This is a follow-up from #1236. The internal changes are now reflected\nin the public method signatures as well.",
"is_bot": false,
"headline": "[!!!][TASK] Don't allow null as template identifier in parser (#1239)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-13T13:24:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "90f2f03397bfa1107c09f516388ff16c54c3eb09",
"body": "This patch addresses various deprecations that were introduced in\nrecent dev versions of PHP 8.5. All changes should be non-breaking\nand thus can be backported to Fluid v4.\n\n* `TemplateParser` now uses empty strings instead of `null` as array\n key for runtime cache. Public API will be adjusted in v\n[…]\n\n* GroupedForViewHelper ensures to use an empty string instead of\n `null` for array key to avoid the deprecation and considered\n non-breaking because php converted null-index to `\"\"` index\n anyway.",
"is_bot": false,
"headline": "[TASK] Address deprecations introduced by PHP 8.5 (#1236)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-09-13T12:14:55Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "83a314972330ee876c2028c0cb104333908a8172",
"body": "The method is never called or referenced. Also, there don't\nseem to be any usages of 3rd-party packages.",
"is_bot": false,
"headline": "[TASK] Remove unused resetState() method (#1217)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-22T10:03:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "060b4787f1dbcac42a642e7196a6b8333199ca41",
"body": "If a component or ViewHelper argument is not required and is not\nsupplied in the ViewHelper call, Fluid takes care of filling in the\ndefault value. But this also happens if the argument is supplied and\nis set to `NULL`. This usually happens if an undefined variable is\nprovided to the ViewHelper. For\n[…]\ntrictArgumentProcessor` prevents the\nfallback from happening if the argument is supplied, but with `NULL`\nas value. This patch adjusts the `StrictArgumentProcessor` to also\npass `NULL` values through.",
"is_bot": false,
"headline": "[BUGFIX] Allow NULL for optional arguments with default (#1221)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-22T09:16:03Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e154ee806eea1579878ee278947502adc87ec5ca",
"body": "This is a clean-up after #1173.",
"is_bot": false,
"headline": "[TASK] Remove obsolete $tagAttributes property (#1218)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-20T18:12:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "21ac61973dd0091efc45ab3067951a412aae2dca",
"body": "This patch introduces new API methods on the RenderingContext to\nobtain and replace Fluid's argument processor implementation. By\ndefault, the StrictArgumentProcessor is used, but the\nArgumentProcessorInterface exists to provide an alternative\nimplementation. However, it might be a good idea wrap cu\n[…]\ne StrictArgumentProcessor by using\nthe decorator design pattern to ensure valid input types\nto ViewHelpers and components.\n\nThis is a breaking feature because of the interface change.\n\nResolves: #1105",
"is_bot": false,
"headline": "[!!!][FEATURE] Ability to replace argument processor (#1204)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-20T17:11:35Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "7fd755e367f525f309bff2833b3e51dae6e306e5",
"body": "Since Fluid v5 will ship with a new, improved implementation of\ncache warmup, the old code related to warmup has been deprecated\nin Fluid v4 and is now removed. The new cache warmup feature is\nadded in a follow-up patch.",
"is_bot": false,
"headline": "[!!!][TASK] Remove old cache warmup implementation (#1209)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-20T16:47:52Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c4218a45f83c8dafdaabc0e1db2e30388bb6b746",
"body": "…#1189)\" (#1214)\n\nThis reverts commit 54cddfd80801ae100af0b33c6671d801b9d3847a.\n\nreact/promise 3.3.0 release fixed the issue.",
"is_bot": false,
"headline": "Revert \"[TASK] Apply react/promise patch to fix PHP 8.5 deprecation (…",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2025-08-20T12:45:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e36d4c2651eab8af6a6dfb265626555c4ed276d7",
"body": "Fluid v4.2.0 introduced a new API to process component and ViewHelper\narguments (see #1062). To avoid breaking changes in Fluid v4 with\nhow ViewHelpers handle their arguments, a `LenientArgumentProcessor`\nhas been introduced which has been used exclusively for ViewHelpers.\nComponents have used the n\n[…]\n required, integer given\").\n\nThere might be consequences for custom ViewHelpers, however in\nmost cases this change will make a lot of custom validation code\nthat was necessary until Fluid v5 obsolete.",
"is_bot": false,
"headline": "[!!!][TASK] Strict argument processing for ViewHelpers (#1194)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-18T14:16:07Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "8271df914934968442f96cd47e4353b33e0d1dd1",
"body": "The current implementation of `AbstractViewHelper` includes\nFluid's validation logic for ViewHelper arguments in the public\nmethod `validateArguments()`. This method is called internally\nby each ViewHelper in `initializeArgumentsAndRender()`, just\nbefore initialization and rendering of the ViewHelpe\n[…]\nstom validation steps after the internal\nvalidation has been completed successfully. This should allow\nusers that previously overrode `validateArguments()` to\nmove their custom logic to the new event.",
"is_bot": false,
"headline": "[FEATURE] Introduce ViewHelperArgumentsValidated event (#1200) (#1213)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-18T14:11:29Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "65688c56d6a5f23d83eb4d7b77e5d7288b945ee3",
"body": "In order to implement a reasonable cache warmup, the template\ncache needs to be independent of the context where the template\nwill be used later in the project. For cache files, this means that\nthey cannot limit how a template will be used later on\n(partial, layout or template).\n\nThis patch removes the context-dependent prefixes for cache\nfile names to achieve this.",
"is_bot": false,
"headline": "[TASK] Avoid context-dependent prefixes for cache files (#1205)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-18T14:03:13Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cc9d2c77010e43d74210bba59d9778229d73f0ee",
"body": "Use alias offsetSet() instead: \"The SplObjectStorage::contains(),\nSplObjectStorage::attach(), and SplObjectStorage::detach() methods\nhave been deprecated in favour of SplObjectStorage::offsetExists(),\nSplObjectStorage::offsetSet(), and SplObjectStorage::offsetUnset()\nrespectively.\"\n\nReleases: main, 4.5",
"is_bot": false,
"headline": "[TASK] Avoid PHP 8.5 deprecation \\SplObjectStorage->attach() (#1212)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2025-08-18T14:00:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "31580e8d469a5d7b734bb84a65c66b0c84266101",
"body": "Fluid v5 will have a straightforward way to warmup the cache of\nFluid templates. As preparation for that, all code related to the\nold cache warmup is now deprecated.\n\nThis includes the `<f:cache.warmup>` ViewHelper, which allowed\nto provide special context to templates during cache warmup.\nSince all\n[…]\n dependencies of Fluid templates have been\ndeprecated and will no longer be an issue with Fluid v5, this\nworkaround will no longer be necessary.\n\nCo-authored-by: Simon Praetorius <simon@praetorius.me>",
"is_bot": false,
"headline": "[TASK] Deprecate old cache warmup feature (#1211)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2025-08-18T13:34:25Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "65ef1d82edcccbcb7953bb5db31eb3a447728773",
"body": "Two new ViewHelpers `<f:startsWith>` and `<f:endsWith>` are added to Fluid. `<f:startsWith>` checks if the subject string starts with a specified string. `<f:endsWith>` checks if the subject string ends with a specified string. These ViewHelpers extend the AbstractConditionViewHelper.\n\nArguments:\n\ns\n[…]\nHello World\">\n\n</f:startsWith>\n<f:endsWith search=\"World\" subject=\"Hello World\">\n\n</f:endsWith>\n\n<f:variable name=\"result\" value=\"{myVariable -> f:startsWith(search: 'Hello')}\" />\n```\n\nResolves: #1126",
"is_bot": false,
"headline": "[FEATURE] <f:startsWith> and <f:endsWith> ViewHelpers (#1170)",
"author_name": "Michael Staatz",
"author_login": "staatzstreich",
"committed_at": "2025-08-17T16:31:49Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c90fffb2fa806f24d4d6f64c89a5d4c6dfef01f3",
"body": "A new <f:merge> ViewHelper is introduced to Fluid, which merges two arrays, optionally recursively.\n\nArguments:\n\narray $array\narray $with\nbool $recursive\nSee also [the php array_merge()](https://www.php.net/manual/en/function.array-merge.php) and [the php array_merge_recursive()](https://www.php.net\n[…]\n.php) functions.\n\nExamples:\n\n```\n<f:variable name=\"merged\" value=\"{f:merge(array: input, with: toMerge)}\" />\n<f:variable name=\"merged\" value=\"{input -> f:merge(with: toMerge)}\" />\n```\n\nResolves: #1123",
"is_bot": false,
"headline": "[FEATURE] <f:merge> ViewHelper (#1206)",
"author_name": "Michael Staatz",
"author_login": "staatzstreich",
"committed_at": "2025-08-17T16:08:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "a264bd2b976f4c759543039adb1757673813b05f",
"body": "In #1151, it was already clarified that the `HtmlspecialcharsViewHelper`\nis in fact not used internally by Fluid. This means that we can also\nget rid of the custom compilation logic, which makes maintainance\nunnecessarily hard.",
"is_bot": false,
"headline": "[TASK] Remove micro-optimization from HtmlspecialcharsViewHelper (#1197)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-15T15:54:26Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0a756cb838d0f073851e4a2570b73107b5df0ee3",
"body": "Currently, the ViewHelper validation API calls the internal ViewHelper\nAPI to obtain its argument definitions for validation. However, the\nproper way is to use the `ViewHelperResolver`, since it is possible\nto redefine certain argument definitions there.\n\nThis is demonstrated and tested by the `CustomViewHelperResolver`\nin the examples. However, this example only worked until now because\nof the lenient validation implementation of ViewHelpers.",
"is_bot": false,
"headline": "[BUGFIX] Call proper API to obtain ViewHelper API (#1199)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-15T15:54:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7fc105cacaff2825366aedb318d50a35b10e6caa",
"body": "The ViewHelper is called with a `string`, but the argument is declared\nas `integer`.",
"is_bot": false,
"headline": "[BUGFIX] Address bug in test ViewHelper",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-15T14:50:05Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d39fc164472acc12b9246e275a9c4817616c5841",
"body": null,
"is_bot": false,
"headline": "[TASK] Apply new cgl rules",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-15T13:54:46Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1f8a8cdbdd51ab72850dce0168bfe07a8824b5ee",
"body": "With TYPO3 v14, there will be a deeper integration of the new\ncomponents feature into TYPO3. At that point, TYPO3-specific\ndocumentation of components can be added to the official TYPO3\ndocumentation.\n\nFor now, we try to cover TYPO3-specific parts in the Fluid\ndocumentation by adding tips in the relevant places.",
"is_bot": false,
"headline": "[DOCS] Improve TYPO3-related sections of components docs (#1188)",
"author_name": "Simon Praetorius",
"author_login": "s2b",
"committed_at": "2025-08-13T12:44:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "54cddfd80801ae100af0b33c6671d801b9d3847a",
"body": "Dev dependency.\nThis has been fixed upstream but not released, yet.\n\nPHP:\nhttps://github.com/php/php-src/commit/5f8d648af6ef4e29a3c7f2b2029d08466c12bc6f\n\nReact/promise:\nhttps://github.com/reactphp/promise/commit/d3998c8a5d3cff0b39b2f332da550e996f35b947",
"is_bot": false,
"headline": "[TASK] Apply react/promise patch to fix PHP 8.5 deprecation (#1189)",
"author_name": "Christian Kuhn",
"author_login": "lolli42",
"committed_at": "2025-08-13T12:15:36Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "866c192774e7367a4f58ef6d54e1c547f4dd94e2",
"body": "Removed the `get` method entirely and enhanced `getByPath` to handle both local variables and chain traversal. Updated tests to cover the new behavior, improving test robustness by adding cases for nested and array-based paths.\n\nResolves: #1164",
"is_bot": false,
"headline": "[BUGFIX] Fix path access in `ChainedVariableProvider::get` (#1165)",
"author_name": "Sascha Egerer",
"author_login": "sascha-egerer",
"committed_at": "2025-08-12T13:05:56Z",
"body_truncated": false,
"is_coding_agent": false
}
],
"releases_count": 38,
"commits_last_year": 126,
"latest_release_at": "2026-04-16T17:27:38Z",
"latest_release_tag": "4.6.1",
"releases_from_tags": false,
"days_since_last_push": 46,
"active_weeks_last_year": 26,
"days_since_latest_release": 105,
"mean_days_between_releases": 15.9
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": true,
"health_percentage": 50,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "typo3fluid/fluid",
"exists": true,
"license": "LGPL-3.0-or-later",
"keywords": [],
"ecosystem": "packagist",
"matches_repo": true,
"registry_url": "https://packagist.org/packages/typo3fluid/fluid",
"is_deprecated": false,
"latest_version": "5.3.1",
"repository_url": "https://github.com/TYPO3/Fluid",
"versions_count": 117,
"total_downloads": 16388135,
"dependents_count": 90,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": 401469,
"first_published_at": null,
"latest_published_at": "2026-04-16T17:09:54Z",
"latest_version_yanked": null,
"days_since_latest_publish": 105
}
]
},
"popularity": {
"forks": 107,
"stars": 169,
"watchers": 22,
"fork_history": {
"days": [
{
"date": "2015-09-16",
"count": 1
},
{
"date": "2015-09-18",
"count": 1
},
{
"date": "2015-09-24",
"count": 1
},
{
"date": "2016-01-29",
"count": 1
},
{
"date": "2016-02-03",
"count": 1
},
{
"date": "2016-03-01",
"count": 1
},
{
"date": "2016-03-05",
"count": 1
},
{
"date": "2016-04-04",
"count": 1
},
{
"date": "2016-04-07",
"count": 1
},
{
"date": "2016-09-16",
"count": 1
},
{
"date": "2016-09-21",
"count": 1
},
{
"date": "2017-01-30",
"count": 1
},
{
"date": "2017-02-10",
"count": 1
},
{
"date": "2017-03-19",
"count": 1
},
{
"date": "2017-04-08",
"count": 1
},
{
"date": "2017-05-14",
"count": 1
},
{
"date": "2017-07-05",
"count": 1
},
{
"date": "2017-07-11",
"count": 1
},
{
"date": "2017-07-28",
"count": 1
},
{
"date": "2017-07-30",
"count": 1
},
{
"date": "2017-09-27",
"count": 1
},
{
"date": "2017-10-19",
"count": 1
},
{
"date": "2017-10-28",
"count": 1
},
{
"date": "2017-12-06",
"count": 1
},
{
"date": "2017-12-07",
"count": 1
},
{
"date": "2017-12-29",
"count": 1
},
{
"date": "2018-01-08",
"count": 1
},
{
"date": "2018-01-15",
"count": 1
},
{
"date": "2018-01-18",
"count": 1
},
{
"date": "2018-02-08",
"count": 1
},
{
"date": "2018-02-28",
"count": 1
},
{
"date": "2018-05-08",
"count": 1
},
{
"date": "2018-05-29",
"count": 1
},
{
"date": "2018-05-30",
"count": 1
},
{
"date": "2018-07-18",
"count": 1
},
{
"date": "2018-12-05",
"count": 1
},
{
"date": "2019-03-13",
"count": 1
},
{
"date": "2019-03-28",
"count": 1
},
{
"date": "2019-04-08",
"count": 1
},
{
"date": "2019-05-15",
"count": 1
},
{
"date": "2019-06-19",
"count": 1
},
{
"date": "2019-06-20",
"count": 3
},
{
"date": "2019-06-22",
"count": 1
},
{
"date": "2019-07-31",
"count": 1
},
{
"date": "2019-08-03",
"count": 1
},
{
"date": "2019-09-25",
"count": 1
},
{
"date": "2020-01-08",
"count": 1
},
{
"date": "2020-01-29",
"count": 1
},
{
"date": "2020-03-29",
"count": 1
},
{
"date": "2020-04-30",
"count": 1
},
{
"date": "2020-05-27",
"count": 1
},
{
"date": "2020-10-27",
"count": 1
},
{
"date": "2020-12-16",
"count": 1
},
{
"date": "2021-04-28",
"count": 1
},
{
"date": "2021-05-04",
"count": 1
},
{
"date": "2021-05-12",
"count": 1
},
{
"date": "2021-06-02",
"count": 1
},
{
"date": "2021-06-08",
"count": 1
},
{
"date": "2021-08-30",
"count": 1
},
{
"date": "2021-10-21",
"count": 1
},
{
"date": "2021-10-26",
"count": 1
},
{
"date": "2021-11-15",
"count": 1
},
{
"date": "2021-11-19",
"count": 1
},
{
"date": "2022-02-07",
"count": 1
},
{
"date": "2022-05-26",
"count": 1
},
{
"date": "2022-07-14",
"count": 1
},
{
"date": "2022-07-29",
"count": 1
},
{
"date": "2022-07-30",
"count": 1
},
{
"date": "2022-08-05",
"count": 1
},
{
"date": "2022-09-08",
"count": 1
},
{
"date": "2022-09-20",
"count": 1
},
{
"date": "2022-11-10",
"count": 1
},
{
"date": "2022-12-09",
"count": 1
},
{
"date": "2023-01-30",
"count": 1
},
{
"date": "2023-03-22",
"count": 1
},
{
"date": "2023-04-18",
"count": 1
},
{
"date": "2023-04-26",
"count": 1
},
{
"date": "2023-05-11",
"count": 1
},
{
"date": "2023-11-24",
"count": 1
},
{
"date": "2023-12-01",
"count": 1
},
{
"date": "2023-12-27",
"count": 1
},
{
"date": "2024-01-12",
"count": 1
},
{
"date": "2024-03-14",
"count": 1
},
{
"date": "2024-05-21",
"count": 1
},
{
"date": "2024-09-27",
"count": 1
},
{
"date": "2024-10-18",
"count": 1
},
{
"date": "2025-02-11",
"count": 1
},
{
"date": "2025-03-27",
"count": 1
},
{
"date": "2025-04-20",
"count": 1
},
{
"date": "2025-04-27",
"count": 1
},
{
"date": "2025-05-05",
"count": 1
},
{
"date": "2025-05-19",
"count": 1
},
{
"date": "2025-05-22",
"count": 1
},
{
"date": "2025-07-29",
"count": 1
},
{
"date": "2025-08-02",
"count": 1
},
{
"date": "2025-08-04",
"count": 1
},
{
"date": "2025-08-07",
"count": 2
},
{
"date": "2025-08-09",
"count": 1
},
{
"date": "2025-08-29",
"count": 1
},
{
"date": "2025-09-04",
"count": 1
},
{
"date": "2025-09-05",
"count": 1
},
{
"date": "2025-09-09",
"count": 1
},
{
"date": "2026-02-19",
"count": 1
},
{
"date": "2026-04-25",
"count": 1
}
],
"complete": true,
"collected": 107,
"total_forks": 107
},
"star_history": null,
"open_issues_and_prs": 72
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [
"examples"
],
"has_llms_txt": false,
"has_dockerfile": false,
"has_mcp_signal": false,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [],
"largest_source_bytes": 42311,
"source_files_sampled": 381,
"oversized_source_files": 0,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"dependencies": {
"manifests": [
"composer.json"
],
"advisories": {
"error": "No resolved dependencies carried a version and a supported ecosystem",
"scope": "repository_graph",
"source": null,
"findings": [],
"collected": false,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 0,
"malicious_count": 0,
"assessed_package": null,
"unassessed_count": 11,
"direct_affected_count": 0
},
"ecosystems": [
"packagist"
],
"dependencies": [],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "ext-json",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "ext-mbstring",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "ext-simplexml",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "friendsofphp/php-cs-fixer",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "infection/infection",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "php",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan-phpunit",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpunit/phpunit",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "psr/container",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "t3docs/fluid-documentation-generator",
"direct": false,
"version": null,
"ecosystem": "packagist"
}
],
"collected": true,
"truncated": false,
"total_count": 11,
"direct_count": 0,
"indirect_count": 11
}
},
"maintainership": {
"issues": {
"open_prs": 13,
"merged_prs": 924,
"open_issues": 59,
"closed_ratio": 0.818,
"closed_issues": 265,
"closed_unmerged_prs": 122
},
"bus_factor": 3,
"bot_contributors": 0,
"top_contributors": [
{
"type": "User",
"login": "NamelessCoder",
"commits": 343,
"avatar_url": "https://avatars.githubusercontent.com/u/2228052?v=4"
},
{
"type": "User",
"login": "s2b",
"commits": 258,
"avatar_url": "https://avatars.githubusercontent.com/u/458524?v=4"
},
{
"type": "User",
"login": "lolli42",
"commits": 229,
"avatar_url": "https://avatars.githubusercontent.com/u/2178068?v=4"
},
{
"type": "User",
"login": "skurfuerst",
"commits": 222,
"avatar_url": "https://avatars.githubusercontent.com/u/190777?v=4"
},
{
"type": "User",
"login": "kdambekalns",
"commits": 181,
"avatar_url": "https://avatars.githubusercontent.com/u/95873?v=4"
},
{
"type": "User",
"login": "robertlemke",
"commits": 104,
"avatar_url": "https://avatars.githubusercontent.com/u/95582?v=4"
},
{
"type": "User",
"login": "afoeder",
"commits": 23,
"avatar_url": "https://avatars.githubusercontent.com/u/1125168?v=4"
},
{
"type": "User",
"login": "sbuerk",
"commits": 20,
"avatar_url": "https://avatars.githubusercontent.com/u/1453466?v=4"
},
{
"type": "User",
"login": "hlubek",
"commits": 18,
"avatar_url": "https://avatars.githubusercontent.com/u/33351?v=4"
},
{
"type": "User",
"login": "maddy2101",
"commits": 16,
"avatar_url": "https://avatars.githubusercontent.com/u/1925287?v=4"
}
],
"contributors_sampled": 100,
"top_contributor_share": 0.211
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"build.yml",
"test-documentation.yml"
],
"has_docs_dir": true,
"linter_configs": [
".php-cs-fixer.dist.php"
],
"has_editorconfig": true,
"has_linter_config": true,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": null,
"reason": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 10,
"reason": "30 out of 30 merged PRs checked by a CI test -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 4,
"reason": "Found 14/30 approved changesets -- score normalized to 4",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 10,
"reason": "project has 37 contributing companies or organizations",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": null,
"reason": "no workflows found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 0,
"reason": "no update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 0,
"reason": "project is not fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 1,
"reason": "2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": null,
"reason": "packaging workflow not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": null,
"reason": "no dependencies found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 0,
"reason": "SAST tool is not run on all commits -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": null,
"reason": "No tokens found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 10,
"reason": "0 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "ef902a525a983f6dde46c37c9f0c1a912da3a7cc",
"ran_at": "2026-07-30T22:49:36Z",
"aggregate_score": 4.2,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": false
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-07-30T07:59:28Z",
"oldest_open_prs": [
{
"number": 396,
"created_at": "2018-07-16T09:21:56Z",
"last_comment_at": "2018-07-16T10:45:28Z",
"last_comment_author": "NamelessCoder"
},
{
"number": 398,
"created_at": "2018-07-26T13:48:29Z",
"last_comment_at": "2019-03-25T12:22:12Z",
"last_comment_author": "coveralls"
},
{
"number": 469,
"created_at": "2019-07-31T12:56:57Z",
"last_comment_at": "2019-07-31T13:46:46Z",
"last_comment_author": "mbrodala"
},
{
"number": 572,
"created_at": "2021-10-26T14:48:46Z",
"last_comment_at": "2025-06-02T11:45:05Z",
"last_comment_author": "s2b"
},
{
"number": 671,
"created_at": "2022-12-09T11:31:37Z",
"last_comment_at": "2023-08-16T16:12:17Z",
"last_comment_author": "spthiel"
},
{
"number": 1052,
"created_at": "2025-04-16T22:18:00Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 1229,
"created_at": "2025-09-04T21:09:55Z",
"last_comment_at": "2026-02-15T20:52:17Z",
"last_comment_author": "schliesser"
},
{
"number": 1234,
"created_at": "2025-09-11T03:12:05Z",
"last_comment_at": "2025-09-16T01:10:28Z",
"last_comment_author": "billdagou"
},
{
"number": 1271,
"created_at": "2025-12-07T17:22:13Z",
"last_comment_at": "2026-03-13T06:50:54Z",
"last_comment_author": "mbrodala"
},
{
"number": 1321,
"created_at": "2026-02-19T17:32:25Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 1372,
"created_at": "2026-04-15T17:13:17Z",
"last_comment_at": "2026-04-16T17:30:49Z",
"last_comment_author": "lolli42"
},
{
"number": 1375,
"created_at": "2026-04-24T05:37:03Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 1384,
"created_at": "2026-07-01T08:30:27Z",
"last_comment_at": null,
"last_comment_author": null
}
],
"last_merged_pr_at": "2026-06-14T12:03:57Z",
"ci_last_conclusion": "SUCCESS",
"oldest_open_issues": [
{
"number": 177,
"created_at": "2016-09-29T14:30:00Z",
"last_comment_at": null,
"last_comment_author": null
},
{
"number": 212,
"created_at": "2016-10-27T06:06:11Z",
"last_comment_at": "2016-10-27T11:07:13Z",
"last_comment_author": "AndreasA"
},
{
"number": 274,
"created_at": "2017-03-02T13:38:13Z",
"last_comment_at": "2023-11-24T16:34:29Z",
"last_comment_author": "lolli42"
},
{
"number": 280,
"created_at": "2017-04-18T15:46:10Z",
"last_comment_at": "2023-11-24T17:40:31Z",
"last_comment_author": "s2b"
},
{
"number": 336,
"created_at": "2017-08-10T14:47:54Z",
"last_comment_at": "2023-05-15T12:31:53Z",
"last_comment_author": "masi"
},
{
"number": 373,
"created_at": "2018-02-27T08:17:27Z",
"last_comment_at": "2018-12-07T15:16:55Z",
"last_comment_author": "NamelessCoder"
},
{
"number": 382,
"created_at": "2018-03-21T14:59:30Z",
"last_comment_at": "2023-11-24T17:07:41Z",
"last_comment_author": "lolli42"
},
{
"number": 391,
"created_at": "2018-07-04T08:16:31Z",
"last_comment_at": "2021-06-03T11:41:15Z",
"last_comment_author": "aimeos"
},
{
"number": 407,
"created_at": "2018-09-28T09:19:39Z",
"last_comment_at": "2021-01-22T12:53:14Z",
"last_comment_author": "NamelessCoder"
},
{
"number": 412,
"created_at": "2018-10-22T09:51:41Z",
"last_comment_at": "2023-11-24T17:19:23Z",
"last_comment_author": "lolli42"
},
{
"number": 417,
"created_at": "2018-11-07T11:12:31Z",
"last_comment_at": "2021-05-18T20:23:08Z",
"last_comment_author": "bmack"
},
{
"number": 428,
"created_at": "2019-02-14T14:37:42Z",
"last_comment_at": "2021-05-21T08:58:04Z",
"last_comment_author": "hilburger"
},
{
"number": 514,
"created_at": "2020-03-06T21:06:23Z",
"last_comment_at": "2021-05-20T19:18:32Z",
"last_comment_author": "hilburger"
},
{
"number": 522,
"created_at": "2020-09-02T13:37:53Z",
"last_comment_at": "2023-11-24T17:39:23Z",
"last_comment_author": "s2b"
},
{
"number": 533,
"created_at": "2021-03-17T18:11:28Z",
"last_comment_at": "2024-08-11T09:17:14Z",
"last_comment_author": "s2b"
},
{
"number": 556,
"created_at": "2021-07-08T16:30:57Z",
"last_comment_at": "2025-03-25T07:47:12Z",
"last_comment_author": "mbrodala"
},
{
"number": 590,
"created_at": "2022-05-07T22:23:38Z",
"last_comment_at": "2024-08-11T08:59:25Z",
"last_comment_author": "s2b"
},
{
"number": 938,
"created_at": "2024-07-25T10:35:50Z",
"last_comment_at": "2024-08-11T08:04:40Z",
"last_comment_author": "s2b"
},
{
"number": 953,
"created_at": "2024-08-11T07:57:02Z",
"last_comment_at": "2025-02-11T18:39:10Z",
"last_comment_author": "s2b"
},
{
"number": 975,
"created_at": "2024-08-18T10:25:47Z",
"last_comment_at": null,
"last_comment_author": null
}
]
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/TYPO3/Fluid",
"host": "github.com",
"name": "Fluid",
"owner": "TYPO3"
},
"metrics": {
"overall": {
"key": "overall",
"band": "excellent",
"name": "Overall health",
"note": "The weighted overall 68 is calibrated to 80 on the published index scale (record calibration 2026-08-02).",
"notes": [
{
"code": "overall_calibration",
"params": {
"raw": 68,
"calibrated": 80,
"calibration": "2026-08-02"
}
}
],
"value": 80,
"inputs": {
"security": 42,
"vitality": 69,
"community": 70,
"governance": 79,
"calibration": "2026-08-02",
"engineering": 80,
"ai_readiness": 41,
"weighted_overall_raw": 68
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "good",
"name": "Vitality",
"value": 69,
"weight": 0.21,
"metrics": [
{
"key": "development_activity",
"band": "moderate",
"name": "Development activity",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"commits_last_year": 126,
"human_commit_share": 1,
"days_since_last_push": 46,
"active_weeks_last_year": 26
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 46 days ago",
"points": 18,
"status": "partial",
"details": [
{
"code": "push_recency",
"params": {
"days": 46
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "26/52 weeks with commits",
"points": 18,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 26
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "126 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 126
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1",
"points": 1,
"status": "partial",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "excellent",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 90,
"inputs": {
"releases_count": 38,
"latest_release_tag": "4.6.1",
"releases_from_tags": false,
"days_since_latest_release": 105,
"mean_days_between_releases": 15.9
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "38 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 38
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 105 days ago",
"points": 27,
"status": "partial",
"details": [
{
"code": "release_recency",
"params": {
"days": 105
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~15.9 days",
"points": 27,
"status": "met",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 15.9
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "exceptional",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 51,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 51 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 51
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "good",
"name": "Community & Adoption",
"value": 70,
"weight": 0.17,
"metrics": [
{
"key": "popularity",
"band": "moderate",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 60,
"inputs": {
"forks": 107,
"stars": 169,
"watchers": 22,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "169 stars",
"points": 36.1,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 169
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "107 forks",
"points": 16.9,
"status": "partial",
"details": [
{
"code": "forks",
"params": {
"count": 107
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "22 watchers",
"points": 7.4,
"status": "partial",
"details": [
{
"code": "watchers",
"params": {
"count": 22
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "good",
"name": "Community health",
"note": null,
"notes": [],
"value": 70,
"inputs": {
"has_readme": true,
"has_license": true,
"readme_badges": null,
"has_contributing": true,
"has_issue_template": false,
"has_code_of_conduct": false,
"readme_badge_services": [],
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (LGPL-3.0)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "LGPL-3.0"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 18,
"status": "met",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "excellent",
"name": "Ecosystem adoption (downloads)",
"note": null,
"notes": [],
"value": 88,
"inputs": {
"packages": [
"typo3fluid/fluid"
],
"dependents": 90,
"ecosystems": "packagist",
"total_downloads": 16388135,
"monthly_downloads": 401469
},
"components": [
{
"key": "monthly_downloads",
"name": "Monthly downloads",
"detail": "401,469 downloads/month across packagist",
"points": 74.7,
"status": "partial",
"details": [
{
"code": "downloads_monthly",
"params": {
"count": 401469,
"ecosystems": "packagist"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "90 packages depend on it",
"points": 13.1,
"status": "partial",
"details": [
{
"code": "registry_dependents",
"params": {
"count": 90
}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "good",
"name": "Sustainability & Governance",
"value": 79,
"weight": 0.23,
"metrics": [
{
"key": "maintainer_resilience",
"band": "good",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 77,
"inputs": {
"bus_factor": 3,
"contributors_sampled": 100,
"top_contributor_share": 0.211
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "3 contributor(s) cover half of all commits",
"points": 36,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 3
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 21% of commits",
"points": 17.8,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 21
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "100 contributors",
"points": 13.5,
"status": "met",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 100
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 37 contributing companies or organizations",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "good",
"name": "Issue & PR responsiveness",
"note": "Excluded from scoring (no data or not applicable): Newcomer PR acceptance. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"newcomer_pr_acceptance"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 77,
"inputs": {
"merged_prs": 924,
"open_issues": 59,
"closed_issues": 265,
"prs_merged_7d": null,
"prs_decided_7d": null,
"prs_merged_30d": null,
"prs_decided_30d": null,
"issue_closed_ratio": 0.818,
"closed_unmerged_prs": 122,
"first_time_authors_30d": null,
"first_time_prs_merged_30d": null,
"first_time_prs_decided_30d": null
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "82% of issues closed",
"points": 34.4,
"status": "partial",
"details": [
{
"code": "issues_closed_share",
"params": {
"share": 82
}
}
],
"max_points": 42
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "924/1046 decided PRs merged",
"points": 26.5,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 924,
"decided": 1046
}
}
],
"max_points": 30
},
{
"key": "newcomer_pr_acceptance",
"name": "Newcomer PR acceptance",
"detail": "no first-time contributor's PR decided in 30d",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_newcomer_prs",
"params": {
"days": 30
}
}
],
"max_points": 13
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 14/30 approved changesets -- score normalized to 4",
"points": 6,
"status": "partial",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "good",
"name": "Ownership & stewardship",
"note": null,
"notes": [],
"value": 67,
"inputs": {
"followers": 117,
"owner_type": "Organization",
"is_verified": null,
"owner_login": "TYPO3",
"public_repos": 23,
"account_age_days": 6274
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "organization-owned",
"points": 30,
"status": "met",
"details": [
{
"code": "owner_organization",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "117 followers of TYPO3",
"points": 14.9,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 117,
"login": "TYPO3"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "23 public repos, account ~17 yr old",
"points": 22.1,
"status": "partial",
"details": [
{
"code": "public_repos",
"params": {
"count": 23
}
},
{
"code": "account_age_years",
"params": {
"years": 17
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "exceptional",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"typo3fluid/fluid"
],
"ecosystems": "packagist",
"any_deprecated": false,
"min_days_since_publish": 105
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on packagist",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "packagist"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 105 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 105
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "117 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 117
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "excellent",
"name": "Engineering Quality",
"value": 80,
"weight": 0.19,
"metrics": [
{
"key": "engineering_practices",
"band": "excellent",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 90,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": true,
"has_linter_config": true,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "2 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 2
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": ".php-cs-fixer.dist.php",
"points": 16,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": ".php-cs-fixer.dist.php"
}
}
],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 6.4,
"status": "met",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "30 out of 30 merged PRs checked by a CI test -- score normalized to 10",
"points": 20,
"status": "met",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "good",
"name": "Documentation",
"note": null,
"notes": [],
"value": 65,
"inputs": {
"topics": [],
"has_wiki": false,
"homepage": null,
"has_readme": true,
"has_docs_dir": true,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 25,
"status": "met",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "weak",
"name": "Security",
"value": 42,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "weak",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Branch-Protection, Dangerous-Workflow, Packaging, Pinned-Dependencies, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"branch_protection",
"dangerous_workflow",
"packaging",
"pinned_dependencies",
"signed_releases",
"token_permissions"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 42,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 12,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 6,
"scorecard_aggregate": 4.2
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "30 out of 30 merged PRs checked by a CI test -- score normalized to 10",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 14/30 approved changesets -- score normalized to 4",
"points": 3,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 37 contributing companies or organizations",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no workflows found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "no update tool detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is not fuzzed",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1",
"points": 0.8,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow not detected",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "no dependencies found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is not run on all commits -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "No tokens found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "0 existing vulnerabilities detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "exceptional",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"commit_weight_rule": {
"min_commits": 50,
"min_commit_share": 0.1
},
"review_only_matches": 0,
"below_threshold_exposures": [],
"assessed_self_published_locations": 22
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "weak",
"name": "AI Readiness",
"value": 41,
"weight": 0.04,
"metrics": [
{
"key": "ai_agent_context",
"band": "weak",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.98,
"agent_instruction_files": [],
"agent_instruction_max_bytes": null
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": "no CLAUDE.md / AGENTS.md / editor rules",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_instructions",
"params": {}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "98 of 100 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 98,
"sampled": 100
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "weak",
"name": "Verify loop (build / test / typecheck)",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Pinned-Dependencies. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_pinned_dependencies"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 37,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [],
"has_dockerfile": false,
"typed_language": false,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": true,
"typecheck_configs": [],
"agent_commit_share": 0,
"toolchain_manifests": [],
"dependency_bot_commit_share": 0
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": ".php-cs-fixer.dist.php",
"points": 11,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": ".php-cs-fixer.dist.php"
}
}
],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "no agent-authored commits among the last 100",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_agent_authored_commits",
"params": {
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "no automated dependency updates observed",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_dependency_automation",
"params": {}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "no dependencies found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "moderate",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"primary_language": "PHP",
"largest_source_bytes": 42311,
"source_files_sampled": 381,
"oversized_source_files": 0
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "PHP without a type-check config",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_typecheck_config_language",
"params": {
"language": "PHP"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "0/381 source files over 60KB",
"points": 55,
"status": "met",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 381,
"oversized": 0
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "weak",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 40,
"inputs": {
"example_dirs": [
"examples"
],
"has_mcp_signal": false,
"api_schema_files": []
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
},
{
"key": "mcp_server",
"name": "MCP server",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "runnable_examples",
"name": "Runnable examples",
"detail": "examples",
"points": 40,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": "examples"
}
}
],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
}
],
"classification": {
"top": [
"library"
],
"labels": [
"library"
],
"scores": {
"library": 6
},
"primary": "library",
"evidence": [
{
"tier": "distribution",
"label": "library",
"source": "registry:packagist",
"weight": 6
}
],
"artifacts": [],
"confidence": "medium",
"host_extension": false,
"runs_as_process": false,
"consumed_by_code": true
},
"metrics_version": "2.5.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
"No resolved dependencies carried a version and a supported ecosystem"
],
"report_type": "repository",
"generated_at": "2026-07-30T22:50:04.584181Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/t/TYPO3/Fluid.svg",
"full_name": "TYPO3/Fluid",
"license_state": "standard",
"license_spdx": "LGPL-3.0"
}