: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
wordfence::status(1, 'error', $msg);
if (($key == 'apiKey' || $key == 'isPaid' || $key == 'other_WFNet') && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) {
if ($key == 'isPaid' || $key == 'other_WFNet') {
wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val, 'synced');
} catch (wfWAFStorageFileException $e) {
error_log($e->getMessage());
} catch (wfWAFStorageEngineMySQLiException $e) {
error_log($e->getMessage());
if (!self::$tableExists) {
if ($wpdb->query($wpdb->prepare("INSERT INTO {$table} (name, val, autoload) values (%s, %s, %s) ON DUPLICATE KEY UPDATE val = %s, autoload = %s", $key, $val, $autoload, $val, $autoload)) !== false && $autoload != self::DONT_AUTOLOAD) {
self::updateCachedOption($key, $val);
if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController') && (substr($key, 0, 4) == 'cbl_' || $key == 'blockedTime' || $key == 'disableWAFIPBlocking')) {
wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
public static function setJSON($key, $val, $autoload = self::AUTOLOAD) {
self::set($key, @json_encode($val), $autoload);
public static function setBool($key, $val, $autoload = self::AUTOLOAD) {
self::set($key, wfUtils::truthyToBoolean($val) ? 1 : 0, $autoload);
public static function setOrRemove($key, $value, $autoload = self::AUTOLOAD) {
self::set($key, $value, $autoload);
public static function get($key, $default = false, $allowCached = true, &$isDefault = false) {
if ($allowCached && self::hasCachedOption($key)) {
return self::getCachedOption($key);
if (!self::$tableExists) {
if (!($option = $wpdb->get_row($wpdb->prepare("SELECT name, val, autoload FROM {$table} WHERE name = %s", $key)))) {
if ($option->autoload != self::DONT_AUTOLOAD) {
self::updateCachedOption($key, $option->val);
public static function getInt($key, $default = 0, $allowCached = true) {
return (int) self::get($key, $default, $allowCached);
public static function getJSON($key, $default = false, $allowCached = true) {
$json = self::get($key, $default, $allowCached, $isDefault);
$decoded = @json_decode($json, true);
public static function getBool($key, $default = false, $allowCached = true) {
return wfUtils::truthyToBoolean(self::get($key, $default, $allowCached));
* Runs a test against the database to verify set_ser is working via MySQLi.
public static function testDB() {
$nonce = bin2hex(wfWAFUtils::random_bytes(32));
$payload = array('nonce' => $nonce);
$allow = wfConfig::get('allowMySQLi', true);
wfConfig::set('allowMySQLi', true);
wfConfig::set_ser('dbTest', $payload, false, wfConfig::DONT_AUTOLOAD);
$stored = wfConfig::get_ser('dbTest', false, false);
wfConfig::set('allowMySQLi', $allow);
if (is_array($stored) && isset($stored['nonce']) && hash_equals($nonce, $stored['nonce'])) {
wfConfig::delete_ser_chunked('dbTest');
private static function canCompressValue() {
if (!function_exists('gzencode') || !function_exists('gzdecode')) {
$disabled = explode(',', ini_get('disable_functions'));
if (in_array('gzencode', $disabled) || in_array('gzdecode', $disabled)) {
private static function isCompressedValue($data) {
//Based on http://www.ietf.org/rfc/rfc1952.txt
$magicBytes = substr($data, 0, 2);
if ($magicBytes !== (chr(0x1f) . chr(0x8b))) {
//Small chance of false positives here -- can check the header CRC if it turns out it's needed
private static function ser_chunked_key($key) {
return 'wordfence_chunked_' . $key . '_';
public static function get_ser($key, $default = false, $cache = true) {
if (self::hasCachedOption($key)) {
return self::getCachedOption($key);
if (!self::$tableExists) {
//Check for a chunked value first
$chunkedValueKey = self::ser_chunked_key($key);
$header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header');
$header = unserialize($header);
$count = $header['count'];
$path = tempnam(sys_get_temp_dir(), $key); //Writing to a file like this saves some of PHP's in-memory copying when just appending each chunk to a string
$fh = fopen($path, 'r+');
for ($i = 0; $i < $count; $i++) {
$chunk = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . $i);
self::getDB()->flush(); //clear cache
wordfence::status(2, 'error', sprintf(/* translators: Key in key-value store. */ __("Error reassembling value for %s", 'wordfence'), $key));
$length += strlen($chunk);
$serialized = fread($fh, $length);
if (self::canCompressValue() && self::isCompressedValue($serialized)) {
$inflated = @gzdecode($serialized);
if ($inflated !== false) {
self::updateCachedOption($key, unserialize($inflated));
return self::getCachedOption($key);
return unserialize($inflated);
self::updateCachedOption($key, unserialize($serialized));
return self::getCachedOption($key);
return unserialize($serialized);
$serialized = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $key);
self::getDB()->flush(); //clear cache
if (self::canCompressValue() && self::isCompressedValue($serialized)) {
$inflated = @gzdecode($serialized);
if ($inflated !== false) {
return unserialize($inflated);
self::updateCachedOption($key, unserialize($serialized));
return self::getCachedOption($key);
return unserialize($serialized);
public static function set_ser($key, $val, $allowCompression = false, $autoload = self::AUTOLOAD) {
* Because of the small default value for `max_allowed_packet` and `max_long_data_size`, we're stuck splitting
* large values into multiple chunks. To minimize memory use, the MySQLi driver is used directly when possible.
$useMySQLi = wfUtils::useMySQLi();
if (!self::$tableExists) {
self::delete_ser_chunked($key); //Ensure any old values for a chunked value are deleted first
if (self::canCompressValue() && $allowCompression) {
$data = gzencode(serialize($val));
$dataLength = strlen($data);
$maxAllowedPacketBytes = self::getDB()->getMaxAllowedPacketBytes();
$chunkSize = intval((($maxAllowedPacketBytes < 1024 /* MySQL minimum, probably failure to fetch it */ ? 1024 * 1024 /* MySQL default */ : $maxAllowedPacketBytes) - 50) / 1.2); //Based on max_allowed_packet + 20% for escaping and SQL
$chunkSize = $chunkSize - ($chunkSize % 2); //Ensure it's even
$chunkedValueKey = self::ser_chunked_key($key);
if ($dataLength > $chunkSize) {
while (($chunks * $chunkSize) < $dataLength) {
$dataChunk = substr($data, $chunks * $chunkSize, $chunkSize);
$chunkKey = $chunkedValueKey . $chunks;
$stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (name, val, autoload) VALUES (?, ?, 'no')");
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value chunk for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
$stmt->bind_param("sb", $chunkKey, $null);
if (!$stmt->send_long_data(1, $dataChunk)) {
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value chunk for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value chunk for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", $dataChunk), $chunkedValueKey . $chunks)) {
$errno = mysqli_errno($wpdb->dbh);
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value chunk for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $errno, $wpdb->last_error));
else if (function_exists('mysql_errno')) {
// phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
$errno = mysql_errno($wpdb->dbh);
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value chunk for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $errno, $wpdb->last_error));
if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", bin2hex(serialize(array('count' => $chunks)))), $chunkedValueKey . 'header')) {
wordfence::status(2, 'error', sprintf(
/* translators: Key in key-value store. */
__("Error writing value header for %s", 'wordfence'), $key));
$exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
$stmt = $dbh->prepare("UPDATE " . self::table() . " SET val=?, autoload=? WHERE name=?");
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
$stmt->bind_param("bss", $null, $autoload, $key);
$stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (val, name, autoload) VALUES (?, ?, ?)");
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
$stmt->bind_param("bss", $null, $key, $autoload);
if (!$stmt->send_long_data(0, $data)) {
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error writing value for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
wordfence::status(2, 'error', sprintf(
/* translators: 1. Key in key-value store. 2. MySQL error number. 3. MySQL error message. */
__('Error finishing writing value for %1$s (MySQLi error: [%2$s] %3$s)', 'wordfence'), $key, $dbh->errno, $dbh->error));
self::getDB()->queryWrite(sprintf("update " . self::table() . " set val=X'%s', autoload=%%s where name=%%s", $data), $autoload, $key);
self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', %%s)", $data), $key, $autoload);
if ($autoload != self::DONT_AUTOLOAD) {
self::updateCachedOption($key, $val);
private static function delete_ser_chunked($key) {
if (!self::$tableExists) {
self::removeCachedOption($key);
$chunkedValueKey = self::ser_chunked_key($key);
$header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header');
$header = unserialize($header);
$count = $header['count'];
for ($i = 0; $i < $count; $i++) {
self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . $i);
self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . 'header');
public static function f($key){
echo esc_attr(self::get($key));
public static function p() {
return self::get('isPaid');
public static function cbp($key){
if(self::get('isPaid') && self::get($key)){
public static function cb($key){
public static function sel($key, $val, $isDefault = false){
if((! self::get($key)) && $isDefault){ echo ' selected '; }
if(self::get($key) == $val){ echo ' selected '; }
private static function getDB(){
private static function table(){
return wfDB::networkTable('wfConfig');
public static function haveAlertEmails(){
$emails = self::getAlertEmails();
return sizeof($emails) > 0 ? true : false;
public static function alertEmailBlacklist() {
return array('3c4aa9bd643bd9bb9873014227151a85b24ab8d72fe02cc5799b0edc56eabb67', 'aa06081e3962a3c17a85a06ddf9e418ca1ba8fead3f9b7a20beaf51848a1fd75', 'a25a360bded101e25ebabe5643161ddbb6c3fa33838bbe9a123c2ec0cda8d370', '36e8407dfa80d64cfe42ede4d9d5ce2d4840a5e4781b5f8a7b3b8eacec86fcad', '50cf95aec25369583efdfeff9f0818b4b9266f10e140ea2b648e30202450c21b', '72a09e746cb90ff2646ba1f1d0c0f5ffed6b380642bbbf826d273fffa6ef673b');
public static function getAlertEmails() {
$blacklist = self::alertEmailBlacklist();
$dat = explode(',', self::get('alertEmails'));
foreach ($dat as $email) {
$email = strtolower(trim($email));
if (preg_match('/\@/', $email)) {
$hash = hash('sha256', $email);
if (!in_array($hash, $blacklist)) {
public static function getAlertLevel(){
if (self::get('alertOn_scanIssues')) {
return self::get('alertOn_severityLevel', 0);
public static function liveTrafficEnabled(&$overriden = null){
$enabled = self::get('liveTrafficEnabled');
if (WORDFENCE_DISABLE_LIVE_TRAFFIC || WF_IS_WP_ENGINE) {
if ($overriden !== null) {
public static function enableAutoUpdate(){
wfConfig::set('autoUpdate', '1');
wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
wp_schedule_event(time(), 'daily', 'wordfence_daily_autoUpdate');
public static function disableAutoUpdate(){
wfConfig::set('autoUpdate', '0');
wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
public static function createLock($name, $timeout = null) { //Our own version of WP_Upgrader::create_lock that uses our table instead
$lock_option = $name . '.lock';
$lock_result = $wpdb->query($wpdb->prepare("INSERT IGNORE INTO `$table` (`name`, `val`, `autoload`) VALUES (%s, %s, 'no')", $lock_option, time()));
$lock_result = self::get($lock_option, false, false);
if ($lock_result > (time() - $timeout)) {
self::releaseLock($name);
return self::createLock($name, $timeout);
public static function releaseLock($name) {
self::remove($name . '.lock');
public static function autoUpdate(){
require(dirname(__FILE__) . '/wfVersionSupport.php');
* @var string $wfPHPDeprecatingVersion
* @var string $wfPHPMinimumVersion
if (version_compare(PHP_VERSION, $wfPHPMinimumVersion, '<')) {