PluginProbe
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking / trunk
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking vtrunk
1.5.0 1.4.0 1.3.0 1.3.1 trunk 0.0.0-alpha.1 0.0.0-alpha.2 0.0.0-alpha.3 0.0.1-beta.1 0.0.1-beta.2 0.0.1-beta.3 0.0.1-beta.4 1.0.0 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4
surecookie / inc / modules / automatic-scanning / diff-engine.php

diff-engine.php in SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking trunk, at inc/modules/automatic-scanning/diff-engine.php

155 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Automatic Scanning diff engine.
4 *
5 * Pure functions that build a compact per-scan snapshot and compute the
6 * change set between two scans. Cookies are sticky in storage, so changes are
7 * always derived scan-to-scan from these snapshots - never from the
8 * accumulated cookie option. No cookie values are ever stored.
9 *
10 * @package SureCookie\Inc\Modules\AutomaticScanning
11 * @since 1.2.0
12 */
13
14 namespace SureCookie\Inc\Modules\AutomaticScanning;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * DiffEngine
22 *
23 * @since 1.2.0
24 */
25 class DiffEngine {
26 /**
27 * Build a compact snapshot of a scan's reported cookies and domains.
28 *
29 * @param array<string, array<int, array<string, mixed>>> $cookies_by_category Reported cookies grouped by category.
30 * @param array<int, string> $domains Reported third-party domains.
31 * @since 1.2.0
32 * @return array{signatures: array<string, array{name:string, domain:string, category:string}>, domains: array<int, string>}
33 */
34 public static function build_snapshot( array $cookies_by_category, array $domains ): array {
35 $signatures = [];
36
37 foreach ( $cookies_by_category as $cookies ) {
38 if ( ! is_array( $cookies ) ) {
39 continue;
40 }
41
42 foreach ( $cookies as $cookie ) {
43 $signature = (string) ( $cookie['signature_id'] ?? '' );
44
45 if ( $signature === '' ) {
46 continue;
47 }
48
49 $signatures[ $signature ] = [
50 'name' => (string) ( $cookie['name'] ?? '' ),
51 'domain' => (string) ( $cookie['domain'] ?? '' ),
52 'category' => (string) ( $cookie['category'] ?? '' ),
53 ];
54 }
55 }
56
57 $unique_domains = [];
58 foreach ( $domains as $domain ) {
59 $domain = (string) $domain;
60 if ( $domain !== '' ) {
61 $unique_domains[ $domain ] = true;
62 }
63 }
64
65 return [
66 'signatures' => $signatures,
67 'domains' => array_keys( $unique_domains ),
68 ];
69 }
70
71 /**
72 * A change set with nothing in it, in the shape {@see self::diff()} returns.
73 * Used for the first recorded scan, which has nothing to compare against.
74 *
75 * @since 1.5.0
76 * @return array{added: array<int, array<string, string>>, removed: array<int, array<string, string>>, recategorized: array<int, array<string, string>>, domains_added: array<int, string>}
77 */
78 public static function empty_diff(): array {
79 return [
80 'added' => [],
81 'removed' => [],
82 'recategorized' => [],
83 'domains_added' => [],
84 ];
85 }
86
87 /**
88 * Compute the change set between a previous and a current snapshot.
89 *
90 * @param array<string, mixed> $previous Previous scan snapshot.
91 * @param array<string, mixed> $current Current scan snapshot.
92 * @since 1.2.0
93 * @return array{added: array<int, array<string, string>>, removed: array<int, array<string, string>>, recategorized: array<int, array<string, string>>, domains_added: array<int, string>}
94 */
95 public static function diff( array $previous, array $current ): array {
96 $prev_sig = isset( $previous['signatures'] ) && is_array( $previous['signatures'] ) ? $previous['signatures'] : [];
97 $curr_sig = isset( $current['signatures'] ) && is_array( $current['signatures'] ) ? $current['signatures'] : [];
98 $prev_domains = isset( $previous['domains'] ) && is_array( $previous['domains'] ) ? $previous['domains'] : [];
99 $curr_domains = isset( $current['domains'] ) && is_array( $current['domains'] ) ? $current['domains'] : [];
100
101 $added = [];
102 $recategorized = [];
103
104 foreach ( $curr_sig as $signature => $data ) {
105 if ( ! is_array( $data ) ) {
106 continue;
107 }
108
109 if ( ! isset( $prev_sig[ $signature ] ) ) {
110 $added[] = array_merge( [ 'signature_id' => (string) $signature ], $data );
111 continue;
112 }
113
114 $old_category = (string) ( $prev_sig[ $signature ]['category'] ?? '' );
115 $new_category = (string) ( $data['category'] ?? '' );
116
117 if ( $old_category !== $new_category ) {
118 $recategorized[] = [
119 'signature_id' => (string) $signature,
120 'name' => (string) ( $data['name'] ?? '' ),
121 'domain' => (string) ( $data['domain'] ?? '' ),
122 'from' => $old_category,
123 'to' => $new_category,
124 ];
125 }
126 }
127
128 $removed = [];
129 foreach ( $prev_sig as $signature => $data ) {
130 if ( ! is_array( $data ) || isset( $curr_sig[ $signature ] ) ) {
131 continue;
132 }
133 $removed[] = array_merge( [ 'signature_id' => (string) $signature ], $data );
134 }
135
136 return [
137 'added' => $added,
138 'removed' => $removed,
139 'recategorized' => $recategorized,
140 'domains_added' => array_values( array_diff( $curr_domains, $prev_domains ) ),
141 ];
142 }
143
144 /**
145 * Whether a computed diff contains any change.
146 *
147 * @param array<string, mixed> $diff Diff produced by self::diff().
148 * @since 1.2.0
149 * @return bool
150 */
151 public static function has_changes( array $diff ): bool {
152 return ! empty( $diff['added'] ) || ! empty( $diff['removed'] ) || ! empty( $diff['recategorized'] ) || ! empty( $diff['domains_added'] );
153 }
154 }
155