Issues (183)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

system/model.php (43 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Classe mère de tous les modèles
5
 * @version 1.0
6
 * @todo deprecate this. Model is not MVC compliant
7
 */
8
abstract class Model
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9
{
10
    public static $is_logue = false;
11
    protected static $dbInstance = null;
12
    protected $errors = array();
13
    protected $nombre_ligne; // pour l'affichage du nombre de lignes des listes (utilisés dans les Xobjets)
14
    protected $log_id;
15
    private static $session;
16
    public $requete;
17
18
    public function getIsLogue()
19
    {
20
        return (bool)self::$is_logue;
21
    }
22
23
    public function setIsLogue($bool = false)
24
    {
25
        self::$is_logue = (bool)$bool;
26
        return $this;
27
    }
28
29
    /* **********
30
    * Créateur	*
31
    ********** */
32
    /**
33
     * Crée une instance de classe avec les paramètres transmis
34
     * Typiquement $_REQUEST d'un formulaire
35
     **/
36
    public function __construct($params = array())
37
    {
38
        $this->initialisationVariablesAvantContructeur();
39
        foreach (array_keys(get_object_vars($this)) as $attribute) {
40
            if (isset($params[$attribute])) {
41
                $this->setAttribute($attribute, $params[$attribute]);
42
            }
43
        }
44
        return $this;
0 ignored issues
show
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
45
    }
46
47
    public function getAttributes()
48
    {
49
        return array_keys(get_object_vars($this));
50
    }
51
52
    /**
53
     * cette fonction est appelée au tout début du constructeur.
54
     * elle est principalement utilisée pour la gestion des cases à cocher qui doivent être décochée
55
     * si elles ne sont pas envoyée dans le $_POST du formulaire.
56
     */
57
    public function initialisationVariablesAvantContructeur()
58
    {
59
        /* fonction à redéfinir dans le modèle au besoin */
60
    }
61
62
    /**
63
     * Retourne le nom du controller associé à la classe appelante
64
     * @return string : Le nom du controleur
65
     */
66
    public static function getControllerName()
67
    {
68
        $classe_appelante = get_called_class();
69
        if (substr($classe_appelante, 0, 4) == 'Base') {
70
            $classe_appelante = substr($classe_appelante, 4);
71
        }
72
        return \FMUP\StringHandling::toCamelCase($classe_appelante);
73
    }
74
75
    /**
76
     * transforme un objet en tableau
77
     */
78
    public function objectToTable($objet)
79
    {
80
        $tableau = array();
81
        foreach ($objet as $attribute => $value) {
82
            $tableau[$attribute] = $value;
83
        }
84
        return $tableau;
85
    }
86
87
    /**
88
     * transforme un objet en tableau sans recupérer l'id
89
     */
90
    public function objectToTableSansId($objet)
91
    {
92
        $tableau = array();
93
        foreach ($objet as $attribute => $value) {
94
            if ($attribute != "id") {
95
                $tableau[$attribute] = $value;
96
            }
97
        }
98
        return $tableau;
99
    }
100
101
    /**
102
     * Crée une instance d'un modèle avec les paramètres transmis
103
     * Typiquement le résultat d'une requète
104
     * @param array $params : Données de l'objet
105
     * @param string $class_name : Type d'objet
106
     * @return Object
107
     */
108
    protected static function create($params, $class_name)
109
    {
110
        $class = new $class_name();
111
        foreach ($params as $attribut => $value) {
112
            $class->$attribut = $value;
113
        }
114
        return $class;
115
    }
116
117
    /* *************************
118
    * Affichage et convertion *
119
    ************************* */
120
    /**
121
     * Retourne une classe sous forme de chaîne
122
     **/
123
    public function __toString()
124
    {
125
        ob_start();
126
        var_dump($this);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($this); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
127
        return ob_get_clean();
128
    }
129
130
    /**
131
     * Convertit une collection en tableau
132
     * @param {Array} La collection
133
     * @param {Integer} L'attribut à mettre dans les index du tableau
134
     * @param {Integer} L'attribut ou les attributs (tableau) à mettre dans les valeurs du tableau
135
     **/
136
    public static function arrayFromCollection($collection, $element_value, $element_text)
137
    {
138
        $array = array();
139
        foreach ($collection as $element) {
140
            if (is_array($element_text)) {
141
                $liste_attributs = array();
142
                foreach ($element_text as $attribut) {
143
                    $liste_attributs[] = $element->getAttribute($attribut);
144
                }
145
                $array[$element->getAttribute($element_value)] = implode(' - ', $liste_attributs);
146
            } else {
147
                $array[$element->getAttribute($element_value)] = $element->getAttribute($element_text);
148
            }
149
        }
150
        return $array;
151
    }
152
153
    /**
154
     * Convertit un objet en tableau
155
     * @param {Array} La collection
156
     **/
157
    public function arrayFromObject()
158
    {
159
        $array = array();
160
        foreach (array_keys(get_object_vars($this)) as $attribute) {
161
            $array[$attribute] = $this->getAttribute($attribute);
162
        }
163
        return $array;
164
    }
165
166
    /**
167
     * Crée des objets à partir d'une matrice (typiquement le résultat d'une requète)
168
     */
169
    protected static function objectsFromMatrix($matrix, $class_name, $modeIterator = true)
170
    {
171
        if ($modeIterator) {
172
            if (!$matrix instanceof \Iterator && is_array($matrix)) {
173
                $matrix = new \ArrayIterator($matrix);
174
            }
175
            return new \ArrayToObjectIterator($matrix, $class_name);
176
        }
177
        $liste = array();
178
        if ($matrix instanceof Traversable || is_array($matrix)) {
179
            foreach ($matrix as $array) {
180
                array_push($liste, $class_name::create($array, $class_name));
181
            }
182
        }
183
        return $liste;
184
    }
185
186
    public static function objectsFromArray($array, $class_name)
187
    {
188
        return $class_name::create($array, $class_name);
189
    }
190
191
    protected static function objectsFromMatrixByAttribute($matrix, $class_name, $attribute = 'id')
192
    {
193
        $liste = array();
194
        if (!empty($matrix) && (is_array($matrix) || $matrix instanceof Traversable)) {
195
            foreach ($matrix as $array) {
196
                $objet = $class_name::create($array, $class_name);
197
                $liste[$objet->getAttribute($attribute)] = $objet;
198
            }
199
        }
200
        return $liste;
201
    }
202
203
    /**
204
     * Retourne tous les éléments éventuellement filtrés de la table
205
     * @param array $where : [OPT] Un tableau de tous les filtres éventuels
206
     * @param array $options : [OPT] Options disponibles :
207
     *        - order : Tri sur la requête
208
     *        - findOne : Utilisé pour déterminer qu'on ne recherche qu'un résultat
209
     *          (TODO pour afficher les objets supprimés, ça ressemble à un doublon, fonctionnement ésotérique...)
210
     *        - afficher_supprimes : TODO vérifier si c'est vraiment utilisé, apparement non
211
     *    Seulement MySQL
212
     *        - limit : Pagination
213
     *    Seulement MSSQL
214
     *        - top : Semi-pagination qui ne passe pas par la sous-vue
215
     *        - paging : C'est un tableau composé de numero_page et nb_element
216
     *                     Utilisé pour simuler une pagination grace à une sous-vue
217
     * @return array[object]
0 ignored issues
show
The doc-type array[object] could not be parsed: Expected "]" at position 2, but found "object". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
218
     */
219
    public static function findAll($where = array(), $options = array())
220
    {
221
        $classe_appelante = get_called_class();
222
223
        //si on appelle depuis un object complexe, on recupere la requete correspondante
224
        if (call_user_func(array($classe_appelante, 'afficherParDefautNonSupprimes'))) {
225
            if (!isset($where['supprime']) &&
226
                (!isset($options['fonction']) || $options['fonction'] != 'findOne')
227
            ) {
228
                $where['supprime'] = 'supprime = 0';
229
            }
230
        }
231
        //sinon appelle de l'objet de Base généré par le génératOR
232
        $result = Model::findAllFromTable(
233
            call_user_func(array($classe_appelante, 'getTableName')),
234
            $where,
235
            $options
236
        );
237
238
        // Création d'un tableau d'objets
239
        return Model::objectsFromMatrix($result, $classe_appelante);
240
    }
241
242
    /**
243
     * Retourne un élément grâce à son ID
244
     * @param int $id : Un identifiant
245
     * @return null|object
246
     */
247
    public static function findOne($id)
248
    {
249
        $classe_appelante = get_called_class();
250
251
        $return = call_user_func(
252
            array($classe_appelante, 'findAll'),
253
            array('id = ' . Sql::secureId($id)),
254
            array('fonction' => 'findOne')
255
        );
256
        if (count($return) > 0) {
257
            return $return[0];
258
        } else {
259
            return null;
260
        }
261
    }
262
263
    /**
264
     * Retourne le premier élément d'un findAll
265
     * @param array $where : [OPT] Un tableau de tous les filtres éventuels
266
     * @param string $order : [OPT] Le champ sur lequel ordonner
267
     * @return false|object
268
     */
269
    public static function findFirst($where = array(), $order = '')
270
    {
271
        $classe_appelante = get_called_class();
272
273
        $return = call_user_func(
274
            array($classe_appelante, 'findAll'),
275
            $where,
276
            array('order' => $order, 'limit' => '0, 1', 'top' => '1')
277
        );
278
        if (count($return)) {
279
            return $return[0];
280
        } else {
281
            return false;
282
        }
283
    }
284
285
    /**
286
     * Retourne le nombre d'éléments d'une requête
287
     * @param array $where : Un tableau de clauses pour la requête
288
     * @return int : Le nombre d'éléments
289
     */
290
    public static function count($where = array())
291
    {
292
        $classe_appelante = get_called_class();
293
        return Model::countFromTable(call_user_func(array($classe_appelante, 'getTableName')), $where);
294
    }
295
296
297
    /**
298
     * Supprime l'objet dans la base de données
299
     * @return bool : Le résultat du traitement, VRAI si suppression
300
     * @todo : Supprimer les objets liés
301
     */
302
    public function delete()
303
    {
304
        $classe_appelante = get_called_class();
305
        $retour = $this->deleteFromTable(call_user_func(array($classe_appelante, 'getTableName')));
306
        return $retour;
307
    }
308
309
    /**
310
     * si la table de l'objet contient un champ date_suppression,
311
     * et qu'il ne faut afficher que les données non supprimées par défaut
312
     * alors réécrire cette fonction dans l'objet avec return true
313
     * @return bool
314
     */
315
    public static function afficherParDefautNonSupprimes()
316
    {
317
        return false;
318
    }
319
320
    /**
321
     * si la table de l'objet contient un champ visible, et qu'il ne faut afficher que les données visibles par défaut
322
     * alors réécrire cette fonction dans l'objet avec return true
323
     * @return bool
324
     */
325
    public static function afficherParDefautDataVisibles()
326
    {
327
        return false;
328
    }
329
330
331
    /* **************************
332
    * Requète et SQL générique *
333
    ************************** */
334
    /**
335
     * Trouve tous les enregistrements d'une table donnée
336
     * @param string $table : Le nom de la table
337
     * @param array $where :Un tableau de conditions
338
     * @param array $options : L'ordre, le limit, ...
339
     * @return array : Tableau contenant les objets
340
     */
341
    protected static function findAllFromTable($table, $where = array(), $options = array())
342
    {
343
        $SQL = "SELECT * FROM $table";
344
        $SQL .= Sql::parseWhere($where);
345
        $isIterator = (!isset($options["iterator"]) || $options["iterator"]);
346
        if (isset($options["group_by"]) && $options["group_by"]) {
347
            $SQL .= " group by " . $options["group_by"];
348
        }
349
        if (isset($options["order"]) && $options["order"]) {
350
            $SQL .= " ORDER BY " . $options["order"];
351
        }
352
353
        if (isset($options["top"]) && $options["top"]) {
354
            $SQL .= " LIMIT " . $options["top"];
355
        } elseif (!empty($options['limit'])) {
356
            $SQL .= " LIMIT " . $options["limit"];
357
        }
358
359
        // Exécution de la requète
360
        $db = \Model::getDb();
361
        $result = $isIterator ? $db->getIterator($SQL) : $db->fetchAll($SQL);
0 ignored issues
show
Deprecated Code introduced by
The method FMUP\Db::fetchAll() has been deprecated with message: use self::getIterator() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
362
        return $result;
363
    }
364
365
    /**
366
     * Retourne un tableau avec l'ID de la DMD et un attribut
367
     *      --> utilsié pour alléger les menus déroulant de l'application
368
     * @param {Array} un tableau de tous les filtres éventuels
369
     * @param {string} le champ sur lequel ordonner
370
     **/
371 View Code Duplication
    public static function findAllAttributeFromTable($table, $attribute, $where = array(), $options = array())
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
    {
373
        $SQL = "SELECT    id\n";
374
        if ($attribute != 'id') {
375
            $SQL .= "," . $attribute . "\n";
376
        }
377
        $SQL .= "FROM $table ";
378
        $SQL .= Sql::parseWhere($where);
379
380
        if (isset($options["order"]) && $options["order"]) {
381
            $SQL .= " ORDER BY " . $options["order"];
382
        }
383
        if (isset($options["limit"]) && $options["limit"]) {
384
            $SQL .= " LIMIT " . $options["limit"];
385
        }
386
        //echo($SQL);
387
        // Exécution de la requète
388
        $db = \Model::getDb();
389
        $result = $db->fetchAll($SQL);
0 ignored issues
show
Deprecated Code introduced by
The method FMUP\Db::fetchAll() has been deprecated with message: use self::getIterator() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
390
        return $result;
391
    }
392
393 View Code Duplication
    public static function findAllAttributeFromLeftJoinTable(
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394
        $table,
395
        $left_table,
396
        $link_table,
397
        $attribute,
398
        $where = array(),
399
        $options = array()
400
    )
401
    {
402
        $SQL = "SELECT $attribute \n";
403
        $SQL .= " FROM $table  \n LEFT JOIN $left_table  \n";
404
        $SQL .= " ON $link_table  \n";
405
        $SQL .= Sql::parseWhere($where);
406
407
        if (isset($options["order"]) && $options["order"]) {
408
            $SQL .= " ORDER BY " . $options["order"] . "  \n";
409
        }
410
        if (isset($options["limit"]) && $options["limit"]) {
411
            $SQL .= " LIMIT " . $options["limit"];
412
        }
413
        //debug::output($SQL);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
414
        // Exécution de la requète
415
        $db = \Model::getDb();
416
        $result = $db->getIterator($SQL);
417
        return $result;
418
    }
419
420
    /**
421
     * Retourne le nombre d'éléments d'une requéte pour une table donnée
422
     * @param {string} Le nom de la table
423
     * @param {Array} un tableau de condititions
424
     **/
425
    protected static function countFromTable($table, $where = array(), $options = array())
426
    {
427
        $SQL = "SELECT COUNT(*) AS nb FROM $table";
428
        $SQL .= sql::ParseWhere($where);
429
        if (!empty($options["group_by"])) {
430
            $SQL .= " group by " . $options["group_by"];
431
        }
432
        // Exécution de la requète
433
        $db = \Model::getDb();
434
        $result = $db->fetchRow($SQL);
435
        return $result["nb"];
436
    }
437
438
    /**
439
     * Retourne la somme des valeurs d'une colonne pour une table et une colonne donnée
440
     * @param {string} Le nom de la table
441
     * @param {string} le nom de la colonne à sommer
442
     * @param {Array} un tableau de condition
443
     * @param {Array} un tableau d'options
444
     * @return mixed La somme si pas de group by en option, un tableau de couple somme / valeur de la colonne sinon
445
     **/
446
    protected static function sumFromTable($table, $colonne, $where = array(), $options = array())
447
    {
448
        $select = "SUM($colonne) as somme";
449
        $group_by = "";
450
        if (isset($options["group_by"]) && $options["group_by"]) {
451
            $select .= ", $table.$colonne";
452
            $group_by = " GROUP BY " . $options["group_by"];
453
        }
454
        $sql = "SELECT $select FROM $table";
455
        $sql .= sql::ParseWhere($where);
456
        $sql .= $group_by;
457
        // Exécution de la requête
458
        $db = \Model::getDb();
459
        return ($group_by != "") ? $db->fetchAll($sql) : $db->fetchRow($sql)["somme"];
0 ignored issues
show
Deprecated Code introduced by
The method FMUP\Db::fetchAll() has been deprecated with message: use self::getIterator() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
460
    }
461
462
    /**
463
     * Supprime l'objet dans la base de données
464
     * Place le champ supprime à 1 à la place si le champ est présent dans la table
465
     * @param string $table : Le nom de la table
466
     * @return bool : VRAI si la suppression est effectuée
467
     */
468
    protected function deleteFromTable($table)
469
    {
470
        if (($this->id > 0) && ($this->canBeDeleted())) {
471
            // Cas ou le champ de suppression existe
472
            if (property_exists($this, 'supprime')) {
473
                $this->supprime = true;
0 ignored issues
show
The property supprime does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
474
                $infos_suppression = '';
475
                if (property_exists($this, 'date_suppression')) {
476
                    $infos_suppression .= ', date_suppression = CURRENT_TIMESTAMP()';
477
                    $this->date_suppression = date('Y-m-d H:i:s');
0 ignored issues
show
The property date_suppression does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
478
                }
479
                if (property_exists($this, 'id_suppresseur')) {
480
                    if (self::getSession()->has('id_utilisateur')) {
481
                        $id_utilisateur = self::getSession()->get('id_utilisateur');
482
                    }
483
                    $infos_suppression .= ', id_suppresseur = ' . Sql::secureId($id_utilisateur);
0 ignored issues
show
The variable $id_utilisateur does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
484
                    $this->id_suppresseur = $id_utilisateur;
0 ignored issues
show
The property id_suppresseur does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
485
                }
486
                // Loger le changement
487
                $this->logerChangement("delete");
488
                $SQL = "UPDATE $table
489
                        SET supprime = 1
490
                        $infos_suppression
491
                        WHERE id = " . $this->id;
0 ignored issues
show
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
492
                $db = Model::getDb();
493
                return (bool)$db->execute($SQL);
0 ignored issues
show
The method execute() does not seem to exist on object<FMUP\Db>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
494
                // Cas de la suppression physique
495
            } else {
496
                // Loger le changement
497
                $this->logerChangement("delete");
498
                $SQL = "DELETE FROM $table WHERE id = " . $this->id;
499
                $db = Model::getDb();
500
                $return = (bool)$db->query($SQL);
501
                if ($return) {
502
                    $this->id = "";
503
                    return true;
504
                } else {
505
                    return false;
506
                }
507
            }
508
        } else {
509
            return false;
510
        }
511
    }
512
513
    /**
514
     * Retourne l'instance de base de données du controlleur actif
515
     * @return \FMUP\Db
516
     */
517
    public static function getDb()
518
    {
519
        if (!self::$dbInstance) {
520
            self::$dbInstance = \FMUP\Db\Manager::getInstance()->get();
521
        }
522
        return self::$dbInstance;
523
    }
524
525
    public static function setDb($dbInstance)
526
    {
527
        self::$dbInstance = $dbInstance;
528
    }
529
530
    /* ************************
531
    * Sauvegarde des données *
532
    ************************ */
533
    /**
534
     * Sauvegarde ou met à jour l'objet dans la base de donnée
535
     * @param bool $force_enregistrement
536
     *      si TRUE, alors le système contrepasse le VALIDATE et enregistre quand même l'objet
537
     *            (ATTENTION à l'utilisation de ce paramètre)
538
     * @return bool
539
     *      VRAI si une action a eu lieu en base (si la requête ne change rien ou le validate bloque, retourne FAUX)
540
     * @throws \FMUP\Exception
541
     */
542
    public function save($force_enregistrement = false)
543
    {
544
        // debug::output($this, true);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
545
        if ($force_enregistrement || $this->validate(true)) {
0 ignored issues
show
The call to Model::validate() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
546
            if (Is::id($this->id)) {
547
                /* Loger le changement */
548
                $this->logerChangement("update");
549
                if ($this->update() !== false) {
550
                    $this->comparerDifferences();
551
                } else {
552
                    throw new \FMUP\Exception("Erreur pendant l'enregistrement");
553
                }
554
            } else {
555
                /* Loger le changement */
556
                $this->id = $this->insert();
557
                $this->logerChangement("insert");
558
            }
559
            //appel de la fonction post SAVE, si l'enregistrement a fonctionné
560
            if ($this->id) {
561
                $this->setValeursDefautPostSave();
562
            }
563
            return $this->id;
564
        } else {
565
            return false;
566
        }
567
    }
568
569
    /**
570
     * Insertion d'un objet en base
571
     * @return bool
572
     */
573
    abstract protected function insert();
574
575
    /**
576
     * Mise à jour d'un objet en base
577
     * @return bool
578
     */
579
    abstract protected function update();
580
581
    /**
582
     * Le message affiché à l'update
583
     */
584
    public static function getMessageUpdateOK()
585
    {
586
        return "Mise à jour réalisée avec succès.";
587
    }
588
589
    /**
590
     * Le message affiché à la suppression
591
     */
592
    public static function getMessageSuppressionOK()
593
    {
594
        return "Suppression réalisée avec succès.";
595
    }
596
597
    /* ***************************************
598
    * gestion des créateur et modificateurs *
599
    *            d'objet en base            *
600
    *************************************** */
601
602
    /**
603
     * Retourne le createur de l'objet
604
     * @return Utilisateur
605
     */
606
    public function getCreateur()
607
    {
608
        $createur = Utilisateur::findOne($this->id_createur);
0 ignored issues
show
The property id_createur does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
609
        if (!$createur) {
610
            $createur = new Utilisateur();
611
        }
612
        return $createur;
613
    }
614
615
    /**
616
     * Retourne le dernier modificateur de l'objet
617
     * @return Utilisateur
618
     */
619
    public function getModificateur()
620
    {
621
        $modificateur = Utilisateur::findOne($this->id_modificateur);
0 ignored issues
show
The property id_modificateur does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
622
        if (!$modificateur) {
623
            $modificateur = new Utilisateur();
624
        }
625
        return $modificateur;
626
    }
627
628
    /**
629
     * Modifie le champ date_creation par la date actuelle
630
     **/
631
    public function setDateCreation($value = null)
632
    {
633
        $creationDate = null;
634
        try {
635
            $creationDate = new \DateTime($value);
636
        } catch (\Exception $e) {
637
            $creationDate = \DateTime::createFromFormat('d/m/Y H:i:s', $value);
638
        }
639
        $this->date_creation = $creationDate->format('Y-m-d H:i:s');
0 ignored issues
show
The property date_creation does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
640
        return true;
641
    }
642
643
    /**
644
     * Modifie le champ createur par la personne connectée
645
     **/
646
    public function setIdCreateur($value = 0)
647
    {
648
        $sessionIdUser = self::getSession()->get('id_utilisateur');
649
        if ($value) {
650
            $this->id_createur = $value;
651
        } elseif (!empty($sessionIdUser)) {
652
            $this->id_createur = $sessionIdUser;
653
        } else {
654
            $this->id_createur = -1;
655
        }
656
        return true;
657
    }
658
659
    /**
660
     * Modifie le champ date de dernière modification par la date actuelle
661
     **/
662
    public function setDateModification($value = null)
663
    {
664
        $this->date_modification = date('Y-m-d H:i:s', $value ? strtotime($value) : null);
0 ignored issues
show
The property date_modification does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
665
        return true;
666
    }
667
668
    /**
669
     * Modifie le champ dernier modificateur par la personne connectée
670
     **/
671
    public function setIdModificateur($value = 0)
672
    {
673
        if ($value === 'null') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $value (integer) and 'null' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
674
            $this->id_modificateur = '';
675
        } elseif ($value) {
676
            $this->id_modificateur = $value;
677
        } elseif (self::getSession()->has('id_utilisateur')) {
678
            $this->id_modificateur = self::getSession()->get('id_utilisateur');
679
        } else {
680
            // ce cas ne peut arriver que si l'on modifie l'objet dans une page où personne n'est connecté
681
            // (ex: page de première connexion)
682
            $this->id_modificateur = 1;
683
        }
684
        return true;
685
    }
686
687
    /* ******************************
688
    * Affichage d'infos et erreurs *
689
    ****************************** */
690
691
    /**
692
     * Réinitialise le tableau d'erreurs
693
     */
694
    public function initialiseErrors()
695
    {
696
        $this->errors = array();
697
    }
698
699
    /**
700
     * Retourne les erreurs de validation d'un objet
701
     * @param string $attribute : [OPT] Pour vérifier les erreurs sur un atribut en particulier
702
     * @return array
703
     */
704
    public function getErrors($attribute = false)
705
    {
706
        if ($attribute) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attribute of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
707
            $tab_error = $this->errors;
708
            if (isset($tab_error[$attribute])) {
709
                return $tab_error[$attribute];
710
            } else {
711
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Model::getErrors of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
712
            }
713
        } else {
714
            return $this->errors;
715
        }
716
    }
717
718
    /*
719
     * Fonction utilisée par les demandes et commandes afin de trouver les différences
720
     * par rapport a une version modifiée
721
     */
722
    public function compareVersion()
723
    {
724
        return array();
725
    }
726
727
728
    /**
729
     * Met à jour le tableau des erreurs d'un objet.
730
     * @param {Array} $errors Le tableau d'erreurs à ajouter.
0 ignored issues
show
The doc-type {Array} could not be parsed: Unknown type name "{Array}" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
731
     * @return true
732
     */
733
    public function setErrors($errors)
734
    {
735
        $this->errors = array_merge($errors, $this->errors);
736
        return true;
737
    }
738
739
    /* *********************
740
    * Accesseurs généraux *
741
    ********************* */
742
    /**
743
     * Retourne la valeur d'un attribut si il existe
744
     * @param {String} l'attribut auquel accéder
745
     **/
746
    public function getAttribute($attribute)
747
    {
748
        return call_user_func(array($this, 'get' . \FMUP\StringHandling::toCamelCase($attribute)));
749
    }
750
751
    /**
752
     * Modifie la valeur d'un attribut si il existe
753
     * @param {String} l'attribut auquel accéder
754
     * @param {string} la valeur à enregistrer
755
     **/
756
    public function setAttribute($attribute, $value)
757
    {
758
        call_user_func(array($this, 'set' . \FMUP\StringHandling::toCamelCase($attribute)), $value);
759
        return true;
760
    }
761
762
    /**
763
     * Met à jour un objet (*sans* l'enregistrer dans la base de données) avec de
764
     * nouveaux paramètres
765
     * @param {Array} un tableau de nouvelles valeurs
766
     **/
767
    public function modify($params)
768
    {
769
        foreach ($params as $attribute => $value) {
770
            $this->setAttribute($attribute, $value);
771
        }
772
    }
773
774
    /**
775
     * Retourne ou modifie la valeur d'un attribut
776
     * @param string $function : L'attribut auquel accéder
777
     * @param string $argument : [OPT] La valeur à affecter dans le cas d'une affectation
778
     * @return mixed|bool|null : L'argument demandée pour une lecture, VRAI si affectation réussie, null sinon
779
     */
780
    public function __call($function, $argument = array())
781
    {
782
        $attribut = \FMUP\StringHandling::toCamelCase(substr($function, 3));
783
        if (property_exists($this, $attribut)) {
784
            if (preg_match('#^get#i', $function)) {
785
                return $this->$attribut;
786
            }
787
788
            if (preg_match('#^set#i', $function) && count($argument)) {
789
                $this->$attribut = $argument[0];
790
                return true;
791
            }
792
        } else {
793
            if (preg_match('#^get#i', $function) || preg_match('#^set#i', $function)) {
794
                $message = "Attribut inexistant $attribut dans l'objet " . get_called_class();
795
            } else {
796
                $message = "Fonction inexistante $function dans l'objet " . get_called_class();
797
            }
798
            throw new \FMUP\Exception($message);
799
        }
800
        return null;
801
    }
802
803
    /**
804
     * Retourne l'id de l'objet
805
     **/
806
    /*public function getId()
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
807
    {
808
        return $this->id;
809
    }*/
810
811
    /**
812
     * Retourne le code langue de l'utilisateur
813
     **/
814
    public function getCodeLangue()
815
    {
816
        return $this->code_langue;
0 ignored issues
show
The property code_langue does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
817
    }
818
819
    /* *********************
820
    *  Sécurisation des   *
821
    *      éditions       *
822
    ***********************/
823
824
    /**
825
     * Renvoi le tableau des champs autorisés
826
     * pour l'utilisateur en cours
827
     *
828
     * @return tableau des champs autorisés
829
     */
830
    public function listeChampsModifiable()
831
    {
832
        return array();
833
    }
834
835
    /**
836
     * Donne le droit à modifier l'objet mais par une méthode différente (non directe par l'attribut)
837
     **/
838
    public function setIdNew($valeur = '')
839
    {
840
        $this->id = $valeur;
841
    }
842
843
    /**
844
     * Renvoi l'autorisation d'un champ pour l'utilisateur en cours
845
     *
846
     * @param champ à vérifier
847
     * @return booléen d'autorisation
848
     */
849
    public function isChampModifiable($champ)
850
    {
851
        $liste = $this->listeChampsModifiable();
852
        if (isset($liste[$champ])) {
853
            return true;
854
        } else {
855
            return false;
856
        }
857
    }
858
859
    /**
860
     * @param Tableau des valeurs de l'objet typiquement $_POST
861
     * @return l'objet
0 ignored issues
show
The doc-type l'objet could not be parsed: Unknown type name "l'objet" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
862
     */
863
    public function setAttributesSecure($attributes)
864
    {
865
        //Sécurisation de l'id
866
        $identifiant = (isset($attributes['id']) && Is::id($attributes['id'])) ? $attributes['id'] : 0;
867
        //Récupération de l'objet en base (inutile de charger s'il n'y a pas d'ID)
868
        if ($identifiant) {
869
            $object = call_user_func(array(get_class($this), 'FindOne'), $identifiant);
870
        }
871
        if (!$identifiant || !$object) {
0 ignored issues
show
The variable $object does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
872
            $object = $this;
873
        }
874
        // récuperation des champs modifiable pour l'utilisateur courant
875
        $editable_fields = $object->listeChampsModifiable();
876
        //Si on fournit une donnée et si l'on peut la modifier
877
        // dans le cas de POST, il faut gérer les checkbox non cochées
878
        $object->initialisationVariablesAvantContructeur();
879
        foreach ($editable_fields as $field) {
880
            if (isset($attributes[$field])) {
881
                $object->setAttribute($field, $attributes[$field]);
882
            }
883
        }
884
885
        return $object;
886
    }
887
888
    public function setValeursDefautPostSave()
889
    {
890
    }
891
892
    /* *********************
893
    *       Logs          *
894
    ********************* */
895
896
    /**
897
     * fonction utilisée pour récupérer la liste des champs (dans l'ordre) de la table pour les requete de log
898
     * /!\  la clef primaire ID doit s'appeler 'id_objet_log' !!!
899
     * @return string
900
     */
901
    public static function listeChampsObjet()
902
    {
903
        return 'id_objet_log';
904
    }
905
906
    /**
907
     * Spécifie les champs de la table à comparer
908
     * Retourne un taleau vide si tous les champs de la table à comparer
909
     * @return Array $array
910
     */
911
    public function fieldsToCompare()
912
    {
913
        $array = array();
914
        return $array;
915
    }
916
917
    /**
918
     * Spécifie les champs à ne pas prendre en compte pour la comparaison
919
     * Tableau non vide contenant tout le temps, au moins le champ id
920
     * @return Array $array
921
     */
922
    public function fieldsInException()
923
    {
924
        $array = array();
925
        $array['id'] = 'Id';
926
        return $array;
927
    }
928
929
    /**
930
     * fonction de log
931
     * @param $type_action
932
     */
933
    public function logerChangement($type_action)
934
    {
935
        if ($this->getIsLogue() && $this->tableToLog() && $this->id) {
0 ignored issues
show
Documentation Bug introduced by
The method tableToLog does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
936
            $default_id = "";
937
            $tab = call_user_func(array(get_class($this), 'listeChampsObjet'));
938
            $tab = explode(', ', $tab);
939
940
            if (isset($tab[0]) && trim($tab[0]) == '') {
941
                unset($tab[0]);
942
            }
943
944 View Code Duplication
            if (isset($tab[0]) && (trim($tab[0]) == 'id')) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
945
                $tab[0] = 'id_objet_log';
946
            } elseif (isset($tab[1]) && (trim($tab[1]) == 'id')) {
947
                $tab[1] = 'id_objet_log';
948
            }
949
            $liste_champ = implode(', ', $tab);
950
951 View Code Duplication
            if (isset($tab[0]) && (trim($tab[0]) == 'id_objet_log')) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
952
                $tab[0] = 'id';
953
            } elseif (isset($tab[1]) && (trim($tab[1]) == 'id_objet_log')) {
954
                $tab[1] = 'id';
955
            }
956
            $liste_champ_valeur = implode(', ', $tab);
957
958
            $id_utilisateur = self::getSession()->has('id_utilisateur')
959
                ? self::getSession()->get('id_utilisateur')
960
                : -1;
961
962
            $SQL = 'INSERT INTO log__' . $this->getTableName() . '
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
963
                            (' . $liste_champ . '
964
                                , libelle_historisation
965
                                , contenu_log
966
                                , id_utilisateur_log
967
                                , date_action_log
968
                                , action_log
969
                            )
970
                        SELECT ' . $default_id . '
971
                                ' . $liste_champ_valeur . ',
972
                                \'\',
973
                                \'\',
974
                                ' . Sql::secureId($id_utilisateur) . ',
975
                                ' . Sql::secureDate(date('Y-m-d H:i:s')) . ',
976
                                ' . Sql::secure($type_action) . '
977
                        FROM ' . $this->getTableName() . ' T
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
978
                        WHERE id = ' . Sql::secureId($this->id) . '
979
                    ';
980
            $db = Model::getDb();
981
            $db->query($SQL);
982
            $this->log_id = $db->lastInsertId();
983
        }
984
    }
985
986
    /**
987
     * fonction permettant de comparer 2 tableaux de données d'un objet et sa sauvegarde en log
988
     * et d'enregistrer les modifications effectuées
989
     */
990
    public function comparerDifferences()
991
    {
992
        $tab_champs_comparaison = $this->fieldsToCompare();
993
        $tab_champs_exception = $this->fieldsInException();
994
        $tab_champs_base = array();
995
        $tab_champs_log = array();
996
        $tab_contenu = array();
997
998
        $champs_specifiques = false;
999
        if (!empty($tab_champs_comparaison)) {
1000
            $champs_specifiques = true;
1001
        }
1002
1003
        if (empty($tab_champs_exception)) {
1004
            $tab_champs_exception["#exception#"] = "Exception";
1005
        }
1006
1007
1008
        if ($this->getIsLogue() && $this->tableToLog()) {
0 ignored issues
show
Documentation Bug introduced by
The method tableToLog does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1009
            // données de la table courante
1010
            $sql = $this->getSqlLog();
1011
            $db = Model::getDb();
1012
            $res = $db->fetchRow($sql);
1013
1014
            if ($res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1015
                foreach ($res as $index => $value) {
1016
                    if ($champs_specifiques) {
1017
                        if (array_key_exists($index, $tab_champs_comparaison)) {
1018
                            $tab_champs_base[$index] = $value;
1019
                        }
1020
                    } else {
1021
                        if (!array_key_exists($index, $tab_champs_exception)) {
1022
                            $tab_champs_base[$index] = $value;
1023
                        }
1024
                    }
1025
                }
1026
            }
1027
            // données de la table de log
1028
            $sql = $this->getSqlLog('log');
1029
            $res = $db->fetchRow($sql);
1030
1031
            if ($res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1032
                foreach ($res as $index => $value) {
1033
                    if (!array_key_exists($index, $tab_champs_exception)) {
1034
                        if ($index == "id_objet_log") {
1035
                            $index = "id";
1036
                        }
1037
1038
                        if (array_key_exists($index, $tab_champs_base)) {
1039
                            $tab_champs_log[$index] = $value;
1040
                        }
1041
                    }
1042
                }
1043
            }
1044
            $tab_diff = array_diff_assoc($tab_champs_base, $tab_champs_log);
1045
            // insertion de la différence dans la table de log
1046
            if (count($tab_diff) > 0) {
1047
                $libelle = "";
1048
1049
                foreach ($tab_diff as $index => $value) {
1050
                    $field = ($champs_specifiques) ? $tab_champs_comparaison[$index] : $index;
1051
                    $libelle .= "Le champ '" . $field . "' a été modifié : '"
1052
                        . $field . "' a été remplacé par '" . $value . "'\n";
1053
1054
                    $tab_contenu[$index] = array(
1055
                        "old_value" => isset($tab_champs_log[$index]) ? $tab_champs_log[$index] : null,
1056
                        "new_value" => ($value)
1057
                    );
1058
                }
1059
1060
                $contenu = serialize($tab_contenu);
1061
                //$contenu = json_encode($tab_contenu);
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1062
1063
                $sql = "UPDATE log__" . $this->getTableName() . "
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1064
                        SET libelle_historisation = " . Sql::secure(($libelle)) . "
1065
                        , contenu_log = " . Sql::secure($contenu) . "
1066
                        WHERE id = " . Sql::secureId($this->log_id);
1067
                $db = Model::getDb();
1068
                $db->query($sql);
1069
            }
1070
        }
1071
    }
1072
1073
    /**
1074
     * formate les requetes de log
1075
     */
1076
    public function getSqlLog($from = "")
1077
    {
1078
        if ($from == 'log') {
1079
            $table = 'log__' . $this->getTableName();
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1080
            $condition = " T.id = " . Sql::secureId($this->log_id);
1081
        } else {
1082
            $table = $this->getTableName();
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1083
            $condition = " T.id = " . Sql::secureId($this->id);
1084
        }
1085
1086
        $sql = "SELECT T.*
1087
                FROM " . $table . " T
1088
                WHERE " . $condition;
1089
        return $sql;
1090
    }
1091
1092
    /**
1093
     * fonction permettant de récupérer sous forme de tableau les différences
1094
     */
1095
    public static function returnArrayByJson($string = "")
1096
    {
1097
        return json_decode($string, true);
1098
    }
1099
1100
    /**
1101
     * fonction permettant de récupérer les historiques tableau d'un objet
1102
     */
1103
    public function getHistoriqueSurObjetDiffArray()
1104
    {
1105
        $array = array();
1106
        $sql = "SELECT id, contenu_log, id_utilisateur_log, date_action_log, action_log
1107
                FROM log__" . $this->getTableName() . "
0 ignored issues
show
Documentation Bug introduced by
The method getTableName does not exist on object<Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1108
                WHERE id_objet_log = " . Sql::secureId($this->id) . "
1109
                ORDER BY id";
1110
        $db = \Model::getDb();
1111
        $res = $db->getIterator($sql);
1112
1113
        foreach ($res as $rs) {
1114
            $array[$rs["id"]] = array(
1115
                "id_utilisateur_log" => $rs["id_utilisateur_log"]
1116
            , "date_action_log" => $rs["date_action_log"]
1117
            , "action_log" => $rs["action_log"]
1118
            , "contenu_log" => unserialize($rs["contenu_log"])
1119
            );
1120
        }
1121
        return $array;
1122
    }
1123
1124
    /* ************
1125
    * Validation *
1126
    ************ */
1127
    /**
1128
     * L'objet est-il enregistrable en base de données
1129
     * @return bool
1130
     */
1131
    abstract public function validate();
1132
1133
    /**
1134
     * L'objet est-il effaçable
1135
     * @return bool
1136
     */
1137
    abstract public function canBeDeleted();
1138
1139
    /**
1140
     * Détermine si notre objet est unique par rapport aux attributs donnés
1141
     * @param mixed $attribut : Attribut (ou liste d'attribut, dans ce cas tableau) à comparer
1142
     * @param array $where : [OPT] Clauses supplémentaires à prendre en compte pour notre recherche
1143
     * @return bool : VRAI si aucun doublon est trouvé
1144
     */
1145
    public function isUniqueAttribute($attribut, $where = array())
1146
    {
1147
        if (is_array($attribut)) {
1148
            foreach ($attribut as $current_attribut) {
1149
                $where[$current_attribut] = 'IFnull(' . $current_attribut . ', 0) = '
1150
                    . 'IFnull(' . sql::Secure($this->$current_attribut) . ', 0)';
1151
            }
1152
        } else {
1153
            $where[$attribut] = 'IFnull(' . $attribut . ', 0) = IFnull(' . sql::Secure($this->$attribut) . ', 0)';
1154
        }
1155
        $where['id'] = "IFnull(id, 0) <> IFnull(" . sql::secureId($this->id) . ", 0)";
1156
        $doublon = $this->findFirst($where);
1157
1158
        return !$doublon;
1159
    }
1160
1161
    /**
1162
     * @param \FMUP\Session $session
1163
     */
1164
    public static function setSession(\FMUP\Session $session)
1165
    {
1166
        self::$session = $session;
1167
    }
1168
1169
    /**
1170
     * @return \FMUP\Session
1171
     */
1172
    public static function getSession()
1173
    {
1174
        if (!self::$session) {
1175
            self::setSession(\FMUP\Session::getInstance());
1176
        }
1177
        return self::$session;
1178
    }
1179
1180
    public static function hasSession()
1181
    {
1182
        return \FMUP\Sapi::getInstance() && \FMUP\Sapi::getInstance()->is(\FMUP\Sapi::CGI) && (bool)self::$session;
1183
    }
1184
}
1185