Automattic / Automattic/wp-super-cache

Array settings are stored with their internal whitespace collapsed

Open
#1,096 0 comments 0 reactions 0 assignees View on GitHub
bug ready-for-agent
Dominant language
PHP
Stars
436
Forks
130
Avg merge
15h 11m
Merged PRs (30d)
10

Description

## Summary

`wp_cache_setting()`'s array branch flattens `var_export()` output with:

```php
$text = preg_replace( '/[\s]+/', ' ', var_export( $value, true ) );
```

That regex does not distinguish `var_export()`'s own layout whitespace from whitespace **inside** the exported string elements, so any run of spaces, tabs or newlines in a stored value is collapsed to a single space. The write is lossy and silent.

```
input: array( 'Mozilla Foo', "a\tb" )
written: $x = array ( 0 => 'Mozilla Foo', 1 => 'a b', );
read back: array( 'Mozilla Foo', 'a b' )
```

`wp_cache_sanitize_value()` (`inc/settings-forms.php:208-214`) uses the identical pattern and has the same behaviour.

## Why it exists

The config file is rewritten by `wp_cache_replace_line()`, which matches `^ *\$field` one physical line at a time. An entry spread across two lines loses its tail on the next write of that field, leaving an orphan that stops the file parsing — and it is included by `advanced-cache.php` before WordPress boots, so that is a fatal error. Everything written there must therefore be one line.

Collapsing all whitespace is one way to guarantee that. It just also destroys data.

## Concrete impact

`wpsc_plugins` (`inc/plugins-cookies.php:63,80`) stores plugin file paths relative to `ABSPATH`. A plugin living in a directory with a space in its name — `wp-content/plugins/My Plugin/loader.php` — is stored as `wp-content/plugins/My Plugin/loader.php` with the double space intact only if there was one; a path with two consecutive spaces, or a tab, is silently altered and no longer matches the file on disk.

`wpsc_cookies` is lower risk: cookie names cannot contain spaces per RFC 6265.

`wp_cache_mobile_groups` will start using this branch if #1095 is actioned as described, and mobile group names plausibly contain spaces.

For `wp_cache_sanitize_value()`'s five callers the collapse is mostly masked, because that function splits its input on `/[\s,]+/` first, so elements never contain whitespace by the time they are exported. Note `inc/settings-forms.php:220-224` substitutes `___` for spaces around the call and reverses it afterwards — that workaround exists to defeat the *split*, and incidentally sidesteps the collapse too.

## Suggested fix

#1092 solved the same one-line constraint for the string branch losslessly, by escaping the line breaks rather than removing them:

```php
$text = strtr( $text, array( "\r" => '\' . "\r" . \'', "\n" => '\' . "\n" . \'' ) );
```

Applying that to the whole `var_export()` output — for arrays as well as strings — satisfies the one-line rule without touching the data, because `var_export()`'s own layout newlines get escaped rather than collapsed.

Two things to check before shipping it:

- The on-disk shape of every array setting changes. Existing configs keep working (they are only ever `include`d, never parsed as text), but the "setting unchanged" short-circuit in `wp_cache_replace_line()` will miss once per affected setting, causing one extra rewrite as the format settles.
- `SettingsFormUpdatersTest::test_wp_cache_sanitize_value_splits_and_escapes` pins the current collapsed output exactly and will need updating.

That test lives in the integration tier, so this needs `make test-integration`.

Suggested labels: `bug`, `ready-for-agent`.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the array handling in inc/settings-forms.php, including wp_cache_sanitize_value(), and review the related one-line rewrite behavior described for wp_cache_replace_line(). Run the named integration test, SettingsFormUpdatersTest::test_wp_cache_sanitize_value_splits_and_escapes, before changing expectations. Done means array settings preserve internal whitespace, remain valid as one-line configuration entries, and the integration test passes with updated output.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.