Entity.php 13 KB
Newer Older
1
<?php
Bertrand Gauthier's avatar
Bertrand Gauthier committed
2

3
4
5
6
namespace UnicaenLdap\Entity;

use UnicaenLdap\Node;
use UnicaenLdap\Exception;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
7
use UnicaenLdap\Service\AbstractService;
8
use UnicaenLdap\Util;
9
10
use Zend\Ldap\Dn;
use Zend\Ldap\Exception\LdapException;
11
12
13
14
15
16
17
18

/**
 * Classe mère des entrées de l'annuaire LDAP.
 *
 * @author Laurent Lécluse <laurent.lecluse at unicaen.fr>
 */
abstract class Entity
{
19
20
    static protected $etablissement_domaine = 'unicaen.fr';

21
22
    /**
     * Type d'entité
Bertrand Gauthier's avatar
Bertrand Gauthier committed
23
     *
24
25
26
27
28
29
     * @var string
     */
    protected $type;

    /**
     * Service qui gère l'entité
Bertrand Gauthier's avatar
Bertrand Gauthier committed
30
31
     *
     * @var AbstractService
32
33
34
35
36
37
38
     */
    protected $service;

    /**
     * @var Node
     */
    protected $node;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
39

40
41
    /**
     * Liste des classes d'objet nécessaires à la création de l'entité
Bertrand Gauthier's avatar
Bertrand Gauthier committed
42
     *
43
     * @var array
44
     */
45
    protected $objectClass = [];
46

47
48
49
50
51
52
53
    /**
     * Liste des attributs autorisés pour une entité
     *
     * @var array
     */
    protected $authorizedAttributes = [];

54
55
56
57
58
59
60
61
    /**
     * Liste des attributs contenant des dates communs à toutes les entités
     */
    private $sharedDateTimeAttributes = [
        'createTimestamp',
        'modifyTimestamp',
    ];

62
    /**
63
     * Liste des attributs contenant des dates
64
65
66
     *
     * @var array
     */
67
    protected $dateTimeAttributes = [];
68
69

    /**
70
     * Liste des attributs monovalués
71
     *
72
     * @var array
73
     */
74
    protected $monoValuedAttributes = [];
75

76
77
78
79
80
81
82
83
84
85
    /**
     * Liste des patterns génériques utilisés pour différents attributs
     */
    static protected $attribute_with_label_pattern = '/^\{(?<etiquette>[\w-:]+)\}(?<identifiant>.+)$/';

    /**
     * Liste des patterns spécifiques utilisés pour différents attributs
     */
    static protected $postal_address_pattern = '/^(.*)\$(.*)\$(.*)\$(.*)\$(.*)\$(.*)$/';

86

87
88
89
90
91
    /**
     * @param string $type type d'entité
     * @return array
     * @throws Exception
     */
92
93
    public static function getNodeParams($type)
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
94
        $params = [
95
96
97
            'Generic' => ['uid', 'UnicaenLdap\\Entity\\Generic'],
            'Group' => ['cn', 'UnicaenLdap\\Entity\\Group'],
            'People' => ['uid', 'UnicaenLdap\\Entity\\People'],
Bertrand Gauthier's avatar
Bertrand Gauthier committed
98
            'Structure' => ['supannCodeEntite', 'UnicaenLdap\\Entity\\Structure'],
99
            'System' => ['uid', 'UnicaenLdap\\Entity\\System'],
Bertrand Gauthier's avatar
Bertrand Gauthier committed
100
101
102
        ];
        if (!isset($params[$type])) throw new Exception('Paramètres de "' . $type . '" non trouvés');

103
104
105
106
        return $params[$type];
    }

    /**
107
     * Entity constructor.
108
     *
109
110
111
     * @param AbstractService $service Service qui gèrera la future entité
     * @param Node|Dn|array|string $data Noeud si Node, sinon DN
     * @throws LdapException
112
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
113
    public function __construct(AbstractService $service, $data)
114
115
116
    {
        $this->service = $service;

Bertrand Gauthier's avatar
Bertrand Gauthier committed
117
118
119
120
        if ($data instanceof Node) {
            $this->setNode($data);
        } else {
            $this->setNode(Node::create($data, $this->objectClass));
121
122
123
124
        }
    }

    /**
125
     * Retourne le type de l'entité
126
127
128
129
130
131
132
133
134
135
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Retourne le service qui gère l'entité
Bertrand Gauthier's avatar
Bertrand Gauthier committed
136
137
     *
     * @return AbstractService
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
     */
    public function getService()
    {
        return $this->service;
    }

    /**
     * Retourne le nœud Ldap de base
     *
     * @return Node
     */
    public function getNode()
    {
        return $this->node;
    }

    /**
     * Affecte un nœud de base
Bertrand Gauthier's avatar
Bertrand Gauthier committed
156
     *
157
158
     * @param Node $node
     */
Bertrand Gauthier's avatar
Bertrand Gauthier committed
159
    public function setNode(Node $node)
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    {
        $this->node = $node;
    }

    /**
     * Retourne le Dn de l'entité sous forme de chaîne de caractères
     *
     * @return string
     */
    public function getDn()
    {
        return $this->getNode()->getDnString();
    }

    /**
175
     * Retourne le nom de la clé primaire correspondant à l'entité
176
     *
177
178
     * @return mixed
     * @throws Exception
179
     */
180
    public function getKey()
181
    {
182
183
        $params = self::getNodeParams($this->type);
        return reset($params);
184
185
186
    }

    /**
187
     * Retourne la valeur de la clé primaire correspondant à l'entité
188
189
     *
     * @return string
190
191
     * @throws Exception
     * @throws LdapException
192
     */
193
    public function getId()
194
    {
195
        return $this->get($this->getKey());
196
197
    }

198
199
200
201
202
203
204
205
206
207
208
    /**
     * Retourne l'attribut "objectClass"
     *
     * @return array|int|mixed|null
     * @throws LdapException
     */
    public function getObjectClass()
    {
        return $this->get('objectClass');
    }

209
210
211
    /**
     * Retourne la liste des attributs de l'entité
     *
212
     * @param  bool $includeSystemAttributes
213
214
     * @return string[]
     */
215
    public function getAttributesList($includeSystemAttributes = true)
216
    {
217
        return array_keys($this->getNode()->getAttributes($includeSystemAttributes));
218
219
    }

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
    /**
     * Retourne un label constitué des différentes clés passées en paramètre
     * Format : {$key1:$key2:$key3:...}
     *
     * @param string ...$key
     * @return string
     */
    public function getLabel(...$key)
    {
        $label = '';
        $c = 0;
        foreach($key as $k) {
            if(is_string($k)) {
                $label .= ($c == 0) ? $k : ":$k";
            }
            $c++;
        }

        return sprintf('{%s}', $label);
    }

241
242
243
244
    /**
     * Exporte sous forme de tableau le contenu de l'entité
     *
     * @return array
245
     * @throws LdapException
246
247
248
     */
    public function toArray()
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
249
        $result = [];
250
        $attrsList = $this->getAttributesList();
Bertrand Gauthier's avatar
Bertrand Gauthier committed
251
        foreach ($attrsList as $attrName) {
252
253
            $result[$attrName] = $this->get($attrName);
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
254

255
256
257
258
259
260
261
        return $result;
    }

    /**
     * Retourne un attribut
     *
     * @param string $attrName
262
263
     * @return array|int|mixed|null
     * @throws LdapException
264
265
266
     */
    public function get($attrName)
    {
267
        if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) {
268
            $value = $this->getNode()->getDateTimeAttribute($attrName);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
269
        } else {
270
271
            $value = $this->getNode()->getAttribute($attrName);
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
272
        if (empty($value)) {
273
            $value = null;
Bertrand Gauthier's avatar
Bertrand Gauthier committed
274
        } elseif (1 == count($value)) {
275
276
            $value = $value[0];
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
277

278
279
280
281
282
283
284
        return $value;
    }

    /**
     * Affecte une nouvelle valeur à un attribut
     *
     * @param string $attrName
285
     * @param mixed $value
286
     * @return self
287
     * @throws Exception
288
     * @throws LdapException
289
290
291
     */
    public function set($attrName, $value)
    {
292
293
294
295
        if(!in_array($attrName, $this->authorizedAttributes)) {
            throw new Exception(sprintf("L'attribut Ldap '%s' n'est pas autorisé pour une entité '%s'.", $attrName, get_class($this)));
        }

296
297
298
299
300
301
        if(is_array($value)
            && count($value) > 1
            && in_array($attrName, $this->monoValuedAttributes)) {
            throw new Exception(sprintf("L'attribut Ldap '%s' est monovalué et ne doit contenir qu'une seule valeur.", $attrName));
        }

302
        if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) {
303
            $this->getNode()->setDateTimeAttribute($attrName, $value, true);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
304
        } else {
305
306
            $this->getNode()->setAttribute($attrName, $value);
        }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
307

308
309
310
311
312
313
314
        return $this;
    }

    /**
     * Retourne <code>true</code> si l'attribut $param possède la valeur $value, <code>false</code> sinon.
     *
     * @param string $attrName
315
     * @param mixed $value
316
317
318
319
320
321
322
323
324
     * @return boolean
     */
    public function has($attrName, $value)
    {
        return $this->getNode()->attributeHasValue($attrName, $value);
    }

    /**
     * Ajoute une valeur à un attribut
325
     * Si l'attribut est monovalué, la valeur actuelle est remplacée par la nouvelle valeur
326
327
     *
     * @param string $attrName
328
     * @param mixed $value
329
     * @return self
330
     * @throws Exception
331
     * @throws LdapException
332
333
334
     */
    public function add($attrName, $value)
    {
335
336
337
338
        if(!in_array($attrName, $this->authorizedAttributes)) {
            throw new Exception(sprintf("L'attribut Ldap '%s' n'est pas autorisé pour une entité '%s'.", $attrName, get_class($this)));
        }

339
340
        if(in_array($attrName, $this->monoValuedAttributes)) {
            $this->set($attrName, $value);
341
        }
342
        else {
343
            if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) {
344
345
346
347
                $this->getNode()->appendToDateTimeAttribute($attrName, $value);
            } else {
                $this->getNode()->appendToAttribute($attrName, $value);
            }
Bertrand Gauthier's avatar
Bertrand Gauthier committed
348

349
350
            return $this;
        }
351
352
353
354
355
    }

    /**
     * Retire une valeur à un attribut
     *
356
357
     * @param string $attrName
     * @param mixed|array $value
358
359
360
361
362
     * @return self
     */
    public function remove($attrName, $value)
    {
        $this->getNode()->removeFromAttribute($attrName, $value);
Bertrand Gauthier's avatar
Bertrand Gauthier committed
363

364
365
366
        return $this;
    }

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
    /**
     * Ajoute ou affecte une valeur à un attribut Ldap
     *
     * @param string $attrName
     * @param mixed $value
     * @param bool $append
     * @throws Exception
     * @throws LdapException
     */
    protected function appendOrNot($attrName, $value, $append)
    {
        (!$append)
            ? $this->set($attrName, $value)
            : $this->add($attrName, $value);
    }

383
384
385
386
387
    /**
     * Méthode magique...
     *
     * @param string $attrName
     * @return mixed
388
     * @throws LdapException
389
390
391
392
393
394
395
396
397
398
     */
    public function __get($attrName)
    {
        return $this->get($attrName);
    }

    /**
     * Méthode magique...
     *
     * @param string $attrName
399
     * @param mixed $value
400
     * @return self
401
     * @throws LdapException
402
403
404
405
406
407
408
409
410
     */
    public function __set($attrName, $value)
    {
        return $this->set($attrName, $value);
    }

    /**
     * Methode magique ...
     *
411
412
     * @param string $method Nom de la méthode à appeler
     * @param array $arguments Tableau de paramètres
413
414
415
416
     * @return mixed
     */
    public function __call($method, array $arguments)
    {
Bertrand Gauthier's avatar
Bertrand Gauthier committed
417
418
419
420
421
422
423
        $methods = ['get', 'set', 'has', 'add', 'remove'];
        foreach ($methods as $methodName) {
            if (0 === strpos($method, $methodName)) {
                $attrName = lcfirst(substr($method, strlen($methodName)));
                $arguments = array_merge((array)$attrName, $arguments);

                return call_user_func_array([$this, $methodName], $arguments);
424
425
426
            }
        }
    }
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466

    /**
     * Mise à jour de l'entité
     *
     * @return self
     * @throws LdapException
     */
    public function update()
    {
        $this->getNode()->update();

        return $this;
    }

    /**
     * Suppression de l'entité
     *
     * @return self
     */
    public function delete()
    {
        $this->getNode()->delete();

        return $this;
    }

    /**
     * Attache une connexion
     *
     * @return self
     * @throws LdapException
     */
    public function attach()
    {
        $this->getNode()->attachLdap($this->service->getLdap());

        return $this;
    }

    /**
467
     * Formate un nom ou un prénom
468
     *
469
470
471
     * @param string $name
     * @param bool $removeAccents remplace les caractères avec un signe diacritique
     * @return mixed|null|string|string[]
472
     */
473
    protected function formatName($name, $removeAccents = false)
474
    {
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
        $name = preg_replace('/[[:blank:]]+/', ' ', trim($name));

        if ($removeAccents) {
            $name = Util::removeAccents($name);
        }

        return mb_convert_case($name, MB_CASE_TITLE, "UTF-8");
    }

    /**
     * Formate un numéro de téléphone (0XXXXXXXXX) au format international (+33 X XX XX XX XX)
     *
     * @param string $num
     * @return string
     */
    protected function formatTel($num)
    {
        if (is_null($num)) {
            return null;
        }

        $num = preg_replace('/\s+/', '', $num);

        return preg_match("/0\d{9}/", $num)
            ? preg_replace("/0(\d{1})(\d{2})(\d{2})(\d{2})(\d{2})/", "+33 $1 $2 $3 $4 $5", $num)
            : $num;
    }

    /**
     * Formate les données sources correspondant à un booléen
     *
     * @param mixed $value
     * @return string
     */
    protected function formatBoolean($value)
    {
        if (!is_bool($value) && $value === null) {
            return null;
        }

        if (is_string($value)) {
            $value = strtolower($value);
        }

        switch (true) {
            case $value === false;
            case $value === 0:
            case $value === 'n':
                return 'FALSE';
            case $value === true:
            case $value === 1:
            case $value === 'o':
            case $value === 'y':
                return 'TRUE';
            default:
                return null;
        }
532
    }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551

    /**
     * Vérifie une valeur et supprime les valeurs nulles
     *
     * @param mixed $value
     * @return array
     */
    protected function preFormat($value)
    {
        if (is_scalar($value)
            || is_null($value)
            || is_a($value, 'DateTime')) {
            $value = [$value];
        }

        $value = array_filter($value);

        return $value;
    }
552
}