wp/wp-admin/includes/plugin.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
--- a/wp/wp-admin/includes/plugin.php	Tue Dec 15 15:52:01 2020 +0100
+++ b/wp/wp-admin/includes/plugin.php	Wed Sep 21 18:19:35 2022 +0200
@@ -44,6 +44,7 @@
  *
  * @since 1.5.0
  * @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers.
+ * @since 5.8.0 Added support for `Update URI` header.
  *
  * @param string $plugin_file Absolute path to the main plugin file.
  * @param bool   $markup      Optional. If the returned data should have HTML markup applied.
@@ -63,6 +64,7 @@
  *     @type bool   $Network     Whether the plugin can only be activated network-wide.
  *     @type string $RequiresWP  Minimum required version of WordPress.
  *     @type string $RequiresPHP Minimum required version of PHP.
+ *     @type string $UpdateURI   ID of the plugin for update purposes, should be a URI.
  * }
  */
 function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
@@ -79,6 +81,7 @@
 		'Network'     => 'Network',
 		'RequiresWP'  => 'Requires at least',
 		'RequiresPHP' => 'Requires PHP',
+		'UpdateURI'   => 'Update URI',
 		// Site Wide Only is deprecated in favor of Network.
 		'_sitewide'   => 'Site Wide Only',
 	);
@@ -293,7 +296,7 @@
 	}
 
 	// Files in wp-content/plugins directory.
-	$plugins_dir  = @ opendir( $plugin_root );
+	$plugins_dir  = @opendir( $plugin_root );
 	$plugin_files = array();
 
 	if ( $plugins_dir ) {
@@ -303,7 +306,7 @@
 			}
 
 			if ( is_dir( $plugin_root . '/' . $file ) ) {
-				$plugins_subdir = @ opendir( $plugin_root . '/' . $file );
+				$plugins_subdir = @opendir( $plugin_root . '/' . $file );
 
 				if ( $plugins_subdir ) {
 					while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) {
@@ -651,7 +654,9 @@
 		return $requirements;
 	}
 
-	if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current, true ) ) ) {
+	if ( $network_wide && ! isset( $current[ $plugin ] )
+		|| ! $network_wide && ! in_array( $plugin, $current, true )
+	) {
 		if ( ! empty( $redirect ) ) {
 			// We'll override this later if the plugin can be included without fatal error.
 			wp_redirect( add_query_arg( '_error_nonce', wp_create_nonce( 'plugin-activation-error_' . $plugin ), $redirect ) );
@@ -659,14 +664,8 @@
 
 		ob_start();
 
-		if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
-			define( 'WP_SANDBOX_SCRAPING', true );
-		}
-
-		wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
-		$_wp_plugin_file = $plugin;
-		include_once WP_PLUGIN_DIR . '/' . $plugin;
-		$plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin.
+		// Load the plugin to test whether it throws any errors.
+		plugin_sandbox_scrape( $plugin );
 
 		if ( ! $silent ) {
 			/**
@@ -730,6 +729,7 @@
 			$output = ob_get_clean();
 			return new WP_Error( 'unexpected_output', __( 'The plugin generated unexpected output.' ), $output );
 		}
+
 		ob_end_clean();
 	}
 
@@ -764,7 +764,7 @@
 			continue;
 		}
 
-		$network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin );
+		$network_deactivating = ( false !== $network_wide ) && is_plugin_active_for_network( $plugin );
 
 		if ( ! $silent ) {
 			/**
@@ -978,7 +978,7 @@
 
 		// If plugin is in its own directory, recursively delete the directory.
 		// Base check on if plugin includes directory separator AND that it's not the root plugin folder.
-		if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir ) {
+		if ( strpos( $plugin_file, '/' ) && $this_plugin_dir !== $plugins_dir ) {
 			$deleted = $wp_filesystem->delete( $this_plugin_dir, true );
 		} else {
 			$deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file );
@@ -999,8 +999,13 @@
 			continue;
 		}
 
+		$plugin_slug = dirname( $plugin_file );
+
+		if ( 'hello.php' === $plugin_file ) {
+			$plugin_slug = 'hello-dolly';
+		}
+
 		// Remove language files, silently.
-		$plugin_slug = dirname( $plugin_file );
 		if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) {
 			$translations = $plugin_translations[ $plugin_slug ];
 
@@ -1114,12 +1119,10 @@
  * Uses the information from `Requires at least` and `Requires PHP` headers
  * defined in the plugin's main PHP file.
  *
- * If the headers are not present in the plugin's main PHP file,
- * `readme.txt` is also checked as a fallback.
- *
  * @since 5.2.0
  * @since 5.3.0 Added support for reading the headers from the plugin's
  *              main PHP file, with `readme.txt` as a fallback.
+ * @since 5.8.0 Removed support for using `readme.txt` as a fallback.
  *
  * @param string $plugin Path to the plugin file relative to the plugins directory.
  * @return true|WP_Error True if requirements are met, WP_Error on failure.
@@ -1132,26 +1135,11 @@
 		'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '',
 	);
 
-	$readme_file = WP_PLUGIN_DIR . '/' . dirname( $plugin ) . '/readme.txt';
-
-	if ( file_exists( $readme_file ) ) {
-		$readme_headers = get_file_data(
-			$readme_file,
-			array(
-				'requires'     => 'Requires at least',
-				'requires_php' => 'Requires PHP',
-			),
-			'plugin'
-		);
-
-		$requirements = array_merge( $readme_headers, $requirements );
-	}
-
 	$compatible_wp  = is_wp_version_compatible( $requirements['requires'] );
 	$compatible_php = is_php_version_compatible( $requirements['requires_php'] );
 
-	/* translators: %s: URL to Update PHP page. */
 	$php_update_message = '</p><p>' . sprintf(
+		/* translators: %s: URL to Update PHP page. */
 		__( '<a href="%s">Learn more about updating PHP</a>.' ),
 		esc_url( wp_get_update_php_url() )
 	);
@@ -1229,7 +1217,8 @@
  * @since 2.7.0
  *
  * @param string $plugin Path to the plugin file relative to the plugins directory.
- * @return true True if a plugin's uninstall.php file has been found and included.
+ * @return true|void True if a plugin's uninstall.php file has been found and included.
+ *                   Void otherwise.
  */
 function uninstall_plugin( $plugin ) {
 	$file = plugin_basename( $plugin );
@@ -1414,9 +1403,9 @@
 	 * parent file someone is trying to link back to the parent manually. In
 	 * this case, don't automatically add a link back to avoid duplication.
 	 */
-	if ( ! isset( $submenu[ $parent_slug ] ) && $menu_slug != $parent_slug ) {
+	if ( ! isset( $submenu[ $parent_slug ] ) && $menu_slug !== $parent_slug ) {
 		foreach ( (array) $menu as $parent_menu ) {
-			if ( $parent_menu[2] == $parent_slug && current_user_can( $parent_menu[1] ) ) {
+			if ( $parent_menu[2] === $parent_slug && current_user_can( $parent_menu[1] ) ) {
 				$submenu[ $parent_slug ][] = array_slice( $parent_menu, 0, 4 );
 			}
 		}
@@ -1761,13 +1750,13 @@
  * @global array $menu
  *
  * @param string $menu_slug The slug of the menu.
- * @return array|bool The removed menu on success, false if not found.
+ * @return array|false The removed menu on success, false if not found.
  */
 function remove_menu_page( $menu_slug ) {
 	global $menu;
 
 	foreach ( $menu as $i => $item ) {
-		if ( $menu_slug == $item[2] ) {
+		if ( $menu_slug === $item[2] ) {
 			unset( $menu[ $i ] );
 			return $item;
 		}
@@ -1785,7 +1774,7 @@
  *
  * @param string $menu_slug    The slug for the parent menu.
  * @param string $submenu_slug The slug of the submenu.
- * @return array|bool The removed submenu on success, false if not found.
+ * @return array|false The removed submenu on success, false if not found.
  */
 function remove_submenu_page( $menu_slug, $submenu_slug ) {
 	global $submenu;
@@ -1795,7 +1784,7 @@
 	}
 
 	foreach ( $submenu[ $menu_slug ] as $i => $item ) {
-		if ( $submenu_slug == $item[2] ) {
+		if ( $submenu_slug === $item[2] ) {
 			unset( $submenu[ $menu_slug ][ $i ] );
 			return $item;
 		}
@@ -1822,6 +1811,7 @@
 
 	if ( isset( $_parent_pages[ $menu_slug ] ) ) {
 		$parent_slug = $_parent_pages[ $menu_slug ];
+
 		if ( $parent_slug && ! isset( $_parent_pages[ $parent_slug ] ) ) {
 			$url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) );
 		} else {
@@ -1870,33 +1860,40 @@
 		if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
 			$parent = $_wp_real_parent_file[ $parent ];
 		}
+
 		return $parent;
 	}
 
 	if ( 'admin.php' === $pagenow && isset( $plugin_page ) ) {
 		foreach ( (array) $menu as $parent_menu ) {
-			if ( $parent_menu[2] == $plugin_page ) {
+			if ( $parent_menu[2] === $plugin_page ) {
 				$parent_file = $plugin_page;
+
 				if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
 					$parent_file = $_wp_real_parent_file[ $parent_file ];
 				}
+
 				return $parent_file;
 			}
 		}
 		if ( isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
 			$parent_file = $plugin_page;
+
 			if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
 					$parent_file = $_wp_real_parent_file[ $parent_file ];
 			}
+
 			return $parent_file;
 		}
 	}
 
 	if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $pagenow ][ $plugin_page ] ) ) {
 		$parent_file = $pagenow;
+
 		if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) {
 			$parent_file = $_wp_real_parent_file[ $parent_file ];
 		}
+
 		return $parent_file;
 	}
 
@@ -1905,13 +1902,16 @@
 			if ( isset( $_wp_real_parent_file[ $parent ] ) ) {
 				$parent = $_wp_real_parent_file[ $parent ];
 			}
-			if ( ! empty( $typenow ) && ( "$pagenow?post_type=$typenow" === $submenu_array[2] ) ) {
+
+			if ( ! empty( $typenow ) && "$pagenow?post_type=$typenow" === $submenu_array[2] ) {
 				$parent_file = $parent;
 				return $parent;
-			} elseif ( $submenu_array[2] == $pagenow && empty( $typenow ) && ( empty( $parent_file ) || false === strpos( $parent_file, '?' ) ) ) {
+			} elseif ( empty( $typenow ) && $pagenow === $submenu_array[2]
+				&& ( empty( $parent_file ) || false === strpos( $parent_file, '?' ) )
+			) {
 				$parent_file = $parent;
 				return $parent;
-			} elseif ( isset( $plugin_page ) && ( $plugin_page == $submenu_array[2] ) ) {
+			} elseif ( isset( $plugin_page ) && $plugin_page === $submenu_array[2] ) {
 				$parent_file = $parent;
 				return $parent;
 			}
@@ -1953,10 +1953,10 @@
 	if ( empty( $parent ) ) {
 		foreach ( (array) $menu as $menu_array ) {
 			if ( isset( $menu_array[3] ) ) {
-				if ( $menu_array[2] == $pagenow ) {
+				if ( $menu_array[2] === $pagenow ) {
 					$title = $menu_array[3];
 					return $menu_array[3];
-				} elseif ( isset( $plugin_page ) && ( $plugin_page == $menu_array[2] ) && ( $hook == $menu_array[5] ) ) {
+				} elseif ( isset( $plugin_page ) && $plugin_page === $menu_array[2] && $hook === $menu_array[5] ) {
 					$title = $menu_array[3];
 					return $menu_array[3];
 				}
@@ -1968,21 +1968,19 @@
 	} else {
 		foreach ( array_keys( $submenu ) as $parent ) {
 			foreach ( $submenu[ $parent ] as $submenu_array ) {
-				if ( isset( $plugin_page ) &&
-					( $plugin_page == $submenu_array[2] ) &&
-					(
-						( $parent == $pagenow ) ||
-						( $parent == $plugin_page ) ||
-						( $plugin_page == $hook ) ||
-						( 'admin.php' === $pagenow && $parent1 != $submenu_array[2] ) ||
-						( ! empty( $typenow ) && $parent == $pagenow . '?post_type=' . $typenow )
-					)
+				if ( isset( $plugin_page )
+					&& $plugin_page === $submenu_array[2]
+					&& ( $pagenow === $parent
+						|| $plugin_page === $parent
+						|| $plugin_page === $hook
+						|| 'admin.php' === $pagenow && $parent1 !== $submenu_array[2]
+						|| ! empty( $typenow ) && "$pagenow?post_type=$typenow" === $parent )
 					) {
 						$title = $submenu_array[3];
 						return $submenu_array[3];
 				}
 
-				if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) { // Not the current page.
+				if ( $submenu_array[2] !== $pagenow || isset( $_GET['page'] ) ) { // Not the current page.
 					continue;
 				}
 
@@ -1997,10 +1995,11 @@
 		}
 		if ( empty( $title ) ) {
 			foreach ( $menu as $menu_array ) {
-				if ( isset( $plugin_page ) &&
-					( $plugin_page == $menu_array[2] ) &&
-					( 'admin.php' === $pagenow ) &&
-					( $parent1 == $menu_array[2] ) ) {
+				if ( isset( $plugin_page )
+					&& $plugin_page === $menu_array[2]
+					&& 'admin.php' === $pagenow
+					&& $parent1 === $menu_array[2]
+				) {
 						$title = $menu_array[3];
 						return $menu_array[3];
 				}
@@ -2113,6 +2112,7 @@
 		if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
 			return false;
 		}
+
 		foreach ( array_keys( $_wp_submenu_nopriv ) as $key ) {
 			if ( isset( $_wp_submenu_nopriv[ $key ][ $pagenow ] ) ) {
 				return false;
@@ -2121,38 +2121,27 @@
 				return false;
 			}
 		}
+
 		return true;
 	}
 
-	if ( isset( $plugin_page ) && ( $plugin_page == $parent ) && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
+	if ( isset( $plugin_page ) && $plugin_page === $parent && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) {
 		return false;
 	}
 
 	if ( isset( $submenu[ $parent ] ) ) {
 		foreach ( $submenu[ $parent ] as $submenu_array ) {
-			if ( isset( $plugin_page ) && ( $submenu_array[2] == $plugin_page ) ) {
-				if ( current_user_can( $submenu_array[1] ) ) {
-					return true;
-				} else {
-					return false;
-				}
-			} elseif ( $submenu_array[2] == $pagenow ) {
-				if ( current_user_can( $submenu_array[1] ) ) {
-					return true;
-				} else {
-					return false;
-				}
+			if ( isset( $plugin_page ) && $submenu_array[2] === $plugin_page ) {
+				return current_user_can( $submenu_array[1] );
+			} elseif ( $submenu_array[2] === $pagenow ) {
+				return current_user_can( $submenu_array[1] );
 			}
 		}
 	}
 
 	foreach ( $menu as $menu_array ) {
-		if ( $menu_array[2] == $parent ) {
-			if ( current_user_can( $menu_array[1] ) ) {
-				return true;
-			} else {
-				return false;
-			}
+		if ( $menu_array[2] === $parent ) {
+			return current_user_can( $menu_array[1] );
 		}
 	}
 
@@ -2188,7 +2177,7 @@
 /**
  * Adds an array of options to the list of allowed options.
  *
- * @since 2.7.0
+ * @since 5.5.0
  *
  * @global array $allowed_options
  *
@@ -2294,7 +2283,7 @@
 	}
 
 	wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin );
-	include WP_PLUGIN_DIR . '/' . $plugin;
+	include_once WP_PLUGIN_DIR . '/' . $plugin;
 }
 
 /**
@@ -2460,6 +2449,8 @@
  * Renders an admin notice in case some plugins have been paused due to errors.
  *
  * @since 5.2.0
+ *
+ * @global string $pagenow
  */
 function paused_plugins_notice() {
 	if ( 'plugins.php' === $GLOBALS['pagenow'] ) {
@@ -2482,3 +2473,88 @@
 		__( 'Go to the Plugins screen' )
 	);
 }
+
+/**
+ * Renders an admin notice when a plugin was deactivated during an update.
+ *
+ * Displays an admin notice in case a plugin has been deactivated during an
+ * upgrade due to incompatibility with the current version of WordPress.
+ *
+ * @since 5.8.0
+ * @access private
+ *
+ * @global string $pagenow
+ * @global string $wp_version
+ */
+function deactivated_plugins_notice() {
+	if ( 'plugins.php' === $GLOBALS['pagenow'] ) {
+		return;
+	}
+
+	if ( ! current_user_can( 'activate_plugins' ) ) {
+		return;
+	}
+
+	$blog_deactivated_plugins = get_option( 'wp_force_deactivated_plugins' );
+	$site_deactivated_plugins = array();
+
+	if ( false === $blog_deactivated_plugins ) {
+		// Option not in database, add an empty array to avoid extra DB queries on subsequent loads.
+		update_option( 'wp_force_deactivated_plugins', array() );
+	}
+
+	if ( is_multisite() ) {
+		$site_deactivated_plugins = get_site_option( 'wp_force_deactivated_plugins' );
+		if ( false === $site_deactivated_plugins ) {
+			// Option not in database, add an empty array to avoid extra DB queries on subsequent loads.
+			update_site_option( 'wp_force_deactivated_plugins', array() );
+		}
+	}
+
+	if ( empty( $blog_deactivated_plugins ) && empty( $site_deactivated_plugins ) ) {
+		// No deactivated plugins.
+		return;
+	}
+
+	$deactivated_plugins = array_merge( $blog_deactivated_plugins, $site_deactivated_plugins );
+
+	foreach ( $deactivated_plugins as $plugin ) {
+		if ( ! empty( $plugin['version_compatible'] ) && ! empty( $plugin['version_deactivated'] ) ) {
+			$explanation = sprintf(
+				/* translators: 1: Name of deactivated plugin, 2: Plugin version deactivated, 3: Current WP version, 4: Compatible plugin version */
+				__( '%1$s %2$s was deactivated due to incompatibility with WordPress %3$s, please upgrade to %1$s %4$s or later.' ),
+				$plugin['plugin_name'],
+				$plugin['version_deactivated'],
+				$GLOBALS['wp_version'],
+				$plugin['version_compatible']
+			);
+		} else {
+			$explanation = sprintf(
+				/* translators: 1: Name of deactivated plugin, 2: Plugin version deactivated, 3: Current WP version */
+				__( '%1$s %2$s was deactivated due to incompatibility with WordPress %3$s.' ),
+				$plugin['plugin_name'],
+				! empty( $plugin['version_deactivated'] ) ? $plugin['version_deactivated'] : '',
+				$GLOBALS['wp_version'],
+				$plugin['version_compatible']
+			);
+		}
+
+		printf(
+			'<div class="notice notice-warning"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>',
+			sprintf(
+				/* translators: %s: Name of deactivated plugin */
+				__( '%s plugin deactivated during WordPress upgrade.' ),
+				$plugin['plugin_name']
+			),
+			$explanation,
+			esc_url( admin_url( 'plugins.php?plugin_status=inactive' ) ),
+			__( 'Go to the Plugins screen' )
+		);
+	}
+
+	// Empty the options.
+	update_option( 'wp_force_deactivated_plugins', array() );
+	if ( is_multisite() ) {
+		update_site_option( 'wp_force_deactivated_plugins', array() );
+	}
+}