Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 112 additions & 112 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,112 +1,112 @@
# IDE & System Related Files
.buildpath
.project
.settings
.DS_Store
.idea
.vscode
.docker
/docker-compose.yml
/nbproject
/.devcontainer
/.github
/.vscode

# Local System Files (i.e. cache, logs, etc.)
/administrator/cache
/administrator/logs
/cache
/installation/cache
/tmp
/configuration.php
/.htaccess
/web.config
/.php-cs-fixer.cache
/media

# Template CSS files generated by NPM
/installation/template/css

# Test Related Files
/phpunit.xml
composer.phar
/test-install
/.phpunit.result.cache


# Vendor directory handling
/libraries/vendor
!libraries/vendor/.gitkeep

/media/vendor
!media/vendor/.gitkeep

# Stubs file holding mapped classes
/stubs.php

# JS/CSS Build
node_modules/
/build/assets_tmp
/scss-lint-report.xml

# Removed in Joomla 4
administrator/templates/isis
administrator/templates/hathor
templates/beez3
build/generatecss.php
media/jui/less

# CSS map files
.map

# phpDocumentor Logs
phpdoc-*

# Patch Tester
/administrator/components/com_patchtester
/administrator/templates/atum/html/com_patchtester
/components/com_patchtester
/media/com_patchtester

# Languages
administrator/language/*
!administrator/language/en-GB
administrator/manifests/packages/*
!administrator/manifests/packages/pkg_en-GB.xml
!administrator/language/overrides/index.html
api/language/*
!api/language/en-GB
language/*
!language/en-GB
!language/overrides/index.html

# OSX
._*
.Spotlight-V100
.Trashes

# Windows
Thumbs.db
Desktop.ini

# Never ignore
# Only apply this rule to the main repository's gitignore files
!/.gitignore
!/build/.gitignore

# Build related
RoboFile.ini

# Media Manager
/media/com_media/js/mediamanager.min.js.map
/media/com_media/css/mediamanager.min.css.map

#cypress
/tests/System/output/screenshots
!/tests/System/output/screenshots/.gitkeep
/tests/System/output/videos
!/tests/System/output/videos/.gitkeep
cypress.config.mjs

# WebAuthn FIDO metadata cache
/plugins/system/webauthn/fido.jwt
# IDE & System Related Files
.buildpath
.project
.settings
.DS_Store
.idea
.vscode
.docker
/docker-compose.yml
/nbproject
/.devcontainer
/.github
/.vscode
# Local System Files (i.e. cache, logs, etc.)
/administrator/cache
/administrator/logs
/cache
/installation/cache
/tmp
/configuration.php
/.htaccess
/web.config
/.php-cs-fixer.cache
/media
# Template CSS files generated by NPM
/installation/template/css
# Test Related Files
/phpunit.xml
composer.phar
/test-install
/.phpunit.result.cache
# Vendor directory handling
/libraries/vendor
!libraries/vendor/.gitkeep
/media/vendor
!media/vendor/.gitkeep
# Stubs file holding mapped classes
/stubs.php
# JS/CSS Build
node_modules/
/build/assets_tmp
/scss-lint-report.xml
# Removed in Joomla 4
administrator/templates/isis
administrator/templates/hathor
templates/beez3
build/generatecss.php
media/jui/less
# CSS map files
.map
# phpDocumentor Logs
phpdoc-*
# Patch Tester
/administrator/components/com_patchtester
/administrator/templates/atum/html/com_patchtester
/components/com_patchtester
/media/com_patchtester
# Languages
administrator/language/*
!administrator/language/en-GB
administrator/manifests/packages/*
!administrator/manifests/packages/pkg_en-GB.xml
!administrator/language/overrides/index.html
api/language/*
!api/language/en-GB
language/*
!language/en-GB
!language/overrides/index.html
# OSX
._*
.Spotlight-V100
.Trashes
# Windows
Thumbs.db
Desktop.ini
# Never ignore
# Only apply this rule to the main repository's gitignore files
!/.gitignore
!/build/.gitignore
# Build related
RoboFile.ini
# Media Manager
/media/com_media/js/mediamanager.min.js.map
/media/com_media/css/mediamanager.min.css.map
#cypress
/tests/System/output/screenshots
!/tests/System/output/screenshots/.gitkeep
/tests/System/output/videos
!/tests/System/output/videos/.gitkeep
cypress.config.mjs
# WebAuthn FIDO metadata cache
/plugins/system/webauthn/fido.jwt
96 changes: 96 additions & 0 deletions libraries/src/Form/CachingFormFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2025 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

declare(strict_types=1);

namespace Joomla\CMS\Form;

/**
* Caching decorator for the Form factory.
*
* - No interface/signature changes (BC-safe).
* - Reuses the same Form instance for a given (name, options) key within a request.
* - Allows bypassing cache via ['fresh' => true] in $options.
*
* Register via DI to override the default binding of FormFactoryInterface:
* $container->share(FormFactoryInterface::class, function (\Joomla\DI\Container $c) {
* $inner = $c->get('core.form.factory'); // whatever concrete is bound as
* return new CachingFormFactory($inner);
* });
*/
final class CachingFormFactory implements FormFactoryInterface
{
public function __construct(private FormFactoryInterface $inner)
{
}

/** @var array<string, Form> */
private array $cache = [];
/**
* {@inheritdoc}
*/
public function createForm(string $name, array $options = []): Form
{
// Allow callers to opt out of caching explicitly.
if (!empty($options['fresh'])) {
// Do not store in cache when 'fresh' is requested.
$opts = $options;
unset($opts['fresh']);
return $this->inner->createForm($name, $opts);
}

$key = $this->makeKey($name, $options);
return $this->cache[$key] ??= $this->inner->createForm($name, $this->normalizedOptions($options));
}

/**
* Removes a cached Form for the given name/options combination.
* Useful when a caller knows the underlying XML or dynamic fields changed mid-request.
*/
public function invalidate(string $name, array $options = []): void
{
$key = $this->makeKey($name, $options);
unset($this->cache[$key]);
}

/**
* Clears all cached Form instances (per-request scope).
*/
public function invalidateAll(): void
{
$this->cache = [];
}

/**
* Build a stable cache key from name + options.
* Excludes volatile/nonce-like options that shouldn't affect identity.
*/
private function makeKey(string $name, array $options): string
{
$opts = $this->normalizedOptions($options);
// Remove flags that should not influence identity:
unset(
$opts['fresh'], // our local bypass flag
$opts['debug'], // debugging shouldn't split cache entries
$opts['timestamp'] // any time-based hint
);
// Sort for deterministic encoding.
ksort($opts);
return $name . '|' . md5(json_encode($opts, JSON_THROW_ON_ERROR));
}

/**
* Normalize options to ensure deterministic keys and pass-through.
*/
private function normalizedOptions(array $options): array
{
// Ensure consistent types/casing if needed. Adjust as your concrete factory expects.
return $options;
}
}
Loading