Repository navigation
v4.8.0
Changed
- Encryption keys are resolved from a constant, an environment variable of the same name, or the WordPress salts, and the library no longer reads or writes
wp-config.php(#27). It used to grep the file for adefine()and, failing that, write one — neither works on Bedrock, Trellis, or any deploy where the file is read-only or generated, so the write silently no-opped and the site fell back toLOGGED_IN_KEY/NONCE_KEYwithout saying so. Rotating salts then made every stored credential unreadable and the plugin reported it as a rejected credential. Sites that already define their own constant are unaffected, because adefine()in an executedwp-config.phpwas always what satisfied the lookup.
Added
-
A settings table backed by its own database table can map fields to real columns (#26).
WP_Settings_Table_Custom_Table_Storagehardcoded its schema, so a consumer with a typed table could not use it, and a consumer adopting it gave up SQL on its own fields — retention by date and search by recipient both became "decode every row in PHP". Passcolumnsas a list of row keys, or arow key => column namemap where the two differ, and those fields are written to and read from their own columns.id_column,status_column,data_column,created_columnandupdated_columnrename a column or takenullto drop it. For a table the consumer already owns, pass aCREATE TABLEbody to install verbatim, or turn installation off entirely. Unmapped keys still go into the JSON column, and a non-scalar value stays there too, so the defaults are the schema it shipped with. -
A field can declare
'encrypted' => trueand the library ciphers it on save and deciphers it on render. Encryption used to be a per-call flag onWP_Setting::get()andset(), so every caller had to remember it in both directions — aset()missing the flag wrote the secret in plaintext, aget()missing it rendered base64 into the input, and neither failed. Declaring it on the field settles it once. The per-call flags still work for values that aren't fields. -
A value that cannot be decrypted says whether the encryption key changed. openssl payloads now carry a fingerprint of the key that wrote them, so
WP_Setting::try_decrypt()sets the exception code toWP_Setting::CRYPT_KEY_CHANGED, andWP_Setting::decrypt_failure_message()returns "the encryption key changed, re-enter this value" instead of a message that sends the admin to check the far end. -
WP_Setting::rewrap_encrypted()re-encrypts named settings that were written under a different key. Call it from an upgrade hook with the legacy key to move stored values onto the current one — for a plugin sunsetting its own key constant in favor of the salts, or moving between constants. Repeating the pass is a no-op, and a value that will not decrypt under the legacy key is left untouched. -
A second unscoped copy of this library on the same site is reported (#28). Two plugins that each vendor it share one class and one
WP_Setting::$text_domain, so whichever constructs last owns everyget(),set()andregister_setting()— the other plugin's fields register under the wrong prefix and its reads resolve the wrong options, silently. The library now asks the registered autoloaders which directories claim its namespace and, when there is more than one, names the path and version of each with_doing_it_wrong()onadmin_init. Theclass_exists()guards could not report this themselves: under PSR-4 the losing copy's file is never included, because PHP only calls an autoloader for a class that is not already declared.Consumer note: a plugin vendoring this library must scope it with php-scoper or Mozart. Two unscoped consumers on one site remain unsupported — the notice makes the collision visible, it cannot fix it.
Fixed
- The sodium backend works on a site whose key or nonce is not exactly the length secretbox demands. Key material shorter than 32 bytes made every sodium encrypt throw; it is now derived to the required length, leaving correctly-sized keys — and every payload written under them — untouched.