├── CHANGES.md ├── faster-updates.php ├── LICENSE ├── testing_instructions.md ├── readme.txt ├── modules ├── themes │ ├── class-main.php │ └── class-upgrader.php └── plugins │ ├── class-main.php │ └── class-upgrader.php └── functions └── move.php /CHANGES.md: -------------------------------------------------------------------------------- 1 | [unreleased] 2 | * initial pass 3 | * add generic hooks for overriding update processing for `update-core.php` 4 | * add `wp_opcache_invalidate_directory()` 5 | * add fixes for VirtualBox issues 6 | * removed unused hooks from `move_dir()` 7 | -------------------------------------------------------------------------------- /faster-updates.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | */ 9 | 10 | /** 11 | * Plugin Name: Faster Updates 12 | * Author: WP Core Contributors 13 | * Description: Speeds up plugin/theme updates by moving directories rather than recursively copying files. Only for updating from 'update-core.php'. 14 | * Version: 0.5.5 15 | * Network: true 16 | * License: MIT 17 | * Text Domain: faster-updates 18 | * Requires PHP: 5.6 19 | * Requires at least: 6.0 20 | * GitHub Plugin URI: https://github.com/afragen/faster-updates 21 | * Primary Branch: main 22 | */ 23 | 24 | namespace Faster_Updates; 25 | 26 | /* 27 | * Exit if called directly. 28 | * PHP version check and exit. 29 | */ 30 | if ( ! defined( 'WPINC' ) ) { 31 | die; 32 | } 33 | 34 | require_once __DIR__ . '/functions/move.php'; 35 | require_once __DIR__ . '/modules/plugins/class-main.php'; 36 | require_once __DIR__ . '/modules/themes/class-main.php'; 37 | 38 | new \Faster_Updates\Modules\Plugins\Main(); 39 | new \Faster_Updates\Modules\Themes\Main(); 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Andy Fragen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /testing_instructions.md: -------------------------------------------------------------------------------- 1 | # Testing Instructions 2 | 3 | 1. In `wp-config.php`, set `WP_DEBUG` and `WP_DEBUG_LOG` to true, and `WP_DEBUG_DISPLAY` to false. You can also install the WP Debugging plugin from the plugin repository to set these more easily. 😉 4 | 1. Install and activate older versions of some simple and complex plugins such as akismet jetpack mailpoet woocommerce wordpress-seo wpforms-lite. You can download older versions from the plugins repository by navigating to Development > Advanced, then scroll to the bottom and download an older version. WP-CLI users can pass a --version parameter with the version number to install and activate. You may also simply decrease the version number in the main plugin file locally. 5 | 1. Navigate to Dashboard > Updates. 6 | 1. Update one plugin. 7 | 1. Update two plugins. 8 | 1. Update the remaining plugins. 9 | 1. Check for errors in the admin screens and frontend. 10 | 1. Check for errors in `wp-content/debug.log`. 11 | 1. Check the plugin directories in `wp-content/plugins`. Ensure that the main directory, and subdirectories have files in them. 12 | 1. Report any errors you encounter, or let us know if you don't encounter any errors. 13 | 1. Feel free to do the same for a few themes. 14 | 15 | If you use VirtualBox in your development environment, commonly used in Chassis and VVV, please help. 16 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | # Faster Updates 2 | 3 | Plugin Name: Faster Updates 4 | Contributors: afragen, costdev, pbiron 5 | License: MIT 6 | Requires PHP: 5.6 7 | Requires at least: 6.0 8 | Tested up to: 6.2 9 | Stable Tag: x.x.x 10 | 11 | Speeds up plugin/theme updates by moving files rather than copying them. 12 | 13 | ## Description 14 | 15 | For testing only. Only works when updating from update-core.php page. [Testing instructions](https://github.com/afragen/faster-updates/blob/main/testing_instructions.md) 16 | 17 | Speeds up plugin/theme updates by moving files rather than copying them. Reduces the chance of running out of diskspace during updates. Lower memory usage reduces the chance of timeouts during updates. 18 | 19 | Substitution of move_dir for copy_dir adding more efficiency to the plugin/theme update process. This could improve the efficiency and performance for 99+% of users who opt-in and will likely fix #53832, #54166, and #34676. 20 | 21 | ### VirtualBox 22 | 23 | VirtualBox is being tested. If you encounter any problems while using VirtualBox please let us know. 24 | 25 | ## Changelog 26 | 27 | #### [unreleased] 28 | * initial pass 29 | * add generic hooks for overriding update processing for `update-core.php` 30 | * add `wp_opcache_invalidate_directory()` 31 | * add fixes for VirtualBox issues 32 | * removed unused hooks from `move_dir()` 33 | -------------------------------------------------------------------------------- /modules/themes/class-main.php: -------------------------------------------------------------------------------- 1 | 87 |
$to'
53 | )
54 | );
55 | }
56 |
57 | if ( trailingslashit( $from ) === trailingslashit( $to ) ) {
58 | return false;
59 | }
60 |
61 | $result = false;
62 |
63 | if ( 'direct' === $wp_filesystem->method ) {
64 | if ( $wp_filesystem->delete( $to, true ) ) {
65 | $result = @rename( $from, $to );
66 | }
67 | } else {
68 | // Non-direct filesystems use some version of rename without a fallback.
69 | $result = $wp_filesystem->move( $from, $to, $overwrite );
70 | }
71 |
72 | if ( $result ) {
73 | /*
74 | * When using an environment with shared folders,
75 | * there is a delay in updating the filesystem's cache.
76 | *
77 | * This is a known issue in environments with a VirtualBox provider.
78 | *
79 | * A 200ms delay gives time for the filesystem to update its cache,
80 | * prevents "Operation not permitted", and "No such file or directory" warnings.
81 | *
82 | * This delay is used in other projects, including Composer.
83 | * @link https://github.com/composer/composer/blob/main/src/Composer/Util/Platform.php#L228-L233
84 | */
85 | usleep( 200000 );
86 | wp_opcache_invalidate_directory( $to );
87 | }
88 |
89 | if ( ! $result ) {
90 | if ( ! $wp_filesystem->is_dir( $to ) ) {
91 | if ( ! $wp_filesystem->mkdir( $to, FS_CHMOD_DIR ) ) {
92 | return new \WP_Error( 'mkdir_failed_move_dir', __( 'Could not create directory.' ), $to );
93 | }
94 | }
95 |
96 | $result = copy_dir( $from, $to, array( basename( $to ) ) );
97 |
98 | // Clear the source directory.
99 | if ( ! is_wp_error( $result ) ) {
100 | $wp_filesystem->delete( $from, true );
101 | }
102 | }
103 |
104 | return $result;
105 | }
106 |
107 | /**
108 | * Invalidate OPcache of directory of files.
109 | *
110 | * @since 6.2.0
111 | *
112 | * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
113 | *
114 | * @param string $dir The path to invalidate.
115 | * @return void
116 | */
117 | function wp_opcache_invalidate_directory( $dir ) {
118 | global $wp_filesystem;
119 |
120 | if ( ! is_string( $dir ) || '' === trim( $dir ) ) {
121 | if ( WP_DEBUG ) {
122 | $error_message = sprintf(
123 | /* translators: %s: The function name. */
124 | __( '%s expects a non-empty string.' ),
125 | 'wp_opcache_invalidate_directory()'
126 | );
127 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
128 | trigger_error( $error_message );
129 | }
130 | return;
131 | }
132 |
133 | $dirlist = $wp_filesystem->dirlist( $dir, false, true );
134 |
135 | if ( empty( $dirlist ) ) {
136 | return;
137 | }
138 |
139 | /*
140 | * Recursively invalidate opcache of files in a directory.
141 | *
142 | * WP_Filesystem_*::dirlist() returns an array of file and directory information.
143 | *
144 | * This does not include a path to the file or directory.
145 | * To invalidate files within sub-directories, recursion is needed
146 | * to prepend an absolute path containing the sub-directory's name.
147 | *
148 | * @param array $dirlist Array of file/directory information from WP_Filesystem_Base::dirlist(),
149 | * with sub-directories represented as nested arrays.
150 | * @param string $path Absolute path to the directory.
151 | */
152 | $invalidate_directory = function( $dirlist, $path ) use ( &$invalidate_directory ) {
153 | $path = trailingslashit( $path );
154 |
155 | foreach ( $dirlist as $name => $details ) {
156 | if ( 'f' === $details['type'] ) {
157 | wp_opcache_invalidate( $path . $name, true );
158 | continue;
159 | }
160 |
161 | if ( is_array( $details['files'] ) && ! empty( $details['files'] ) ) {
162 | $invalidate_directory( $details['files'], $path . $name );
163 | }
164 | }
165 | };
166 |
167 | $invalidate_directory( $dirlist, $dir );
168 | }
169 |
--------------------------------------------------------------------------------
/modules/themes/class-upgrader.php:
--------------------------------------------------------------------------------
1 | '', // Please always pass this.
64 | 'destination' => '', // ...and this.
65 | 'clear_destination' => false,
66 | 'clear_working' => false,
67 | 'abort_if_destination_exists' => true,
68 | 'hook_extra' => array(),
69 | );
70 |
71 | $args = wp_parse_args( $args, $defaults );
72 |
73 | // These were previously extract()'d.
74 | $source = $args['source'];
75 | $destination = $args['destination'];
76 | $clear_destination = $args['clear_destination'];
77 |
78 | set_time_limit( 300 );
79 |
80 | if ( empty( $source ) || empty( $destination ) ) {
81 | return new \WP_Error( 'bad_request', $this->strings['bad_request'] );
82 | }
83 | $this->skin->feedback( 'installing_package' );
84 |
85 | /**
86 | * Filters the installation response before the installation has started.
87 | *
88 | * Returning a value that could be evaluated as a `WP_Error` will effectively
89 | * short-circuit the installation, returning that value instead.
90 | *
91 | * @since 2.8.0
92 | *
93 | * @param bool|WP_Error $response Installation response.
94 | * @param array $hook_extra Extra arguments passed to hooked filters.
95 | */
96 | $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
97 |
98 | if ( is_wp_error( $res ) ) {
99 | return $res;
100 | }
101 |
102 | // Retain the original source and destinations.
103 | $remote_source = $args['source'];
104 | $local_destination = $destination;
105 |
106 | $source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
107 | $remote_destination = $wp_filesystem->find_folder( $local_destination );
108 |
109 | // Locate which directory to copy to the new folder. This is based on the actual folder holding the files.
110 | if ( 1 === count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) {
111 | // Only one folder? Then we want its contents.
112 | $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
113 | } elseif ( 0 === count( $source_files ) ) {
114 | // There are no files?
115 | return new \WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] );
116 | } else {
117 | // It's only a single file, the upgrader will use the folder name of this file as the destination folder.
118 | // Folder name is based on zip filename.
119 | $source = trailingslashit( $args['source'] );
120 | }
121 |
122 | /**
123 | * Filters the source file location for the upgrade package.
124 | *
125 | * @since 2.8.0
126 | * @since 4.4.0 The $hook_extra parameter became available.
127 | *
128 | * @param string $source File source location.
129 | * @param string $remote_source Remote file source location.
130 | * @param WP_Upgrader $upgrader WP_Upgrader instance.
131 | * @param array $hook_extra Extra arguments passed to hooked filters.
132 | */
133 | $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
134 |
135 | if ( is_wp_error( $source ) ) {
136 | return $source;
137 | }
138 |
139 | // Has the source location changed? If so, we need a new source_files list.
140 | if ( $source !== $remote_source ) {
141 | $source_files = array_keys( $wp_filesystem->dirlist( $source ) );
142 | }
143 |
144 | /*
145 | * Protection against deleting files in any important base directories.
146 | * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
147 | * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
148 | * to copy the directory into the directory, whilst they pass the source
149 | * as the actual files to copy.
150 | */
151 | $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
152 |
153 | if ( is_array( $wp_theme_directories ) ) {
154 | $protected_directories = array_merge( $protected_directories, $wp_theme_directories );
155 | }
156 |
157 | if ( in_array( $destination, $protected_directories, true ) ) {
158 | $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
159 | $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
160 | }
161 |
162 | if ( $clear_destination ) {
163 | // We're going to clear the destination if there's something there.
164 | $this->skin->feedback( 'remove_old' );
165 |
166 | $removed = $this->clear_destination( $remote_destination );
167 |
168 | /**
169 | * Filters whether the upgrader cleared the destination.
170 | *
171 | * @since 2.8.0
172 | *
173 | * @param true|WP_Error $removed Whether the destination was cleared.
174 | * True upon success, WP_Error on failure.
175 | * @param string $local_destination The local package destination.
176 | * @param string $remote_destination The remote package destination.
177 | * @param array $hook_extra Extra arguments passed to hooked filters.
178 | */
179 | $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
180 |
181 | if ( is_wp_error( $removed ) ) {
182 | return $removed;
183 | }
184 | } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
185 | // If we're not clearing the destination folder and something exists there already, bail.
186 | // But first check to see if there are actually any files in the folder.
187 | $_files = $wp_filesystem->dirlist( $remote_destination );
188 | if ( ! empty( $_files ) ) {
189 | $wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
190 | return new \WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
191 | }
192 | }
193 |
194 | // Create destination if needed.
195 | if ( ! $wp_filesystem->exists( $remote_destination ) ) {
196 | if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
197 | return new \WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
198 | }
199 | }
200 |
201 | $result = \Faster_Updates\Functions\move_dir( $source, $remote_destination, true );
202 |
203 | // Clear the working folder?
204 | if ( $args['clear_working'] ) {
205 | $wp_filesystem->delete( $remote_source, true );
206 | }
207 |
208 | if ( is_wp_error( $result ) ) {
209 | return $result;
210 | }
211 |
212 | $destination_name = basename( str_replace( $local_destination, '', $destination ) );
213 | if ( '.' === $destination_name ) {
214 | $destination_name = '';
215 | }
216 |
217 | $this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
218 |
219 | /**
220 | * Filters the installation response after the installation has finished.
221 | *
222 | * @since 2.8.0
223 | *
224 | * @param bool $response Installation response.
225 | * @param array $hook_extra Extra arguments passed to hooked filters.
226 | * @param array $result Installation result data.
227 | */
228 | $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
229 |
230 | if ( is_wp_error( $res ) ) {
231 | $this->result = $res;
232 | return $res;
233 | }
234 |
235 | // Bombard the calling function will all the info which we've just used.
236 | return $this->result;
237 | }
238 |
239 | }
240 |
--------------------------------------------------------------------------------
/modules/plugins/class-upgrader.php:
--------------------------------------------------------------------------------
1 | '', // Please always pass this.
64 | 'destination' => '', // ...and this.
65 | 'clear_destination' => false,
66 | 'clear_working' => false,
67 | 'abort_if_destination_exists' => true,
68 | 'hook_extra' => array(),
69 | );
70 |
71 | $args = wp_parse_args( $args, $defaults );
72 |
73 | // These were previously extract()'d.
74 | $source = $args['source'];
75 | $destination = $args['destination'];
76 | $clear_destination = $args['clear_destination'];
77 |
78 | set_time_limit( 300 );
79 |
80 | if ( empty( $source ) || empty( $destination ) ) {
81 | return new \WP_Error( 'bad_request', $this->strings['bad_request'] );
82 | }
83 | $this->skin->feedback( 'installing_package' );
84 |
85 | /**
86 | * Filters the installation response before the installation has started.
87 | *
88 | * Returning a value that could be evaluated as a `WP_Error` will effectively
89 | * short-circuit the installation, returning that value instead.
90 | *
91 | * @since 2.8.0
92 | *
93 | * @param bool|WP_Error $response Installation response.
94 | * @param array $hook_extra Extra arguments passed to hooked filters.
95 | */
96 | $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
97 |
98 | if ( is_wp_error( $res ) ) {
99 | return $res;
100 | }
101 |
102 | // Retain the original source and destinations.
103 | $remote_source = $args['source'];
104 | $local_destination = $destination;
105 |
106 | $source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
107 | $remote_destination = $wp_filesystem->find_folder( $local_destination );
108 |
109 | // Locate which directory to copy to the new folder. This is based on the actual folder holding the files.
110 | if ( 1 === count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) {
111 | // Only one folder? Then we want its contents.
112 | $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
113 | } elseif ( 0 === count( $source_files ) ) {
114 | // There are no files?
115 | return new \WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] );
116 | } else {
117 | // It's only a single file, the upgrader will use the folder name of this file as the destination folder.
118 | // Folder name is based on zip filename.
119 | $source = trailingslashit( $args['source'] );
120 | }
121 |
122 | /**
123 | * Filters the source file location for the upgrade package.
124 | *
125 | * @since 2.8.0
126 | * @since 4.4.0 The $hook_extra parameter became available.
127 | *
128 | * @param string $source File source location.
129 | * @param string $remote_source Remote file source location.
130 | * @param WP_Upgrader $upgrader WP_Upgrader instance.
131 | * @param array $hook_extra Extra arguments passed to hooked filters.
132 | */
133 | $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
134 |
135 | if ( is_wp_error( $source ) ) {
136 | return $source;
137 | }
138 |
139 | // Has the source location changed? If so, we need a new source_files list.
140 | if ( $source !== $remote_source ) {
141 | $source_files = array_keys( $wp_filesystem->dirlist( $source ) );
142 | }
143 |
144 | /*
145 | * Protection against deleting files in any important base directories.
146 | * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
147 | * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
148 | * to copy the directory into the directory, whilst they pass the source
149 | * as the actual files to copy.
150 | */
151 | $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
152 |
153 | if ( is_array( $wp_theme_directories ) ) {
154 | $protected_directories = array_merge( $protected_directories, $wp_theme_directories );
155 | }
156 |
157 | if ( in_array( $destination, $protected_directories, true ) ) {
158 | $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
159 | $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
160 | }
161 |
162 | if ( $clear_destination ) {
163 | // We're going to clear the destination if there's something there.
164 | $this->skin->feedback( 'remove_old' );
165 |
166 | $removed = $this->clear_destination( $remote_destination );
167 |
168 | /**
169 | * Filters whether the upgrader cleared the destination.
170 | *
171 | * @since 2.8.0
172 | *
173 | * @param true|WP_Error $removed Whether the destination was cleared.
174 | * True upon success, WP_Error on failure.
175 | * @param string $local_destination The local package destination.
176 | * @param string $remote_destination The remote package destination.
177 | * @param array $hook_extra Extra arguments passed to hooked filters.
178 | */
179 | $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
180 |
181 | if ( is_wp_error( $removed ) ) {
182 | return $removed;
183 | }
184 | } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
185 | // If we're not clearing the destination folder and something exists there already, bail.
186 | // But first check to see if there are actually any files in the folder.
187 | $_files = $wp_filesystem->dirlist( $remote_destination );
188 | if ( ! empty( $_files ) ) {
189 | $wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
190 | return new \WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
191 | }
192 | }
193 |
194 | // Create destination if needed.
195 | if ( ! $wp_filesystem->exists( $remote_destination ) ) {
196 | if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
197 | return new \WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
198 | }
199 | }
200 |
201 | $result = \Faster_Updates\Functions\move_dir( $source, $remote_destination, true );
202 |
203 | // Clear the working folder?
204 | if ( $args['clear_working'] ) {
205 | $wp_filesystem->delete( $remote_source, true );
206 | }
207 |
208 | if ( is_wp_error( $result ) ) {
209 | return $result;
210 | }
211 |
212 | $destination_name = basename( str_replace( $local_destination, '', $destination ) );
213 | if ( '.' === $destination_name ) {
214 | $destination_name = '';
215 | }
216 |
217 | $this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
218 |
219 | /**
220 | * Filters the installation response after the installation has finished.
221 | *
222 | * @since 2.8.0
223 | *
224 | * @param bool $response Installation response.
225 | * @param array $hook_extra Extra arguments passed to hooked filters.
226 | * @param array $result Installation result data.
227 | */
228 | $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
229 |
230 | if ( is_wp_error( $res ) ) {
231 | $this->result = $res;
232 | return $res;
233 | }
234 |
235 | // Bombard the calling function will all the info which we've just used.
236 | return $this->result;
237 | }
238 |
239 | }
240 |
--------------------------------------------------------------------------------