Migrate to strong typed (compatible only with PHP 7.0+).
This commit is contained in:
parent
503efef448
commit
f43028d72d
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* DuckBrain - Microframework
|
||||
*
|
||||
* Clase diseñada para crear y devolver una única instancia mysqli.
|
||||
* Clase diseñada para crear y devolver una única instancia mysqli (database).
|
||||
* Depende de manera forzada de que estén definidas las constantes:
|
||||
* dbhost, dbname, dbpass y dbuser
|
||||
*
|
||||
@ -12,15 +12,21 @@
|
||||
*/
|
||||
|
||||
namespace Libs;
|
||||
use mysqli;
|
||||
|
||||
class Database {
|
||||
class Database extends \mysqli {
|
||||
static private $db;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
static public function getConnection() {
|
||||
/**
|
||||
* Devuelve una instancia homogénea (singlenton) a la base de datos.
|
||||
*
|
||||
* @return mysqli
|
||||
*/
|
||||
static public function getConnection() : mysqli {
|
||||
if (!isset(self::$db)) {
|
||||
self::$db = new \mysqli(dbhost, dbuser, dbpass, dbname);
|
||||
self::$db = new mysqli(dbhost, dbuser, dbpass, dbname);
|
||||
if (self::$db->connect_errno) {
|
||||
echo '<style>body{white-space: pre-line;}</style>';
|
||||
throw new \Exception('No se ha podido conectar a la base de datos.');
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace Libs;
|
||||
|
||||
use Libs\Database;
|
||||
use mysqli;
|
||||
|
||||
class ModelMySQL {
|
||||
|
||||
@ -44,7 +45,7 @@ class ModelMySQL {
|
||||
*
|
||||
* @return mysqli
|
||||
*/
|
||||
protected static function db() {
|
||||
protected static function db() : mysqli {
|
||||
if (is_null(static::$db))
|
||||
static::$db = Database::getConnection();
|
||||
|
||||
@ -63,7 +64,7 @@ class ModelMySQL {
|
||||
* @return mysqli_result
|
||||
* Contiene el resultado de la llamada SQL.
|
||||
*/
|
||||
protected static function query($query) {
|
||||
protected static function query($query) : mysqli_result {
|
||||
$db = static::db();
|
||||
|
||||
$result = $db->query($query);
|
||||
@ -103,13 +104,13 @@ class ModelMySQL {
|
||||
* Construye la sentencia SQL a partir static::$querySelect y una vez
|
||||
* construída, llama a resetQuery.
|
||||
*
|
||||
* @param boolean $resetQuery
|
||||
* @param bool $resetQuery
|
||||
* Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return string
|
||||
* Contiene la sentencia SQL.
|
||||
*/
|
||||
protected static function buildQuery($resetQuery = true) {
|
||||
protected static function buildQuery(bool $resetQuery = true) : string {
|
||||
if (static::$querySelect['sql_calc_found_rows'])
|
||||
$sql = 'SELECT SQL_CALC_FOUND_ROWS '.join(', ', static::$querySelect['select']);
|
||||
else
|
||||
@ -169,7 +170,7 @@ class ModelMySQL {
|
||||
* @return ModelMySQL
|
||||
* Retorna un objeto de la clase actual.
|
||||
*/
|
||||
protected static function getInstance($elem = []) {
|
||||
protected static function getInstance(array $elem = []) : ModelMySQL {
|
||||
$class = get_called_class();
|
||||
$instance = new $class;
|
||||
|
||||
@ -181,10 +182,15 @@ class ModelMySQL {
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve los atributos a guardar de la case actual.
|
||||
* Los atributos serán aquellos que seran public y
|
||||
* no esten excluidos en static::$ignoresave y aquellos
|
||||
* que sean private o protected pero estén en static::$forceSave.
|
||||
*
|
||||
* @return array
|
||||
* Contiene los atributos indexados del objeto actual.
|
||||
*/
|
||||
protected function getVars() {
|
||||
protected function getVars() : array {
|
||||
$reflection = new \ReflectionClass($this);
|
||||
$properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);
|
||||
$result = [];
|
||||
@ -206,10 +212,12 @@ class ModelMySQL {
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve el nombre de la clase actual aunque sea una clase extendida.
|
||||
*
|
||||
* @return string
|
||||
* Devuelve el nombre de la clase actual.
|
||||
*/
|
||||
public static function className() {
|
||||
public static function className() : string {
|
||||
return strtolower(substr(strrchr(get_called_class(), '\\'), 1));
|
||||
}
|
||||
|
||||
@ -220,7 +228,7 @@ class ModelMySQL {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function table() {
|
||||
protected static function table() : string {
|
||||
if (isset(static::$table))
|
||||
return static::$table;
|
||||
return static::className().static::$tableSufix;
|
||||
@ -310,8 +318,10 @@ class ModelMySQL {
|
||||
*
|
||||
* @param array $columns
|
||||
* Columnas que se selecionarán en la consulta SQL.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function select($columns) {
|
||||
public static function select(array $columns) : ModelMySQL {
|
||||
$db = static::db();
|
||||
$select = [];
|
||||
foreach($columns as $column) {
|
||||
@ -328,8 +338,10 @@ class ModelMySQL {
|
||||
*
|
||||
* @param array $tables
|
||||
* Tablas que se selecionarán en la consulta SQL.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function from($tables) {
|
||||
public static function from(array $tables) : ModelMySQL {
|
||||
$db = static::db();
|
||||
$from = [];
|
||||
foreach($tables as $table) {
|
||||
@ -353,11 +365,12 @@ class ModelMySQL {
|
||||
* @param string $value
|
||||
* (opcional) El valor el valor a comparar en la columna.
|
||||
*
|
||||
* @param boolean $no_quote
|
||||
* @param bool $no_quote
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function where($column, $operator, $value=null, $no_quote = false) {
|
||||
public static function where(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||
if (is_null($value)) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
@ -382,10 +395,12 @@ class ModelMySQL {
|
||||
* @param array $arr
|
||||
* Arreglo con todos los valores a comparar con la columna.
|
||||
*
|
||||
* @param boolean $in
|
||||
* (opcional) Define si se usará IN o NOT IN en la sentencia SQL.
|
||||
* @param bool $in
|
||||
* Define si se tienen que comprobar negativa o positivamente.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function where_in($column, $arr, $in = true) {
|
||||
public static function where_in(string $column, array $arr, bool $in = true) : ModelMySQL {
|
||||
foreach($arr as $index => $value) {
|
||||
$arr[$index] = static::db()->real_escape_string($value);
|
||||
}
|
||||
@ -412,8 +427,10 @@ class ModelMySQL {
|
||||
*
|
||||
* @param string $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function leftJoin($table, $columnA, $operator, $columnB = null) {
|
||||
public static function leftJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operator;
|
||||
$operator = '=';
|
||||
@ -443,8 +460,9 @@ class ModelMySQL {
|
||||
* @param string $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function rightJoin($table, $columnA, $operator, $columnB = null) {
|
||||
public static function rightJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operator;
|
||||
$operator = '=';
|
||||
@ -472,8 +490,10 @@ class ModelMySQL {
|
||||
*
|
||||
* @param string $columnB
|
||||
* (opcional) Columna a comparar para hacer el join.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function innerJoin($table, $columnA, $operator, $columnB = null) {
|
||||
public static function innerJoin(string $table, string $columnA, string $operator, string $columnB = null) : ModelMySQL {
|
||||
if (is_null($columnB)) {
|
||||
$columnB = $operator;
|
||||
$operator = '=';
|
||||
@ -499,11 +519,12 @@ class ModelMySQL {
|
||||
* @param string $value
|
||||
* (opcional) El valor el valor a comparar en la columna.
|
||||
*
|
||||
* @param $no_quote
|
||||
* @param bool $no_quote
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function and($column, $operator, $value=null, $no_quote = false) {
|
||||
public static function and(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||
if (is_null($value)) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
@ -531,10 +552,12 @@ class ModelMySQL {
|
||||
* @param string $value
|
||||
* (opcional) El valor el valor a comparar en la columna.
|
||||
*
|
||||
* @param $no_quote
|
||||
* @param bool $no_quote
|
||||
* (opcional) Se usa cuando $value es una columna o un valor que no requiere comillas.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function or($column, $operator, $value=null, $no_quote = false) {
|
||||
public static function or(string $column, string $operator, string $value=null, bool $no_quote = false) : ModelMySQL {
|
||||
if (is_null($value)) {
|
||||
$value = $operator;
|
||||
$operator = '=';
|
||||
@ -555,16 +578,23 @@ class ModelMySQL {
|
||||
*
|
||||
* @param array $arr
|
||||
* Columnas por las que se agrupará.
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function groupBy($arr) {
|
||||
public static function groupBy(array $arr) : ModelMySQL {
|
||||
static::$querySelect['groupBy'] = join(', ', $arr);
|
||||
return new static();
|
||||
}
|
||||
|
||||
public static function limit($initial, $final = 0) {
|
||||
$initial = (int)$initial;
|
||||
$final = (int)$final;
|
||||
|
||||
/**
|
||||
* Define LIMIT en la sentencia SQL.
|
||||
*
|
||||
* @param int $initial
|
||||
* @param int $final
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function limit(int $initial, int $final = 0) : ModelMySQL {
|
||||
if ($final==0)
|
||||
static::$querySelect['limit'] = $initial;
|
||||
else
|
||||
@ -582,8 +612,10 @@ class ModelMySQL {
|
||||
* @param string $order
|
||||
* (opcional) Define si el orden será de manera ascendente (ASC),
|
||||
* descendente (DESC) o aleatorio (RAND).
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function orderBy($value, $order = 'ASC') {
|
||||
public static function orderBy(string $value, string $order = 'ASC') : ModelMySQL {
|
||||
if ($value == "RAND") {
|
||||
static::$querySelect['orderBy'] = 'RAND()';
|
||||
return new static();
|
||||
@ -602,15 +634,15 @@ class ModelMySQL {
|
||||
/**
|
||||
* Retorna la cantidad de filas que hay en un query.
|
||||
*
|
||||
* @param boolean $resetQuery
|
||||
* @param bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @param boolean $useLimit
|
||||
* @param bool $useLimit
|
||||
* (opcional) Permite usar limit para estabecer un máximo inical y final para contar. Requiere que se haya definido antes el límite (por defecto en false).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function count($resetQuery = true, $useLimit = false) {
|
||||
public static function count(bool $resetQuery = true, bool $useLimit = false) : int {
|
||||
if (!$resetQuery)
|
||||
$backup = [
|
||||
'select' => static::$querySelect['select'],
|
||||
@ -653,7 +685,7 @@ class ModelMySQL {
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function found_row() {
|
||||
public static function found_row() : int {
|
||||
$result = static::query('SELECT FOUND_ROWS() AS quantity')->fetch_assoc();
|
||||
return $result['quantity'];
|
||||
}
|
||||
@ -663,7 +695,7 @@ class ModelMySQL {
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function sql_calc_found_rows() {
|
||||
public static function sql_calc_found_rows() : ModelMySQL {
|
||||
static::$querySelect['sql_calc_found_rows'] = true;
|
||||
return new static();
|
||||
}
|
||||
@ -674,7 +706,7 @@ class ModelMySQL {
|
||||
* @param mixed $id
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function getById($id) {
|
||||
public static function getById($id) : ModelMySQL {
|
||||
return static::where(static::$primaryKey, $id)->getFirst();
|
||||
}
|
||||
|
||||
@ -686,8 +718,10 @@ class ModelMySQL {
|
||||
*
|
||||
* @param array $in
|
||||
* (opcional) Columnas en las que se va a buscar (null para buscar en todas).
|
||||
*
|
||||
* @return ModelMySQL
|
||||
*/
|
||||
public static function search($search, $in = null) {
|
||||
public static function search(string $search, array $in = null) : ModelMySQL {
|
||||
if ($in == null) {
|
||||
$className = get_called_class();
|
||||
$in = array_keys((new $className())->getVars());
|
||||
@ -714,12 +748,13 @@ class ModelMySQL {
|
||||
/**
|
||||
* Obtener los resultados de la consulta SQL.
|
||||
*
|
||||
* @param boolean $resetQuery
|
||||
* @param bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return ModelMySQL[]
|
||||
* @return array
|
||||
* Contiene un arreglo de instancias de la clase actual.
|
||||
*/
|
||||
public static function get($resetQuery = true) { // Devuelve array vacío si no encuentra nada.
|
||||
public static function get(bool $resetQuery = true) : array { // Devuelve array vacío si no encuentra nada.
|
||||
$sql = static::buildQuery($resetQuery);
|
||||
$result = static::query($sql);
|
||||
|
||||
@ -735,13 +770,13 @@ class ModelMySQL {
|
||||
/**
|
||||
* El primer elemento de la consulta SQL.
|
||||
*
|
||||
* @param boolean $resetQuery
|
||||
* @param bool $resetQuery
|
||||
* (opcional) Indica si el query debe reiniciarse o no (por defecto es true).
|
||||
*
|
||||
* @return mixed
|
||||
* Puede retornar un objeto ModelMySQL o null.
|
||||
*/
|
||||
public static function getFirst($resetQuery = true) { // Devuelve null si no encuentra nada.
|
||||
public static function getFirst(bool $resetQuery = true) { // Devuelve null si no encuentra nada.
|
||||
static::limit(1);
|
||||
$instances = static::get($resetQuery);
|
||||
return empty($instances) ? null : $instances[0];
|
||||
@ -750,9 +785,10 @@ class ModelMySQL {
|
||||
/**
|
||||
* Obtener todos los elementos del la tabla de la instancia actual.
|
||||
*
|
||||
* @return ModelMySQL[]
|
||||
* @return array
|
||||
* Contiene un arreglo de instancias de la clase actual.
|
||||
*/
|
||||
public static function all() {
|
||||
public static function all() : array {
|
||||
$sql = 'SELECT * FROM '.static::table();
|
||||
|
||||
$result = static::query($sql);
|
||||
@ -770,9 +806,12 @@ class ModelMySQL {
|
||||
* Permite definir como nulo el valor de un atributo.
|
||||
* Sólo funciona para actualizar un elemento de la BD, no para insertar.
|
||||
*
|
||||
* @trows \Exception
|
||||
* Devolverá un error en caso de usarse en un insert.
|
||||
*
|
||||
* @param array $atts
|
||||
*/
|
||||
public function setNull($atts) {
|
||||
public function setNull(array $atts) {
|
||||
if (!isset($this->id))
|
||||
throw new \Exception(
|
||||
"\nEl método setNull sólo funciona para actualizar, no al insertar."
|
||||
|
@ -34,7 +34,7 @@ class Router {
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return array
|
||||
@ -42,7 +42,7 @@ class Router {
|
||||
* path - Contiene la ruta con las pseudovariables reeplazadas por expresiones regulares.
|
||||
* callback - Contiene el callback en formato Namespace\Clase::Método.
|
||||
*/
|
||||
private static function parse($path, $callback) {
|
||||
private static function parse(string $path, $callback) : array {
|
||||
preg_match_all('/{(\w+)}/s', $path, $matches, PREG_PATTERN_ORDER);
|
||||
$paramNames = $matches[1];
|
||||
|
||||
@ -69,8 +69,10 @@ class Router {
|
||||
*
|
||||
* Ej: Si la url del sistema está en "https://ejemplo.com/duckbrain"
|
||||
* entonces la ruta base sería "/duckbrain"
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function basePath() {
|
||||
public static function basePath() : string {
|
||||
if (defined('SITE_URL'))
|
||||
return parse_url(SITE_URL, PHP_URL_PATH);
|
||||
return str_replace($_SERVER['DOCUMENT_ROOT'], '/', ROOT_DIR);
|
||||
@ -86,7 +88,7 @@ class Router {
|
||||
* llamamos a Router::redirect('/docs'), entonces seremos
|
||||
* redirigidos a "https://ejemplo.com/duckbrain/docs".
|
||||
*/
|
||||
public static function redirect($path) {
|
||||
public static function redirect(string $path) {
|
||||
header('Location: '.static::basePath().substr($path,1));
|
||||
}
|
||||
|
||||
@ -94,14 +96,14 @@ class Router {
|
||||
* Añade un middleware a la última ruta usada.
|
||||
* Solo se puede usar un middleware a la vez.
|
||||
*
|
||||
* @param string $callback
|
||||
* @param mixed $callback
|
||||
*
|
||||
* @return static
|
||||
* @return Router
|
||||
* Devuelve un enlace estático.
|
||||
*/
|
||||
public static function middleware($callback){
|
||||
public static function middleware($callback) : Router{
|
||||
if (!isset(static::$last))
|
||||
return;
|
||||
return new static();
|
||||
|
||||
$method = static::$last[0];
|
||||
$index = static::$last[1];
|
||||
@ -116,15 +118,15 @@ class Router {
|
||||
}
|
||||
|
||||
/*
|
||||
* @return object
|
||||
* @return Neuron
|
||||
* Devuelve un objeto que contiene los atributos:
|
||||
* post - Donde se encuentran los valores enviados por $_POST.
|
||||
* get - Donde se encuentran los valores enviados por $_GET.
|
||||
* json - Donde se encuentran los valores JSON enviados en el body.
|
||||
*
|
||||
*/
|
||||
private static function getReq() {
|
||||
$req = (object) '';
|
||||
private static function getReq() : Neuron {
|
||||
$req = new Neuron();
|
||||
$req->get = new Neuron($_GET);
|
||||
$req->post = new Neuron($_POST);
|
||||
$req->json = new Neuron(static::get_json());
|
||||
@ -137,7 +139,7 @@ class Router {
|
||||
* @return object
|
||||
* Devuelve un objeto con los datos recibidos en JSON.
|
||||
*/
|
||||
private static function get_json() {
|
||||
private static function get_json() : object {
|
||||
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
|
||||
if ($contentType === "application/json") {
|
||||
return json_decode(trim(file_get_contents("php://input")));
|
||||
@ -151,13 +153,13 @@ class Router {
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* @return Router
|
||||
* Devuelve un enlace estático.
|
||||
*/
|
||||
public static function get($path, $callback) {
|
||||
public static function get(string $path, $callback) {
|
||||
static::$get[] = static::parse($path, $callback);
|
||||
static::$last = ['get', count(static::$get)-1];
|
||||
return new static();
|
||||
@ -169,13 +171,13 @@ class Router {
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* @return Router
|
||||
* Devuelve un enlace estático.
|
||||
*/
|
||||
public static function post($path, $callback) {
|
||||
public static function post(string $path, $callback) : Router {
|
||||
static::$post[] = static::parse($path, $callback);
|
||||
static::$last = ['post', count(static::$post)-1];
|
||||
return new static();
|
||||
@ -187,14 +189,14 @@ class Router {
|
||||
* @param string $path
|
||||
* Ruta con pseudovariables.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $callback
|
||||
* Callback que será llamado cuando la ruta configurada en $path coincida.
|
||||
*
|
||||
* @return static
|
||||
* @return Router
|
||||
* Devuelve un enlace estático
|
||||
*/
|
||||
|
||||
public static function put($path, $callback) {
|
||||
public static function put(string $path, $callback) : Router {
|
||||
static::$put[] = static::parse($path, $callback);
|
||||
static::$last = ['put', count(static::$put)-1];
|
||||
return new static();
|
||||
@ -212,7 +214,7 @@ class Router {
|
||||
* @return static
|
||||
* Devuelve un enlace estático
|
||||
*/
|
||||
public static function delete($path, $callback) {
|
||||
public static function delete(string $path, $callback) : Router {
|
||||
static::$delete[] = static::parse($path, $callback);
|
||||
static::$last = ['delete', count(static::$delete)-1];
|
||||
return new static();
|
||||
@ -220,8 +222,10 @@ class Router {
|
||||
|
||||
/*
|
||||
* Devuelve la ruta actual.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function currentPath() {
|
||||
public static function currentPath() : string {
|
||||
return preg_replace('/'.preg_quote(static::basePath(), '/').'/',
|
||||
'/', strtok($_SERVER['REQUEST_URI'], '?'), 1);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class View {
|
||||
* @param string $viewPath
|
||||
* (opcional) Ruta donde se encuentra la vista. En caso de que la vista no se encuentre en esa ruta, se usará la ruta por defecto "src/Views/".
|
||||
*/
|
||||
public static function render($viewName, $params = [], $viewPath = null) {
|
||||
public static function render(string $viewName, array $params = [], string $viewPath = null) {
|
||||
$view = new Neuron($params);
|
||||
unset($params);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user