• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 7255789135

19 Dec 2023 12:48AM UTC coverage: 49.39%. Remained the same
7255789135

push

github

web-flow
Merge pull request #20975 from Yoast/JRF/QA/no-fqn-in-non-namespaced-files

CS/QA: don't use fully qualified global functions and constants in non-namespaced files

12 of 126 new or added lines in 27 files covered. (9.52%)

2 existing lines in 1 file now uncovered.

15426 of 31233 relevant lines covered (49.39%)

4.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/admin/class-yoast-plugin-conflict.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Admin
6
 * @since   1.7.0
7
 */
8

9
/**
10
 * Base class for handling plugin conflicts.
11
 */
12
class Yoast_Plugin_Conflict {
13

14
        /**
15
         * The plugins must be grouped per section.
16
         *
17
         * It's possible to check for each section if there are conflicting plugins.
18
         *
19
         * @var array
20
         */
21
        protected $plugins = [];
22

23
        /**
24
         * All the current active plugins will be stored in this private var.
25
         *
26
         * @var array
27
         */
28
        protected $all_active_plugins = [];
29

30
        /**
31
         * After searching for active plugins that are in $this->plugins the active plugins will be stored in this
32
         * property.
33
         *
34
         * @var array
35
         */
36
        protected $active_conflicting_plugins = [];
37

38
        /**
39
         * Property for holding instance of itself.
40
         *
41
         * @var Yoast_Plugin_Conflict
42
         */
43
        protected static $instance;
44

45
        /**
46
         * For the use of singleton pattern. Create instance of itself and return this instance.
47
         *
48
         * @param string $class_name Give the classname to initialize. If classname is
49
         *                           false (empty) it will use it's own __CLASS__.
50
         *
51
         * @return Yoast_Plugin_Conflict
52
         */
53
        public static function get_instance( $class_name = '' ) {
×
54

55
                if ( is_null( self::$instance ) ) {
×
56
                        if ( ! is_string( $class_name ) || $class_name === '' ) {
×
57
                                $class_name = self::class;
×
58
                        }
59

60
                        self::$instance = new $class_name();
×
61
                }
62

63
                return self::$instance;
×
64
        }
65

66
        /**
67
         * Setting instance, all active plugins and search for active plugins.
68
         *
69
         * Protected constructor to prevent creating a new instance of the
70
         * *Singleton* via the `new` operator from outside this class.
71
         */
72
        protected function __construct() {
×
73
                // Set active plugins.
74
                $this->all_active_plugins = get_option( 'active_plugins' );
×
75

76
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
77
                if ( isset( $_GET['action'] ) && is_string( $_GET['action'] ) ) {
×
78
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information and only comparing the variable in a condition.
79
                        $action = wp_unslash( $_GET['action'] );
×
80
                        if ( $action === 'deactivate' ) {
×
81
                                $this->remove_deactivated_plugin();
×
82
                        }
83
                }
84

85
                // Search for active plugins.
86
                $this->search_active_plugins();
×
87
        }
88

89
        /**
90
         * Check if there are conflicting plugins for given $plugin_section.
91
         *
92
         * @param string $plugin_section Type of plugin conflict (such as Open Graph or sitemap).
93
         *
94
         * @return bool
95
         */
96
        public function check_for_conflicts( $plugin_section ) {
×
97

98
                static $sections_checked;
×
99

100
                // Return early if there are no active conflicting plugins at all.
101
                if ( empty( $this->active_conflicting_plugins ) ) {
×
102
                        return false;
×
103
                }
104

105
                if ( $sections_checked === null ) {
×
106
                        $sections_checked = [];
×
107
                }
108

NEW
109
                if ( ! in_array( $plugin_section, $sections_checked, true ) ) {
×
110
                        $sections_checked[] = $plugin_section;
×
111
                        return ( ! empty( $this->active_conflicting_plugins[ $plugin_section ] ) );
×
112
                }
113

114
                return false;
×
115
        }
116

117
        /**
118
         * Checks for given $plugin_sections for conflicts.
119
         *
120
         * @param array $plugin_sections Set of sections.
121
         */
122
        public function check_plugin_conflicts( $plugin_sections ) {
×
123
                foreach ( $plugin_sections as $plugin_section => $readable_plugin_section ) {
×
124
                        // Check for conflicting plugins and show error if there are conflicts.
125
                        if ( $this->check_for_conflicts( $plugin_section ) ) {
×
126
                                $this->set_error( $plugin_section, $readable_plugin_section );
×
127
                        }
128
                }
129

130
                // List of all active sections.
NEW
131
                $sections = array_keys( $plugin_sections );
×
132
                // List of all sections.
NEW
133
                $all_plugin_sections = array_keys( $this->plugins );
×
134

135
                /*
136
                 * Get all sections that are inactive.
137
                 * These plugins need to be cleared.
138
                 *
139
                 * This happens when Sitemaps or OpenGraph implementations toggle active/disabled.
140
                 */
NEW
141
                $inactive_sections = array_diff( $all_plugin_sections, $sections );
×
142
                if ( ! empty( $inactive_sections ) ) {
×
143
                        foreach ( $inactive_sections as $section ) {
×
NEW
144
                                array_walk( $this->plugins[ $section ], [ $this, 'clear_error' ] );
×
145
                        }
146
                }
147

148
                // For active sections clear errors for inactive plugins.
149
                foreach ( $sections as $section ) {
×
150
                        // By default, clear errors for all plugins of the section.
151
                        $inactive_plugins = $this->plugins[ $section ];
×
152

153
                        // If there are active plugins, filter them from being cleared.
154
                        if ( isset( $this->active_conflicting_plugins[ $section ] ) ) {
×
NEW
155
                                $inactive_plugins = array_diff( $this->plugins[ $section ], $this->active_conflicting_plugins[ $section ] );
×
156
                        }
157

NEW
158
                        array_walk( $inactive_plugins, [ $this, 'clear_error' ] );
×
159
                }
160
        }
161

162
        /**
163
         * Setting an error on the screen.
164
         *
165
         * @param string $plugin_section          Type of conflict group (such as Open Graph or sitemap).
166
         * @param string $readable_plugin_section This is the value for the translation.
167
         */
168
        protected function set_error( $plugin_section, $readable_plugin_section ) {
×
169

170
                $notification_center = Yoast_Notification_Center::get();
×
171

172
                foreach ( $this->active_conflicting_plugins[ $plugin_section ] as $plugin_file ) {
×
173

174
                        $plugin_name = $this->get_plugin_name( $plugin_file );
×
175

176
                        $error_message = '';
×
177
                        /* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */
178
                        $error_message .= '<p>' . sprintf( __( 'The %1$s plugin might cause issues when used in conjunction with %2$s.', 'wordpress-seo' ), '<em>' . $plugin_name . '</em>', 'Yoast SEO' ) . '</p>';
×
179
                        $error_message .= '<p>' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '</p>';
×
180

181
                        /* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */
182
                        $error_message .= '<a class="button button-primary" href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . $plugin_file . '&amp;plugin_status=all', 'deactivate-plugin_' . $plugin_file ) . '">' . sprintf( __( 'Deactivate %s', 'wordpress-seo' ), $this->get_plugin_name( $plugin_file ) ) . '</a> ';
×
183

184
                        $identifier = $this->get_notification_identifier( $plugin_file );
×
185

186
                        // Add the message to the notifications center.
187
                        $notification_center->add_notification(
×
188
                                new Yoast_Notification(
×
189
                                        $error_message,
×
190
                                        [
191
                                                'type' => Yoast_Notification::ERROR,
×
192
                                                'id'   => 'wpseo-conflict-' . $identifier,
×
193
                                        ]
194
                                )
195
                        );
196
                }
197
        }
198

199
        /**
200
         * Clear the notification for a plugin.
201
         *
202
         * @param string $plugin_file Clear the optional notification for this plugin.
203
         */
204
        public function clear_error( $plugin_file ) {
×
205
                $identifier = $this->get_notification_identifier( $plugin_file );
×
206

207
                $notification_center = Yoast_Notification_Center::get();
×
208
                $notification_center->remove_notification_by_id( 'wpseo-conflict-' . $identifier );
×
209
        }
210

211
        /**
212
         * Loop through the $this->plugins to check if one of the plugins is active.
213
         *
214
         * This method will store the active plugins in $this->active_plugins.
215
         */
216
        protected function search_active_plugins() {
×
217
                foreach ( $this->plugins as $plugin_section => $plugins ) {
×
218
                        $this->check_plugins_active( $plugins, $plugin_section );
×
219
                }
220
        }
221

222
        /**
223
         * Loop through plugins and check if each plugin is active.
224
         *
225
         * @param array  $plugins        Set of plugins.
226
         * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
227
         */
228
        protected function check_plugins_active( $plugins, $plugin_section ) {
×
229
                foreach ( $plugins as $plugin ) {
×
230
                        if ( $this->check_plugin_is_active( $plugin ) ) {
×
231
                                $this->add_active_plugin( $plugin_section, $plugin );
×
232
                        }
233
                }
234
        }
235

236
        /**
237
         * Check if given plugin exists in array with all_active_plugins.
238
         *
239
         * @param string $plugin Plugin basename string.
240
         *
241
         * @return bool
242
         */
243
        protected function check_plugin_is_active( $plugin ) {
×
NEW
244
                return in_array( $plugin, $this->all_active_plugins, true );
×
245
        }
246

247
        /**
248
         * Add plugin to the list of active plugins.
249
         *
250
         * This method will check first if key $plugin_section exists, if not it will create an empty array
251
         * If $plugin itself doesn't exist it will be added.
252
         *
253
         * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
254
         * @param string $plugin         Plugin basename string.
255
         */
256
        protected function add_active_plugin( $plugin_section, $plugin ) {
×
NEW
257
                if ( ! array_key_exists( $plugin_section, $this->active_conflicting_plugins ) ) {
×
258
                        $this->active_conflicting_plugins[ $plugin_section ] = [];
×
259
                }
260

NEW
261
                if ( ! in_array( $plugin, $this->active_conflicting_plugins[ $plugin_section ], true ) ) {
×
262
                        $this->active_conflicting_plugins[ $plugin_section ][] = $plugin;
×
263
                }
264
        }
265

266
        /**
267
         * Search in $this->plugins for the given $plugin.
268
         *
269
         * If there is a result it will return the plugin category.
270
         *
271
         * @param string $plugin Plugin basename string.
272
         *
273
         * @return int|string
274
         */
275
        protected function find_plugin_category( $plugin ) {
×
276
                foreach ( $this->plugins as $plugin_section => $plugins ) {
×
NEW
277
                        if ( in_array( $plugin, $plugins, true ) ) {
×
278
                                return $plugin_section;
×
279
                        }
280
                }
281
        }
282

283
        /**
284
         * Get plugin name from file.
285
         *
286
         * @param string $plugin Plugin path relative to plugins directory.
287
         *
288
         * @return string|bool Plugin name or false when no name is set.
289
         */
290
        protected function get_plugin_name( $plugin ) {
×
NEW
291
                $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
×
292

293
                if ( $plugin_details['Name'] !== '' ) {
×
294
                        return $plugin_details['Name'];
×
295
                }
296

297
                return false;
×
298
        }
299

300
        /**
301
         * When being in the deactivation process the currently deactivated plugin has to be removed.
302
         */
303
        private function remove_deactivated_plugin() {
×
304
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself.
305
                if ( ! isset( $_GET['plugin'] ) || ! is_string( $_GET['plugin'] ) ) {
×
306
                        return;
×
307
                }
308

309
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself.
310
                $deactivated_plugin = sanitize_text_field( wp_unslash( $_GET['plugin'] ) );
×
NEW
311
                $key_to_remove      = array_search( $deactivated_plugin, $this->all_active_plugins, true );
×
312

313
                if ( $key_to_remove !== false ) {
×
314
                        unset( $this->all_active_plugins[ $key_to_remove ] );
×
315
                }
316
        }
317

318
        /**
319
         * Get the identifier from the plugin file.
320
         *
321
         * @param string $plugin_file Plugin file to get Identifier from.
322
         *
323
         * @return string
324
         */
325
        private function get_notification_identifier( $plugin_file ) {
×
NEW
326
                return md5( $plugin_file );
×
327
        }
328
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc