Make WordPress Core


Ignore:
Timestamp:
10/01/2015 07:45:35 AM (11 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.