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.6.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 All 35 releases
← All changes | core/Compatibility.class.php +316 -74 2.155.5.0 View file →
@@ -1,75 +1,317 @@
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 - /**
14 - * @var array<string, string>
15 - */
16 - private $components = array();
17 -
18 - /**
19 - * UI constructor.
20 - * @param $slug
21 - */
22 - public function __construct()
23 - {
24 - $this->load(dirname(dirname(__FILE__)) . '/compatibility', 0);
25 -
26 - add_action('after_setup_theme', function(){
27 - add_action('f12_cf7_doubleoptin_ui_after_load_compatibilities', array($this, 'registerComponents'), 10, 1);
28 - do_action('f12_cf7_doubleoptin_ui_after_load_compatibilities', $this);
29 - });
30 - }
31 -
32 - public function registerComponents($Compatibility)
33 - {
34 - foreach ($this->components as $component) {
35 - if (isset($component['name']) && isset($component['path'])) {
36 - require_once($component['path']);
37 - new $component['name']();
38 - }
39 - }
40 - }
41 -
42 - private function addComponent($name, $path)
43 - {
44 - $this->components[] = array(
45 - 'name' => $name,
46 - 'path' => $path
47 - );
48 - }
49 -
50 - private function load($directory, $lvl)
51 - {
52 - if (is_dir($directory)) {
53 - $handle = opendir($directory);
54 -
55 - if (!$handle) {
56 - return;
57 - }
58 -
59 - while (false !== ($entry = readdir($handle))) {
60 - if ($entry != '.' && $entry != '..') {
61 - if (is_dir($directory . '/' . $entry) && $lvl == 0) {
62 - $this->load($directory . '/' . $entry, $lvl + 1);
63 - } else {
64 - if (preg_match('!Controller([a-zA-Z_0-9]+)\.class\.php!', $entry, $matches)) {
65 - if (isset($matches[1])) {
66 - $this->addComponent('\\'.__NAMESPACE__.'\Controller' . $matches[1], $directory . '/' . $entry);
67 - }
68 - }
69 - }
70 - }
71 - }
72 - }
73 - }
74 - }
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 + }
75 317 }