primo commit
This commit is contained in:
350
libraries/fof40/JoomlaAbstraction/CacheCleaner.php
Normal file
350
libraries/fof40/JoomlaAbstraction/CacheCleaner.php
Normal file
@ -0,0 +1,350 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\JoomlaAbstraction;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Exception;
|
||||
use FOF40\Container\Container;
|
||||
use Joomla\Application\AbstractApplication;
|
||||
use Joomla\Application\ConfigurationAwareApplicationInterface;
|
||||
use Joomla\CMS\Cache\Cache;
|
||||
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
|
||||
use Joomla\CMS\Cache\Controller\CallbackController;
|
||||
use Joomla\CMS\Cache\Exception\CacheExceptionInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
||||
use Joomla\Registry\Registry;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* A utility class to help you quickly clean the Joomla! cache
|
||||
*/
|
||||
class CacheCleaner
|
||||
{
|
||||
/**
|
||||
* Clears the com_modules and com_plugins cache. You need to call this whenever you alter the publish state or
|
||||
* parameters of a module or plugin from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearPluginsAndModulesCache()
|
||||
{
|
||||
self::clearPluginsCache();
|
||||
self::clearModulesCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the com_plugins cache. You need to call this whenever you alter the publish state or parameters of a
|
||||
* plugin from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearPluginsCache()
|
||||
{
|
||||
self::clearCacheGroups(['com_plugins'], [0, 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the com_modules cache. You need to call this whenever you alter the publish state or parameters of a
|
||||
* module from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearModulesCache()
|
||||
{
|
||||
self::clearCacheGroups(['com_modules'], [0, 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified cache groups.
|
||||
*
|
||||
* @param array $clearGroups Which cache groups to clear. Usually this is com_yourcomponent to clear
|
||||
* your component's cache.
|
||||
* @param array $cacheClients Which cache clients to clear. 0 is the back-end, 1 is the front-end. If you
|
||||
* do not specify anything, both cache clients will be cleared.
|
||||
* @param string|null $event An event to run upon trying to clear the cache. Empty string to disable. If
|
||||
* NULL and the group is "com_content" I will trigger onContentCleanCache.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function clearCacheGroups(array $clearGroups, array $cacheClients = [
|
||||
0, 1,
|
||||
], ?string $event = null): void
|
||||
{
|
||||
// Early return on nonsensical input
|
||||
if (empty($clearGroups) || empty($cacheClients))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure I have an application object
|
||||
try
|
||||
{
|
||||
$app = Factory::getApplication();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no application object things will break; let's get outta here.
|
||||
if (!is_object($app))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$isJoomla4 = version_compare(JVERSION, '3.9999.9999', 'gt');
|
||||
|
||||
// Loop all groups to clean
|
||||
foreach ($clearGroups as $group)
|
||||
{
|
||||
// Groups must be non-empty strings
|
||||
if (empty($group) || !is_string($group))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop all clients (applications)
|
||||
foreach ($cacheClients as $client_id)
|
||||
{
|
||||
$client_id = (int) ($client_id ?? 0);
|
||||
|
||||
$options = $isJoomla4
|
||||
? self::clearCacheGroupJoomla4($group, $client_id, $app)
|
||||
: self::clearCacheGroupJoomla3($group, $client_id, $app);
|
||||
|
||||
// Do not call any events if I failed to clean the cache using the core Joomla API
|
||||
if (!($options['result'] ?? false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you're cleaning com_content and you have passed no event name I will use onContentCleanCache.
|
||||
*/
|
||||
if ($group === 'com_content')
|
||||
{
|
||||
$cacheCleaningEvent = $event ?: 'onContentCleanCache';
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Joomla's cache cleaning plugin event (e.g. onContentCleanCache) as well.
|
||||
*
|
||||
* @see BaseDatabaseModel::cleanCache()
|
||||
*/
|
||||
if (empty($cacheCleaningEvent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$fakeContainer = Container::getInstance('com_FOOBAR');
|
||||
$fakeContainer->platform->runPlugins($cacheCleaningEvent, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a cache group on Joomla 3
|
||||
*
|
||||
* @param string $group The cache to clean, e.g. com_content
|
||||
* @param int $client_id The application ID for which the cache will be cleaned
|
||||
* @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY!
|
||||
*
|
||||
* @return array Cache controller options, including cleaning result
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function clearCacheGroupJoomla3(string $group, int $client_id, object $app): array
|
||||
{
|
||||
$options = [
|
||||
'defaultgroup' => $group,
|
||||
'cachebase' => ($client_id) ? self::getAppConfigParam($app, 'cache_path', JPATH_SITE . '/cache') : JPATH_ADMINISTRATOR . '/cache',
|
||||
'result' => true,
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
$cache = Cache::getInstance('callback', $options);
|
||||
/** @noinspection PhpUndefinedMethodInspection Available via __call(), not tagged in Joomla core */
|
||||
$cache->clean();
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$options['result'] = false;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a cache group on Joomla 4
|
||||
*
|
||||
* @param string $group The cache to clean, e.g. com_content
|
||||
* @param int $client_id The application ID for which the cache will be cleaned
|
||||
* @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY!
|
||||
*
|
||||
* @return array Cache controller options, including cleaning result
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function clearCacheGroupJoomla4(string $group, int $client_id, object $app): array
|
||||
{
|
||||
// Get the default cache folder. Start by using the JPATH_CACHE constant.
|
||||
$cacheBaseDefault = JPATH_CACHE;
|
||||
$appClientId = 0;
|
||||
|
||||
if (method_exists($app, 'getClientId'))
|
||||
{
|
||||
$appClientId = $app->getClientId();
|
||||
}
|
||||
|
||||
// -- If we are asked to clean cache on the other side of the application we need to find a new cache base
|
||||
if ($client_id != $appClientId)
|
||||
{
|
||||
$cacheBaseDefault = (($client_id) ? JPATH_SITE : JPATH_ADMINISTRATOR) . '/cache';
|
||||
}
|
||||
|
||||
// Get the cache controller's options
|
||||
$options = [
|
||||
'defaultgroup' => $group,
|
||||
'cachebase' => self::getAppConfigParam($app, 'cache_path', $cacheBaseDefault),
|
||||
'result' => true,
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
$container = Factory::getContainer();
|
||||
|
||||
if (empty($container))
|
||||
{
|
||||
throw new \RuntimeException('Cannot get Joomla 4 application container');
|
||||
}
|
||||
|
||||
/** @var CacheControllerFactoryInterface $cacheControllerFactory */
|
||||
$cacheControllerFactory = $container->get('cache.controller.factory');
|
||||
|
||||
if (empty($cacheControllerFactory))
|
||||
{
|
||||
throw new \RuntimeException('Cannot get Joomla 4 cache controller factory');
|
||||
}
|
||||
|
||||
/** @var CallbackController $cache */
|
||||
$cache = $cacheControllerFactory->createCacheController('callback', $options);
|
||||
|
||||
if (empty($cache) || !property_exists($cache, 'cache') || !method_exists($cache->cache, 'clean'))
|
||||
{
|
||||
throw new \RuntimeException('Cannot get Joomla 4 cache controller');
|
||||
}
|
||||
|
||||
$cache->cache->clean();
|
||||
}
|
||||
catch (CacheExceptionInterface $exception)
|
||||
{
|
||||
$options['result'] = false;
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$options['result'] = false;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private static function getAppConfigParam(?object $app, string $key, $default = null)
|
||||
{
|
||||
/**
|
||||
* Any kind of Joomla CMS, Web, API or CLI application extends from AbstractApplication and has the get()
|
||||
* method to return application configuration parameters.
|
||||
*/
|
||||
if (is_object($app) && ($app instanceof AbstractApplication))
|
||||
{
|
||||
return $app->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom application may instead implement the Joomla\Application\ConfigurationAwareApplicationInterface
|
||||
* interface (Joomla 4+), in whihc case it has the get() method to return application configuration parameters.
|
||||
*/
|
||||
if (is_object($app)
|
||||
&& interface_exists('Joomla\Application\ConfigurationAwareApplicationInterface', true)
|
||||
&& ($app instanceof ConfigurationAwareApplicationInterface))
|
||||
{
|
||||
return $app->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Joomla 3 custom application may simply implement the get() method without implementing an interface.
|
||||
*/
|
||||
if (is_object($app) && method_exists($app, 'get'))
|
||||
{
|
||||
return $app->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* At this point the $app variable is not an object or is something I can't use. Does the Joomla Factory still
|
||||
* has the legacy static method getConfig() to get the application configuration? If so, use it.
|
||||
*/
|
||||
if (method_exists(Factory::class, 'getConfig'))
|
||||
{
|
||||
try
|
||||
{
|
||||
$jConfig = Factory::getConfig();
|
||||
|
||||
if (is_object($jConfig) && ($jConfig instanceof Registry))
|
||||
{
|
||||
$jConfig->get($key, $default);
|
||||
}
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
/**
|
||||
* Factory tries to go through the application object. It might fail if there is a custom application
|
||||
* which doesn't implement the interfaces Factory expects. In this case we get a Fatal Error whcih we
|
||||
* can trap and fall through to the next if-block.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When we are here all hope is nearly lost. We have to do a crude approximation of Joomla Factory's code to
|
||||
* create an application configuration Registry object and retrieve the configuration values. This will work as
|
||||
* long as the JConfig class (defined in configuration.php) has been loaded.
|
||||
*/
|
||||
$configPath = defined('JPATH_CONFIGURATION') ? JPATH_CONFIGURATION :
|
||||
(defined('JPATH_ROOT') ? JPATH_ROOT : null);
|
||||
$configPath = $configPath ?? (__DIR__ . '/../../..');
|
||||
$configFile = $configPath . '/configuration.php';
|
||||
|
||||
if (!class_exists('JConfig') && @file_exists($configFile) && @is_file($configFile) && @is_readable($configFile))
|
||||
{
|
||||
require_once $configFile;
|
||||
}
|
||||
|
||||
if (class_exists('JConfig'))
|
||||
{
|
||||
try
|
||||
{
|
||||
$jConfig = new Registry();
|
||||
$configObject = new \JConfig();
|
||||
$jConfig->loadObject($configObject);
|
||||
|
||||
return $jConfig->get($key, $default);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All hope is lost. I can't find the application configuration. I am returning the default value and hope stuff
|
||||
* won't break spectacularly...
|
||||
*/
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
158
libraries/fof40/JoomlaAbstraction/ComponentVersion.php
Normal file
158
libraries/fof40/JoomlaAbstraction/ComponentVersion.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\JoomlaAbstraction;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Exception;
|
||||
use Joomla\CMS\Factory as JoomlaFactory;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Retrieve the version of a component from the cached XML manifest or, if it's not present, the version recorded in the
|
||||
* database.
|
||||
*/
|
||||
abstract class ComponentVersion
|
||||
{
|
||||
/**
|
||||
* A cache with the version numbers of components
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @since 3.1.5
|
||||
*/
|
||||
private static $version = array();
|
||||
|
||||
/**
|
||||
* Get a component's version. The XML manifest on disk will be tried first. If it's not there or does not have a
|
||||
* version string the manifest cache in the database is tried. If that fails a fake version number will be returned.
|
||||
*
|
||||
* @param string $component The name of the component, e.g. com_foobar
|
||||
*
|
||||
* @return string The version string
|
||||
*
|
||||
* @since 3.1.5
|
||||
*/
|
||||
public static function getFor(string $component): string
|
||||
{
|
||||
if (!isset(self::$version[$component]))
|
||||
{
|
||||
self::$version[$component] = null;
|
||||
}
|
||||
|
||||
if (is_null(self::$version[$component]))
|
||||
{
|
||||
self::$version[$component] = self::getVersionFromManifest($component);
|
||||
}
|
||||
|
||||
if (is_null(self::$version[$component]))
|
||||
{
|
||||
self::$version[$component] = self::getVersionFromDatabase($component);
|
||||
}
|
||||
|
||||
if (is_null(self::$version[$component]))
|
||||
{
|
||||
self::$version[$component] = 'dev-' . str_replace(' ', '_', microtime(false));
|
||||
}
|
||||
|
||||
return self::$version[$component];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a component's version from the manifest cache in the database
|
||||
*
|
||||
* @param string $component The component's bname
|
||||
*
|
||||
* @return string|null The component version or null if none is defined
|
||||
*
|
||||
* @since 3.1.5
|
||||
*/
|
||||
private static function getVersionFromDatabase(string $component): ?string
|
||||
{
|
||||
$db = JoomlaFactory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->qn('manifest_cache'))
|
||||
->from($db->qn('#__extensions'))
|
||||
->where($db->qn('element') . ' = ' . $db->q($component))
|
||||
->where($db->qn('type') . ' = ' . $db->q('component'));
|
||||
|
||||
try
|
||||
{
|
||||
$json = $db->setQuery($query)->loadResult();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($json))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$options = json_decode($json, true);
|
||||
|
||||
if (empty($options))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($options['version']))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $options['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a component's version from the manifest file on disk. IMPORTANT! The manifest for com_something must be named
|
||||
* something.xml.
|
||||
*
|
||||
* @param string $component The component's bname
|
||||
*
|
||||
* @return string The component version or null if none is defined
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
private static function getVersionFromManifest(string $component): ?string
|
||||
{
|
||||
$bareComponent = str_replace('com_', '', $component);
|
||||
$file = JPATH_ADMINISTRATOR . '/components/' . $component . '/' . $bareComponent . '.xml';
|
||||
|
||||
if (!is_file($file) || !is_readable($file))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = @file_get_contents($file);
|
||||
|
||||
if (empty($data))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$xml = new SimpleXMLElement($data, LIBXML_COMPACT | LIBXML_NONET | LIBXML_ERR_NONE);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$versionNode = $xml->xpath('/extension/version');
|
||||
|
||||
if (empty($versionNode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string)($versionNode[0]);
|
||||
}
|
||||
}
|
||||
196
libraries/fof40/JoomlaAbstraction/DynamicGroups.php
Normal file
196
libraries/fof40/JoomlaAbstraction/DynamicGroups.php
Normal file
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\JoomlaAbstraction;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF40\Container\Container;
|
||||
|
||||
/**
|
||||
* Dynamic user to user group assignment.
|
||||
*
|
||||
* This class allows you to add / remove the currently logged in user to a user group without writing the information to
|
||||
* the database. This is useful when you want to allow core and third party code to allow or prohibit display of
|
||||
* information and / or taking actions based on a condition controlled in your code.
|
||||
*/
|
||||
class DynamicGroups
|
||||
{
|
||||
/**
|
||||
* Add the current user to a user group just for this page load.
|
||||
*
|
||||
* @param int $groupID The group ID to add the current user into.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function addGroup(int $groupID): void
|
||||
{
|
||||
self::addRemoveGroup($groupID, true);
|
||||
self::cleanUpUserObjectCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current user from a user group just for this page load.
|
||||
*
|
||||
* @param int $groupID The group ID to remove the current user from.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeGroup(int $groupID): void
|
||||
{
|
||||
self::addRemoveGroup($groupID, false);
|
||||
self::cleanUpUserObjectCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to add or remove the current user from a user group just for this page load.
|
||||
*
|
||||
* @param int $groupID The group ID to add / remove the current user from.
|
||||
* @param bool $add Add (true) or remove (false) the user?
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function addRemoveGroup(int $groupID, bool $add): void
|
||||
{
|
||||
// Get a fake container (we need it for its platform interface)
|
||||
$container = Container::getInstance('com_FOOBAR');
|
||||
|
||||
/**
|
||||
* Make sure that Joomla has retrieved the user's groups from the database.
|
||||
*
|
||||
* By going through the User object's getAuthorisedGroups we force Joomla to go through Access::getGroupsByUser
|
||||
* which retrieves the information from the database and caches it into the Access helper class.
|
||||
*/
|
||||
$container->platform->getUser()->getAuthorisedGroups();
|
||||
$container->platform->getUser($container->platform->getUser()->id)->getAuthorisedGroups();
|
||||
|
||||
/**
|
||||
* Now we can get a Reflection object into Joomla's Access helper class and manipulate its groupsByUser cache.
|
||||
*/
|
||||
$className = 'Joomla\\CMS\\Access\\Access';
|
||||
|
||||
try
|
||||
{
|
||||
$reflectedAccess = new \ReflectionClass($className);
|
||||
}
|
||||
catch (\ReflectionException $e)
|
||||
{
|
||||
// This should never happen!
|
||||
$container->platform->logDebug('Cannot locate the Joomla\\CMS\\Access\\Access class. Is your Joomla installation broken or too old / too new?');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$groupsByUser = $reflectedAccess->getProperty('groupsByUser');
|
||||
$groupsByUser->setAccessible(true);
|
||||
$rawGroupsByUser = $groupsByUser->getValue();
|
||||
|
||||
/**
|
||||
* Next up, we need to manipulate the keys of the cache which contain user to user group assignments.
|
||||
*
|
||||
* $rawGroupsByUser (Access::$groupsByUser) stored the group ownership as userID:recursive e.g. 0:1 for the
|
||||
* default user, recursive. We need to deal with four keys: 0:1, 0:0, myID:1 and myID:0
|
||||
*/
|
||||
$user = $container->platform->getUser();
|
||||
$keys = ['0:1', '0:0', $user->id . ':1', $user->id . ':0'];
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (!array_key_exists($key, $rawGroupsByUser))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$groups = $rawGroupsByUser[$key];
|
||||
|
||||
if ($add)
|
||||
{
|
||||
if (in_array($groupID, $groups))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$groups[] = $groupID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!in_array($groupID, $groups))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$removeKey = array_search($groupID, $groups);
|
||||
unset($groups[$removeKey]);
|
||||
}
|
||||
|
||||
$rawGroupsByUser[$key] = $groups;
|
||||
}
|
||||
|
||||
// We can commit our changes back to the cache property and make it publicly inaccessible again.
|
||||
$groupsByUser->setValue(null, $rawGroupsByUser);
|
||||
$groupsByUser->setAccessible(false);
|
||||
|
||||
/**
|
||||
* We are not done. Caching user groups is only one aspect of Joomla access management. Joomla also caches the
|
||||
* identities, i.e. the user group assignment per user, in a different cache. We need to reset it to for our
|
||||
* user.
|
||||
*
|
||||
* Do note that we CAN NOT use clearStatics since that also clears the user group assignment which we assigned
|
||||
* dynamically. Therefore calling it would destroy our work so far.
|
||||
*/
|
||||
$refProperty = $reflectedAccess->getProperty('identities');
|
||||
$refProperty->setAccessible(true);
|
||||
$identities = $refProperty->getValue();
|
||||
|
||||
$keys = array($user->id, 0);
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (!array_key_exists($key, $identities))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($identities[$key]);
|
||||
}
|
||||
|
||||
$refProperty->setValue(null, $identities);
|
||||
$refProperty->setAccessible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the current user's authenticated groups cache.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function cleanUpUserObjectCache(): void
|
||||
{
|
||||
// Get a fake container (we need it for its platform interface)
|
||||
$container = Container::getInstance('com_FOOBAR');
|
||||
|
||||
$user = $container->platform->getUser();
|
||||
$reflectedUser = new \ReflectionObject($user);
|
||||
|
||||
// Clear the user group cache
|
||||
$refProperty = $reflectedUser->getProperty('_authGroups');
|
||||
$refProperty->setAccessible(true);
|
||||
$refProperty->setValue($user, array());
|
||||
$refProperty->setAccessible(false);
|
||||
|
||||
// Clear the view access level cache
|
||||
$refProperty = $reflectedUser->getProperty('_authLevels');
|
||||
$refProperty->setAccessible(true);
|
||||
$refProperty->setValue($user, array());
|
||||
$refProperty->setAccessible(false);
|
||||
|
||||
// Clear the authenticated actions cache. I haven't seen it used anywhere but it's there, so...
|
||||
$refProperty = $reflectedUser->getProperty('_authActions');
|
||||
$refProperty->setAccessible(true);
|
||||
$refProperty->setValue($user, array());
|
||||
$refProperty->setAccessible(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user