| Server IP : 54.36.91.62 / Your IP : 216.73.217.112 Web Server : Apache System : Linux webm013.cluster127.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : coopiak ( 151928) PHP Version : 8.3.23 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/coopiak/amisdesseniors-fr/montpellier/libraries/kunena/src/Table/ |
Upload File : |
<?php
/**
* Kunena Component
*
* @package Kunena.Framework
* @subpackage Table
*
* @copyright Copyright (C) 2008 - @currentyear@ Kunena Team. All rights reserved.
* @license https://www.gnu.org/copyleft/gpl.html GNU/GPL
* @link https://www.kunena.org
**/
namespace Kunena\Forum\Libraries\Table;
\defined('_JEXEC') or die();
use Closure;
use InvalidArgumentException;
use Joomla\CMS\Factory;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\QueryInterface;
use Kunena\Forum\Libraries\Database\KunenaDatabaseObject;
use RuntimeException;
use UnexpectedValueException;
/**
* Abstract Table Object class
*
* Parent class to all table objects.
*
* Note: If you set default values for the fields, please keep at least one default key value as null!
*
* @since Kunena 6.0
*/
abstract class KunenaTableObject
{
/**
* Store all instances of this type by Id.
* Always override this variable in your own class!
*
* @var array
* @since Kunena 4.0
*/
protected static $instances = null;
/**
* Name of the database table to model.
* Always override this variable in your own class!
*
* @var string
* @since Kunena 4.0
*/
protected static $tbl = '';
/**
* Array of table fields with their default value (field=>default).
* You can either fill up your table structure or leave base class to fetch the fields for you.
*
* @var array
* @since Kunena 4.0
*/
protected static $tbl_fields;
/**
* Array of primary key fields in the table.
* Always override this variable in your own class!
*
* @var array
* @since Kunena 4.0
*/
protected static $tbl_keys = [];
/**
* DatabaseDriver object.
*
* @var DatabaseDriver
* @since Kunena 4.0
*/
protected static $db;
/**
* Indicator that the tables have been locked.
*
* @var boolean
* @since Kunena 4.0
*/
protected static $_locked = false;
private static $tbl_key;
private static $locked;
/**
* Flag whether the object exists in the database or not.
*
* @var boolean
* @since Kunena 4.0
*/
protected $_exists = false;
/**
* Serialized key for the object.
*
* @var string
* @since Kunena 4.0
*/
protected $_key;
/**
* Constructor to create the table object instance. All objects with key set are stored into global instances.
*
* @param array $keys Name of the primary key field in the table.
*
* @since Kunena 4.0
*/
public function __construct($keys = null)
{
// First run: Initialise the table properties.
if (\is_null(static::$tbl_fields)) {
static::getFields();
}
$exists = false;
$tbl_keys = [];
if ($keys === null) {
// We need to check a special case where object properties have already been set.
// This happens only if $db->loadObject() or $db->loadObjectList() are called with a class type.
// Build storage key for the object.
foreach (static::$tbl_keys as $keyName) {
$keyValue = isset($this->$keyName) ? $this->$keyName : null;
$exists |= ($keyValue !== null);
$tbl_keys[$keyName] = $keyValue;
}
if ($exists) {
if (\is_array(static::$instances)) {
// Yes, we are in the special case.
$this->_key = \count($tbl_keys) > 1 ? json_encode($tbl_keys) : reset($tbl_keys);
if (isset(static::$instances[$this->_key])) {
// If we already had loaded the object, we can stop now.
// This is because of we keep only one global instance from the object.
return;
}
}
// Initialise the object.
$this->_exists = true;
$this->initialise(true);
}
}
if (!$exists) {
$exists = $this->load($keys);
if ($exists) {
// Build storage key for the object.
foreach (static::$tbl_keys as $keyName) {
$tbl_keys[$keyName] = isset($this->$keyName) ? $this->$keyName : null;
}
}
}
if ($exists && \is_array(static::$instances)) {
$this->_key = \count($tbl_keys) > 1 ? json_encode($tbl_keys) : reset($tbl_keys);
if (!isset(static::$instances[$this->_key])) {
// Add the new object into the instances.
static::$instances[$this->_key] = $this;
}
}
}
/**
* Get the database columns.
*
* @return int[]|string[] An array of the field names, or false if an error occurs.
*
* @since Kunena 4.0
*
* @throws UnexpectedValueException
*/
public static function getFields()
{
if (static::$tbl_fields === null) {
// Lookup the fields for this table only once.
static::$tbl_fields = static::$db->getTableColumns(static::$tbl, false);
if (empty(static::$tbl_fields)) {
throw new UnexpectedValueException(sprintf('No columns found for %s table', static::$tbl));
}
}
return array_keys(static::$tbl_fields);
}
/**
* Override this function if you need to initialise object right after creating it.
*
* Can be used for example if the database fields need to be converted to array or Joomla\Registry\Registry.
*
* @param bool $sqlFetch True only if properties were assigned before constructor was called.
*
* @return void
*
* @since Kunena 4.0
*/
protected function initialise($sqlFetch = false): void
{
}
/**
* @param null $keys keys
* @param bool $reset reset
*
* @return boolean|KunenaTableObject
*
* @since Kunena 6.0
*/
protected function load($keys = null, $reset = true)
{
try {
$keys = $this->getKeyValues($keys);
} catch (UnexpectedValueException $e) {
if ($e->getCode() == 0) {
// Key not fully given, no need to load the item.
foreach ($keys as $field => $value) {
// Make sure the object contains the search fields.
$this->$field = $value;
}
return false;
}
// Error, throw it forward.
throw $e;
}
if ($reset) {
$this->reset();
}
$k = static::$tbl;
// Initialise the query.
$query = static::$db->createQuery()
->select('*')
->from(static::$db->quoteName($k));
foreach ($keys as $field => $value) {
// Make sure the object contains the search fields.
// This is incompatible to Joomla\CMS\Table\Table, but needed by this class.
$this->$field = $value;
// Add the search tuple to the query.
$query->where(static::$db->quoteName($field) . ' = ' . static::$db->quote($value));
}
$query->setLimit(1);
static::$db->setQuery($query);
$row = static::$db->loadAssoc();
// Check that we have a result.
if (empty($row)) {
return false;
}
// Bind the object with the row and return.
return $this->_exists = $this->bind($row);
}
/**
* Returns all keys and their values as an array.
*
* @param array|string $fields fields
* @param bool $throw throw
*
* @return array
*
* @since Kunena 4.0
*/
protected function getKeyValues($fields = null, $throw = true): array
{
// FIXME: bug...
static $fieldNames = null;
$tableKeys = static::$tbl_keys;
$keys = [];
if (\is_null($fields)) {
// No fields were given as parameter: use table instance.
foreach ($tableKeys as $keyName) {
$keyValue = isset($this->$keyName) ? $this->$keyName : null;
$keys[$keyName] = $keyValue;
// If null primary keys aren't allowed
if ($throw && \is_null($keyValue)) {
throw new UnexpectedValueException(sprintf('%s: Null primary key not allowed   %s..', \get_class($this), $keyName), 0);
}
}
} else {
if (\is_null($fieldNames)) {
// Lazy initialize fields list.
$fieldNames = static::getFields();
}
if (!\is_array($fields)) {
$fields = (array) $fields;
}
foreach ($fields as $keyName => $keyValue) {
// Check if key in given numeric location exists.
if (is_numeric($keyName)) {
if (!isset($tableKeys[$keyName])) {
throw new UnexpectedValueException(sprintf('%s: Missing key in index %s.', \get_class($this), $keyName), 1);
}
// Find out key name in given numeric location and use it.
$keyName = $tableKeys[$keyName];
}
$keys[$keyName] = $keyValue;
// Verify that the used key exists in the table.
if (!\in_array($keyName, $fieldNames)) {
throw new UnexpectedValueException(sprintf('%s: Missing field in database: %s.', \get_class($this), $keyName), 2);
}
}
}
// Make sure user didn't pass empty array.
if (empty($keys)) {
throw new UnexpectedValueException(sprintf('%s: No fields given.', \get_class($this)), 3);
}
return $keys;
}
/**
* Method to reset class properties to the defaults set in the class
* definition. It will ignore the primary key.
*
* If you want to reset other properties, you need to override the function.
*
* @return KunenaTableObject
*
* @since Kunena 4.0
*/
public function reset(): KunenaTableObject
{
// Get the default values for the class from the table.
foreach (static::$tbl_fields as $k => $v) {
// If the property is not the primary key.
if (!\in_array($k, static::$tbl_keys)) {
$this->$k = $v->Default;
}
}
return $this;
}
/**
* Method to bind an associative array or object to the Joomla\CMS\Table\Table instance.This
* method only binds properties that are publicly accessible and optionally
* takes an array of properties to ignore when binding.
*
* @param mixed $src An associative array or object to bind to the Joomla\CMS\Table\Table instance.
* @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
*
* @return KunenaTableObject
*
* @since Kunena 4.0
*
* @throws InvalidArgumentException
*/
public function bind($src, $ignore = []): KunenaTableObject
{
// If the source value is not an array or object return false.
if (!\is_object($src) && !\is_array($src)) {
throw new InvalidArgumentException(sprintf('%s::bind(*%s*)', \get_class($this), \gettype($src)));
}
// If the source value is an object, get its accessible properties.
if (\is_object($src)) {
$src = get_object_vars($src);
}
// If the ignore value is a string, explode it over spaces.
if (!\is_array($ignore)) {
$ignore = explode(' ', $ignore);
}
// Bind the source value, excluding the ignored fields.
foreach ($this->getProperties() as $k => $v) {
// Only process fields not in the ignore array.
if (!\in_array($k, $ignore)) {
if (isset($src[$k])) {
$this->$k = $src[$k];
}
}
}
return $this;
}
/**
* Returns an associative array of object properties.
*
* @return Closure
*
* @since Kunena 4.0
*/
public function getProperties()
{
// Use closure to return public variables only.
$self = $this;
return function () use ($self) {
return get_object_vars($self);
};
}
/**
* Method to get the DatabaseDriver object.
*
* @return DatabaseDriver The internal database driver object.
*
* @since Kunena 4.0
*/
public static function getDatabase(): DatabaseDriver
{
return static::$db;
}
/**
* Method to set the DatabaseDriver object.
*
* @link http://docs.joomla.org/Joomla\CMS\Table\Table/setDbo
*
* @param DatabaseDriver $db A DatabaseDriver object to be used by the table object.
*
* @return boolean True on success.
*
* @since K4.0
*/
public static function setDbo(DatabaseDriver $db): bool
{
static::$db = $db;
return true;
}
/**
* Returns the global instance to the object.
*
* Note that using array of fields will always make a query to the database, but it's very useful feature if you
* want to search one item by using arbitrary set of matching fields. If there are more than one matching object,
* first one gets returned.
*
* @param int|array $keys An optional primary key value to load the object by, or an array of fields to match.
*
* @return KunenaDatabaseObject
*
* @since Kunena 4.0
*
* @throw RuntimeException
*/
public static function getInstance($keys): KunenaDatabaseObject
{
$k = json_encode(self::resolveKeys($keys));
// FIXME:
$k = (int) $keys;
// If we are creating or loading a new item or we load instance by alternative keys,
// we need to create a new object.
if (!isset(static::$instances[$k])) {
$c = \get_called_class();
$instance = new $c($keys);
// @var KunenaTableObject $instance
if (!$instance->exists()) {
return $instance;
}
// Instance exists: make sure that we return the global instance.
$k = $instance->_key;
}
// Return global instance from the identifier.
$instance = static::$instances[$k];
// But before that, check that we have valid item.
if ($k != $instance->_key) {
throw new RuntimeException(\get_called_class() . ": Identifier doesn't match ({$k} != {$instance->_key})");
}
return $instance;
}
/**
* Returns all keys and their values as an array.
*
* @param array|string $fields fields
*
* @return array
*
* @since Kunena 4.0
*
* @throws UnexpectedValueException
*/
protected static function resolveKeys($fields): array
{
// First run: Initialise the table properties.
if (\is_null(static::$tbl_fields)) {
static::getFields();
}
$keys = [];
if (!\is_array($fields)) {
$fields = (array) $fields;
}
foreach ($fields as $keyName => $keyValue) {
// Check if key in given numeric location exists.
if (is_numeric($keyName)) {
if (!isset(static::$tbl_keys[$keyName])) {
throw new UnexpectedValueException(sprintf('%s: Missing key in index: %s.', \get_called_class(), $keyName), 1);
}
// Find out key name in given numeric location and use it.
$keyName = static::$tbl_keys[$keyName];
}
$keys[$keyName] = $keyValue;
// Verify that the used key exists in the table.
if (!isset(static::$tbl_fields[$keyName])) {
throw new UnexpectedValueException(sprintf('%s: Missing field in database: %s.', \get_called_class(), $keyName), 2);
}
}
// Make sure user didn't pass empty array.
if (empty($keys)) {
throw new UnexpectedValueException(sprintf('%s: No fields given.', \get_called_class()), 3);
}
return $keys;
}
/**
* For internal use only.
*
* @return array|null
*
* @since Kunena 6.0
*/
public static function &getInstances(): ?array
{
return static::$instances;
}
/**
* Removes all or selected instances from the object cache.
*
* @param null|int|array $ids ids
*
* @return void
*
* @since Kunena 4.0
*/
public static function freeInstances($ids = null): void
{
if (!isset(static::$instances)) {
return;
}
if ($ids === null) {
$ids = array_keys(static::$instances);
}
$ids = (array) $ids;
foreach ($ids as $id) {
unset(static::$instances[$id]);
}
}
/**
* @internal
*
* @return \Joomla\Database\DatabaseQuery
*
* @since Kunena 6.0
*/
public static function getQuery()
{
$db = static::$db;
return $db->createQuery()
->select($db->quoteName('a.*'))
->from($db->quoteName(static::$tbl, 'a'));
}
/**
* @internal
*
* @param QueryInterface $query query
*
* @return array
*
* @since Kunena 6.0
*/
public static function &loadInstances(QueryInterface $query): array
{
$db = Factory::getContainer()->get('DatabaseDriver');
$db->createQuery();
$items = (array) $db->loadObjectList('id', \get_called_class());
if (\is_array(static::$instances)) {
static::$instances += $items;
}
return $items;
}
/**
* @return string
*
* @since Kunena 6.0
*/
public function getId(): string
{
return $this->_key;
}
/**
* Method to provide a shortcut to binding, checking and storing a Joomla\CMS\Table\Table
* instance to the database table. The method will check a row in once the
* data has been stored and if an ordering filter is present will attempt to
* reOrder the table rows based on the filter. The ordering filter is an instance
* property name. The rows that will be reOrdered are those whose value matches
* the Joomla\CMS\Table\Table instance for the property specified.
*
* @param mixed $src An associative array or object to bind to the Joomla\CMS\Table\Table instance.
* @param string $orderingFilter Filter for the order updating
* @param mixed $ignore An optional array or space separated list of properties
* to ignore while binding.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*/
/*
public function save($src, $orderingFilter = '', $ignore = '')
{
// Attempt to bind the source to the instance.
if (!$this->bind($src, $ignore))
{
return false;
}
// Run any sanity checks on the instance and verify that it is ready for storage.
if (!$this->check())
{
return false;
}
// Attempt to store the properties to the database table.
if (!$this->store())
{
return false;
}
// Attempt to check the row in, just in case it was checked out.
if (!$this->checkIn())
{
return false;
}
// If an ordering filter is set, attempt reOrder the rows in the table based on the filter and value.
if ($orderingFilter)
{
$filterValue = $this->$orderingFilter;
$this->reOrder($orderingFilter ?
static::$db->quoteName($orderingFilter) . ' = ' . static::$db->quote($filterValue) : '');
}
// Throw exception to empty and return true.
throw new Exception('');
return true;
}
*/
/**
* Create almost identical copy of the object, but clean up the key fields.
*
* New object will also return false on $new->exists() until it gets saved.
*
* @return void
*
* @since Kunena 4.0
*/
public function __clone()
{
foreach (static::$tbl_keys as $keyName) {
$this->$keyName = null;
}
$this->_exists = false;
}
/**
* Method to perform sanity checks on the Joomla\CMS\Table\TableObject instance properties to ensure
* they are safe to store in the database. Child classes should override this
* method to make sure the data they are storing in the database is safe and
* as expected before storage.
*
* @return boolean True if the instance is sane and able to be stored in the database.
*
* @since Kunena 4.0
*/
public function check(): bool
{
return true;
}
/**
* @param bool $updateNulls update
*
* @return boolean
*
* @since Kunena 6.0
*/
public function store($updateNulls = false): bool
{
if ($this->exists()) {
try {
static::$db->updateObject(static::$tbl, $this, static::$tbl_keys, $updateNulls);
} catch (RuntimeException $e) {
throw new RuntimeException(\get_class($this) . '::store failed - ' . $e->getMessage());
return false;
}
} else {
try {
static::$db->insertObject(static::$tbl, $this, static::$tbl_keys);
} catch (RuntimeException $e) {
throw new RuntimeException(\get_class($this) . '::store failed - ' . $e->getMessage());
return false;
}
}
if (static::$locked) {
$this->unlock();
}
$this->_exists = true;
return true;
}
/**
* Returns true if the object exists in the database.
*
* @param boolean $exists Internal parameter to change state.
*
* @return boolean True if object exists in database.
*
* @since Kunena 4.0
*/
public function exists($exists = null): bool
{
$return = $this->_exists;
if ($exists !== null) {
$this->_exists = $exists;
}
return $return;
}
/**
* Method to unlock the database table for writing.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*/
protected function unlock(): bool
{
static::$db->unlockTables();
static::$_locked = false;
return true;
}
/**
* Method to delete a row from the database table by primary key value.
*
* @param mixed $keys An optional primary key value to delete. If not set the
* instance property value is used. If array given, you can
* use arbitrary fields to delete more than one item.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*
* @throws UnexpectedValueException
*/
public function delete($keys = null): bool
{
try {
$keys = $this->getKeyValues($keys);
} catch (UnexpectedValueException $e) {
throw $e;
}
$k = static::$tbl;
// Delete the row by given keys/fields.
$query = static::$db->createQuery()
->delete()
->from(static::$db->quoteName($k));
foreach ($keys as $key => $value) {
$query->where(static::$db->quoteName($key) . ' = ' . static::$db->quote($value));
}
$query->setLimit(1);
static::$db->setQuery($query);
static::$db->execute();
return true;
}
/**
* Method to check a row out if the necessary properties/fields exist. To
* prevent race conditions while editing rows in a database, a row can be
* checked out if the fields 'checked_out' and 'checked_out_time' are available.
* While a row is checked out, any attempt to store the row by a user other
* than the one who checked the row out should be held until the row is checked
* in again.
*
* @param integer $userId The Id of the user checking out the row.
* @param mixed $pk An optional primary key value to check out. If not set
* the instance property value is used.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*/
public function checkOut(int $userId, $pk = null): bool
{
// If there is no checked_out or checked_out_time field, just return true.
if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time')) {
return true;
}
$k = static::$tbl_key;
$pk = (\is_null($pk)) ? $this->$k : $pk;
// If no primary key is given, return false.
if ($pk === null) {
throw new UnexpectedValueException('Null primary key not allowed.');
}
// Get the current time in MySQL format.
$time = Factory::getDate()->toSql();
// Check the row out by primary key.
$query = static::$db->createQuery()
->update(static::$tbl)
->set(static::$db->quoteName('checked_out') . ' = ' . static::$db->quote((int) $userId))
->set(static::$db->quoteName('checked_out_time') . ' = ' . static::$db->quote($time))
->where(static::$db->quoteName($k) . ' = ' . static::$db->quote($pk));
static::$db->setQuery($query);
static::$db->execute();
// Set table values in the object.
$this->checked_out = (int) $userId;
$this->checked_out_time = $time;
return true;
}
/**
* Method to check a row in if the necessary properties/fields exist. Checking
* a row in will allow other users the ability to edit the row.
*
* @param mixed $pk An optional primary key value to check out. If not set the instance property value is used.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*/
public function checkIn($pk = null): bool
{
// If there is no checked_out or checked_out_time field, just return true.
if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time')) {
return true;
}
$k = static::$tbl_keys;
$pk = (\is_null($pk)) ? $this->$k : $pk;
// If no primary key is given, return false.
if ($pk === null) {
throw new UnexpectedValueException('Null primary key not allowed.');
}
// Check the row in by primary key.
$query = static::$db->createQuery();
$nullDate = static::$db->getNullDate() ? static::$db->quote(static::$db->getNullDate()) : 'NULL';
$query->update(static::$tbl)
->set(static::$db->quoteName('checked_out') . ' = 0')
->set(static::$db->quoteName('checked_out_time') . ' = ' . $nullDate)
->where(static::$db->quoteName($k) . ' = ' . static::$db->quote($pk));
static::$db->setQuery($query);
// Check for a database error.
static::$db->execute();
// Set table values in the object.
$this->checked_out = 0;
$this->checked_out_time = null;
return true;
}
/**
* Method to increment the hits for a row if the necessary property/field exists.
*
* @internal param mixed $pk An optional primary key value to increment. If not set the instance property value is
* used.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*/
public function hit(): bool
{
$pk = null;
// If there is no hits field, just return true.
if (!property_exists($this, 'hits')) {
return true;
}
$k = static::$tbl_keys;
$pk = (\is_null($pk)) ? $this->$k : $pk;
// If no primary key is given, return false.
if ($pk === null) {
return false;
}
// Check the row in by primary key.
$query = static::$db->createQuery()
->update(static::$tbl)
->set(static::$db->quoteName('hits') . ' = (' . static::$db->quoteName('hits') . ' + 1)')
->where(static::$db->quoteName($k) . ' = ' . static::$db->quote($pk));
static::$db->setQuery($query);
static::$db->execute();
// Set table values in the object.
$this->hits++;
return true;
}
/**
* Method to determine if a row is checked out and therefore editable by
* a user. If the row is checked out by the same user, then it is considered
* not checked out -- as the user can still edit it.
*
* @param integer $with The userid to preform the match with, if an item is checked
* out by this user the function will return false.
* @param integer $against The userid to perform the match against when the function
* is used as a static function.
*
* @return boolean True if checked out.
*
* @since Kunena 4.0
*/
public function isCheckedOut($with = 0, $against = null): bool
{
// Handle the non-static case.
if (isset($this) && ($this instanceof Table) && \is_null($against)) {
$against = $this->get('checked_out');
}
// The item is not checked out or is checked out by the same user.
if (!$against || ($against == $with)) {
return false;
}
static::$db = Factory::getContainer()->get('DatabaseDriver');
static::$db->setQuery('SELECT COUNT(' . static::$db->quoteName('userid') . ')' . ' FROM ' . static::$db->quoteName('#__session') . ' WHERE ' . static::$db->quoteName('userid') . ' = ' . static::$db->quote((int) $against));
// If a session exists for the user then it is checked out.
return (bool) static::$db->loadResult();
}
/**
* Method to lock the database table for writing.
*
* @return boolean True on success.
*
* @since Kunena 4.0
*
* @throws RuntimeException
*/
protected function lock(): bool
{
static::$db->lockTable(static::$tbl);
static::$_locked = true;
return true;
}
}
KunenaTableObject::setDbo(Factory::getContainer()->get('db'));