Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
82 / 82
100.00% covered (success)
100.00%
28 / 28
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractAdapter
100.00% covered (success)
100.00%
82 / 82
100.00% covered (success)
100.00%
28 / 28
51
100.00% covered (success)
100.00%
1 / 1
 setModel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setModelId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getModel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModelId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOriginal
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModified
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUsername
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setUserId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setDomain
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setRoute
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setMethod
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setMetadata
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addMetadata
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getUsername
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRoute
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasMetadata
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getMetadata
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 setStateData
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getStateData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 hasStateData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setDiff
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 resolveDiff
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
9
 hasDiff
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 prepareData
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 send
n/a
0 / 0
n/a
0 / 0
0
 getStates
n/a
0 / 0
n/a
0 / 0
0
 getStateById
n/a
0 / 0
n/a
0 / 0
0
 getStateByModel
n/a
0 / 0
n/a
0 / 0
0
 getStateByTimestamp
n/a
0 / 0
n/a
0 / 0
0
 getStateByDate
n/a
0 / 0
n/a
0 / 0
0
 getSnapshot
n/a
0 / 0
n/a
0 / 0
0
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Audit\Adapter;
16
17/**
18 * Auditor abstract adapter
19 *
20 * @category   Pop
21 * @package    Pop\Audit
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    3.0.0
26 */
27abstract class AbstractAdapter implements AdapterInterface
28{
29
30    /**
31     * Constants for action states
32     * @var string
33     */
34    const CREATED = 'created';
35    const UPDATED = 'updated';
36    const DELETED = 'deleted';
37
38    /**
39     * Model name
40     * @var ?string
41     */
42    protected ?string $model = null;
43
44    /**
45     * Model ID
46     * @var int|string|null
47     */
48    protected int|string|null $modelId = null;
49
50    /**
51     * Action (created, updated, deleted)
52     * @var ?string
53     */
54    protected ?string $action = null;
55
56    /**
57     * Original model state differences
58     * @var array
59     */
60    protected array $original = [];
61
62    /**
63     * Modified model state differences
64     * @var array
65     */
66    protected array $modified = [];
67
68    /**
69     * Final state data
70     * @var array
71     */
72    protected array $stateData = [];
73
74    /**
75     * Whether the original and modified model states differ
76     * @var bool
77     */
78    protected bool $diffExists = false;
79
80    /**
81     * Username
82     * @var ?string
83     */
84    protected ?string $username = null;
85
86    /**
87     * User ID
88     * @var int|string|null
89     */
90    protected int|string|null $userId = null;
91
92    /**
93     * Domain
94     * @var ?string
95     */
96    protected ?string $domain = null;
97
98    /**
99     * Route
100     * @var ?string
101     */
102    protected ?string $route = null;
103
104    /**
105     * Method
106     * @var ?string
107     */
108    protected ?string $method = null;
109
110    /**
111     * Metadata
112     * @var array
113     */
114    protected array$metadata = [];
115
116    /**
117     * Set the model name
118     *
119     * @param  string $model
120     * @return AbstractAdapter
121     */
122    public function setModel(string $model): AbstractAdapter
123    {
124        $this->model = $model;
125        return $this;
126    }
127
128    /**
129     * Set the model ID
130     *
131     * @param  int|string $modelId
132     * @return AbstractAdapter
133     */
134    public function setModelId(int|string $modelId): AbstractAdapter
135    {
136        $this->modelId = $modelId;
137        return $this;
138    }
139
140    /**
141     * Get the model name
142     *
143     * @return string|null
144     */
145    public function getModel(): string|null
146    {
147        return $this->model;
148    }
149
150    /**
151     * Get the model ID
152     *
153     * @return int|string|null
154     */
155    public function getModelId(): int|string|null
156    {
157        return $this->modelId;
158    }
159
160    /**
161     * Get the action
162     *
163     * @return string|null
164     */
165    public function getAction(): string|null
166    {
167        return $this->action;
168    }
169
170    /**
171     * Get the original model state differences
172     *
173     * @return array
174     */
175    public function getOriginal(): array
176    {
177        return $this->original;
178    }
179
180    /**
181     * Get the modified model state differences
182     *
183     * @return array
184     */
185    public function getModified(): array
186    {
187        return $this->modified;
188    }
189
190    /**
191     * Set the username
192     *
193     * @param  string $username
194     * @return AbstractAdapter
195     */
196    public function setUsername(string $username): AbstractAdapter
197    {
198        $this->username = $username;
199        return $this;
200    }
201
202    /**
203     * Set the user ID
204     *
205     * @param  int|string $userId
206     * @return AbstractAdapter
207     */
208    public function setUserId(int|string $userId): AbstractAdapter
209    {
210        $this->userId = $userId;
211        return $this;
212    }
213
214    /**
215     * Set the domain
216     *
217     * @param  string $domain
218     * @return AbstractAdapter
219     */
220    public function setDomain(string $domain): AbstractAdapter
221    {
222        $this->domain = $domain;
223        return $this;
224    }
225
226    /**
227     * Set the route
228     *
229     * @param  string $route
230     * @return AbstractAdapter
231     */
232    public function setRoute($route): AbstractAdapter
233    {
234        $this->route = $route;
235        return $this;
236    }
237
238    /**
239     * Set the method
240     *
241     * @param  string $method
242     * @return AbstractAdapter
243     */
244    public function setMethod(string $method): AbstractAdapter
245    {
246        $this->method = $method;
247        return $this;
248    }
249
250    /**
251     * Set the metadata
252     *
253     * @param  array $metadata
254     * @return AbstractAdapter
255     */
256    public function setMetadata(array $metadata): AbstractAdapter
257    {
258        $this->metadata = $metadata;
259        return $this;
260    }
261
262    /**
263     * Add to the metadata
264     *
265     * @param  string $name
266     * @param  mixed $value
267     * @return AbstractAdapter
268     */
269    public function addMetadata(string $name, mixed $value): AbstractAdapter
270    {
271        $this->metadata[$name] = $value;
272        return $this;
273    }
274
275    /**
276     * Get the username
277     *
278     * @return string|null
279     */
280    public function getUsername(): string|null
281    {
282        return $this->username;
283    }
284
285    /**
286     * Get the user ID
287     *
288     * @return int|string|null
289     */
290    public function getUserId(): int|string|null
291    {
292        return $this->userId;
293    }
294
295    /**
296     * Get the domain
297     *
298     * @return string|null
299     */
300    public function getDomain(): string|null
301    {
302        return $this->domain;
303    }
304
305    /**
306     * Get the route
307     *
308     * @return string|null
309     */
310    public function getRoute(): string|null
311    {
312        return $this->route;
313    }
314
315    /**
316     * Get the method
317     *
318     * @return string|null
319     */
320    public function getMethod(): string|null
321    {
322        return $this->method;
323    }
324
325    /**
326     * Determine if there is metadata
327     *
328     * @param  ?string $name
329     * @return bool
330     */
331    public function hasMetadata(?string $name = null): bool
332    {
333        if ($name !== null) {
334            return isset($this->metadata[$name]);
335        } else {
336            return !empty($this->metadata);
337        }
338    }
339
340    /**
341     * Get the metadata
342     *
343     * @param  ?string $name
344     * @return mixed
345     */
346    public function getMetadata(?string $name = null): mixed
347    {
348        if ($name !== null) {
349            return (isset($this->metadata[$name])) ? $this->metadata[$name] : null;
350        } else {
351            return $this->metadata;
352        }
353    }
354
355    /**
356     * Set the final state data
357     *
358     * @param  array $state
359     * @return AbstractAdapter
360     */
361    public function setStateData(array $state): AbstractAdapter
362    {
363        $this->stateData = $state;
364        return $this;
365    }
366
367    /**
368     * Get the final state
369     *
370     * @param  ?string $name
371     * @return mixed
372     */
373    public function getStateData(?string $name = null): mixed
374    {
375        if ($name !== null) {
376            return (isset($this->stateData[$name])) ? $this->stateData[$name] : null;
377        } else {
378            return $this->stateData;
379        }
380    }
381
382    /**
383     * Determine if there is final state data
384     *
385     * @param  ?string $name
386     * @return bool
387     */
388    public function hasStateData(?string $name = null): bool
389    {
390        return ($name !== null) ? array_key_exists($name, $this->stateData) : !empty($this->stateData);
391    }
392
393    /**
394     * Set the differences in values between the model states (that have already been processed)
395     *
396     * @param  array $old
397     * @param  array $new
398     * @return AbstractAdapter
399     */
400    public function setDiff(array $old = [], array $new = []): AbstractAdapter
401    {
402        $this->original = $old;
403        $this->modified = $new;
404
405        if (empty($old) && !empty($new)) {
406            $this->action = AbstractAdapter::CREATED;
407        } else if (empty($new) && !empty($old)) {
408            $this->action = AbstractAdapter::DELETED;
409        } else {
410            $this->action = AbstractAdapter::UPDATED;
411        }
412
413        $this->diffExists = ($this->original !== $this->modified);
414
415        return $this;
416    }
417
418    /**
419     * Resolve the differences in values between the model states
420     *
421     * @param  array $old
422     * @param  array $new
423     * @param  bool  $state
424     * @return AbstractAdapter
425     */
426    public function resolveDiff(array $old = [], array $new = [], bool $state = true): AbstractAdapter
427    {
428        $this->original = [];
429        $this->modified = [];
430
431        if ($state) {
432            $this->setStateData($new);
433        }
434        if (empty($old) && !empty($new)) {
435            $this->modified = $new;
436            $this->action   = AbstractAdapter::CREATED;
437        } else if (empty($new) && !empty($old)) {
438            $this->original = $old;
439            $this->action   = AbstractAdapter::DELETED;
440        } else {
441            foreach ($old as $key => $value) {
442                if (array_key_exists($key, $new) && $new[$key] != $value) {
443                    $this->original[$key] = $value;
444                    $this->modified[$key] = $new[$key];
445                }
446            }
447            $this->action = AbstractAdapter::UPDATED;
448        }
449
450        $this->diffExists = ($this->original !== $this->modified);
451
452        return $this;
453    }
454
455    /**
456     * Check if the model states are different
457     *
458     * @return bool
459     */
460    public function hasDiff(): bool
461    {
462        return (($this->action !== null) && $this->diffExists);
463    }
464
465    /**
466     * Prepare data
467     *
468     * @param  bool $jsonEncode
469     * @return array
470     */
471    public function prepareData(bool $jsonEncode = true): array
472    {
473        return [
474            'user_id'   => $this->userId,
475            'username'  => $this->username,
476            'domain'    => $this->domain,
477            'route'     => $this->route,
478            'method'    => $this->method,
479            'model'     => $this->model,
480            'model_id'  => $this->modelId,
481            'action'    => $this->action,
482            'old'       => ($jsonEncode) ? json_encode($this->original) : $this->original,
483            'new'       => ($jsonEncode) ? json_encode($this->modified) : $this->modified,
484            'state'     => ($jsonEncode) ? json_encode($this->stateData) : $this->stateData,
485            'metadata'  => ($jsonEncode) ? json_encode($this->metadata) : $this->metadata,
486            'timestamp' => date('Y-m-d H:i:s')
487        ];
488    }
489
490    /**
491     * Send the results of the audit
492     *
493     * @return mixed
494     */
495    abstract public function send(): mixed;
496
497    /**
498     * Get model states
499     *
500     * @return array
501     */
502    abstract public function getStates(): array;
503
504    /**
505     * Get model state by ID
506     *
507     * @param  int|string $id
508     * @return array
509     */
510    abstract public function getStateById(int|string $id): array;
511
512    /**
513     * Get model state by model
514     *
515     * @param  string          $model
516     * @param  int|string|null $modelId
517     * @return array
518     */
519    abstract public function getStateByModel(string $model, int|string|null $modelId = null): array;
520
521    /**
522     * Get model state by timestamp
523     *
524     * @param  int  $from
525     * @param  ?int $backTo
526     * @return array
527     */
528    abstract public function getStateByTimestamp(int $from, ?int $backTo = null): array;
529
530    /**
531     * Get model state by date
532     *
533     * @param  string  $from
534     * @param  ?string $backTo
535     * @return array
536     */
537    abstract public function getStateByDate(string $from, ?string $backTo = null): array;
538
539    /**
540     * Get model snapshot by ID
541     *
542     * @param  int|string $id
543     * @param  bool       $post
544     * @return array
545     */
546    abstract public function getSnapshot(int|string $id, bool $post = false): array;
547
548}