| @@ -276,9 +276,22 @@ | ||
| 276 | 276 | * |
| 277 | 277 | * @param array{media:string,async:bool,tags:string[],urls:string[]} $run Run to merge. |
| 278 | 278 | */ |
| 279 | 279 | private static function merge_run( array $run ): ?string { |
| 280 | - $key = md5( implode( '|', array_map( static fn( $p ) => $p . ':' . (int) @filemtime( $p ), $run['urls'] ) ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- a missing file contributes 0 to the key; handled below. | |
| 280 | + // Nowhere to write means nothing to link: a <link> to a file that was | |
| 281 | + // never written unstyles the page. | |
| 282 | + if ( ! Minifier::min_dir_writable() ) { | |
| 283 | + return null; | |
| 284 | + } | |
| 285 | + // Named by content and by site. See Asset_Combiner::combined_key(). | |
| 286 | + $parts = array(); | |
| 287 | + foreach ( $run['urls'] as $path ) { | |
| 288 | + $parts[] = array( | |
| 289 | + 'path' => $path, | |
| 290 | + 'url' => self::path_to_url( $path ), | |
| 291 | + ); | |
| 292 | + } | |
| 293 | + $key = Asset_Combiner::combined_key( $parts, 'css' ); | |
| 281 | 294 | $dir = Asset_Combiner::cache_dir(); |
| 282 | 295 | $file = $dir . '/combined-' . $key . '.css'; |
| 283 | 296 | $url = Asset_Combiner::cache_url() . '/combined-' . $key . '.css'; |
| 284 | 297 | |
| @@ -324,10 +337,13 @@ | ||
| 324 | 337 | $css = Asset_Combiner::minify_css_body( $css ); |
| 325 | 338 | if ( ! is_dir( $dir ) ) { |
| 326 | 339 | wp_mkdir_p( $dir ); |
| 327 | 340 | } |
| 328 | - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_put_contents_file_put_contents -- WP_Filesystem requires admin context, unavailable on the frontend. | |
| 329 | - file_put_contents( $file, $css, LOCK_EX ); | |
| 341 | + // Atomic: a concurrent render sees the whole file or none of it. | |
| 342 | + // A failed write links nothing rather than a file that is not there. | |
| 343 | + if ( ! Asset_Manifest::write_atomic( $file, $css ) && ! file_exists( $file ) ) { | |
| 344 | + return null; | |
| 345 | + } | |
| 330 | 346 | } |
| 331 | 347 | |
| 332 | 348 | // Carry Async CSS across the merge (issue #330). Every sheet in the |
| 333 | 349 | // run was async'd — that is a condition of grouping them, enforced in |