WordPress.org

Make WordPress Core


Ignore:
Timestamp:
10/01/2015 07:45:35 AM (5 years ago)
Author:
dd32
Message:

Rewrite insert_with_markers() to use flock() when available, significant cleanup of the function too.

The call to flock() is an exclusive advisory lock, which in my testing only PHP respects (apache continues to read it).
Not all filesystems support locking (remote NFS mounts for example) so this offers minimal benefit to those platforms, but offers much better protection against file corruption on systems which do support it.
The call is blocking, so a second process will wait for the first to complete before writing if supported.

See #31767

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/misc.php

    r34672 r34740  
    103103 */
    104104function insert_with_markers( $filename, $marker, $insertion ) {
    105     if (!file_exists( $filename ) || is_writeable( $filename ) ) {
    106         if (!file_exists( $filename ) ) {
    107             $markerdata = '';
    108         } else {
    109             $markerdata = explode( "\n", implode( '', file( $filename ) ) );
    110         }
    111 
    112         if ( !$f = @fopen( $filename, 'w' ) )
    113             return false;
    114 
    115         $foundit = false;
    116         if ( $markerdata ) {
    117             $state = true;
    118             foreach ( $markerdata as $n => $markerline ) {
    119                 if (strpos($markerline, '# BEGIN ' . $marker) !== false)
    120                     $state = false;
    121                 if ( $state ) {
    122                     if ( $n + 1 < count( $markerdata ) )
    123                         fwrite( $f, "{$markerline}\n" );
    124                     else
    125                         fwrite( $f, "{$markerline}" );
    126                 }
    127                 if (strpos($markerline, '# END ' . $marker) !== false) {
    128                     fwrite( $f, "# BEGIN {$marker}\n" );
    129                     if ( is_array( $insertion ))
    130                         foreach ( $insertion as $insertline )
    131                             fwrite( $f, "{$insertline}\n" );
    132                     fwrite( $f, "# END {$marker}\n" );
    133                     $state = true;
    134                     $foundit = true;
    135                 }
    136             }
    137         }
    138         if (!$foundit) {
    139             fwrite( $f, "\n# BEGIN {$marker}\n" );
    140             foreach ( $insertion as $insertline )
    141                 fwrite( $f, "{$insertline}\n" );
    142             fwrite( $f, "# END {$marker}\n" );
    143         }
    144         fclose( $f );
    145         return true;
    146     } else {
     105    if ( ! is_writeable( $filename ) ) {
    147106        return false;
    148107    }
     108
     109    if ( ! is_array( $insertion ) ) {
     110        $insertion = array( $insertion );
     111    }
     112
     113    $start_marker = "# BEGIN {$marker}";
     114    $end_marker   = "# END {$marker}";
     115
     116    $fp = fopen( $filename, 'r+' );
     117    if ( ! $fp ) {
     118        return false;
     119    }
     120
     121    // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired.
     122    flock( $fp, LOCK_EX );
     123
     124    $lines = array();
     125    while ( ! feof( $fp ) ) {
     126        $lines[] = rtrim( fgets( $fp ), "\r\n" );
     127    }
     128
     129    // Split out the existing file into the preceeding lines, and those that appear after the marker
     130    $pre_lines = $post_lines = array();
     131    $found_marker = $found_end_marker = false;
     132    foreach ( $lines as $line ) {
     133        if ( ! $found_marker && false !== strpos( $line, $start_marker ) ) {
     134            $found_marker = true;
     135            continue;
     136        } elseif ( ! $found_end_marker && false !== strpos( $line, $end_marker ) ) {
     137            $found_end_marker = true;
     138            continue;
     139        }
     140        if ( ! $found_marker ) {
     141            $pre_lines[] = $line;
     142        } elseif ( $found_marker && $found_end_marker ) {
     143            $post_lines[] = $line;
     144        }
     145    }
     146
     147    // Generate the new file data
     148    $new_file_data = implode( "\n", array_merge(
     149        $pre_lines,
     150        array( $start_marker ),
     151        $insertion,
     152        array( $end_marker ),
     153        $post_lines
     154    ) );
     155
     156    // Write to the start of the file, and truncate it to that length
     157    fseek( $fp, 0 );
     158    $bytes = fwrite( $fp, $new_file_data );
     159    if ( $bytes ) {
     160        ftruncate( $fp, $bytes );
     161    }
     162
     163    flock( $fp, LOCK_UN );
     164    fclose( $fp );
     165
     166    return (bool) $bytes;
    149167}
    150168
Note: See TracChangeset for help on using the changeset viewer.