Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions tests/php/smoke/WpscCacheSettingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,66 @@ public function test_value_round_trips( string $value ): void {
$this->assertSame( $value, $this->read_back() );
}

/** Array values must preserve whitespace inside every string element. */
public function test_array_value_round_trips_internal_whitespace(): void {
$value = array(
'double space' => 'Mozilla Foo',
'tab' => "a\tb",
'newlines' => "one\ntwo\r\nthree",
'trailing backslash' => 'C:\\cache\\',
'escape sequence' => '\' . "\n" . \'',
'nested' => array( 'mixed' => "left\t right" ),
);

wp_cache_setting( self::FIELD, $value );

$this->assertSame( $value, $this->read_back() );
}

/** Array values containing newlines must still occupy one physical line. */
public function test_array_value_with_newlines_is_written_on_one_line(): void {
$before = count( file( $GLOBALS['wp_cache_config_file'] ) );
$value = array( 'line endings' => "one\ntwo\r\nthree" );

wp_cache_setting( self::FIELD, $value );

$this->assertCount(
$before,
file( $GLOBALS['wp_cache_config_file'] ),
'The array value was written across more than one physical line.'
);
$this->assertSame( $value, $this->read_back() );
}

/** Rewriting an array containing newlines must leave the file parseable. */
public function test_overwriting_array_with_newlines_leaves_the_file_parseable(): void {
wp_cache_setting( self::FIELD, array( 'multiline' => "one\ntwo" ) );
wp_cache_setting( self::FIELD, array( 'plain' => 'value' ) );

$this->assertSame( array( 'plain' => 'value' ), $this->read_back() );
}

/** Array elements cannot end their string literal and inject a statement. */
public function test_array_value_cannot_smuggle_a_statement(): void {
$value = array( "/var/www/cache/';\$GLOBALS['wpsc_config_side_effect'] = true;" );

wp_cache_setting( self::FIELD, $value );
$written = $this->read_back();

$this->assertArrayNotHasKey( 'wpsc_config_side_effect', $GLOBALS );
$this->assertSame( $value, $written );
}

/** Ordinary arrays keep their existing compact source representation. */
public function test_ordinary_array_value_is_written_unchanged(): void {
wp_cache_setting( self::FIELD, array( 'one', 'two' ) );

$this->assertStringContainsString(
'$' . self::FIELD . " = array ( 0 => 'one', 1 => 'two', );",
file_get_contents( $GLOBALS['wp_cache_config_file'] )
);
}

/**
* The entry must occupy exactly one physical line whatever the value holds.
*
Expand Down
50 changes: 48 additions & 2 deletions wp-cache-phase2.php
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,53 @@ function is_writeable_ACLSafe( $path ) {
return true;
}

/**
* Export a value as PHP source that occupies one physical line.
*
* @param mixed $value Value to export.
* @return string Exported PHP source.
*/
function wpsc_var_export_one_line( $value ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export -- Generating PHP source for the config file, which is what var_export() is for.
$exported = var_export( $value, true );
$one_line = '';
$in_string = false;
$escaped = false;
$length = strlen( $exported );

for ( $i = 0; $i < $length; ++$i ) {
$character = $exported[ $i ];

if ( ! $in_string && ( ' ' === $character || "\t" === $character || "\r" === $character || "\n" === $character ) ) {
if ( '' !== $one_line && ' ' !== $one_line[ strlen( $one_line ) - 1 ] ) {
$one_line .= ' ';
}
continue;
}

if ( "\r" === $character || "\n" === $character ) {
$one_line .= "\r" === $character ? '\' . "\r" . \'' : '\' . "\n" . \'';
$escaped = false;
continue;
}

$one_line .= $character;

if ( ! $in_string ) {
$in_string = "'" === $character;
continue;
}

if ( "'" === $character && ! $escaped ) {
$in_string = false;
}

$escaped = '\\' === $character ? ! $escaped : false;
}

return $one_line;
}

function wp_cache_setting( $field, $value ) {
global $wp_cache_config_file;

Expand All @@ -1683,8 +1730,7 @@ function wp_cache_setting( $field, $value ) {
$output_value = $value === true ? 'true' : 'false';
return wp_cache_replace_line( '^ *\$' . $field, "\$$field = $output_value;", $wp_cache_config_file );
} elseif ( is_object( $value ) || is_array( $value ) ) {
$text = var_export( $value, true );
$text = preg_replace( '/[\s]+/', ' ', $text );
$text = wpsc_var_export_one_line( $value );
return wp_cache_replace_line( '^ *\$' . $field, "\$$field = $text;", $wp_cache_config_file );
} else {
/*
Expand Down