PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / core / Compatibility.class.php

Compatibility.class.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at core/Compatibility.class.php

317 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn {
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * Class Compatibility
10 */
11 class Compatibility {
12 /**
13 * @var array<string, string>
14 */
15 private $components = array();
16
17 /**
18 * @var CF7DoubleOptIn $Controller
19 */
20 private $Controller;
21
22 /**
23 * UI constructor.
24 *
25 * @param $slug
26 */
27 public function __construct( $Controller ) {
28 $this->Controller = $Controller;
29
30 $this->get_logger()->debug( 'Loading components for the UI.', [
31 'plugin' => 'double-opt-in',
32 ] );
33 $this->load_components();
34
35 $this->get_logger()->debug( 'Registering a callback for the after_setup_theme action.', [
36 'plugin' => 'double-opt-in',
37 'hook' => 'after_setup_theme',
38 ] );
39 add_action( 'after_setup_theme', function () {
40 $this->get_logger()->debug( 'after_setup_theme hook triggered. Registering components.', [
41 'plugin' => 'double-opt-in',
42 ] );
43
44 add_action( 'f12_cf7_doubleoptin_ui_after_load_compatibilities', array(
45 $this,
46 'registerComponents'
47 ), 10, 1 );
48
49 $this->get_logger()->info( 'Triggering UI after load compatibilities action.', [
50 'plugin' => 'double-opt-in',
51 'hook' => 'f12_cf7_doubleoptin_ui_after_load_compatibilities',
52 ] );
53 do_action( 'f12_cf7_doubleoptin_ui_after_load_compatibilities', $this );
54
55 $this->get_logger()->notice( 'UI initialization actions are complete.', [
56 'plugin' => 'double-opt-in',
57 ] );
58 } );
59 }
60
61 public function get_logger() {
62 return $this->Controller->get_logger();
63 }
64
65 /**
66 * Loads the components for the current application.
67 *
68 * This method is responsible for loading the components for the current application.
69 * It retrieves the directories where the components are located using the `get_component_directories()`
70 * method and then iterates through each directory to load the components using the `load()` method.
71 *
72 * @return void
73 * @since 3.0.0
74 * @see get_component_directories()
75 * @see load()
76 */
77 private function load_components() {
78 $this->get_logger()->info( 'Starting to load UI components from directories.', [
79 'plugin' => 'double-opt-in',
80 ] );
81
82 $directories = $this->get_component_directories();
83
84 if ( empty( $directories ) ) {
85 $this->get_logger()->warning( 'No component directories found to load.', [
86 'plugin' => 'double-opt-in',
87 ] );
88 return;
89 }
90
91 $this->get_logger()->debug( 'Found ' . count( $directories ) . ' component directories.', [
92 'plugin' => 'double-opt-in',
93 'directories' => $directories,
94 ] );
95
96 foreach ( $directories as $directory ) {
97 $this->get_logger()->info( 'Loading components from directory: ' . $directory, [
98 'plugin' => 'double-opt-in',
99 'directory' => $directory,
100 ] );
101 $this->load( $directory, 0 );
102 }
103
104 $this->get_logger()->notice( 'All UI components have been loaded.', [
105 'plugin' => 'double-opt-in',
106 ] );
107 }
108
109 /**
110 * Retrieves the component directories.
111 *
112 * This method allows developers to retrieve an array of component directories. These directories are used for
113 * displaying UI pages.
114 *
115 * @return array An array of component directories.
116 *
117 * @since 3.0.0
118 */
119 private function get_component_directories() {
120 $this->get_logger()->info( 'Retrieving component directories from filter.', [
121 'plugin' => 'double-opt-in',
122 ] );
123
124 /**
125 * Component Directories
126 *
127 * This filter allows developers to add custom ui directories which will be used to display ui pages.
128 *
129 * @param array $directories
130 *
131 * @since 3.0.0
132 */
133 $directories = apply_filters( 'f12_cf7_doubleoption_compatbility_get_component_directories', [ dirname( dirname( __FILE__ ) ) . '/compatibility' ] );
134
135 // Log the result of the filter application.
136 if ( empty( $directories ) ) {
137 $this->get_logger()->warning( 'Component directories filter returned an empty array.', [
138 'plugin' => 'double-opt-in',
139 ] );
140 } else {
141 $this->get_logger()->debug( 'Found ' . count( $directories ) . ' component directories.', [
142 'plugin' => 'double-opt-in',
143 'directories' => $directories,
144 ] );
145 }
146
147 return $directories;
148 }
149
150 public function registerComponents( $Compatibility ) {
151 $this->get_logger()->info( 'Starting component registration.', [
152 'plugin' => 'double-opt-in',
153 ] );
154
155 if ( empty( $this->components ) ) {
156 $this->get_logger()->warning( 'No components found to register.', [
157 'plugin' => 'double-opt-in',
158 ] );
159 return;
160 }
161
162 $this->get_logger()->debug( 'Found ' . count($this->components) . ' components to process.', [
163 'plugin' => 'double-opt-in',
164 'components' => $this->components,
165 ] );
166
167 foreach ( $this->components as $component ) {
168 if ( isset( $component['name'] ) && isset( $component['path'] ) ) {
169 $this->get_logger()->info( 'Registering component: ' . $component['name'], [
170 'plugin' => 'double-opt-in',
171 'component_name' => $component['name'],
172 'component_path' => $component['path'],
173 ] );
174
175 // Guard BEFORE the require, not after it.
176 //
177 // Component directories come from a filter, and the
178 // legacy Pro monolith (<= 3.7) still hooks its own
179 // compatibility/ directory in here. It declares the same
180 // class names in the same namespace as the addon-elementor
181 // and addon-opt-out compat classes. Two different files
182 // declaring one class name is exactly what require_once
183 // does NOT protect against — it deduplicates by path —
184 // so loading the second one is a fatal
185 // "Cannot redeclare class", and the whole admin is gone.
186 //
187 // Whoever loads first wins. The site is functionally
188 // degraded in that case, but it stays reachable, which is
189 // the precondition for the operator being able to fix the
190 // real problem (see LegacyMonolithCheck, which tells them
191 // to deactivate the old plugin).
192 //
193 // The class_exists() call two lines down already existed;
194 // it just ran too late to prevent anything.
195 if ( class_exists( $component['name'], false ) ) {
196 $this->get_logger()->warning( 'Component ' . $component['name'] . ' is already declared elsewhere — skipping this copy to avoid a fatal redeclare.', [
197 'plugin' => 'double-opt-in',
198 'component_name' => $component['name'],
199 'component_path' => $component['path'],
200 ] );
201 continue;
202 }
203
204 require_once( $component['path'] );
205
206 if ( class_exists($component['name']) ) {
207 new $component['name']( $this->Controller );
208 $this->get_logger()->notice( 'Component ' . $component['name'] . ' successfully registered.', [
209 'plugin' => 'double-opt-in',
210 ] );
211 } else {
212 $this->get_logger()->error( 'Failed to register component. Class ' . $component['name'] . ' not found after requiring file.', [
213 'plugin' => 'double-opt-in',
214 'component_path' => $component['path'],
215 ] );
216 }
217 } else {
218 $this->get_logger()->warning( 'Skipped a component due to missing "name" or "path" keys.', [
219 'plugin' => 'double-opt-in',
220 'component_data' => $component,
221 ] );
222 }
223 }
224
225 $this->get_logger()->info( 'All available components have been processed.', [
226 'plugin' => 'double-opt-in',
227 ] );
228 }
229
230 private function addComponent( $name, $path ) {
231 $this->get_logger()->info( 'Adding new component to the list.', [
232 'plugin' => 'double-opt-in',
233 'component_name' => $name,
234 'component_path' => $path,
235 ] );
236
237 $this->components[] = array(
238 'name' => $name,
239 'path' => $path
240 );
241
242 $this->get_logger()->debug( 'Component "' . $name . '" successfully added.', [
243 'plugin' => 'double-opt-in',
244 'total_components' => count($this->components),
245 ] );
246 }
247
248 private function load( $directory, $lvl ) {
249 $this->get_logger()->info( 'Starting component loading process from directory.', [
250 'plugin' => 'double-opt-in',
251 'directory' => $directory,
252 'level' => $lvl,
253 ] );
254
255 if ( ! is_dir( $directory ) ) {
256 $this->get_logger()->warning( 'Specified directory does not exist or is not a directory.', [
257 'plugin' => 'double-opt-in',
258 'directory' => $directory,
259 ] );
260 return;
261 }
262
263 $handle = opendir( $directory );
264
265 if ( ! $handle ) {
266 $this->get_logger()->error( 'Failed to open directory handle.', [
267 'plugin' => 'double-opt-in',
268 'directory' => $directory,
269 ] );
270 return;
271 }
272
273 while ( false !== ( $entry = readdir( $handle ) ) ) {
274 if ( $entry != '.' && $entry != '..' ) {
275 $full_path = $directory . '/' . $entry;
276
277 if ( is_dir( $full_path ) && $lvl == 0 ) {
278 $this->get_logger()->debug( 'Found a subdirectory. Recursively loading.', [
279 'plugin' => 'double-opt-in',
280 'subdirectory' => $entry,
281 'current_level' => $lvl,
282 ] );
283 $this->load( $full_path, $lvl + 1 );
284 } else {
285 if ( preg_match( '!Controller([a-zA-Z_0-9]+)\.class\.php!', $entry, $matches ) ) {
286 $this->get_logger()->info( 'Found a potential controller file.', [
287 'plugin' => 'double-opt-in',
288 'filename' => $entry,
289 ] );
290
291 if ( isset( $matches[1] ) ) {
292 $component_name = '\\' . __NAMESPACE__ . '\Controller' . $matches[1];
293 $this->get_logger()->debug( 'Adding component to the list.', [
294 'plugin' => 'double-opt-in',
295 'component_name' => $component_name,
296 'file_path' => $full_path,
297 ] );
298 $this->addComponent( $component_name, $full_path );
299 } else {
300 $this->get_logger()->warning( 'Controller filename matched, but could not extract component name.', [
301 'plugin' => 'double-opt-in',
302 'filename' => $entry,
303 ] );
304 }
305 }
306 }
307 }
308 }
309
310 closedir( $handle );
311
312 $this->get_logger()->notice( 'Finished loading components from directory: ' . $directory, [
313 'plugin' => 'double-opt-in',
314 ] );
315 }
316 }
317 }