PluginProbe ʕ •ᴥ•ʔ
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 23.8
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v23.8
27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 19.1 19.10 19.11 19.12 19.13 19.14 19.2 19.3 19.4 19.5 19.5.1 19.6 19.6.1 19.7 19.7.1 19.7.2 19.8 19.9 20.0 20.1 20.10 20.11 20.12 20.13 20.2 20.2.1 20.3 20.4 20.5 20.6 20.7 20.8 20.9 21.0 21.1 21.2 21.3 21.4 21.5 21.6 21.7 21.8 21.8.1 21.9 21.9.1 22.0 22.1 22.2 22.3 22.4 22.5 22.6 22.7 22.8 22.9 23.0 23.1 23.2 23.3 23.4 23.5 23.6 23.7 23.8 23.9 24.0 24.1 24.2 24.3 24.4 24.5 24.6 24.7 24.8 24.8.1 24.9 25.0 25.1 25.2 25.3 25.3.1 25.4 25.5 25.6 25.7 25.8 25.9 26.0 26.1 26.1.1 26.2 26.3 26.4 26.5 26.6 26.7 26.8 26.9 27.0 27.1 27.1.1 27.2 27.3 27.4
wordpress-seo / admin / class-yoast-plugin-conflict.php
wordpress-seo / admin Last commit date
ajax 2 years ago capabilities 2 years ago endpoints 2 years ago exceptions 7 years ago filters 2 years ago formatter 1 year ago google_search_console 2 years ago import 2 years ago listeners 8 years ago menu 1 year ago metabox 1 year ago notifiers 3 years ago pages 2 years ago roles 2 years ago services 5 years ago statistics 2 years ago taxonomy 1 year ago tracking 1 year ago views 1 year ago watchers 2 years ago admin-settings-changed-listener.php 2 years ago ajax.php 2 years ago class-admin-asset-analysis-worker-location.php 5 years ago class-admin-asset-dev-server-location.php 2 years ago class-admin-asset-location.php 8 years ago class-admin-asset-manager.php 1 year ago class-admin-asset-seo-location.php 4 years ago class-admin-editor-specific-replace-vars.php 2 years ago class-admin-gutenberg-compatibility-notification.php 2 years ago class-admin-help-panel.php 5 years ago class-admin-init.php 2 years ago class-admin-recommended-replace-vars.php 2 years ago class-admin-user-profile.php 1 year ago class-admin-utils.php 2 years ago class-admin.php 1 year ago class-asset.php 1 year ago class-bulk-description-editor-list-table.php 5 years ago class-bulk-editor-list-table.php 2 years ago class-bulk-title-editor-list-table.php 6 years ago class-collector.php 2 years ago class-config.php 1 year ago class-database-proxy.php 2 years ago class-export.php 2 years ago class-expose-shortlinks.php 2 years ago class-gutenberg-compatibility.php 1 year ago class-meta-columns.php 2 years ago class-my-yoast-proxy.php 2 years ago class-option-tab.php 4 years ago class-option-tabs-formatter.php 2 years ago class-option-tabs.php 2 years ago class-paper-presenter.php 5 years ago class-plugin-availability.php 1 year ago class-plugin-conflict.php 2 years ago class-premium-popup.php 2 years ago class-premium-upsell-admin-block.php 1 year ago class-primary-term-admin.php 2 years ago class-product-upsell-notice.php 2 years ago class-remote-request.php 2 years ago class-schema-person-upgrade-notification.php 2 years ago class-suggested-plugins.php 2 years ago class-wincher-dashboard-widget.php 2 years ago class-yoast-columns.php 2 years ago class-yoast-dashboard-widget.php 2 years ago class-yoast-form.php 1 year ago class-yoast-input-validation.php 1 year ago class-yoast-network-admin.php 2 years ago class-yoast-network-settings-api.php 4 years ago class-yoast-notification-center.php 1 year ago class-yoast-notification.php 1 year ago class-yoast-notifications.php 2 years ago class-yoast-plugin-conflict.php 2 years ago index.php 10 years ago interface-collection.php 7 years ago interface-installable.php 8 years ago
class-yoast-plugin-conflict.php
343 lines
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
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 * @return void
123 */
124 public function check_plugin_conflicts( $plugin_sections ) {
125 foreach ( $plugin_sections as $plugin_section => $readable_plugin_section ) {
126 // Check for conflicting plugins and show error if there are conflicts.
127 if ( $this->check_for_conflicts( $plugin_section ) ) {
128 $this->set_error( $plugin_section, $readable_plugin_section );
129 }
130 }
131
132 // List of all active sections.
133 $sections = array_keys( $plugin_sections );
134 // List of all sections.
135 $all_plugin_sections = array_keys( $this->plugins );
136
137 /*
138 * Get all sections that are inactive.
139 * These plugins need to be cleared.
140 *
141 * This happens when Sitemaps or OpenGraph implementations toggle active/disabled.
142 */
143 $inactive_sections = array_diff( $all_plugin_sections, $sections );
144 if ( ! empty( $inactive_sections ) ) {
145 foreach ( $inactive_sections as $section ) {
146 array_walk( $this->plugins[ $section ], [ $this, 'clear_error' ] );
147 }
148 }
149
150 // For active sections clear errors for inactive plugins.
151 foreach ( $sections as $section ) {
152 // By default, clear errors for all plugins of the section.
153 $inactive_plugins = $this->plugins[ $section ];
154
155 // If there are active plugins, filter them from being cleared.
156 if ( isset( $this->active_conflicting_plugins[ $section ] ) ) {
157 $inactive_plugins = array_diff( $this->plugins[ $section ], $this->active_conflicting_plugins[ $section ] );
158 }
159
160 array_walk( $inactive_plugins, [ $this, 'clear_error' ] );
161 }
162 }
163
164 /**
165 * Setting an error on the screen.
166 *
167 * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
168 * @param string $readable_plugin_section This is the value for the translation.
169 *
170 * @return void
171 */
172 protected function set_error( $plugin_section, $readable_plugin_section ) {
173
174 $notification_center = Yoast_Notification_Center::get();
175
176 foreach ( $this->active_conflicting_plugins[ $plugin_section ] as $plugin_file ) {
177
178 $plugin_name = $this->get_plugin_name( $plugin_file );
179
180 $error_message = '';
181 /* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */
182 $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>';
183 $error_message .= '<p>' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '</p>';
184
185 /* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */
186 $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> ';
187
188 $identifier = $this->get_notification_identifier( $plugin_file );
189
190 // Add the message to the notifications center.
191 $notification_center->add_notification(
192 new Yoast_Notification(
193 $error_message,
194 [
195 'type' => Yoast_Notification::ERROR,
196 'id' => 'wpseo-conflict-' . $identifier,
197 ]
198 )
199 );
200 }
201 }
202
203 /**
204 * Clear the notification for a plugin.
205 *
206 * @param string $plugin_file Clear the optional notification for this plugin.
207 *
208 * @return void
209 */
210 public function clear_error( $plugin_file ) {
211 $identifier = $this->get_notification_identifier( $plugin_file );
212
213 $notification_center = Yoast_Notification_Center::get();
214 $notification_center->remove_notification_by_id( 'wpseo-conflict-' . $identifier );
215 }
216
217 /**
218 * Loop through the $this->plugins to check if one of the plugins is active.
219 *
220 * This method will store the active plugins in $this->active_plugins.
221 *
222 * @return void
223 */
224 protected function search_active_plugins() {
225 foreach ( $this->plugins as $plugin_section => $plugins ) {
226 $this->check_plugins_active( $plugins, $plugin_section );
227 }
228 }
229
230 /**
231 * Loop through plugins and check if each plugin is active.
232 *
233 * @param array $plugins Set of plugins.
234 * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
235 *
236 * @return void
237 */
238 protected function check_plugins_active( $plugins, $plugin_section ) {
239 foreach ( $plugins as $plugin ) {
240 if ( $this->check_plugin_is_active( $plugin ) ) {
241 $this->add_active_plugin( $plugin_section, $plugin );
242 }
243 }
244 }
245
246 /**
247 * Check if given plugin exists in array with all_active_plugins.
248 *
249 * @param string $plugin Plugin basename string.
250 *
251 * @return bool
252 */
253 protected function check_plugin_is_active( $plugin ) {
254 return in_array( $plugin, $this->all_active_plugins, true );
255 }
256
257 /**
258 * Add plugin to the list of active plugins.
259 *
260 * This method will check first if key $plugin_section exists, if not it will create an empty array
261 * If $plugin itself doesn't exist it will be added.
262 *
263 * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
264 * @param string $plugin Plugin basename string.
265 *
266 * @return void
267 */
268 protected function add_active_plugin( $plugin_section, $plugin ) {
269 if ( ! array_key_exists( $plugin_section, $this->active_conflicting_plugins ) ) {
270 $this->active_conflicting_plugins[ $plugin_section ] = [];
271 }
272
273 if ( ! in_array( $plugin, $this->active_conflicting_plugins[ $plugin_section ], true ) ) {
274 $this->active_conflicting_plugins[ $plugin_section ][] = $plugin;
275 }
276 }
277
278 /**
279 * Search in $this->plugins for the given $plugin.
280 *
281 * If there is a result it will return the plugin category.
282 *
283 * @param string $plugin Plugin basename string.
284 *
285 * @return int|string
286 */
287 protected function find_plugin_category( $plugin ) {
288 foreach ( $this->plugins as $plugin_section => $plugins ) {
289 if ( in_array( $plugin, $plugins, true ) ) {
290 return $plugin_section;
291 }
292 }
293 }
294
295 /**
296 * Get plugin name from file.
297 *
298 * @param string $plugin Plugin path relative to plugins directory.
299 *
300 * @return string|bool Plugin name or false when no name is set.
301 */
302 protected function get_plugin_name( $plugin ) {
303 $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
304
305 if ( $plugin_details['Name'] !== '' ) {
306 return $plugin_details['Name'];
307 }
308
309 return false;
310 }
311
312 /**
313 * When being in the deactivation process the currently deactivated plugin has to be removed.
314 *
315 * @return void
316 */
317 private function remove_deactivated_plugin() {
318 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself.
319 if ( ! isset( $_GET['plugin'] ) || ! is_string( $_GET['plugin'] ) ) {
320 return;
321 }
322
323 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself.
324 $deactivated_plugin = sanitize_text_field( wp_unslash( $_GET['plugin'] ) );
325 $key_to_remove = array_search( $deactivated_plugin, $this->all_active_plugins, true );
326
327 if ( $key_to_remove !== false ) {
328 unset( $this->all_active_plugins[ $key_to_remove ] );
329 }
330 }
331
332 /**
333 * Get the identifier from the plugin file.
334 *
335 * @param string $plugin_file Plugin file to get Identifier from.
336 *
337 * @return string
338 */
339 private function get_notification_identifier( $plugin_file ) {
340 return md5( $plugin_file );
341 }
342 }
343