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 (1 issue)

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
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;
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]
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);
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())
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);
390
        return $result;
391
    }
392
393 View Code Duplication
    public static function findAllAttributeFromLeftJoinTable(
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);
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"];
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;
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');
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);
484
                    $this->id_suppresseur = $id_utilisateur;
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;
492
                $db = Model::getDb();
493
                return (bool)$db->execute($SQL);
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);
545
        if ($force_enregistrement || $this->validate(true)) {
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);
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);
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');
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);
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') {
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) {
707
            $tab_error = $this->errors;
708
            if (isset($tab_error[$attribute])) {
709
                return $tab_error[$attribute];
710
            } else {
711
                return false;
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.
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()
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;
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
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) {
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) {
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')) {
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')) {
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() . '
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
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()) {
1009
            // données de la table courante
1010
            $sql = $this->getSqlLog();
1011
            $db = Model::getDb();
1012
            $res = $db->fetchRow($sql);
1013
1014
            if ($res) {
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) {
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);
1062
1063
                $sql = "UPDATE log__" . $this->getTableName() . "
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();
1080
            $condition = " T.id = " . Sql::secureId($this->log_id);
1081
        } else {
1082
            $table = $this->getTableName();
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() . "
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