Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CampaignLog
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 2
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
72
 changes
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
420
1<?php
2
3class CampaignLog {
4    /** @var string[] */
5    private static $basic_fields = [
6        'start', 'end', 'enabled', 'preferred', 'locked', 'geo', 'buckets'
7    ];
8    /** @var string[] */
9    private static $list_fields = [ 'projects', 'languages', 'countries', 'regions' ];
10    /** @var string[] */
11    private static $map_fields = [ 'banners' ];
12
13    /** @var mixed[] */
14    private $begin;
15    /** @var mixed[] */
16    private $end;
17    /** @var string */
18    private $campaign;
19    /** @var string */
20    private $action;
21    /** @var string|int */
22    private $timestamp;
23    /** @var string|null */
24    private $comment;
25
26    /**
27     * @param stdClass|null $row
28     */
29    public function __construct( $row = null ) {
30        $this->begin = [];
31        $this->end = [];
32        if ( $row ) {
33            // Both functions intentionally drop invalid values like "" and "0"
34            $commaExplode = static fn ( ?string $str ) => $str ? explode( ', ', $str ) : [];
35            $jsonDecode = static fn ( ?string $json ) => $json ? json_decode( $json, true ) : [];
36
37            $store = function ( string $name, ?callable $decode = null ) use ( $row ) {
38                $this->begin[$name] = $row->{"notlog_begin_$name"};
39                $this->end[$name] = $row->{"notlog_end_$name"};
40                if ( $decode ) {
41                    $this->begin[$name] = $decode( $this->begin[$name] );
42                    $this->end[$name] = $decode( $this->end[$name] );
43                }
44            };
45
46            foreach ( static::$basic_fields as $name ) {
47                $store( $name );
48            }
49            foreach ( static::$list_fields as $name ) {
50                $store( $name, $commaExplode );
51            }
52            foreach ( static::$map_fields as $name ) {
53                $store( $name, $jsonDecode );
54            }
55        }
56
57        $this->campaign = $row->notlog_not_name;
58        $this->action = $row->notlog_action;
59        $this->timestamp = $row->notlog_timestamp;
60        $this->comment = $row->notlog_comment ?? '';
61    }
62
63    /**
64     * TODO: Use in {@see LogPager}
65     * @return array<string,array>
66     */
67    public function changes() {
68        $removed = [];
69        $added = [];
70
71        $diff_basic = function ( $name ) use ( &$removed, &$added ) {
72            if ( $this->begin[ $name ] !== $this->end[ $name ] ) {
73                if ( $this->begin[ $name ] !== null ) {
74                    $removed[ $name ] = $this->begin[ $name ];
75                }
76                if ( $this->end[ $name ] !== null ) {
77                    $added[ $name ] = $this->end[ $name ];
78                }
79            }
80        };
81        $diff_list = function ( $name ) use ( &$removed, &$added ) {
82            if ( $this->begin[ $name ] !== $this->end[ $name ] ) {
83                $removed[ $name ] = array_diff( $this->begin[ $name ], $this->end[ $name ] );
84                if ( !$removed[ $name ] || $removed[ $name ] === [ "" ] ) {
85                    unset( $removed[ $name ] );
86                }
87                $added[ $name ] = array_diff( $this->end[ $name ], $this->begin[ $name ] );
88                if ( !$added[ $name ] || $added[ $name ] === [ "" ] ) {
89                    unset( $added[ $name ] );
90                }
91            }
92        };
93        $diff_map = function ( $name ) use ( &$removed, &$added ) {
94            $removed[ $name ] = $this->begin[ $name ];
95            $added[ $name ] = $this->end[ $name ];
96
97            if ( $this->begin[ $name ] && $this->end[ $name ] ) {
98                $all_keys = array_keys( array_merge( $this->begin[ $name ], $this->end[ $name ] ) );
99                foreach ( $all_keys as $item ) {
100                    # simplification: match contents, but diff at item level
101                    if ( array_key_exists( $item, $this->begin[ $name ] )
102                        && array_key_exists( $item, $this->end[ $name ] )
103                        && $added[ $name ][ $item ] === $removed[ $name ][ $item ]
104                    ) {
105                        unset( $added[ $name ][ $item ] );
106                        unset( $removed[ $name ][ $item ] );
107                    }
108                }
109            }
110            if ( !$removed[ $name ] ) {
111                unset( $removed[ $name ] );
112            }
113            if ( !$added[ $name ] ) {
114                unset( $added[ $name ] );
115            }
116        };
117        foreach ( static::$basic_fields as $name ) {
118            $diff_basic( $name );
119        }
120        foreach ( static::$list_fields as $name ) {
121            $diff_list( $name );
122        }
123        foreach ( static::$map_fields as $name ) {
124            $diff_map( $name );
125        }
126
127        return [ 'removed' => $removed, 'added' => $added ];
128    }
129}