Skip to content

Commit b80e128

Browse files
committed
fix: restore caching and data loading in FormFactory and UserFactory (#46369)
Signed-off-by: sshekhar563 <shekharsiddhant93@gmail.com>
1 parent 95a83b5 commit b80e128

File tree

2 files changed

+119
-19
lines changed

2 files changed

+119
-19
lines changed

libraries/src/Form/FormFactory.php

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
<?php
2-
32
/**
43
* Joomla! Content Management System
54
*
6-
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
5+
* @copyright (C) 2017 Open Source Matters, Inc.
76
* @license GNU General Public License version 2 or later; see LICENSE.txt
87
*/
98

109
namespace Joomla\CMS\Form;
1110

1211
use Joomla\Database\DatabaseAwareTrait;
12+
use RuntimeException;
1313

1414
// phpcs:disable PSR1.Files.SideEffects
1515
\defined('_JEXEC') or die;
1616
// phpcs:enable PSR1.Files.SideEffects
1717

1818
/**
19-
* Default factory for creating Form objects
19+
* Default factory for creating Form objects.
20+
*
21+
* This version restores caching and XML data loading behavior
22+
* that was originally part of Form::getInstance(), addressing
23+
* performance and duplication issues noted in #46369.
2024
*
2125
* @since 4.0.0
2226
*/
@@ -25,21 +29,80 @@ class FormFactory implements FormFactoryInterface
2529
use DatabaseAwareTrait;
2630

2731
/**
28-
* Method to get an instance of a form.
32+
* Cache of created form instances.
33+
*
34+
* @var Form[]
35+
*/
36+
private static array $forms = [];
37+
38+
/**
39+
* Creates or returns a cached instance of a form.
2940
*
30-
* @param string $name The name of the form.
31-
* @param array $options An array of form options.
41+
* Behaves similarly to the old Form::getInstance():
42+
* - Returns a cached instance if already loaded.
43+
* - Loads XML or string data into the form.
44+
*
45+
* @param string $name The name of the form.
46+
* @param string|null $data The XML file path or XML string to load.
47+
* @param array $options Optional form options.
48+
* @param bool $replace Whether to replace existing fields.
49+
* @param string|null $xpath XPath to search for fields.
3250
*
3351
* @return Form
3452
*
53+
* @throws RuntimeException When the form cannot be loaded.
54+
*
3555
* @since 4.0.0
3656
*/
37-
public function createForm(string $name, array $options = []): Form
38-
{
39-
$form = new Form($name, $options);
57+
public function createForm(
58+
string $name,
59+
?string $data = null,
60+
array $options = [],
61+
bool $replace = true,
62+
?string $xpath = null
63+
): Form {
64+
// If a form with this name already exists, return the cached instance
65+
if (isset(self::$forms[$name])) {
66+
return self::$forms[$name];
67+
}
4068

69+
// Create a new Form instance
70+
$form = new Form($name, $options);
4171
$form->setDatabase($this->getDatabase());
4272

73+
// Load XML or string data if provided
74+
if ($data) {
75+
// Check if data is an XML string or a file path
76+
if (str_starts_with($data, '<')) {
77+
// Data is XML string
78+
if (!$form->load($data, $replace, $xpath)) {
79+
throw new RuntimeException(sprintf('%s() could not load XML form data', __METHOD__));
80+
}
81+
} else {
82+
// Data is file path
83+
if (!$form->loadFile($data, $replace, $xpath)) {
84+
throw new RuntimeException(sprintf('%s() could not load form file: %s', __METHOD__, $data));
85+
}
86+
}
87+
}
88+
89+
// Cache this form instance
90+
self::$forms[$name] = $form;
91+
4392
return $form;
4493
}
94+
95+
/**
96+
* Clears cached form instances.
97+
*
98+
* Useful for testing or if forms need to be reloaded at runtime.
99+
*
100+
* @return void
101+
*
102+
* @since 4.0.0
103+
*/
104+
public static function clearCache(): void
105+
{
106+
self::$forms = [];
107+
}
45108
}

libraries/src/User/UserFactory.php

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<?php
2-
32
/**
43
* Joomla! Content Management System
54
*
6-
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
5+
* @copyright (C) 2019 Open Source Matters, Inc.
76
* @license GNU General Public License version 2 or later; see LICENSE.txt
87
*/
98

@@ -23,38 +22,56 @@
2322
class UserFactory implements UserFactoryInterface
2423
{
2524
/**
26-
* The database.
25+
* The database connection.
2726
*
2827
* @var DatabaseInterface
2928
*/
3029
private $db;
3130

31+
/**
32+
* Static cache for loaded user instances.
33+
*
34+
* @var User[]
35+
*/
36+
private static array $cache = [];
37+
3238
/**
3339
* UserFactory constructor.
3440
*
35-
* @param DatabaseInterface $db The database
41+
* @param DatabaseInterface $db The database connection
3642
*/
3743
public function __construct(DatabaseInterface $db)
3844
{
3945
$this->db = $db;
4046
}
4147

4248
/**
43-
* Method to get an instance of a user for the given id.
49+
* Method to get a cached instance of a user for the given ID.
50+
* Returns from cache if already loaded, otherwise creates and caches a new instance.
4451
*
45-
* @param int $id The id
52+
* @param int $id The user ID
4653
*
4754
* @return User
4855
*
4956
* @since 4.0.0
5057
*/
5158
public function loadUserById(int $id): User
5259
{
53-
return new User($id);
60+
// Return cached user if available
61+
if (isset(self::$cache[$id])) {
62+
return self::$cache[$id];
63+
}
64+
65+
// Otherwise create and cache a new instance
66+
$user = new User($id);
67+
self::$cache[$id] = $user;
68+
69+
return $user;
5470
}
5571

5672
/**
57-
* Method to get an instance of a user for the given username.
73+
* Method to get a cached instance of a user for the given username.
74+
* Reuses cached data if possible to avoid redundant database queries.
5875
*
5976
* @param string $username The username
6077
*
@@ -64,15 +81,35 @@ public function loadUserById(int $id): User
6481
*/
6582
public function loadUserByUsername(string $username): User
6683
{
67-
// Initialise some variables
84+
// Try to find cached user by username first
85+
foreach (self::$cache as $user) {
86+
if ($user->username === $username) {
87+
return $user;
88+
}
89+
}
90+
91+
// Otherwise query database for the user ID
6892
$query = $this->db->getQuery(true)
6993
->select($this->db->quoteName('id'))
7094
->from($this->db->quoteName('#__users'))
7195
->where($this->db->quoteName('username') . ' = :username')
7296
->bind(':username', $username)
7397
->setLimit(1);
98+
7499
$this->db->setQuery($query);
100+
$id = (int) $this->db->loadResult();
101+
102+
return $this->loadUserById($id);
103+
}
75104

76-
return $this->loadUserById((int) $this->db->loadResult());
105+
/**
106+
* Clears the cached user instances.
107+
* Useful for testing or when user data changes at runtime.
108+
*
109+
* @return void
110+
*/
111+
public static function clearCache(): void
112+
{
113+
self::$cache = [];
77114
}
78115
}

0 commit comments

Comments
 (0)