← All changes
|
modules/atomic-widgets/styles/css-files-manager.php
+26
-11
4.3.0-beta1
→
4.3.1
View file →
| @@ -119,12 +119,10 @@ | ||
| 119 | 119 | $filesystem->delete( $path ); |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | /** |
| 123 | - * Write to a temp file first and then move it into place. The move step is atomic on | |
| 124 | - * POSIX filesystems, so the public URL cannot serve a partial or zero-byte asset while a | |
| 125 | - * concurrent render is in progress. If the atomic move is not supported by the current | |
| 126 | - * filesystem adapter, fall back to a direct write. | |
| 123 | + * Write to a temp file first and then replace the destination with it, so a concurrent | |
| 124 | + * render can never see a partial, zero-byte or absent asset on the public URL. | |
| 127 | 125 | */ |
| 128 | 126 | private function write_atomically( string $filesystem_path, string $css ): bool { |
| 129 | 127 | $filesystem = $this->get_filesystem(); |
| 130 | 128 | |
| @@ -139,19 +137,36 @@ | ||
| 139 | 137 | if ( false === $is_written ) { |
| 140 | 138 | return false; |
| 141 | 139 | } |
| 142 | 140 | |
| 143 | - if ( ! method_exists( $filesystem, 'move' ) || ! $filesystem->move( $tmp_path, $filesystem_path, true ) ) { | |
| 144 | - $fallback = $filesystem->put_contents( $filesystem_path, $css, self::PERMISSIONS ); | |
| 141 | + if ( $this->replace_file( $tmp_path, $filesystem_path ) ) { | |
| 142 | + return true; | |
| 143 | + } | |
| 145 | 144 | |
| 146 | - if ( $filesystem->exists( $tmp_path ) ) { | |
| 147 | - $filesystem->delete( $tmp_path ); | |
| 148 | - } | |
| 145 | + $fallback = $filesystem->put_contents( $filesystem_path, $css, self::PERMISSIONS ); | |
| 149 | 146 | |
| 150 | - return false !== $fallback; | |
| 147 | + if ( $filesystem->exists( $tmp_path ) ) { | |
| 148 | + $filesystem->delete( $tmp_path ); | |
| 151 | 149 | } |
| 152 | 150 | |
| 153 | - return true; | |
| 151 | + return false !== $fallback; | |
| 152 | + } | |
| 153 | + | |
| 154 | + /** | |
| 155 | + * `WP_Filesystem_Direct::move()` unlinks the destination before renaming, leaving a window | |
| 156 | + * in which the already-enqueued URL 404s. A bare `rename()` replaces the file in place with | |
| 157 | + * no such window - atomically on POSIX, and via `MoveFileEx` on Windows - so it is preferred | |
| 158 | + * whenever the adapter is local. Remote adapters keep the `move()` path. | |
| 159 | + */ | |
| 160 | + private function replace_file( string $tmp_path, string $destination ): bool { | |
| 161 | + $filesystem = $this->get_filesystem(); | |
| 162 | + | |
| 163 | + if ( $filesystem instanceof \WP_Filesystem_Direct ) { | |
| 164 | + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.rename_rename | |
| 165 | + return @rename( $tmp_path, $destination ); | |
| 166 | + } | |
| 167 | + | |
| 168 | + return method_exists( $filesystem, 'move' ) && $filesystem->move( $tmp_path, $destination, true ); | |
| 154 | 169 | } |
| 155 | 170 | |
| 156 | 171 | private function ensure_directory_exists( string $directory ): bool { |
| 157 | 172 | $filesystem = $this->get_filesystem(); |