diff --git src/wp-admin/includes/file.php src/wp-admin/includes/file.php
index cca39895ba..5b4146eebb 100644
--- src/wp-admin/includes/file.php
+++ src/wp-admin/includes/file.php
@@ -1728,11 +1728,25 @@ function copy_dir( $from, $to, $skip_list = array() ) {
 	$from = trailingslashit( $from );
 	$to   = trailingslashit( $to );
 
+	/**
+	 * Filters whether to invalidate files in the PHP opcache
+	 * after it is written during an upgrade.
+	 *
+	 * @since 5.5.0
+	 *
+	 * @param bool $invalidate Whether to invalidate. Default true.
+	 */
+	$opcache_invalidate_allowed = function_exists( 'opcache_invalidate' ) && apply_filters( 'wp_opcache_invalidate_allowed', true );
+
 	foreach ( (array) $dirlist as $filename => $fileinfo ) {
 		if ( in_array( $filename, $skip_list, true ) ) {
 			continue;
 		}
 
+		if ( $opcache_invalidate_allowed && in_array( pathinfo( $filename, PATHINFO_EXTENSION ), array( 'php', 'phtml' ), true ) ) {
+			opcache_invalidate( $to . $filename );
+		}
+
 		if ( 'f' === $fileinfo['type'] ) {
 			if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) {
 				// If copy failed, chmod file to 0644 and try again.
diff --git src/wp-admin/includes/update-core.php src/wp-admin/includes/update-core.php
index 595ebc053a..1abfd9bb2e 100644
--- src/wp-admin/includes/update-core.php
+++ src/wp-admin/includes/update-core.php
@@ -1339,11 +1339,18 @@ function _copy_dir( $from, $to, $skip_list = array() ) {
 	$from = trailingslashit( $from );
 	$to   = trailingslashit( $to );
 
+	/** This filter is documented in wp-admin/includes/file.php */
+	$opcache_invalidate_allowed = function_exists( 'opcache_invalidate' ) && apply_filters( 'wp_opcache_invalidate_allowed', true );
+
 	foreach ( (array) $dirlist as $filename => $fileinfo ) {
 		if ( in_array( $filename, $skip_list, true ) ) {
 			continue;
 		}
 
+		if ( $opcache_invalidate_allowed && in_array( pathinfo( $filename, PATHINFO_EXTENSION ), array( 'php', 'phtml' ), true ) ) {
+			opcache_invalidate( $to . $filename );
+		}
+
 		if ( 'f' === $fileinfo['type'] ) {
 			if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) {
 				// If copy failed, chmod file to 0644 and try again.
