: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$timezone = new DateTimeZone($tz);
$dtStr = gmdate("c", (int) $timestamp); //Have to do it this way because of PHP 5.2
$dt = new DateTime($dtStr, $timezone);
return (int) ($timezone->getOffset($dt) / 60);
$gmt = get_option('gmt_offset');
return (int) ($gmt * 60);
* Formats and returns the given timestamp using the time zone set for the WordPress installation.
* @param string $format See the PHP docs on DateTime for the format options.
* @param int|bool $timestamp Assumed to be in UTC. If false, defaults to the current timestamp.
public static function formatLocalTime($format, $timestamp = false) {
if ($timestamp === false) {
$utc = new DateTimeZone('UTC');
$dtStr = gmdate("c", (int) $timestamp); //Have to do it this way because of PHP 5.2
$dt = new DateTime($dtStr, $utc);
$tz = get_option('timezone_string');
$dt->setTimezone(new DateTimeZone($tz));
$gmt = get_option('gmt_offset');
if (PHP_VERSION_ID < 50510) {
$dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10
$dt = new DateTime($dtStr, $utc);
$direction = ($gmt > 0 ? '+' : '-');
$dt->setTimezone(new DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT)));
return $dt->format($format);
* Parses the given time string and returns its DateTime with the server's configured time zone.
* @param string $timestring
public static function parseLocalTime($timestring) {
$utc = new DateTimeZone('UTC');
$tz = get_option('timezone_string');
$tz = new DateTimeZone($tz);
return new DateTime($timestring, $tz);
$gmt = get_option('gmt_offset');
if (PHP_VERSION_ID < 50510) {
$timestamp = strtotime($timestring);
$dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10
return new DateTime($dtStr, $utc);
$direction = ($gmt > 0 ? '+' : '-');
$tz = new DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT));
return new DateTime($timestring, $tz);
return new DateTime($timestring);
* Base64URL-encodes the given payload. This is identical to base64_encode except it substitutes characters
* not safe for use in URLs.
public static function base64url_encode($payload) {
$intermediate = base64_encode($payload);
$intermediate = rtrim($intermediate, '=');
$intermediate = str_replace('+', '-', $intermediate);
$intermediate = str_replace('/', '_', $intermediate);
* Base64URL-decodes the given payload. This is identical to base64_encode except it allows for the characters
* substituted by base64url_encode.
public static function base64url_decode($payload) {
$intermediate = str_replace('_', '/', $payload);
$intermediate = str_replace('-', '+', $intermediate);
$intermediate = base64_decode($intermediate);
* Returns a signed JWT for the given payload. Payload is expected to be an array suitable for JSON-encoding.
* @param int $maxAge How long the JWT will be considered valid.
public static function generateJWT($payload, $maxAge = 604800 /* 7 days */) {
$payload['_exp'] = time() + $maxAge;
$key = wfConfig::get('longEncKey');
$header = '{"alg":"HS256","typ":"JWT"}';
$body = self::base64url_encode($header) . '.' . self::base64url_encode(json_encode($payload));
$signature = hash_hmac('sha256', $body, $key, true);
return $body . '.' . self::base64url_encode($signature);
* Decodes and returns the payload of a JWT. This also validates the signature.
* @return array|bool The decoded payload or false if the token is invalid or fails validation.
public static function decodeJWT($token) {
$components = explode('.', $token);
if (count($components) != 3) {
$key = wfConfig::get('longEncKey');
$body = $components[0] . '.' . $components[1];
$signature = hash_hmac('sha256', $body, $key, true);
$testSignature = self::base64url_decode($components[2]);
if (!hash_equals($signature, $testSignature)) {
$json = self::base64url_decode($components[1]);
$payload = @json_decode($json, true);
if (isset($payload['_exp']) && $payload['_exp'] < time()) {
* Split a path into its components
public static function splitPath($path) {
return preg_split('/[\\/\\\\]/', $path, -1, PREG_SPLIT_NO_EMPTY);
* Convert an absolute path to a path relative to $to
* @param string $absolute the absolute path to convert
* @param string $to the absolute path from which to derive the relative path
* @param bool $leadingSlash if true, prepend the resultant URL with a slash
public static function relativePath($absolute, $to, $leadingSlash = false) {
$trailingSlash = in_array(substr($absolute, -1), array('/', '\\'));
$absoluteComponents = self::splitPath($absolute);
$toComponents = self::splitPath($to);
$relativeComponents = array();
$currentAbsolute = array_shift($absoluteComponents);
$currentTo = array_shift($toComponents);
} while($currentAbsolute === $currentTo && $currentAbsolute !== null);
while ($currentTo !== null) {
array_push($relativeComponents, '..');
$currentTo = array_shift($toComponents);
while ($currentAbsolute !== null) {
array_push($relativeComponents, $currentAbsolute);
$currentAbsolute = array_shift($absoluteComponents);
$leadingSlash ? '/' : '',
implode('/', $relativeComponents),
($trailingSlash && (count($relativeComponents) > 0 || !$leadingSlash)) ? '/' : ''
* Determine the effective port given the output of parse_url
* @param array $urlComponents
* @return int the resolved port number
private static function resolvePort($urlComponents) {
if (array_key_exists('port', $urlComponents) && !empty($urlComponents['port'])) {
return $urlComponents['port'];
if (array_key_exists('scheme', $urlComponents) && $urlComponents['scheme'] === 'https') {
* Check if two site URLs identify the same site
* @param string $a first url
* @param string $b second url
* @param array $ignoredSubdomains An array of subdomains to ignore when matching (e.g., www)
* @return bool true if the URLs match, false otherwise
public static function compareSiteUrls($a, $b, $ignoredSubdomains = array()) {
$patterns = array_map(function($p) { return '/^' . preg_quote($p, '/') . '\\./i'; }, $ignoredSubdomains);
$componentsA = parse_url($a);
if (isset($componentsA['host'])) { $componentsA['host'] = preg_replace($patterns, '', $componentsA['host']); }
$componentsB = parse_url($b);
if (isset($componentsB['host'])) { $componentsB['host'] = preg_replace($patterns, '', $componentsB['host']); }
foreach (array('host', 'port', 'path') as $component) {
$valueA = array_key_exists($component, $componentsA) ? $componentsA[$component] : null;
$valueB = array_key_exists($component, $componentsB) ? $componentsB[$component] : null;
if ($valueA !== $valueB) {
if ($component === 'port') {
$portA = self::resolvePort($componentsA);
$portB = self::resolvePort($componentsB);
public static function getHomePath() {
if (!function_exists('get_home_path')) {
include_once(ABSPATH . 'wp-admin/includes/file.php');
return trailingslashit($_SERVER['DOCUMENT_ROOT']);
public static function includeOnceIfPresent($path) {
if (file_exists($path)) {
return @include_once($path); //Calling `include_once` for an already included file will return true
public static function isCurlSupported() {
if (self::includeOnceIfPresent(ABSPATH . 'wp-includes/class-wp-http-curl.php'))
return WP_Http_Curl::test();
private static function isValidJsonValue($value) {
return json_encode($value) !== false;
private static function filterInvalidJsonValues($data, &$modified, &$valid = null) {
foreach ($data as $key => $value) {
$value = self::filterInvalidJsonValues($value, $itemModified, $itemValid);
if (($itemValid || $itemModified) && self::isValidJsonValue(array($key => $value))) {
$filtered[$key] = $value;
$modified[$key] = $itemModified;
$valid = self::isValidJsonValue($data);
else if (is_string($data)) {
return base64_encode($data);
public static function jsonEncodeSafely($data) {
$encoded = json_encode($data);
if ($encoded === false) {
$data = self::filterInvalidJsonValues($data, $modified);
$data['__modified__'] = $modified;
$encoded = json_encode($data);
* Convenience function to extract a matched pattern from a string. If $pattern has no matching groups, the entire
* matched portion is returned. If it has at least one matching group, the first one is returned (others are
* ignored). If there is no match, false is returned.
* @param bool $expandToLine Whether or not to expand the captured value to include the entire line's contents
public static function pregExtract($pattern, $subject, $expandToLine = false) {
if (preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE)) {
if (count($matches) > 1) {
$end = $start + strlen($text);
$end = $start + strlen($text);
if (preg_match_all('/[\r\n]/', substr($subject, 0, $start), $matches, PREG_OFFSET_CAPTURE)) {
$start = $matches[0][count($matches[0]) - 1][1] + 1;
if (preg_match('/[\r\n]/', $subject, $matches, PREG_OFFSET_CAPTURE, $end)) {
$end = strlen($subject) - 0;
$text = substr($subject, $start, $end - $start);
* Returns whether or not MySQLi should be used directly when needed. Returns true if there's a valid DB handle,
* our database test succeeded, our constant is not set to prevent it, and then either $wpdb indicates it's using
* mysqli (older WordPress versions) or we're on PHP 7+ (only mysqli is ever used).
public static function useMySQLi() {
$useMySQLi = (is_object($dbh) && (PHP_MAJOR_VERSION >= 7 || $wpdb->use_mysqli) && wfConfig::get('allowMySQLi', true) && WORDFENCE_ALLOW_DIRECT_MYSQLI);
// GeoIP lib uses these as well
if (!function_exists('inet_ntop')) {
function inet_ntop($ip) {
return wfUtils::_inet_ntop($ip);
if (!function_exists('inet_pton')) {
function inet_pton($ip) {
return wfUtils::_inet_pton($ip);
public static function createFromEnvironment() {
$serverInfo->setSoftware(self::NGINX);
$serverInfo->setSoftwareName('Flywheel');
else if (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false) {
$serverInfo->setSoftware(self::IIS);
$serverInfo->setSoftwareName('iis');
else if (strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
$serverInfo->setSoftware(self::NGINX);
$serverInfo->setSoftwareName('nginx');
else if (stripos($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false || $sapi == 'litespeed') {
$serverInfo->setSoftware(self::LITESPEED);
$serverInfo->setSoftwareName('litespeed');
else if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) {
$serverInfo->setSoftware(self::APACHE);
$serverInfo->setSoftwareName('apache');
$serverInfo->setHandler($sapi);
public function isApache() {
return $this->getSoftware() === self::APACHE;
public function isNGINX() {
return $this->getSoftware() === self::NGINX;
public function isLiteSpeed() {
return $this->getSoftware() === self::LITESPEED;
public function isIIS() {
return $this->getSoftware() === self::IIS;
public function isApacheModPHP() {
return $this->isApache() && function_exists('apache_get_modules');
* Not sure if this can be implemented at the PHP level.
public function isApacheSuPHP() {
return $this->isApache() && $this->isCGI() &&
function_exists('posix_getuid') &&
getmyuid() === posix_getuid();
public function isCGI() {
return !$this->isFastCGI() && stripos($this->getHandler(), 'cgi') !== false;
public function isFastCGI() {
return stripos($this->getHandler(), 'fastcgi') !== false || stripos($this->getHandler(), 'fpm-fcgi') !== false;
public function getHandler() {