diff --git a/src/Control/CLIRequestBuilder.php b/src/Control/CLIRequestBuilder.php index a5ec492e9ec..a51bb08ee35 100644 --- a/src/Control/CLIRequestBuilder.php +++ b/src/Control/CLIRequestBuilder.php @@ -48,11 +48,11 @@ public static function cleanEnvironment(array $variables) if (isset($variables['_SERVER']['argv'][2])) { $args = array_slice($variables['_SERVER']['argv'], 2); foreach ($args as $arg) { - if (strpos($arg, '=') == false) { + if (strpos((string) $arg, '=') == false) { $variables['_GET']['args'][] = $arg; } else { $newItems = []; - parse_str((substr($arg, 0, 2) == '--') ? substr($arg, 2) : $arg, $newItems); + parse_str((substr((string) $arg, 0, 2) == '--') ? substr($arg, 2) : $arg, $newItems); $variables['_GET'] = array_merge($variables['_GET'], $newItems); } } @@ -79,7 +79,7 @@ public static function createFromVariables(array $variables, $input, $url = null { $request = parent::createFromVariables($variables, $input, $url); // unset scheme so that SS_BASE_URL can provide `is_https` information if required - $scheme = parse_url(Environment::getEnv('SS_BASE_URL'), PHP_URL_SCHEME); + $scheme = parse_url((string) Environment::getEnv('SS_BASE_URL'), PHP_URL_SCHEME); if ($scheme) { $request->setScheme($scheme); } diff --git a/src/Control/ContentNegotiator.php b/src/Control/ContentNegotiator.php index fa8c8ff5a51..bf51514db07 100644 --- a/src/Control/ContentNegotiator.php +++ b/src/Control/ContentNegotiator.php @@ -79,8 +79,8 @@ public static function enabled_for($response) // Disable content negotiation for other content types if ($contentType - && substr($contentType, 0, 9) != 'text/html' - && substr($contentType, 0, 21) != 'application/xhtml+xml' + && substr((string) $contentType, 0, 9) != 'text/html' + && substr((string) $contentType, 0, 21) != 'application/xhtml+xml' ) { return false; } @@ -88,7 +88,7 @@ public static function enabled_for($response) if (ContentNegotiator::getEnabled()) { return true; } else { - return (substr($response->getBody(), 0, 5) == '<' . '?xml'); + return (substr((string) $response->getBody(), 0, 5) == '<' . '?xml'); } } @@ -140,8 +140,8 @@ public static function process(HTTPResponse $response) $chosenFormat = "xhtml"; } else { foreach ($mimes as $format => $mime) { - $regExp = '/' . str_replace(['+', '/'], ['\+', '\/'], $mime) . '(;q=(\d+\.\d+))?/i'; - if (isset($_SERVER['HTTP_ACCEPT']) && preg_match($regExp, $_SERVER['HTTP_ACCEPT'], $matches)) { + $regExp = '/' . str_replace(['+', '/'], ['\+', '\/'], $mime ?: '') . '(;q=(\d+\.\d+))?/i'; + if (isset($_SERVER['HTTP_ACCEPT']) && preg_match((string) $regExp, $_SERVER['HTTP_ACCEPT'], $matches)) { $preference = isset($matches[2]) ? $matches[2] : 1; if (!isset($q[$preference])) { $q[$preference] = $format; @@ -189,17 +189,17 @@ public function xhtml(HTTPResponse $response) $content = preg_replace( '//', '', - $content + $content ?: '' ); - $content = str_replace(' ', ' ', $content); - $content = str_replace('
', '
', $content); - $content = str_replace('
', '
', $content); - $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content); - $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content); - $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content); - $content = preg_replace("#(\]*[\s]+selected)(?!\s*\=)#si", "$1=\"selected\"$2", $content); - $content = preg_replace("#(\]*[\s]+checked)(?!\s*\=)#si", "$1=\"checked\"$2", $content); + $content = str_replace(' ', ' ', $content ?: ''); + $content = str_replace('
', '
', $content ?: ''); + $content = str_replace('
', '
', $content ?: ''); + $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content ?: ''); + $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content ?: ''); + $content = preg_replace('#(]*[^/>])>#i', '\\1/>', $content ?: ''); + $content = preg_replace("#(\]*[\s]+selected)(?!\s*\=)#si", "$1=\"selected\"$2", $content ?: ''); + $content = preg_replace("#(\]*[\s]+checked)(?!\s*\=)#si", "$1=\"checked\"$2", $content ?: ''); $response->setBody($content); } @@ -226,20 +226,20 @@ public function html(HTTPResponse $response) $response->addHeader("Vary", "Accept"); $content = $response->getBody(); - $hasXMLHeader = (substr($content, 0, 5) == '<' . '?xml'); + $hasXMLHeader = (substr((string) $content, 0, 5) == '<' . '?xml'); // Fix base tag $content = preg_replace( '//', '', - $content + $content ?: '' ); - $content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content); + $content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content ?: ''); $content = str_replace( ['/>', 'xml:lang', 'application/xhtml+xml'], ['>', 'lang', 'text/html'], - $content + $content ?: '' ); // Only replace the doctype in templates with the xml header @@ -247,10 +247,10 @@ public function html(HTTPResponse $response) $content = preg_replace( '/]+>/', '', - $content + $content ?: '' ); } - $content = preg_replace('/setBody($content); } diff --git a/src/Control/Controller.php b/src/Control/Controller.php index 09e8e7e1a80..583157bcb77 100644 --- a/src/Control/Controller.php +++ b/src/Control/Controller.php @@ -407,11 +407,11 @@ public function getViewer($action) while ($parentClass !== parent::class) { // _action templates have higher priority if ($action && $action != 'index') { - $actionTemplates[] = strtok($parentClass, '_') . '_' . $action; + $actionTemplates[] = strtok((string) $parentClass, '_') . '_' . $action; } // class templates have lower priority - $classTemplates[] = strtok($parentClass, '_'); - $parentClass = get_parent_class($parentClass); + $classTemplates[] = strtok((string) $parentClass, '_'); + $parentClass = get_parent_class($parentClass ?: ''); } // Add controller templates for inheritance chain @@ -447,8 +447,8 @@ public function removeAction($fullURL, $action = null) } $returnURL = $fullURL; - if (($pos = strpos($fullURL, $action)) !== false) { - $returnURL = substr($fullURL, 0, $pos); + if (($pos = strpos((string) $fullURL, (string) $action)) !== false) { + $returnURL = substr((string) $fullURL, 0, $pos); } return $returnURL; @@ -471,12 +471,12 @@ protected function definingClassForAction($action) $class = static::class; while ($class != 'SilverStripe\\Control\\RequestHandler') { - $templateName = strtok($class, '_') . '_' . $action; + $templateName = strtok((string) $class, '_') . '_' . $action; if (SSViewer::hasTemplate($templateName)) { return $class; } - $class = get_parent_class($class); + $class = get_parent_class($class ?: ''); } return null; @@ -500,8 +500,8 @@ public function hasActionTemplate($action) $templates = []; while ($parentClass != __CLASS__) { - $templates[] = strtok($parentClass, '_') . '_' . $action; - $parentClass = get_parent_class($parentClass); + $templates[] = strtok((string) $parentClass, '_') . '_' . $action; + $parentClass = get_parent_class($parentClass ?: ''); } return SSViewer::hasTemplate($templates); @@ -585,7 +585,7 @@ public function can($perm, $member = null) $member = Security::getCurrentUser(); } if (is_array($perm)) { - $perm = array_map([$this, 'can'], $perm, array_fill(0, count($perm), $member)); + $perm = array_map([$this, 'can'], $perm ?: [], array_fill(0, count($perm ?: []), $member)); return min($perm); } if ($this->hasMethod($methodName = 'can' . $perm)) { @@ -679,27 +679,27 @@ public static function join_links($arg = null) foreach ($args as $arg) { // Find fragment identifier - keep the last one - if (strpos($arg, '#') !== false) { - list($arg, $fragmentIdentifier) = explode('#', $arg, 2); + if (strpos((string) $arg, '#') !== false) { + list($arg, $fragmentIdentifier) = explode('#', (string) $arg, 2); } // Find querystrings - if (strpos($arg, '?') !== false) { - list($arg, $suffix) = explode('?', $arg, 2); - parse_str($suffix, $localargs); + if (strpos((string) $arg, '?') !== false) { + list($arg, $suffix) = explode('?', (string) $arg, 2); + parse_str((string) $suffix, $localargs); $queryargs = array_merge($queryargs, $localargs); } if ((is_string($arg) && $arg) || is_numeric($arg)) { $arg = (string) $arg; - if ($result && substr($result, -1) != '/' && $arg[0] != '/') { + if ($result && substr((string) $result, -1) != '/' && $arg[0] != '/') { $result .= "/$arg"; } else { - $result .= (substr($result, -1) == '/' && $arg[0] == '/') ? ltrim($arg, '/') : $arg; + $result .= (substr((string) $result, -1) == '/' && $arg[0] == '/') ? ltrim($arg, '/') : $arg; } } } if ($queryargs) { - $result .= '?' . http_build_query($queryargs); + $result .= '?' . http_build_query($queryargs ?: []); } if ($fragmentIdentifier) { diff --git a/src/Control/CookieJar.php b/src/Control/CookieJar.php index 0ebaa350fc4..ace6cdc7e10 100644 --- a/src/Control/CookieJar.php +++ b/src/Control/CookieJar.php @@ -113,7 +113,7 @@ public function get($name, $includeUnsent = true) } //Normalise cookie names by replacing '.' with '_' - $safeName = str_replace('.', '_', $name); + $safeName = str_replace('.', '_', $name ?: ''); if (isset($cookies[$safeName])) { return $cookies[$safeName]; } @@ -170,7 +170,7 @@ protected function outputCookie( ) { // if headers aren't sent, we can set the cookie if (!headers_sent($file, $line)) { - return setcookie($name, $value, $expiry, $path, $domain, $secure, $httpOnly); + return setcookie((string) $name, (string) $value, $expiry ?: 0, (string) $path, (string) $domain, (bool) $secure, (bool) $httpOnly); } if (Cookie::config()->uninherited('report_errors')) { diff --git a/src/Control/Director.php b/src/Control/Director.php index bbb3815d460..a82b6fa66d3 100644 --- a/src/Control/Director.php +++ b/src/Control/Director.php @@ -214,7 +214,7 @@ public static function mockRequest( $session->set($key, $value); } // Unset removed keys - foreach (array_diff_key($sessionArray, $_SESSION) as $key => $value) { + foreach (array_diff_key($sessionArray ?: [], $_SESSION) as $key => $value) { $session->clear($key); } } @@ -239,7 +239,7 @@ public static function mockRequest( }; // Strip any hash - $url = strtok($url, '#'); + $url = strtok((string) $url, '#'); // Handle absolute URLs // If a port is mentioned in the absolute URL, be sure to add that into the HTTP host @@ -251,13 +251,13 @@ public static function mockRequest( // Ensure URL is properly made relative. // Example: url passed is "/ss31/my-page" (prefixed with BASE_URL), this should be changed to "my-page" $url = self::makeRelative($url); - if (strpos($url, '?') !== false) { - list($url, $getVarsEncoded) = explode('?', $url, 2); - parse_str($getVarsEncoded, $newVars['_GET']); + if (strpos((string) $url, '?') !== false) { + list($url, $getVarsEncoded) = explode('?', (string) $url, 2); + parse_str((string) $getVarsEncoded, $newVars['_GET']); } else { $newVars['_GET'] = []; } - $newVars['_SERVER']['REQUEST_URI'] = Director::baseURL() . ltrim($url, '/'); + $newVars['_SERVER']['REQUEST_URI'] = Director::baseURL() . ltrim((string) $url, '/'); $newVars['_REQUEST'] = array_merge($newVars['_GET'], $newVars['_POST']); // Normalise vars @@ -325,7 +325,7 @@ public function handleRequest(HTTPRequest $request) // Normalise route rule if (is_string($controllerOptions)) { - if (substr($controllerOptions, 0, 2) == '->') { + if (substr((string) $controllerOptions, 0, 2) == '->') { $controllerOptions = ['Redirect' => substr($controllerOptions, 2)]; } else { $controllerOptions = ['Controller' => $controllerOptions]; @@ -446,14 +446,14 @@ public static function absoluteURL($url, $relativeParent = self::BASE) } // Check if there is already a protocol given - if (preg_match('/^http(s?):\/\//', $url)) { + if (preg_match('/^http(s?):\/\//', (string) $url)) { return $url; } // Absolute urls without protocol are added // E.g. //google.com -> http://google.com - if (strpos($url, '//') === 0) { - return self::protocol() . substr($url, 2); + if (strpos((string) $url, '//') === 0) { + return self::protocol() . substr((string) $url, 2); } // Determine method for mapping the parent to this relative url @@ -488,13 +488,13 @@ public static function absoluteURL($url, $relativeParent = self::BASE) protected static function parseHost($url) { // Get base hostname - $host = parse_url($url, PHP_URL_HOST); + $host = parse_url((string) $url, PHP_URL_HOST); if (!$host) { return null; } // Include port - $port = parse_url($url, PHP_URL_PORT); + $port = parse_url((string) $url, PHP_URL_PORT); if ($port) { $host .= ':' . $port; } @@ -510,7 +510,7 @@ protected static function parseHost($url) */ protected static function validateUserAndPass($url) { - $parsedURL = parse_url($url); + $parsedURL = parse_url((string) $url); // Validate user (disallow slashes) if (!empty($parsedURL['user']) && strstr($parsedURL['user'], '\\')) { @@ -581,7 +581,7 @@ public static function host(HTTPRequest $request = null) public static function port(HTTPRequest $request = null) { $host = static::host($request); - return (int)parse_url($host, PHP_URL_PORT) ?: null; + return (int)parse_url((string) $host, PHP_URL_PORT) ?: null; } /** @@ -593,7 +593,7 @@ public static function port(HTTPRequest $request = null) public static function hostName(HTTPRequest $request = null) { $host = static::host($request); - return parse_url($host, PHP_URL_HOST) ?: null; + return parse_url((string) $host, PHP_URL_HOST) ?: null; } /** @@ -630,7 +630,7 @@ public static function is_https(HTTPRequest $request = null) // Check override from alternate_base_url if ($baseURL = self::config()->uninherited('alternate_base_url')) { $baseURL = Injector::inst()->convertServiceProperty($baseURL); - $protocol = parse_url($baseURL, PHP_URL_SCHEME); + $protocol = parse_url((string) $baseURL, PHP_URL_SCHEME); if ($protocol) { return $protocol === 'https'; } @@ -645,7 +645,7 @@ public static function is_https(HTTPRequest $request = null) // Check default_base_url if ($baseURL = self::config()->uninherited('default_base_url')) { $baseURL = Injector::inst()->convertServiceProperty($baseURL); - $protocol = parse_url($baseURL, PHP_URL_SCHEME); + $protocol = parse_url((string) $baseURL, PHP_URL_SCHEME); if ($protocol) { return $protocol === 'https'; } @@ -665,7 +665,7 @@ public static function baseURL() $alternate = self::config()->get('alternate_base_url'); if ($alternate) { $alternate = Injector::inst()->convertServiceProperty($alternate); - return rtrim(parse_url($alternate, PHP_URL_PATH), '/') . '/'; + return rtrim((string) parse_url((string) $alternate, PHP_URL_PATH), '/') . '/'; } // Get env base url @@ -738,24 +738,24 @@ public static function publicFolder() public static function makeRelative($url) { // Allow for the accidental inclusion whitespace and // in the URL - $url = preg_replace('#([^:])//#', '\\1/', trim($url)); + $url = preg_replace('#([^:])//#', '\\1/', trim((string) $url)); // If using a real url, remove protocol / hostname / auth / port - if (preg_match('#^(?https?:)?//(?[^/]*)(?(/.*)?)$#i', $url, $matches)) { + if (preg_match('#^(?https?:)?//(?[^/]*)(?(/.*)?)$#i', (string) $url, $matches)) { $url = $matches['url']; } // Empty case - if (trim($url, '\\/') === '') { + if (trim((string) $url, '\\/') === '') { return ''; } // Remove base folder or url foreach ([self::publicFolder(), self::baseFolder(), self::baseURL()] as $base) { // Ensure single / doesn't break comparison (unless it would make base empty) - $base = rtrim($base, '\\/') ?: $base; - if (stripos($url, $base) === 0) { - return ltrim(substr($url, strlen($base)), '\\/'); + $base = rtrim((string) $base, '\\/') ?: $base; + if (stripos((string) $url, (string) $base) === 0) { + return ltrim(substr((string) $url, strlen((string) $base)), '\\/'); } } @@ -778,7 +778,7 @@ public static function is_absolute($path) if ($path[0] == '/' || $path[0] == '\\') { return true; } - return preg_match('/^[a-zA-Z]:[\\\\\/]/', $path) == 1; + return preg_match('/^[a-zA-Z]:[\\\\\/]/', (string) $path) == 1; } /** @@ -791,7 +791,7 @@ public static function is_absolute($path) */ public static function is_root_relative_url($url) { - return strpos($url, '/') === 0 && strpos($url, '//') !== 0; + return strpos((string) $url, '/') === 0 && strpos((string) $url, '//') !== 0; } /** @@ -811,21 +811,21 @@ public static function is_root_relative_url($url) public static function is_absolute_url($url) { // Strip off the query and fragment parts of the URL before checking - if (($queryPosition = strpos($url, '?')) !== false) { - $url = substr($url, 0, $queryPosition - 1); + if (($queryPosition = strpos((string) $url, '?')) !== false) { + $url = substr((string) $url, 0, $queryPosition - 1); } - if (($hashPosition = strpos($url, '#')) !== false) { - $url = substr($url, 0, $hashPosition - 1); + if (($hashPosition = strpos((string) $url, '#')) !== false) { + $url = substr((string) $url, 0, $hashPosition - 1); } - $colonPosition = strpos($url, ':'); - $slashPosition = strpos($url, '/'); + $colonPosition = strpos((string) $url, ':'); + $slashPosition = strpos((string) $url, '/'); return ( // Base check for existence of a host on a compliant URL - parse_url($url, PHP_URL_HOST) + parse_url((string) $url, PHP_URL_HOST) // Check for more than one leading slash without a protocol. // While not a RFC compliant absolute URL, it is completed to a valid URL by some browsers, // and hence a potential security risk. Single leading slashes are not an issue though. - || preg_match('%^\s*/{2,}%', $url) + || preg_match('%^\s*/{2,}%', (string) $url) || ( // If a colon is found, check if it's part of a valid scheme definition // (meaning its not preceded by a slash). @@ -903,7 +903,7 @@ public static function getAbsFile($file) // If path is relative to public folder search there first if (self::publicDir()) { $path = Path::join(self::publicFolder(), $file); - if (file_exists($path)) { + if (file_exists((string) $path)) { return $path; } } @@ -922,8 +922,8 @@ public static function getAbsFile($file) public static function fileExists($file) { // replace any appended query-strings, e.g. /path/to/foo.php?bar=1 to /path/to/foo.php - $file = preg_replace('/([^\?]*)?.*/', '$1', $file); - return file_exists(Director::getAbsFile($file)); + $file = preg_replace('/([^\?]*)?.*/', '$1', $file ?: ''); + return file_exists((string) Director::getAbsFile($file)); } /** diff --git a/src/Control/Email/Email.php b/src/Control/Email/Email.php index f71db1b7aa3..2a1cddbc6a0 100644 --- a/src/Control/Email/Email.php +++ b/src/Control/Email/Email.php @@ -292,7 +292,7 @@ private function getDefaultFrom(): string // admin_email can have a string or an array config // https://docs.silverstripe.org/en/4/developer_guides/email/#administrator-emails $adminEmail = $this->config()->get('admin_email'); - if (is_array($adminEmail) && count($adminEmail) > 0) { + if (is_array($adminEmail) && count($adminEmail ?: []) > 0) { $defaultFrom = array_keys($adminEmail)[0]; } else { if (is_string($adminEmail)) { @@ -327,9 +327,9 @@ public function getFrom() private function sanitiseAddress($address) { if (is_array($address)) { - return array_map('trim', $address); + return array_map('trim', $address ?: []); } - return trim($address); + return trim((string) $address); } /** @@ -755,8 +755,8 @@ public function getHTMLTemplate() */ public function setHTMLTemplate($template) { - if (substr($template, -3) == '.ss') { - $template = substr($template, 0, -3); + if (substr((string) $template, -3) == '.ss') { + $template = substr((string) $template, 0, -3); } $this->HTMLTemplate = $template; @@ -781,8 +781,8 @@ public function getPlainTemplate() */ public function setPlainTemplate($template) { - if (substr($template, -3) == '.ss') { - $template = substr($template, 0, -3); + if (substr((string) $template, -3) == '.ss') { + $template = substr((string) $template, 0, -3); } $this->plainTemplate = $template; diff --git a/src/Control/HTTP.php b/src/Control/HTTP.php index 61c9bdabd60..cc5ce5aab07 100644 --- a/src/Control/HTTP.php +++ b/src/Control/HTTP.php @@ -95,19 +95,19 @@ class HTTP */ public static function filename2url($filename) { - $filename = realpath($filename); + $filename = realpath((string) $filename); if (!$filename) { return null; } // Filter files outside of the webroot $base = realpath(BASE_PATH); - $baseLength = strlen($base); - if (substr($filename, 0, $baseLength) !== $base) { + $baseLength = strlen((string) $base); + if (substr((string) $filename, 0, $baseLength) !== $base) { return null; } - $relativePath = ltrim(substr($filename, $baseLength), '/\\'); + $relativePath = ltrim(substr((string) $filename, (int) $baseLength), '/\\'); return Director::absoluteURL($relativePath); } @@ -120,7 +120,7 @@ public static function filename2url($filename) */ public static function absoluteURLs($html) { - $html = str_replace('$CurrentPageURL', Controller::curr()->getRequest()->getURL(), $html); + $html = str_replace('$CurrentPageURL', Controller::curr()->getRequest()->getURL() ?: '', $html ?: ''); return HTTP::urlRewriter($html, function ($url) { //no need to rewrite, if uri has a protocol (determined here by existence of reserved URI character ":") if (preg_match('/^\w+:/', $url)) { @@ -195,7 +195,7 @@ public static function urlRewriter($content, $code) // Execute each expression foreach ($regExps as $regExp) { - $content = preg_replace_callback($regExp, $callback, $content); + $content = preg_replace_callback($regExp ?: '', $callback, $content ?: ''); } return $content; @@ -233,7 +233,7 @@ public static function setGetVar($varname, $varvalue, $currentURL = null, $separ } // try to parse uri - $parts = parse_url($uri); + $parts = parse_url((string) $uri); if (!$parts) { throw new InvalidArgumentException("Can't parse URL: " . $uri); } @@ -259,7 +259,7 @@ public static function setGetVar($varname, $varvalue, $currentURL = null, $separ $path = (isset($parts['path']) && $parts['path'] != '') ? $parts['path'] : ''; // handle URL params which are existing / new - $params = ($params) ? '?' . http_build_query($params, null, $separator) : ''; + $params = ($params) ? '?' . http_build_query($params, '', $separator) : ''; // keep fragments (anchors) intact. $fragment = (isset($parts['fragment']) && $parts['fragment'] != '') ? '#' . $parts['fragment'] : ''; @@ -309,13 +309,13 @@ public static function findByTagAndAttribute($content, $attributes) if ($regexes) { foreach ($regexes as $regex) { - if (preg_match_all($regex, $content, $matches)) { + if (preg_match_all((string) $regex, (string) $content, $matches)) { $result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1])); } } } - return count($result) ? $result : null; + return count($result ?: []) ? $result : null; } /** @@ -350,14 +350,14 @@ public static function get_mime_type($filename) { // If the finfo module is compiled into PHP, use it. $path = BASE_PATH . DIRECTORY_SEPARATOR . $filename; - if (class_exists('finfo') && file_exists($path)) { + if (class_exists('finfo') && file_exists((string) $path)) { $finfo = new finfo(FILEINFO_MIME_TYPE); return $finfo->file($path); } // Fallback to use the list from the HTTP.yml configuration and rely on the file extension // to get the file mime-type - $ext = strtolower(File::get_file_extension($filename)); + $ext = strtolower((string) File::get_file_extension($filename)); // Get the mime-types $mimeTypes = HTTP::config()->uninherited('MimeTypes'); @@ -409,7 +409,7 @@ public static function register_modification_timestamp($timestamp) public static function register_etag($etag) { Deprecation::notice('5.0', 'Use ChangeDetectionMiddleware instead'); - if (strpos($etag, '"') !== 0) { + if (strpos((string) $etag, '"') !== 0) { $etag = "\"{$etag}\""; } self::$etag = $etag; @@ -517,7 +517,7 @@ public static function augmentState(HTTPRequest $request, HTTPResponse $response Deprecation::notice('5.0', 'Use HTTPCacheControlMiddleware API instead'); $supportedDirectives = ['max-age', 'no-cache', 'no-store', 'must-revalidate']; - if ($foundUnsupported = array_diff(array_keys($configCacheControl), $supportedDirectives)) { + if ($foundUnsupported = array_diff(array_keys($configCacheControl ?: []), $supportedDirectives)) { throw new \LogicException( 'Found unsupported legacy directives in HTTP.cache_control: ' . implode(', ', $foundUnsupported) . diff --git a/src/Control/HTTPRequest.php b/src/Control/HTTPRequest.php index d6ac4e026da..825f27a71f4 100644 --- a/src/Control/HTTPRequest.php +++ b/src/Control/HTTPRequest.php @@ -153,7 +153,7 @@ class HTTPRequest implements ArrayAccess */ public function __construct($httpMethod, $url, $getVars = [], $postVars = [], $body = null) { - $this->httpMethod = strtoupper($httpMethod); + $this->httpMethod = strtoupper((string) $httpMethod); $this->setUrl($url); $this->getVars = (array) $getVars; $this->postVars = (array) $postVars; @@ -175,15 +175,15 @@ public function setUrl($url) $this->url = $url; // Normalize URL if its relative (strictly speaking), or has leading slashes - if (Director::is_relative_url($url) || preg_match('/^\//', $url)) { - $this->url = preg_replace(['/\/+/','/^\//', '/\/$/'], ['/','',''], $this->url); + if (Director::is_relative_url($url) || preg_match('/^\//', (string) $url)) { + $this->url = preg_replace(['/\/+/','/^\//', '/\/$/'], ['/','',''], $this->url ?: ''); } - if (preg_match('/^(.*)\.([A-Za-z][A-Za-z0-9]*)$/', $this->url, $matches)) { + if (preg_match('/^(.*)\.([A-Za-z][A-Za-z0-9]*)$/', (string) $this->url, $matches)) { $this->url = $matches[1]; $this->extension = $matches[2]; } if ($this->url) { - $this->dirParts = preg_split('|/+|', $this->url); + $this->dirParts = preg_split('|/+|', (string) $this->url); } else { $this->dirParts = []; } @@ -351,7 +351,7 @@ public function isMedia() */ public function addHeader($header, $value) { - $header = strtolower($header); + $header = strtolower((string) $header); $this->headers[$header] = $value; return $this; } @@ -372,7 +372,7 @@ public function getHeaders() */ public function getHeader($header) { - $header = strtolower($header); + $header = strtolower((string) $header); return (isset($this->headers[$header])) ? $this->headers[$header] : null; } @@ -385,7 +385,7 @@ public function getHeader($header) */ public function removeHeader($header) { - $header = strtolower($header); + $header = strtolower((string) $header); unset($this->headers[$header]); return $this; } @@ -402,11 +402,11 @@ public function getURL($includeGetVars = false) if ($includeGetVars) { $vars = $this->getVars(); - if (count($vars)) { - $url .= '?' . http_build_query($vars); + if (count($vars ?: [])) { + $url .= '?' . http_build_query($vars ?: []); } - } elseif (strpos($url, "?") !== false) { - $url = substr($url, 0, strpos($url, "?")); + } elseif (strpos((string) $url, "?") !== false) { + $url = substr((string) $url, 0, strpos((string) $url, "?")); } return $url; @@ -434,6 +434,7 @@ public function isAjax() * @param string $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { return isset($this->postVars[$offset]) || isset($this->getVars[$offset]); @@ -445,16 +446,19 @@ public function offsetExists($offset) * @param string $offset * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->requestVar($offset); } + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->getVars[$offset] = $value; } + #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->getVars[$offset]); @@ -510,7 +514,7 @@ public static function send_file($fileData, $fileName, $mimeType = null) public function match($pattern, $shiftOnSuccess = false) { // Check if a specific method is required - if (preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) { + if (preg_match('/^([A-Za-z]+) +(.*)$/', (string) $pattern, $matches)) { $requiredMethod = $matches[1]; if ($requiredMethod != $this->httpMethod) { return false; @@ -526,32 +530,32 @@ public function match($pattern, $shiftOnSuccess = false) } // Check for the '//' marker that represents the "shifting point" - $doubleSlashPoint = strpos($pattern, '//'); + $doubleSlashPoint = strpos((string) $pattern, '//'); if ($doubleSlashPoint !== false) { - $shiftCount = substr_count(substr($pattern, 0, $doubleSlashPoint), '/') + 1; - $pattern = str_replace('//', '/', $pattern); - $patternParts = explode('/', $pattern); + $shiftCount = substr_count(substr((string) $pattern, 0, $doubleSlashPoint), '/') + 1; + $pattern = str_replace('//', '/', $pattern ?: ''); + $patternParts = explode('/', (string) $pattern); } else { - $patternParts = explode('/', $pattern); - $shiftCount = sizeof($patternParts); + $patternParts = explode('/', (string) $pattern); + $shiftCount = sizeof($patternParts ?: []); } // Filter out any "empty" matching parts - either from an initial / or a trailing / - $patternParts = array_values(array_filter($patternParts)); + $patternParts = array_values(array_filter($patternParts ?: [])); $arguments = []; foreach ($patternParts as $i => $part) { - $part = trim($part); + $part = trim((string) $part); // Match a variable if (isset($part[0]) && $part[0] == '$') { // A variable ending in ! is required - if (substr($part, -1) == '!') { + if (substr((string) $part, -1) == '!') { $varRequired = true; - $varName = substr($part, 1, -1); + $varName = substr((string) $part, 1, -1); } else { $varRequired = false; - $varName = substr($part, 1); + $varName = substr((string) $part, 1); } // Fail if a required variable isn't populated @@ -567,17 +571,17 @@ public function match($pattern, $shiftOnSuccess = false) } if ($varName === '*') { array_pop($patternParts); - $shiftCount = sizeof($patternParts); - $patternParts = array_merge($patternParts, array_slice($this->dirParts, $i)); + $shiftCount = sizeof($patternParts ?: []); + $patternParts = array_merge($patternParts, array_slice($this->dirParts ?: [], (int) $i)); break; } else { array_pop($patternParts); - $shiftCount = sizeof($patternParts); - $remaining = count($this->dirParts) - $i; + $shiftCount = sizeof($patternParts ?: []); + $remaining = count($this->dirParts ?: []) - $i; for ($j = 1; $j <= $remaining; $j++) { $arguments["$${j}"] = $this->dirParts[$j + $i - 1]; } - $patternParts = array_merge($patternParts, array_keys($arguments)); + $patternParts = array_merge($patternParts, array_keys($arguments ?: [])); break; } } else { @@ -606,7 +610,7 @@ public function match($pattern, $shiftOnSuccess = false) $this->shift($shiftCount); // We keep track of pattern parts that we looked at but didn't shift off. // This lets us say that we have *parsed* the whole URL even when we haven't *shifted* it all - $this->unshiftedButParsedParts = sizeof($patternParts) - $shiftCount; + $this->unshiftedButParsedParts = sizeof($patternParts ?: []) - $shiftCount; } $this->latestParams = $arguments; @@ -640,12 +644,12 @@ public function allParams() */ public function shiftAllParams() { - $keys = array_keys($this->allParams); - $values = array_values($this->allParams); + $keys = array_keys($this->allParams ?: []); + $values = array_values($this->allParams ?: []); $value = array_shift($values); // push additional unparsed URL parts onto the parameter stack - if (array_key_exists($this->unshiftedButParsedParts, $this->dirParts)) { + if (array_key_exists($this->unshiftedButParsedParts, $this->dirParts ?: [])) { $values[] = $this->dirParts[$this->unshiftedButParsedParts]; } @@ -741,11 +745,11 @@ public function remaining() */ public function isEmptyPattern($pattern) { - if (preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) { + if (preg_match('/^([A-Za-z]+) +(.*)$/', (string) $pattern, $matches)) { $pattern = $matches[2]; } - if (trim($pattern) == "") { + if (trim((string) $pattern) == "") { return true; } return false; @@ -787,7 +791,7 @@ public function shift($count = 1) */ public function allParsed() { - return sizeof($this->dirParts) <= $this->unshiftedButParsedParts; + return sizeof($this->dirParts ?: []) <= $this->unshiftedButParsedParts; } /** @@ -835,9 +839,9 @@ public function setIP($ip) public function getAcceptMimetypes($includeQuality = false) { $mimetypes = []; - $mimetypesWithQuality = preg_split('#\s*,\s*#', $this->getHeader('accept')); + $mimetypesWithQuality = preg_split('#\s*,\s*#', (string) $this->getHeader('accept')); foreach ($mimetypesWithQuality as $mimetypeWithQuality) { - $mimetypes[] = ($includeQuality) ? $mimetypeWithQuality : preg_replace('/;.*/', '', $mimetypeWithQuality); + $mimetypes[] = ($includeQuality) ? $mimetypeWithQuality : preg_replace('/;.*/', '', $mimetypeWithQuality ?: ''); } return $mimetypes; } @@ -861,7 +865,7 @@ public function setHttpMethod($method) throw new \InvalidArgumentException('HTTPRequest::setHttpMethod: Invalid HTTP method'); } - $this->httpMethod = strtoupper($method); + $this->httpMethod = strtoupper((string) $method); return $this; } @@ -895,7 +899,7 @@ public function setScheme($scheme) */ private static function isValidHttpMethod($method) { - return in_array(strtoupper($method), ['GET','POST','PUT','DELETE','HEAD']); + return in_array(strtoupper((string) $method), ['GET','POST','PUT','DELETE','HEAD']); } /** diff --git a/src/Control/HTTPRequestBuilder.php b/src/Control/HTTPRequestBuilder.php index 443ae38c62a..de4e88b23a5 100644 --- a/src/Control/HTTPRequestBuilder.php +++ b/src/Control/HTTPRequestBuilder.php @@ -39,8 +39,8 @@ public static function createFromVariables(array $variables, $input, $url = null $url = parse_url($variables['_SERVER']['REQUEST_URI'], PHP_URL_PATH); // Remove base folders from the URL if webroot is hosted in a subfolder - if (substr(strtolower($url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) { - $url = substr($url, strlen(BASE_URL)); + if (substr(strtolower((string) $url), 0, strlen(BASE_URL)) === strtolower(BASE_URL)) { + $url = substr((string) $url, strlen(BASE_URL)); } } @@ -88,10 +88,10 @@ public static function extractRequestHeaders(array $server) { $headers = []; foreach ($server as $key => $value) { - if (substr($key, 0, 5) == 'HTTP_') { - $key = substr($key, 5); - $key = strtolower(str_replace('_', ' ', $key)); - $key = str_replace(' ', '-', ucwords($key)); + if (substr((string) $key, 0, 5) == 'HTTP_') { + $key = substr((string) $key, 5); + $key = strtolower((string) str_replace('_', ' ', $key ?: '')); + $key = str_replace(' ', '-', ucwords((string) $key)); $headers[$key] = $value; } } @@ -118,8 +118,8 @@ public static function extractRequestHeaders(array $server) // Shift PHP_AUTH_* into headers so they are available via request $headers['PHP_AUTH_USER'] = $server['PHP_AUTH_USER']; $headers['PHP_AUTH_PW'] = $server['PHP_AUTH_PW']; - } elseif ($authHeader && preg_match('/Basic\s+(?.*)$/i', $authHeader, $matches)) { - list($name, $password) = explode(':', base64_decode($matches['token'])); + } elseif ($authHeader && preg_match('/Basic\s+(?.*)$/i', (string) $authHeader, $matches)) { + list($name, $password) = explode(':', (string) base64_decode($matches['token'])); $headers['PHP_AUTH_USER'] = $name; $headers['PHP_AUTH_PW'] = $password; } diff --git a/src/Control/HTTPResponse.php b/src/Control/HTTPResponse.php index 1fa01086600..85cfd88f957 100644 --- a/src/Control/HTTPResponse.php +++ b/src/Control/HTTPResponse.php @@ -223,7 +223,7 @@ public function getStatusCode() */ public function getStatusDescription() { - return str_replace(["\r", "\n"], '', $this->statusDescription); + return str_replace(["\r", "\n"], '', $this->statusDescription ?: ''); } /** @@ -266,7 +266,7 @@ public function getBody() */ public function addHeader($header, $value) { - $header = strtolower($header); + $header = strtolower((string) $header); $this->headers[$header] = $value; return $this; } @@ -280,7 +280,7 @@ public function addHeader($header, $value) */ public function getHeader($header) { - $header = strtolower($header); + $header = strtolower((string) $header); if (isset($this->headers[$header])) { return $this->headers[$header]; } @@ -305,7 +305,7 @@ public function getHeaders() */ public function removeHeader($header) { - $header = strtolower($header); + $header = strtolower((string) $header); unset($this->headers[$header]); return $this; } @@ -381,9 +381,9 @@ protected function outputHeaders() $this->getStatusCode(), $this->getStatusDescription() ); - header($method); + header((string) $method); foreach ($this->getHeaders() as $header => $value) { - header("{$header}: {$value}", true, $this->getStatusCode()); + header("{$header}: {$value}", true, (int) $this->getStatusCode()); } } elseif ($this->getStatusCode() >= 300) { // It's critical that these status codes are sent; we need to report a failure if not. diff --git a/src/Control/Middleware/AllowedHostsMiddleware.php b/src/Control/Middleware/AllowedHostsMiddleware.php index f3e50826add..ab928d4280a 100644 --- a/src/Control/Middleware/AllowedHostsMiddleware.php +++ b/src/Control/Middleware/AllowedHostsMiddleware.php @@ -36,7 +36,7 @@ public function getAllowedHosts() public function setAllowedHosts($allowedHosts) { if (is_string($allowedHosts)) { - $allowedHosts = preg_split('/ *, */', $allowedHosts); + $allowedHosts = preg_split('/ *, */', (string) $allowedHosts); } $this->allowedHosts = $allowedHosts; return $this; @@ -52,7 +52,7 @@ public function process(HTTPRequest $request, callable $delegate) // check allowed hosts if ($allowedHosts && !Director::is_cli() - && !in_array($request->getHeader('Host'), $allowedHosts) + && !in_array($request->getHeader('Host'), $allowedHosts ?: []) ) { return new HTTPResponse('Invalid Host', 400); } diff --git a/src/Control/Middleware/CanonicalURLMiddleware.php b/src/Control/Middleware/CanonicalURLMiddleware.php index 12646d43d8b..8c5e27d86d6 100644 --- a/src/Control/Middleware/CanonicalURLMiddleware.php +++ b/src/Control/Middleware/CanonicalURLMiddleware.php @@ -224,7 +224,7 @@ protected function getRedirect(HTTPRequest $request) } // Check www. - if ($this->getForceWWW() && strpos($host, 'www.') !== 0) { + if ($this->getForceWWW() && strpos((string) $host, 'www.') !== 0) { $host = "www.{$host}"; } @@ -300,7 +300,7 @@ protected function requiresSSL(HTTPRequest $request) // Filter redirect based on url $relativeURL = $request->getURL(true); foreach ($patterns as $pattern) { - if (preg_match($pattern, $relativeURL)) { + if (preg_match((string) $pattern, (string) $relativeURL)) { return true; } } @@ -368,12 +368,12 @@ protected function isEnabled() } // If CLI, EnabledEnvs must contain CLI - if (Director::is_cli() && !in_array('cli', $enabledEnvs)) { + if (Director::is_cli() && !in_array('cli', $enabledEnvs ?: [])) { return false; } // Check other envs - return empty($enabledEnvs) || in_array(Director::get_environment_type(), $enabledEnvs); + return empty($enabledEnvs) || in_array(Director::get_environment_type(), $enabledEnvs ?: []); } /** diff --git a/src/Control/Middleware/ChangeDetectionMiddleware.php b/src/Control/Middleware/ChangeDetectionMiddleware.php index 1767a0161e4..871ea863d02 100644 --- a/src/Control/Middleware/ChangeDetectionMiddleware.php +++ b/src/Control/Middleware/ChangeDetectionMiddleware.php @@ -32,7 +32,7 @@ public function process(HTTPRequest $request, callable $delegate) // Ignore etag for no-store $cacheControl = $response->getHeader('Cache-Control'); - if ($cacheControl && strstr($cacheControl, 'no-store')) { + if ($cacheControl && strstr((string) $cacheControl, 'no-store')) { return $response; } @@ -51,7 +51,7 @@ public function process(HTTPRequest $request, callable $delegate) // Check If-Modified-Since $ifModifiedSince = $request->getHeader('If-Modified-Since'); $lastModified = $response->getHeader('Last-Modified'); - if ($ifModifiedSince && $lastModified && strtotime($ifModifiedSince) >= strtotime($lastModified)) { + if ($ifModifiedSince && $lastModified && strtotime((string) $ifModifiedSince) >= strtotime((string) $lastModified)) { return $this->sendNotModified($request, $response); } @@ -76,7 +76,7 @@ protected function generateETag(HTTPResponse $response) } // Generate etag from body - return sprintf('"%s"', md5($response->getBody())); + return sprintf('"%s"', md5((string) $response->getBody())); } /** diff --git a/src/Control/Middleware/ConfirmationMiddleware.php b/src/Control/Middleware/ConfirmationMiddleware.php index 79acbfe21cc..2450c4fc37a 100644 --- a/src/Control/Middleware/ConfirmationMiddleware.php +++ b/src/Control/Middleware/ConfirmationMiddleware.php @@ -84,7 +84,7 @@ protected function getConfirmationUrl(HTTPRequest $request, $confirmationStorage { $url = $this->confirmationFormUrl; - if (substr($url, 0, 1) === '/') { + if (substr((string) $url, 0, 1) === '/') { // add BASE_URL explicitly if not absolute $url = Controller::join_links(Director::baseURL(), $url); } @@ -203,7 +203,7 @@ protected function processItems(HTTPRequest $request, callable $delegate, $items { $storage = Injector::inst()->createWithArgs(Confirmation\Storage::class, [$request->getSession(), $this->confirmationId, false]); - if (!count($storage->getItems())) { + if (!count($storage->getItems() ?: [])) { return $this->buildConfirmationRedirect($request, $storage, $items); } diff --git a/src/Control/Middleware/ConfirmationMiddleware/EnvironmentBypass.php b/src/Control/Middleware/ConfirmationMiddleware/EnvironmentBypass.php index 2e889e6a251..f8c7767fa1f 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/EnvironmentBypass.php +++ b/src/Control/Middleware/ConfirmationMiddleware/EnvironmentBypass.php @@ -63,6 +63,6 @@ public function setEnvironments($environments) */ public function checkRequestForBypass(HTTPRequest $request) { - return in_array(Director::get_environment_type(), $this->environments, true); + return in_array(Director::get_environment_type(), $this->environments ?: [], true); } } diff --git a/src/Control/Middleware/ConfirmationMiddleware/GetParameter.php b/src/Control/Middleware/ConfirmationMiddleware/GetParameter.php index 1d5204f4829..bf309ef71fc 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/GetParameter.php +++ b/src/Control/Middleware/ConfirmationMiddleware/GetParameter.php @@ -89,7 +89,7 @@ protected function generateToken($path, $value) */ protected function checkRequestHasParameter(HTTPRequest $request) { - return array_key_exists($this->name, $request->getVars()); + return array_key_exists($this->name, $request->getVars() ?: []); } public function checkRequestForBypass(HTTPRequest $request) diff --git a/src/Control/Middleware/ConfirmationMiddleware/HttpMethodBypass.php b/src/Control/Middleware/ConfirmationMiddleware/HttpMethodBypass.php index 6a2f501fc15..8b0d72fbee2 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/HttpMethodBypass.php +++ b/src/Control/Middleware/ConfirmationMiddleware/HttpMethodBypass.php @@ -47,10 +47,10 @@ public function addMethods(...$methods) { // uppercase and exclude empties $methods = array_reduce( - $methods, + $methods ?: [], function ($result, $method) { - $method = strtoupper(trim($method)); - if (strlen($method)) { + $method = strtoupper(trim((string) $method)); + if (strlen((string) $method)) { $result[] = $method; } return $result; @@ -59,7 +59,7 @@ function ($result, $method) { ); foreach ($methods as $method) { - if (!in_array($method, $this->methods, true)) { + if (!in_array($method, $this->methods ?: [], true)) { $this->methods[] = $method; } } @@ -76,6 +76,6 @@ function ($result, $method) { */ public function checkRequestForBypass(HTTPRequest $request) { - return in_array($request->httpMethod(), $this->methods, true); + return in_array($request->httpMethod(), $this->methods ?: [], true); } } diff --git a/src/Control/Middleware/ConfirmationMiddleware/PathAware.php b/src/Control/Middleware/ConfirmationMiddleware/PathAware.php index eb83bc485be..f056250d34e 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/PathAware.php +++ b/src/Control/Middleware/ConfirmationMiddleware/PathAware.php @@ -44,7 +44,7 @@ public function setPath($path) */ protected function normalisePath($path) { - if (substr($path, -1) !== '/') { + if (substr((string) $path, -1) !== '/') { return $path . '/'; } else { return $path; diff --git a/src/Control/Middleware/ConfirmationMiddleware/Url.php b/src/Control/Middleware/ConfirmationMiddleware/Url.php index 7076541bbb5..8bf99f6ddb2 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/Url.php +++ b/src/Control/Middleware/ConfirmationMiddleware/Url.php @@ -118,7 +118,7 @@ public function checkRequest(HTTPRequest $request) { $httpMethods = $this->getHttpMethods(); - if (count($httpMethods) && !in_array($request->httpMethod(), $httpMethods, true)) { + if (count($httpMethods ?: []) && !in_array($request->httpMethod(), $httpMethods ?: [], true)) { return false; } @@ -132,7 +132,7 @@ public function checkRequest(HTTPRequest $request) // compare the request parameters with the declared ones foreach ($this->params as $key => $val) { if (is_null($val)) { - $cmp = array_key_exists($key, $getVars); + $cmp = array_key_exists($key, $getVars ?: []); } else { $cmp = isset($getVars[$key]) && $getVars[$key] === strval($val); } @@ -144,7 +144,7 @@ public function checkRequest(HTTPRequest $request) // check only declared parameters exist in the request foreach ($getVars as $key => $val) { - if (!array_key_exists($key, $this->params)) { + if (!array_key_exists($key, $this->params ?: [])) { return false; } } diff --git a/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswith.php b/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswith.php index efea0c528a3..905cc3197e2 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswith.php +++ b/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswith.php @@ -62,7 +62,7 @@ protected function generateToken($path) protected function checkPath($path) { $targetPath = $this->getPath(); - return strncmp($this->normalisePath($path), $targetPath, strlen($targetPath)) === 0; + return strncmp((string) $this->normalisePath($path), (string) $targetPath, strlen((string) $targetPath)) === 0; } public function checkRequestForBypass(HTTPRequest $request) diff --git a/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitive.php b/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitive.php index 5c0557a441b..f8d082005ec 100644 --- a/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitive.php +++ b/src/Control/Middleware/ConfirmationMiddleware/UrlPathStartswithCaseInsensitive.php @@ -14,7 +14,7 @@ protected function checkPath($path) { $pattern = $this->getPath(); - $mb_path = mb_strcut($this->normalisePath($path), 0, strlen($pattern)); - return mb_stripos($mb_path, $pattern) === 0; + $mb_path = mb_strcut((string) $this->normalisePath($path), 0, strlen((string) $pattern)); + return mb_stripos((string) $mb_path, (string) $pattern) === 0; } } diff --git a/src/Control/Middleware/ExecMetricMiddleware.php b/src/Control/Middleware/ExecMetricMiddleware.php index 3d5f5712bc8..fa32e02d0a7 100644 --- a/src/Control/Middleware/ExecMetricMiddleware.php +++ b/src/Control/Middleware/ExecMetricMiddleware.php @@ -43,7 +43,7 @@ public function process(HTTPRequest $request, callable $delegate) */ private function showMetric(HTTPRequest $request) { - return Director::isDev() && array_key_exists('execmetric', $request->getVars()); + return Director::isDev() && array_key_exists('execmetric', $request->getVars() ?: []); } /** diff --git a/src/Control/Middleware/HTTPCacheControlMiddleware.php b/src/Control/Middleware/HTTPCacheControlMiddleware.php index 701b4abaee1..821ff175e48 100644 --- a/src/Control/Middleware/HTTPCacheControlMiddleware.php +++ b/src/Control/Middleware/HTTPCacheControlMiddleware.php @@ -207,7 +207,7 @@ public function getVary() // Load default from config $defaultVary = $this->config()->get('defaultVary'); - return array_keys(array_filter($defaultVary)); + return array_keys(array_filter($defaultVary ?: [])); } /** @@ -246,13 +246,13 @@ protected function combineVary(...$varies) $merged = []; foreach ($varies as $vary) { if ($vary && is_string($vary)) { - $vary = array_filter(preg_split("/\s*,\s*/", trim($vary))); + $vary = array_filter(preg_split("/\s*,\s*/", trim((string) $vary)) ?: []); } if ($vary && is_array($vary)) { $merged = array_merge($merged, $vary); } } - return array_unique($merged); + return array_unique($merged ?: []); } @@ -265,7 +265,7 @@ protected function combineVary(...$varies) */ public function registerModificationDate($date) { - $timestamp = is_numeric($date) ? $date : strtotime($date); + $timestamp = is_numeric($date) ? $date : strtotime((string) $date); if ($timestamp > $this->modificationDate) { $this->modificationDate = $timestamp; } @@ -280,7 +280,7 @@ public function registerModificationDate($date) */ protected function setState($state) { - if (!array_key_exists($state, $this->stateDirectives)) { + if (!array_key_exists($state, $this->stateDirectives ?: [])) { throw new InvalidArgumentException("Invalid state {$state}"); } $this->state = $state; @@ -339,12 +339,12 @@ public function setStateDirective($states, $directive, $value = true) } // make sure the directive is in the list of allowed directives $allowedDirectives = $this->config()->get('allowed_directives'); - $directive = strtolower($directive); - if (!in_array($directive, $allowedDirectives)) { + $directive = strtolower((string) $directive); + if (!in_array($directive, $allowedDirectives ?: [])) { throw new InvalidArgumentException('Directive ' . $directive . ' is not allowed'); } foreach ((array)$states as $state) { - if (!array_key_exists($state, $this->stateDirectives)) { + if (!array_key_exists($state, $this->stateDirectives ?: [])) { throw new InvalidArgumentException("Invalid state {$state}"); } // Set or unset directive @@ -394,7 +394,7 @@ public function removeStateDirective($states, $directive) */ public function hasStateDirective($state, $directive) { - $directive = strtolower($directive); + $directive = strtolower((string) $directive); return isset($this->stateDirectives[$state][$directive]); } @@ -420,7 +420,7 @@ public function hasDirective($directive) */ public function getStateDirective($state, $directive) { - $directive = strtolower($directive); + $directive = strtolower((string) $directive); if (isset($this->stateDirectives[$state][$directive])) { return $this->stateDirectives[$state][$directive]; } diff --git a/src/Control/Middleware/HTTPMiddlewareAware.php b/src/Control/Middleware/HTTPMiddlewareAware.php index e7e861898b5..8804de13399 100644 --- a/src/Control/Middleware/HTTPMiddlewareAware.php +++ b/src/Control/Middleware/HTTPMiddlewareAware.php @@ -57,7 +57,7 @@ protected function callMiddleware(HTTPRequest $request, callable $last) // Reverse middlewares $next = $last; /** @var HTTPMiddleware $middleware */ - foreach (array_reverse($this->getMiddlewares()) as $middleware) { + foreach (array_reverse($this->getMiddlewares() ?: []) as $middleware) { $next = function ($request) use ($middleware, $next) { return $middleware->process($request, $next); }; diff --git a/src/Control/Middleware/PermissionAwareConfirmationMiddleware.php b/src/Control/Middleware/PermissionAwareConfirmationMiddleware.php index d9765793e9d..3caee76e4ca 100644 --- a/src/Control/Middleware/PermissionAwareConfirmationMiddleware.php +++ b/src/Control/Middleware/PermissionAwareConfirmationMiddleware.php @@ -131,7 +131,7 @@ protected function getAuthenticationRedirect(HTTPRequest $request) $loginPage = sprintf( '%s?BackURL=%s', Director::absoluteURL(Security::config()->get('login_url')), - urlencode($backURL) + urlencode((string) $backURL) ); $result = new HTTPResponse(); diff --git a/src/Control/Middleware/RateLimitMiddleware.php b/src/Control/Middleware/RateLimitMiddleware.php index a9350cad3bc..44942abc005 100644 --- a/src/Control/Middleware/RateLimitMiddleware.php +++ b/src/Control/Middleware/RateLimitMiddleware.php @@ -68,7 +68,7 @@ protected function getKeyFromRequest($request) } else { $key .= $request->getIP(); } - return md5($key); + return md5((string) $key); } /** diff --git a/src/Control/Middleware/TrustedProxyMiddleware.php b/src/Control/Middleware/TrustedProxyMiddleware.php index 0d865e6c38d..2954005cbeb 100644 --- a/src/Control/Middleware/TrustedProxyMiddleware.php +++ b/src/Control/Middleware/TrustedProxyMiddleware.php @@ -216,7 +216,7 @@ protected function getIPFromHeaderValue($headerValue) { // Sometimes the IP from a load balancer could be "x.x.x.x, y.y.y.y, z.z.z.z" // so we need to find the most likely candidate - $ips = preg_split('/\s*,\s*/', $headerValue); + $ips = preg_split('/\s*,\s*/', (string) $headerValue); // Prioritise filters $filters = [ @@ -227,7 +227,7 @@ protected function getIPFromHeaderValue($headerValue) foreach ($filters as $filter) { // Find best IP foreach ($ips as $ip) { - if (filter_var($ip, FILTER_VALIDATE_IP, $filter)) { + if (filter_var($ip, FILTER_VALIDATE_IP, $filter ?: 0)) { return $ip; } } diff --git a/src/Control/Middleware/URLSpecialsMiddleware/FlushScheduler.php b/src/Control/Middleware/URLSpecialsMiddleware/FlushScheduler.php index 971b262f5bd..2dc6353cf25 100644 --- a/src/Control/Middleware/URLSpecialsMiddleware/FlushScheduler.php +++ b/src/Control/Middleware/URLSpecialsMiddleware/FlushScheduler.php @@ -28,7 +28,7 @@ trait FlushScheduler */ public function scheduleFlush(HTTPRequest $request) { - $flush = array_key_exists('flush', $request->getVars()) || ($request->getURL() === 'dev/build'); + $flush = array_key_exists('flush', $request->getVars() ?: []) || ($request->getURL() === 'dev/build'); if (!$flush || Director::isManifestFlushed()) { return false; diff --git a/src/Control/Middleware/URLSpecialsMiddleware/SessionEnvTypeSwitcher.php b/src/Control/Middleware/URLSpecialsMiddleware/SessionEnvTypeSwitcher.php index 6906ee6974e..ab98ba8e48d 100644 --- a/src/Control/Middleware/URLSpecialsMiddleware/SessionEnvTypeSwitcher.php +++ b/src/Control/Middleware/URLSpecialsMiddleware/SessionEnvTypeSwitcher.php @@ -23,7 +23,7 @@ public function setSessionEnvType(HTTPRequest $request) { $session = $request->getSession(); - if (array_key_exists('isTest', $request->getVars())) { + if (array_key_exists('isTest', $request->getVars() ?: [])) { if (!is_null($isTest = $request->getVar('isTest'))) { if ($isTest === $session->get('isTest')) { return false; @@ -34,7 +34,7 @@ public function setSessionEnvType(HTTPRequest $request) $session->set('isTest', $isTest); return true; - } elseif (array_key_exists('isDev', $request->getVars())) { + } elseif (array_key_exists('isDev', $request->getVars() ?: [])) { if (!is_null($isDev = $request->getVar('isDev'))) { if ($isDev === $session->get('isDev')) { return false; diff --git a/src/Control/PjaxResponseNegotiator.php b/src/Control/PjaxResponseNegotiator.php index 60bb769aa34..9fb8cee961a 100644 --- a/src/Control/PjaxResponseNegotiator.php +++ b/src/Control/PjaxResponseNegotiator.php @@ -82,7 +82,7 @@ public function respond(HTTPRequest $request, $extraCallbacks = []) if (isset($this->fragmentOverride)) { $fragments = $this->fragmentOverride; } elseif ($fragmentStr = $request->getHeader('X-Pjax')) { - $fragments = explode(',', $fragmentStr); + $fragments = explode(',', (string) $fragmentStr); } else { if ($request->isAjax()) { throw new HTTPResponse_Exception("Ajax requests to this URL require an X-Pjax header.", 400); diff --git a/src/Control/RequestHandler.php b/src/Control/RequestHandler.php index e0f8316d5a1..c787b9707d6 100644 --- a/src/Control/RequestHandler.php +++ b/src/Control/RequestHandler.php @@ -178,7 +178,7 @@ public function handleRequest(HTTPRequest $request) // Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action', if ($action[0] == '$') { - $action = str_replace("-", "_", $request->latestParam(substr($action, 1))); + $action = str_replace("-", "_", $request->latestParam(substr($action, 1)) ?: ''); } if (!$action) { @@ -196,7 +196,7 @@ public function handleRequest(HTTPRequest $request) if (!$this->hasAction($action)) { return $this->httpError(404, "Action '$action' isn't available $classMessage."); } - if (!$this->checkAccessAction($action) || in_array(strtolower($action), ['run', 'doinit'])) { + if (!$this->checkAccessAction($action) || in_array(strtolower((string) $action), ['run', 'doinit'])) { return $this->httpError(403, "Action '$action' isn't allowed $classMessage."); } $result = $this->handleAction($request, $action); @@ -279,7 +279,7 @@ protected function findAction($request) } } - $handlerClass = get_parent_class($handlerClass); + $handlerClass = get_parent_class($handlerClass ?: ''); } return null; } @@ -351,17 +351,17 @@ public function allowedActions($limitToClass = null) } if (is_array($actions)) { - if (array_key_exists('*', $actions)) { + if (array_key_exists('*', $actions ?: [])) { throw new InvalidArgumentException("Invalid allowed_action '*'"); } // convert all keys and values to lowercase to // allow for easier comparison, unless it is a permission code - $actions = array_change_key_case($actions, CASE_LOWER); + $actions = array_change_key_case($actions ?: [], CASE_LOWER); foreach ($actions as $key => $value) { if (is_numeric($key)) { - $actions[$key] = strtolower($value); + $actions[$key] = strtolower((string) $value); } } @@ -388,7 +388,7 @@ public function hasAction($action) // Don't allow access to any non-public methods (inspect instance plus all extensions) $insts = array_merge([$this], (array) $this->getExtensionInstances()); foreach ($insts as $inst) { - if (!method_exists($inst, $action)) { + if (!method_exists($inst, (string) $action)) { continue; } $r = new ReflectionClass(get_class($inst)); @@ -398,15 +398,15 @@ public function hasAction($action) } } - $action = strtolower($action); + $action = strtolower((string) $action); $actions = $this->allowedActions(); // Check if the action is defined in the allowed actions of any ancestry class // as either a key or value. Note that if the action is numeric, then keys are not // searched for actions to prevent actual array keys being recognised as actions. if (is_array($actions)) { - $isKey = !is_numeric($action) && array_key_exists($action, $actions); - $isValue = in_array($action, $actions, true); + $isKey = !is_numeric($action) && array_key_exists($action, $actions ?: []); + $isValue = in_array($action, $actions ?: [], true); if ($isKey || $isValue) { return true; } @@ -414,7 +414,7 @@ public function hasAction($action) $actionsWithoutExtra = $this->config()->get('allowed_actions', true); if (!is_array($actions) || !$actionsWithoutExtra) { - if (!in_array(strtolower($action), ['run', 'doinit']) && method_exists($this, $action)) { + if (!in_array(strtolower((string) $action), ['run', 'doinit']) && method_exists($this, (string) $action)) { return true; } } @@ -430,12 +430,12 @@ public function hasAction($action) */ protected function definingClassForAction($actionOrigCasing) { - $action = strtolower($actionOrigCasing); + $action = strtolower((string) $actionOrigCasing); $definingClass = null; $insts = array_merge([$this], (array) $this->getExtensionInstances()); foreach ($insts as $inst) { - if (!method_exists($inst, $action)) { + if (!method_exists($inst, (string) $action)) { continue; } $r = new ReflectionClass(get_class($inst)); @@ -456,7 +456,7 @@ protected function definingClassForAction($actionOrigCasing) public function checkAccessAction($action) { $actionOrigCasing = $action; - $action = strtolower($action); + $action = strtolower((string) $action); $isAllowed = false; $isDefined = false; @@ -472,22 +472,22 @@ public function checkAccessAction($action) if ($test === true || $test === 1 || $test === '1') { // TRUE should always allow access $isAllowed = true; - } elseif (substr($test, 0, 2) == '->') { + } elseif (substr((string) $test, 0, 2) == '->') { // Determined by custom method with "->" prefix list($method, $arguments) = ClassInfo::parse_class_spec(substr($test, 2)); - $isAllowed = call_user_func_array([$this, $method], $arguments); + $isAllowed = call_user_func_array([$this, $method], $arguments ?: []); } else { // Value is a permission code to check the current member against $isAllowed = Permission::check($test); } } elseif (is_array($allowedActions) - && (($key = array_search($action, $allowedActions, true)) !== false) + && (($key = array_search($action, $allowedActions ?: [], true)) !== false) && is_numeric($key) ) { // Allow numeric array notation (search for array value as action instead of key) $isDefined = true; $isAllowed = true; - } elseif (is_array($allowedActions) && !count($allowedActions)) { + } elseif (is_array($allowedActions) && !count($allowedActions ?: [])) { // If defined as empty array, deny action $isAllowed = false; } elseif ($allowedActions === null) { diff --git a/src/Control/Session.php b/src/Control/Session.php index 4adf1a4e0ba..4df00354afc 100644 --- a/src/Control/Session.php +++ b/src/Control/Session.php @@ -295,7 +295,7 @@ public function start(HTTPRequest $request) // Director::baseURL can return absolute domain names - this extracts the relevant parts // for the session otherwise we can get broken session cookies if (Director::is_absolute_url($path)) { - $urlParts = parse_url($path); + $urlParts = parse_url((string) $path); $path = $urlParts['path']; if (!$domain) { $domain = $urlParts['host']; @@ -409,7 +409,7 @@ public function set($name, $val) protected function markChanged($name) { $diffVar = &$this->changedData; - foreach (explode('.', $name) as $namePart) { + foreach (explode('.', (string) $name) as $namePart) { if (!isset($diffVar[$namePart])) { $diffVar[$namePart] = []; } @@ -432,7 +432,7 @@ protected function markChanged($name) */ public function addToArray($name, $val) { - $names = explode('.', $name); + $names = explode('.', (string) $name); // We still want to do this even if we have strict path checking for legacy code $var = &$this->data; @@ -472,7 +472,7 @@ public function clear($name) // Unset var if ($var !== null) { // Unset parent key - $parentParts = explode('.', $name); + $parentParts = explode('.', (string) $name); $basePart = array_pop($parentParts); if ($parentParts) { $parent = &$this->nestedValueRef(implode('.', $parentParts), $this->data); @@ -491,7 +491,7 @@ public function clear($name) public function clearAll() { if ($this->data && is_array($this->data)) { - foreach (array_keys($this->data) as $key) { + foreach (array_keys($this->data ?: []) as $key) { $this->clear($key); } } @@ -583,7 +583,7 @@ protected function &nestedValueRef($name, &$source) { // Find var to change $var = &$source; - foreach (explode('.', $name) as $namePart) { + foreach (explode('.', (string) $name) as $namePart) { if (!isset($var)) { $var = []; } @@ -608,7 +608,7 @@ protected function nestedValue($name, $source) { // Find var to change $var = $source; - foreach (explode('.', $name) as $namePart) { + foreach (explode('.', (string) $name) as $namePart) { if (!isset($var[$namePart])) { return null; } @@ -631,7 +631,7 @@ protected function recursivelyApplyChanges($changes, $source, &$destination) foreach ($changes as $key => $changed) { if ($changed === true) { // Determine if replacement or removal - if (array_key_exists($key, $source)) { + if (array_key_exists($key, $source ?: [])) { $destination[$key] = $source[$key]; } else { unset($destination[$key]); diff --git a/src/Control/SimpleResourceURLGenerator.php b/src/Control/SimpleResourceURLGenerator.php index ce026c6a102..92b4143137e 100644 --- a/src/Control/SimpleResourceURLGenerator.php +++ b/src/Control/SimpleResourceURLGenerator.php @@ -74,8 +74,8 @@ public function urlForResource($relativePath) return $relativePath; } else { // Save querystring for later - if (strpos($relativePath, '?') !== false) { - list($relativePath, $query) = explode('?', $relativePath); + if (strpos((string) $relativePath, '?') !== false) { + list($relativePath, $query) = explode('?', (string) $relativePath); } // Determine lookup mechanism based on existence of public/ folder. @@ -96,12 +96,12 @@ public function urlForResource($relativePath) // Apply url rewrites $rules = Config::inst()->get(static::class, 'url_rewrites') ?: []; foreach ($rules as $from => $to) { - $relativeURL = preg_replace($from, $to, $relativeURL); + $relativeURL = preg_replace($from ?: '', $to ?: '', $relativeURL ?: ''); } // Apply nonce // Don't add nonce to directories - if ($this->nonceStyle && $exists && is_file($absolutePath)) { + if ($this->nonceStyle && $exists && is_file((string) $absolutePath)) { switch ($this->nonceStyle) { case 'mtime': $method = 'filemtime'; @@ -145,7 +145,7 @@ protected function resolveModuleResource(ModuleResource $resource) if (Director::publicDir()) { // All resources mapped directly to _resources/ $relativePath = Path::join(RESOURCES_DIR, $relativePath); - } elseif (stripos($relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) { + } elseif (stripos((string) $relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) { // @todo Non-public dir support will be removed in 5.0, so remove this block there // If there is no public folder, map to _resources/ but trim leading vendor/ too (4.0 compat) $relativePath = Path::join( @@ -173,10 +173,10 @@ protected function resolveUnsecuredResource($relativePath) // Resolve path to base $absolutePath = Path::join(Director::baseFolder(), $relativePath); - $exists = file_exists($absolutePath); + $exists = file_exists((string) $absolutePath); // Rewrite vendor/ to _resources/ folder - if (stripos($relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) { + if (stripos((string) $relativePath, ManifestFileFinder::VENDOR_DIR . DIRECTORY_SEPARATOR) === 0) { $relativePath = Path::join( RESOURCES_DIR, substr($relativePath, strlen(ManifestFileFinder::VENDOR_DIR)) @@ -199,9 +199,9 @@ protected function inferPublicResourceRequired(&$relativePath) $relativePath = Path::normalise($relativePath, true); // Detect public-only request - $publicOnly = stripos($relativePath, 'public' . DIRECTORY_SEPARATOR) === 0; + $publicOnly = stripos((string) $relativePath, 'public' . DIRECTORY_SEPARATOR) === 0; if ($publicOnly) { - $relativePath = substr($relativePath, strlen(Director::publicDir() . DIRECTORY_SEPARATOR)); + $relativePath = substr((string) $relativePath, strlen(Director::publicDir() . DIRECTORY_SEPARATOR)); } return $publicOnly; } @@ -220,14 +220,14 @@ protected function resolvePublicResource($relativePath) // Search public folder first, and unless `public/` is prefixed, also private base path $publicPath = Path::join(Director::publicFolder(), $relativePath); - if (file_exists($publicPath)) { + if (file_exists((string) $publicPath)) { // String is a literal url committed directly to public folder return [true, $publicPath, $relativePath]; } // Fall back to private path (and assume expose will make this available to _resources/) $privatePath = Path::join(Director::baseFolder(), $relativePath); - if (!$publicOnly && file_exists($privatePath)) { + if (!$publicOnly && file_exists((string) $privatePath)) { // String is private but exposed to _resources/, so rewrite to the symlinked base $relativePath = Path::join(RESOURCES_DIR, $relativePath); return [true, $privatePath, $relativePath]; diff --git a/src/Control/Util/IPUtils.php b/src/Control/Util/IPUtils.php index eb523d7b5b1..3e12d362c56 100644 --- a/src/Control/Util/IPUtils.php +++ b/src/Control/Util/IPUtils.php @@ -39,7 +39,7 @@ public static function checkIP($requestIP, $ips) $ips = [$ips]; } - $method = substr_count($requestIP, ':') > 1 ? 'checkIP6' : 'checkIP4'; + $method = substr_count((string) $requestIP, ':') > 1 ? 'checkIP6' : 'checkIP4'; foreach ($ips as $ip) { if (self::$method($requestIP, trim($ip))) { @@ -64,8 +64,8 @@ public static function checkIP4($requestIP, $ip) return false; } - if (false !== strpos($ip, '/')) { - list($address, $netmask) = explode('/', $ip, 2); + if (false !== strpos((string) $ip, '/')) { + list($address, $netmask) = explode('/', (string) $ip, 2); if ($netmask === '0') { return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); @@ -79,7 +79,7 @@ public static function checkIP4($requestIP, $ip) $netmask = 32; } - return 0 === substr_compare(sprintf('%032b', ip2long($requestIP)), sprintf('%032b', ip2long($address)), 0, $netmask); + return 0 === substr_compare(sprintf('%032b', ip2long((string) $requestIP)), sprintf('%032b', ip2long((string) $address)), 0, $netmask); } /** * Compares two IPv6 addresses. @@ -102,8 +102,8 @@ public static function checkIP6($requestIP, $ip) throw new \RuntimeException('Unable to check IPv6. Check that PHP was not compiled with option "disable-ipv6".'); } - if (false !== strpos($ip, '/')) { - list($address, $netmask) = explode('/', $ip, 2); + if (false !== strpos((string) $ip, '/')) { + list($address, $netmask) = explode('/', (string) $ip, 2); if ($netmask < 1 || $netmask > 128) { return false; @@ -113,8 +113,8 @@ public static function checkIP6($requestIP, $ip) $netmask = 128; } - $bytesAddr = unpack('n*', @inet_pton($address)); - $bytesTest = unpack('n*', @inet_pton($requestIP)); + $bytesAddr = unpack('n*', @inet_pton((string) $address)); + $bytesTest = unpack('n*', @inet_pton((string) $requestIP)); if (!$bytesAddr || !$bytesTest) { return false; diff --git a/src/Core/BaseKernel.php b/src/Core/BaseKernel.php index 2265cc1b02a..b12cae1efbf 100644 --- a/src/Core/BaseKernel.php +++ b/src/Core/BaseKernel.php @@ -313,7 +313,7 @@ protected function detectLegacyEnvironment() { // Is there an _ss_environment.php file? if (!file_exists($this->basePath . '/_ss_environment.php') && - !file_exists(dirname($this->basePath) . '/_ss_environment.php') + !file_exists(dirname((string) $this->basePath) . '/_ss_environment.php') ) { return; } diff --git a/src/Core/ClassInfo.php b/src/Core/ClassInfo.php index 0c98e9cd863..f8198a194d6 100644 --- a/src/Core/ClassInfo.php +++ b/src/Core/ClassInfo.php @@ -76,8 +76,8 @@ public static function allClasses() */ public static function exists($class) { - return class_exists($class, false) - || interface_exists($class, false) + return class_exists((string) $class, false) + || interface_exists((string) $class, false) || ClassLoader::inst()->getItemPath($class); } @@ -112,7 +112,7 @@ public static function reset_db_cache() */ public static function getValidSubClasses($class = SiteTree::class, $includeUnbacked = false) { - if (is_string($class) && !class_exists($class)) { + if (is_string($class) && !class_exists((string) $class)) { return []; } @@ -137,7 +137,7 @@ public static function getValidSubClasses($class = SiteTree::class, $includeUnba */ public static function dataClassesFor($nameOrObject) { - if (is_string($nameOrObject) && !class_exists($nameOrObject)) { + if (is_string($nameOrObject) && !class_exists((string) $nameOrObject)) { return []; } @@ -149,7 +149,7 @@ public static function dataClassesFor($nameOrObject) ); // Filter by table - return array_filter($classes, function ($next) { + return array_filter($classes ?: [], function ($next) { return DataObject::getSchema()->classHasTable($next); }); } @@ -188,13 +188,13 @@ public static function baseDataClass($class) */ public static function subclassesFor($nameOrObject, $includeBaseClass = true) { - if (is_string($nameOrObject) && !class_exists($nameOrObject)) { + if (is_string($nameOrObject) && !class_exists((string) $nameOrObject)) { return []; } // Get class names $className = self::class_name($nameOrObject); - $lowerClassName = strtolower($className); + $lowerClassName = strtolower((string) $className); // Merge with descendants $descendants = ClassLoader::inst()->getManifest()->getDescendantsOf($className); @@ -219,7 +219,7 @@ public static function class_name($nameOrObject) return get_class($nameOrObject); } - $key = strtolower($nameOrObject); + $key = strtolower((string) $nameOrObject); if (!isset(static::$_cache_class_names[$key])) { // Get manifest name $name = ClassLoader::inst()->getManifest()->getItemName($nameOrObject); @@ -245,13 +245,13 @@ public static function class_name($nameOrObject) */ public static function ancestry($nameOrObject, $tablesOnly = false) { - if (is_string($nameOrObject) && !class_exists($nameOrObject)) { + if (is_string($nameOrObject) && !class_exists((string) $nameOrObject)) { return []; } $class = self::class_name($nameOrObject); - $lowerClass = strtolower($class); + $lowerClass = strtolower((string) $class); $cacheKey = $lowerClass . '_' . (string)$tablesOnly; $parent = $class; @@ -261,8 +261,8 @@ public static function ancestry($nameOrObject, $tablesOnly = false) if (!$tablesOnly || DataObject::getSchema()->classHasTable($parent)) { $ancestry[strtolower($parent)] = $parent; } - } while ($parent = get_parent_class($parent)); - self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry); + } while ($parent = get_parent_class($parent ?: '')); + self::$_cache_ancestry[$cacheKey] = array_reverse($ancestry ?: []); } return self::$_cache_ancestry[$cacheKey]; @@ -287,7 +287,7 @@ public static function implementorsOf($interfaceName) */ public static function classImplements($className, $interfaceName) { - $lowerClassName = strtolower($className); + $lowerClassName = strtolower((string) $className); $implementors = self::implementorsOf($interfaceName); return isset($implementors[$lowerClassName]); } @@ -307,7 +307,7 @@ public static function classes_for_file($filePath) $matchedClasses = []; foreach ($classes as $lowerClass => $compareFilePath) { - if (strcasecmp($absFilePath, $compareFilePath) === 0) { + if (strcasecmp((string) $absFilePath, (string) $compareFilePath) === 0) { $matchedClasses[$lowerClass] = $classNames[$lowerClass]; } } @@ -330,7 +330,7 @@ public static function classes_for_folder($folderPath) $matchedClasses = []; foreach ($classes as $lowerClass => $compareFilePath) { - if (stripos($compareFilePath, $absFolderPath) === 0) { + if (stripos((string) $compareFilePath, (string) $absFolderPath) === 0) { $matchedClasses[$lowerClass] = $classNames[$lowerClass]; } } @@ -348,9 +348,9 @@ public static function classes_for_folder($folderPath) */ public static function has_method_from($class, $method, $compclass) { - $lClass = strtolower($class); - $lMethod = strtolower($method); - $lCompclass = strtolower($compclass); + $lClass = strtolower((string) $class); + $lMethod = strtolower((string) $method); + $lCompclass = strtolower((string) $compclass); if (!isset(self::$_cache_methods[$lClass])) { self::$_cache_methods[$lClass] = []; } @@ -387,7 +387,7 @@ public static function table_for_object_field($candidateClass, $fieldName) public static function shortName($nameOrObject) { $name = static::class_name($nameOrObject); - $parts = explode('\\', $name); + $parts = explode('\\', (string) $name); return end($parts); } @@ -403,7 +403,7 @@ public static function hasMethod($object, $method) if (empty($object) || (!is_object($object) && !is_string($object))) { return false; } - if (method_exists($object, $method)) { + if (method_exists($object, (string) $method)) { return true; } return method_exists($object, 'hasMethod') && $object->hasMethod($method); @@ -528,7 +528,7 @@ public static function parse_class_spec($classSpec) $oldBucket = $bucket; // Fetch the key for the bucket at the top of the stack end($bucketStack); - $key = key($bucketStack); + $key = key($bucketStack ?: []); reset($bucketStack); // Re-instate the bucket from the top of the stack $bucket = &$bucketStack[$key]; @@ -552,7 +552,7 @@ public static function parse_class_spec($classSpec) if ($result === []) { // Fetch the key that the array was pushed to end($bucket); - $key = key($bucket); + $key = key($bucket ?: []); reset($bucket); // Store reference to "old" bucket in the stack $bucketStack[$key] = &$bucket; @@ -596,7 +596,7 @@ public static function classesWithExtension( } // only keep classes with the Extension applied - $classes = array_filter($classes, function ($class) use ($extensionClass) { + $classes = array_filter($classes ?: [], function ($class) use ($extensionClass) { return Extensible::has_extension($class, $extensionClass); }); diff --git a/src/Core/Config/ConfigLoader.php b/src/Core/Config/ConfigLoader.php index 620e1ae4e8d..0f364dda074 100644 --- a/src/Core/Config/ConfigLoader.php +++ b/src/Core/Config/ConfigLoader.php @@ -83,7 +83,7 @@ public function popManifest() */ public function countManifests() { - return count($this->manifests); + return count($this->manifests ?: []); } /** diff --git a/src/Core/Config/CoreConfigFactory.php b/src/Core/Config/CoreConfigFactory.php index 7ab037ae5fb..1af0172a2a9 100644 --- a/src/Core/Config/CoreConfigFactory.php +++ b/src/Core/Config/CoreConfigFactory.php @@ -105,7 +105,7 @@ protected function buildYamlTransformer() foreach ($modules as $module) { // Load from _config dirs $path = $module->getPath() . '/_config'; - if (is_dir($path)) { + if (is_dir((string) $path)) { $dirs[] = $path; } } @@ -152,11 +152,11 @@ public function buildYamlTransformerForPath($dirs) return true; }; $constantdefined = function ($const, $value = null) { - if (!defined($const)) { + if (!defined((string) $const)) { return false; } if ($value) { - return constant($const) === $value; + return constant((string) $const) === $value; } return true; }; diff --git a/src/Core/Config/Middleware/ExtensionMiddleware.php b/src/Core/Config/Middleware/ExtensionMiddleware.php index 3c2eb4173cb..2d85ae8502b 100644 --- a/src/Core/Config/Middleware/ExtensionMiddleware.php +++ b/src/Core/Config/Middleware/ExtensionMiddleware.php @@ -65,8 +65,8 @@ protected function getExtraConfig($class, $classConfig, $excludeMiddleware) foreach ($extensions as $extension) { list($extensionClass, $extensionArgs) = ClassInfo::parse_class_spec($extension); // Strip service name specifier - $extensionClass = strtok($extensionClass, '.'); - if (!class_exists($extensionClass)) { + $extensionClass = strtok((string) $extensionClass, '.'); + if (!class_exists((string) $extensionClass)) { throw new InvalidArgumentException("$class references nonexistent $extensionClass in 'extensions'"); } diff --git a/src/Core/Config/Middleware/InheritanceMiddleware.php b/src/Core/Config/Middleware/InheritanceMiddleware.php index 5b0bf1fd6f2..b61f88ce734 100644 --- a/src/Core/Config/Middleware/InheritanceMiddleware.php +++ b/src/Core/Config/Middleware/InheritanceMiddleware.php @@ -33,7 +33,7 @@ public function getClassConfig($class, $excludeMiddleware, $next) } // Skip if not a class or not parent class - $parent = class_exists($class) ? get_parent_class($class) : null; + $parent = class_exists((string) $class) ? get_parent_class($class) : null; if (!$parent) { return $config; } diff --git a/src/Core/Convert.php b/src/Core/Convert.php index db8f14bc438..4b751bee7ab 100644 --- a/src/Core/Convert.php +++ b/src/Core/Convert.php @@ -104,10 +104,10 @@ public static function raw2htmlid($val) } return trim( - preg_replace( + (string) preg_replace( '/_+/', '_', - preg_replace('/[^a-zA-Z0-9\-_:.]+/', '_', $val) + preg_replace('/[^a-zA-Z0-9\-_:.]+/', '_', $val ?: '') ?: '' ), '_' ); @@ -131,7 +131,7 @@ public static function raw2xml($val) return $val; } - return htmlspecialchars($val, ENT_QUOTES, 'UTF-8'); + return htmlspecialchars((string) $val, ENT_QUOTES, 'UTF-8'); } /** @@ -155,7 +155,7 @@ public static function raw2js($val) // Intercepts some characters such as <, >, and & which can interfere ["\\", '"', "\n", "\r", "'", '<', '>', '&'], ["\\\\", '\"', '\n', '\r', "\\'", "\\x3c", "\\x3e", "\\x26"], - $val + $val ?: '' ); } @@ -172,7 +172,7 @@ public static function raw2json($val, $options = 0) { Deprecation::notice('4.4', 'Please use json_encode() instead.'); - return json_encode($val, $options); + return json_encode($val, (int) $options); } /** @@ -187,7 +187,7 @@ public static function array2json($val, $options = 0) { Deprecation::notice('4.4', 'Please use json_encode() instead.'); - return json_encode($val, $options); + return json_encode($val, (int) $options); } /** @@ -253,11 +253,11 @@ public static function xml2raw($val) } // More complex text needs to use html2raw instead - if (strpos($val, '<') !== false) { + if (strpos((string) $val, '<') !== false) { return self::html2raw($val); } - return html_entity_decode($val, ENT_QUOTES, 'UTF-8'); + return html_entity_decode((string) $val, ENT_QUOTES, 'UTF-8'); } /** @@ -271,7 +271,7 @@ public static function json2obj($val) { Deprecation::notice('4.4', 'Please use json_decode() instead.'); - return json_decode($val); + return json_decode((string) $val); } /** @@ -285,7 +285,7 @@ public static function json2array($val) { Deprecation::notice('4.4', 'Please use json_decode() instead.'); - return json_decode($val, true); + return json_decode((string) $val, true); } /** @@ -308,14 +308,14 @@ public static function xml2array($val, $disableDoctypes = false, $disableExterna } // Check doctype - if ($disableDoctypes && preg_match('/\<\!DOCTYPE.+]\>/', $val)) { + if ($disableDoctypes && preg_match('/\<\!DOCTYPE.+]\>/', (string) $val)) { throw new InvalidArgumentException('XML Doctype parsing disabled'); } // Disable external entity loading $oldVal = null; if ($disableExternals) { - $oldVal = libxml_disable_entity_loader($disableExternals); + $oldVal = libxml_disable_entity_loader((bool) $disableExternals); } try { $xml = new SimpleXMLElement($val); @@ -327,7 +327,7 @@ public static function xml2array($val, $disableDoctypes = false, $disableExterna throw $ex; } if ($disableExternals) { - libxml_disable_entity_loader($oldVal); + libxml_disable_entity_loader((bool) $oldVal); } return $result; } @@ -354,7 +354,7 @@ protected static function recursiveXMLToArray($xml) $xml = get_object_vars($xml); } if (is_array($xml)) { - if (count($xml) === 0) { + if (count($xml ?: []) === 0) { return (string)$x; } // for CDATA $r = []; @@ -379,7 +379,7 @@ protected static function recursiveXMLToArray($xml) */ public static function linkIfMatch($string) { - if (preg_match('/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', $string)) { + if (preg_match('/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', (string) $string)) { return "$string"; } @@ -409,62 +409,62 @@ public static function html2raw($data, $preserveLinks = false, $wordWrap = 0, $c $config = $defaultConfig; } - $data = preg_replace("/][^>]*)?>.*?<\/style[^>]*>/is", '', $data); - $data = preg_replace("/][^>]*)?>.*?<\/script[^>]*>/is", '', $data); + $data = preg_replace("/][^>]*)?>.*?<\/style[^>]*>/is", '', $data ?: ''); + $data = preg_replace("/][^>]*)?>.*?<\/script[^>]*>/is", '', $data ?: ''); if ($config['ReplaceBoldAsterisk']) { - $data = preg_replace('%<(strong|b)( [^>]*)?>|%i', '*', $data); + $data = preg_replace('%<(strong|b)( [^>]*)?>|%i', '*', $data ?: ''); } // Expand hyperlinks if (!$preserveLinks && !$config['PreserveLinks']) { $data = preg_replace_callback('/]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ui', function ($matches) { return Convert::html2raw($matches[2]) . "[$matches[1]]"; - }, $data); + }, $data ?: ''); $data = preg_replace_callback('/]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ui', function ($matches) { return Convert::html2raw($matches[2]) . "[$matches[1]]"; - }, $data); + }, $data ?: ''); } // Replace images with their alt tags if ($config['ReplaceImagesWithAlt']) { - $data = preg_replace('/]*alt *= *"([^"]*)"[^>]*>/i', ' \\1 ', $data); - $data = preg_replace('/]*alt *= *([^ ]*)[^>]*>/i', ' \\1 ', $data); + $data = preg_replace('/]*alt *= *"([^"]*)"[^>]*>/i', ' \\1 ', $data ?: ''); + $data = preg_replace('/]*alt *= *([^ ]*)[^>]*>/i', ' \\1 ', $data ?: ''); } // Compress whitespace if ($config['CompressWhitespace']) { - $data = preg_replace("/\s+/u", ' ', $data); + $data = preg_replace("/\s+/u", ' ', $data ?: ''); } // Parse newline tags - $data = preg_replace("/\s*<[Hh][1-6]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data); - $data = preg_replace("/\s*<[Pp]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data); - $data = preg_replace("/\s*<[Dd][Ii][Vv]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data); - $data = preg_replace("/\n\n\n+/", "\n\n", $data); + $data = preg_replace("/\s*<[Hh][1-6]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data ?: ''); + $data = preg_replace("/\s*<[Pp]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data ?: ''); + $data = preg_replace("/\s*<[Dd][Ii][Vv]([^A-Za-z0-9>][^>]*)?> */u", "\n\n", $data ?: ''); + $data = preg_replace("/\n\n\n+/", "\n\n", $data ?: ''); - $data = preg_replace('/<[Bb][Rr]([^A-Za-z0-9>][^>]*)?> */', "\n", $data); - $data = preg_replace('/<[Tt][Rr]([^A-Za-z0-9>][^>]*)?> */', "\n", $data); - $data = preg_replace("/<\/[Tt][Dd]([^A-Za-z0-9>][^>]*)?> */", ' ', $data); - $data = preg_replace('/<\/p>/i', "\n\n", $data); + $data = preg_replace('/<[Bb][Rr]([^A-Za-z0-9>][^>]*)?> */', "\n", $data ?: ''); + $data = preg_replace('/<[Tt][Rr]([^A-Za-z0-9>][^>]*)?> */', "\n", $data ?: ''); + $data = preg_replace("/<\/[Tt][Dd]([^A-Za-z0-9>][^>]*)?> */", ' ', $data ?: ''); + $data = preg_replace('/<\/p>/i', "\n\n", $data ?: ''); // Replace HTML entities - $data = html_entity_decode($data, ENT_QUOTES, 'UTF-8'); + $data = html_entity_decode((string) $data, ENT_QUOTES, 'UTF-8'); // Remove all tags (but optionally keep links) // strip_tags seemed to be restricting the length of the output // arbitrarily. This essentially does the same thing. if (!$preserveLinks && !$config['PreserveLinks']) { - $data = preg_replace('/<\/?[^>]*>/', '', $data); + $data = preg_replace('/<\/?[^>]*>/', '', $data ?: ''); } else { - $data = strip_tags($data, ''); + $data = strip_tags((string) $data, ''); } // Wrap if ($wordWrap) { - $data = wordwrap(trim($data), $wordWrap); + $data = wordwrap(trim((string) $data), (int) $wordWrap); } - return trim($data); + return trim((string) $data); } /** @@ -483,7 +483,7 @@ public static function raw2mailto($data) return str_ireplace( ["\n",'?','=',' ','(',')','&','@','"','\'',';'], ['%0A','%3F','%3D','%20','%28','%29','%26','%40','%22','%27','%3B'], - $data + $data ?: '' ); } @@ -511,7 +511,7 @@ public static function raw2url($title) */ public static function nl2os($data, $nl = PHP_EOL) { - return preg_replace('~\R~u', $nl, $data); + return preg_replace('~\R~u', $nl ?: '', $data ?: ''); } /** @@ -523,7 +523,7 @@ public static function nl2os($data, $nl = PHP_EOL) */ public static function base64url_encode($val) { - return rtrim(strtr(base64_encode(json_encode($val)), '+/', '~_'), '='); + return rtrim(strtr(base64_encode((string) json_encode($val)), '+/', '~_'), '='); } /** @@ -535,7 +535,7 @@ public static function base64url_encode($val) public static function base64url_decode($val) { return json_decode( - base64_decode(str_pad(strtr($val, '~_', '+/'), strlen($val) % 4, '=', STR_PAD_RIGHT)), + (string) base64_decode(str_pad(strtr((string) $val, '~_', '+/'), strlen((string) $val) % 4, '=', STR_PAD_RIGHT)), true ); } @@ -557,7 +557,7 @@ public static function upperCamelToLowerCamel($str) { $return = null; $matches = null; - if (preg_match('/(^[A-Z]{1,})([A-Z]{1})([a-z]+.*)/', $str, $matches)) { + if (preg_match('/(^[A-Z]{1,})([A-Z]{1})([a-z]+.*)/', (string) $str, $matches)) { // If string has trailing lowercase after more than one leading uppercase characters, // match everything but the last leading uppercase character. $return = implode('', [ @@ -565,17 +565,17 @@ public static function upperCamelToLowerCamel($str) $matches[2], $matches[3] ]); - } elseif (preg_match('/(^[A-Z]{1})([a-z]+.*)/', $str, $matches)) { + } elseif (preg_match('/(^[A-Z]{1})([a-z]+.*)/', (string) $str, $matches)) { // If string has trailing lowercase after exactly one leading uppercase characters, // match everything but the last leading uppercase character. $return = implode('', [ strtolower($matches[1]), $matches[2] ]); - } elseif (preg_match('/^[A-Z]+$/', $str)) { + } elseif (preg_match('/^[A-Z]+$/', (string) $str)) { // If string has leading uppercase without trailing lowercase, // just lowerase the whole thing. - $return = strtolower($str); + $return = strtolower((string) $str); } else { // If string has no leading uppercase, just return. $return = $str; @@ -594,9 +594,9 @@ public static function upperCamelToLowerCamel($str) public static function memstring2bytes($memString) { // Remove non-unit characters from the size - $unit = preg_replace('/[^bkmgtpezy]/i', '', $memString); + $unit = preg_replace('/[^bkmgtpezy]/i', '', $memString ?: ''); // Remove non-numeric characters from the size - $size = preg_replace('/[^0-9\.\-]/', '', $memString); + $size = preg_replace('/[^0-9\.\-]/', '', $memString ?: ''); if ($unit) { // Find the position of the unit in the ordered string which is the power @@ -604,7 +604,7 @@ public static function memstring2bytes($memString) return (int)round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); } - return (int)round($size); + return (int)round($size ?: 0.0); } /** @@ -616,13 +616,13 @@ public static function bytes2memstring($bytes, $decimal = 0) { $scales = ['B','K','M','G','T','P','E','Z','Y']; // Get scale - $scale = (int)floor(log($bytes, 1024)); + $scale = (int)floor(log((float) $bytes, 1024)); if (!isset($scales[$scale])) { $scale = 2; } // Size - $num = round($bytes / pow(1024, $scale), $decimal); + $num = round($bytes / pow(1024, $scale), (int) $decimal); return $num . $scales[$scale]; } @@ -637,8 +637,8 @@ public static function bytes2memstring($bytes, $decimal = 0) public static function slashes($path, $separator = DIRECTORY_SEPARATOR, $multiple = true) { if ($multiple) { - return preg_replace('#[/\\\\]+#', $separator, $path); + return preg_replace('#[/\\\\]+#', $separator ?: '', $path ?: ''); } - return str_replace(['/', '\\'], $separator, $path); + return str_replace(['/', '\\'], $separator ?: '', $path ?: ''); } } diff --git a/src/Core/CoreKernel.php b/src/Core/CoreKernel.php index baa420f92de..3b1cf9a8c11 100644 --- a/src/Core/CoreKernel.php +++ b/src/Core/CoreKernel.php @@ -196,11 +196,11 @@ protected function getDatabaseName() $loopCount = (int)$chooseName; $databaseDir = $this->basePath; for ($i = 0; $i < $loopCount-1; $i++) { - $databaseDir = dirname($databaseDir); + $databaseDir = dirname((string) $databaseDir); } // Build name - $database = str_replace('.', '', basename($databaseDir)); + $database = str_replace('.', '', basename((string) $databaseDir)); $prefix = $this->getDatabasePrefix(); if ($prefix) { diff --git a/src/Core/CustomMethods.php b/src/Core/CustomMethods.php index 475dd99f683..afbb9dd1a98 100644 --- a/src/Core/CustomMethods.php +++ b/src/Core/CustomMethods.php @@ -145,7 +145,7 @@ protected function registerExtraMethodCallback($name, $callback) */ public function hasMethod($method) { - return method_exists($this, $method) || $this->getExtraMethodConfig($method); + return method_exists($this, (string) $method) || $this->getExtraMethodConfig($method); } /** @@ -177,7 +177,7 @@ public function allMethodNames($custom = false) { $class = static::class; if (!isset(self::$built_in_methods[$class])) { - self::$built_in_methods[$class] = array_map('strtolower', get_class_methods($this)); + self::$built_in_methods[$class] = array_map('strtolower', get_class_methods($this ?: '')); } if ($custom && isset(self::$extra_methods[$class])) { @@ -207,7 +207,7 @@ protected function findMethodsFromExtension($extension) } else { $class = get_class($extension); if (!isset(self::$built_in_methods[$class])) { - self::$built_in_methods[$class] = array_map('strtolower', get_class_methods($extension)); + self::$built_in_methods[$class] = array_map('strtolower', get_class_methods($extension ?: '')); } $methods = self::$built_in_methods[$class]; } @@ -248,7 +248,7 @@ protected function addMethodsFrom($property, $index = null) 'callSetOwnerFirst' => $extension instanceof Extension, ]; - $newMethods = array_fill_keys($methods, $methodInfo); + $newMethods = array_fill_keys($methods ?: [], $methodInfo); if (isset(self::$extra_methods[$class])) { self::$extra_methods[$class] = diff --git a/src/Core/Environment.php b/src/Core/Environment.php index 794ff742304..39d02b5dc87 100644 --- a/src/Core/Environment.php +++ b/src/Core/Environment.php @@ -65,7 +65,7 @@ public static function setVariables(array $vars) } $GLOBALS[$varName] = $varValue; } - if (array_key_exists('env', $vars)) { + if (array_key_exists('env', $vars ?: [])) { static::$env = $vars['env']; } } @@ -152,7 +152,7 @@ public static function increaseTimeLimitTo($timeLimit = null) $currTimeLimit = ini_get('max_execution_time'); // Only increase if its smaller if ($currTimeLimit > 0 && $currTimeLimit < $timeLimit) { - set_time_limit($timeLimit); + set_time_limit((int) $timeLimit); } } return true; @@ -206,7 +206,7 @@ public static function getEnv($name) public static function putEnv($string) { // Parse name-value pairs - $envVars = parse_ini_string($string) ?: []; + $envVars = parse_ini_string((string) $string) ?: []; foreach ($envVars as $name => $value) { self::setEnv($name, $value); } @@ -230,6 +230,6 @@ public static function setEnv($name, $value) */ public static function isCli() { - return in_array(strtolower(php_sapi_name()), ['cli', 'phpdbg']); + return in_array(strtolower((string) php_sapi_name()), ['cli', 'phpdbg']); } } diff --git a/src/Core/EnvironmentLoader.php b/src/Core/EnvironmentLoader.php index 6ec8333f711..415604b11c1 100644 --- a/src/Core/EnvironmentLoader.php +++ b/src/Core/EnvironmentLoader.php @@ -21,7 +21,7 @@ class EnvironmentLoader public function loadFile($path, $overload = false) { // Not readable - if (!file_exists($path) || !is_readable($path)) { + if (!file_exists((string) $path) || !is_readable((string) $path)) { return null; } diff --git a/src/Core/Extensible.php b/src/Core/Extensible.php index 48de82f65d2..02f43b533ba 100644 --- a/src/Core/Extensible.php +++ b/src/Core/Extensible.php @@ -178,11 +178,11 @@ public static function add_extension($classOrExtension, $extension = null) $extension = $classOrExtension; } - if (!preg_match('/^([^(]*)/', $extension, $matches)) { + if (!preg_match('/^([^(]*)/', (string) $extension, $matches)) { return false; } $extensionClass = $matches[1]; - if (!class_exists($extensionClass)) { + if (!class_exists((string) $extensionClass)) { throw new InvalidArgumentException(sprintf( 'Object::add_extension() - Can\'t find extension class for "%s"', $extensionClass @@ -242,8 +242,8 @@ public static function remove_extension($extension) $config = Config::inst()->get($class, 'extensions', Config::EXCLUDE_EXTRA_SOURCES | Config::UNINHERITED) ?: []; foreach ($config as $key => $candidate) { // extensions with parameters will be stored in config as ExtensionName("Param"). - if (strcasecmp($candidate, $extension) === 0 || - stripos($candidate, $extension . '(') === 0 + if (strcasecmp((string) $candidate, (string) $extension) === 0 || + stripos((string) $candidate, $extension . '(') === 0 ) { $found = true; unset($config[$key]); @@ -287,7 +287,7 @@ public static function get_extensions($class = null, $includeArgumentString = fa } // Clean nullified named extensions - $extensions = array_filter(array_values($extensions)); + $extensions = array_filter(array_values($extensions ?: [])); if ($includeArgumentString) { return $extensions; @@ -336,16 +336,16 @@ public static function get_extra_config_sources($class = null) foreach ($extensions as $extension) { [$extensionClass, $extensionArgs] = ClassInfo::parse_class_spec($extension); // Strip service name specifier - $extensionClass = strtok($extensionClass, '.'); + $extensionClass = strtok((string) $extensionClass, '.'); $sources[] = $extensionClass; - if (!class_exists($extensionClass)) { + if (!class_exists((string) $extensionClass)) { throw new InvalidArgumentException("$class references nonexistent $extensionClass in \$extensions"); } call_user_func([$extensionClass, 'add_to_class'], $class, $extensionClass, $extensionArgs); - foreach (array_reverse(ClassInfo::ancestry($extensionClass)) as $extensionClassParent) { + foreach (array_reverse(ClassInfo::ancestry($extensionClass) ?: []) as $extensionClassParent) { if (ClassInfo::has_method_from($extensionClassParent, 'get_extra_config', $extensionClassParent)) { $extras = $extensionClassParent::get_extra_config($class, $extensionClass, $extensionArgs); if ($extras) { @@ -382,10 +382,10 @@ public static function has_extension($classOrExtension, $requiredExtension = nul $requiredExtension = Extension::get_classname_without_arguments($requiredExtension); $extensions = self::get_extensions($class); foreach ($extensions as $extension) { - if (strcasecmp($extension, $requiredExtension) === 0) { + if (strcasecmp((string) $extension, (string) $requiredExtension) === 0) { return true; } - if (!$strict && is_subclass_of($extension, $requiredExtension)) { + if (!$strict && is_subclass_of($extension, (string) $requiredExtension)) { return true; } $inst = Injector::inst()->get($extension); @@ -415,7 +415,7 @@ public static function has_extension($classOrExtension, $requiredExtension = nul public function invokeWithExtensions($method, &$a1 = null, &$a2 = null, &$a3 = null, &$a4 = null, &$a5 = null, &$a6 = null, &$a7 = null) { $result = []; - if (method_exists($this, $method)) { + if (method_exists($this, (string) $method)) { $thisResult = $this->$method($a1, $a2, $a3, $a4, $a5, $a6, $a7); if ($thisResult !== null) { $result[] = $thisResult; @@ -462,7 +462,7 @@ public function extend($method, &$a1 = null, &$a2 = null, &$a3 = null, &$a4 = nu } foreach ($this->getExtensionInstances() as $instance) { - if (method_exists($instance, $method)) { + if (method_exists($instance, (string) $method)) { try { $instance->setOwner($this); $value = $instance->$method($a1, $a2, $a3, $a4, $a5, $a6, $a7); @@ -497,12 +497,12 @@ public function extend($method, &$a1 = null, &$a2 = null, &$a3 = null, &$a4 = nu public function getExtensionInstance($extension) { $instances = $this->getExtensionInstances(); - if (array_key_exists($extension, $instances)) { + if (array_key_exists($extension, $instances ?: [])) { return $instances[$extension]; } // in case Injector has been used to replace an extension foreach ($instances as $instance) { - if (is_a($instance, $extension)) { + if (is_a($instance, (string) $extension)) { return $instance; } } @@ -555,11 +555,11 @@ public function getExtensionInstances() foreach ($extensions as $extension) { $name = $extension; // Allow service names of the form "%$ServiceName" - if (substr($name, 0, 2) == '%$') { - $name = substr($name, 2); + if (substr((string) $name, 0, 2) == '%$') { + $name = substr((string) $name, 2); } - $name = trim(strtok($name, '(')); - if (class_exists($name)) { + $name = trim((string) strtok((string) $name, '(')); + if (class_exists((string) $name)) { $name = ClassInfo::class_name($name); } $this->extension_instances[$name] = Injector::inst()->get($extension); diff --git a/src/Core/Extension.php b/src/Core/Extension.php index c7fe7f6c319..f7d1fd1fffd 100644 --- a/src/Core/Extension.php +++ b/src/Core/Extension.php @@ -113,6 +113,6 @@ public function getOwner() public static function get_classname_without_arguments($extensionStr) { // Split out both args and service name - return strtok(strtok($extensionStr, '('), '.'); + return strtok((string) strtok((string) $extensionStr, '('), '.'); } } diff --git a/src/Core/Injector/AopProxyService.php b/src/Core/Injector/AopProxyService.php index dba659c7d61..5517623615b 100644 --- a/src/Core/Injector/AopProxyService.php +++ b/src/Core/Injector/AopProxyService.php @@ -24,7 +24,7 @@ public function __construct() public function __call($method, $args) { - if (method_exists($this->proxied, $method)) { + if (method_exists($this->proxied, (string) $method)) { $continue = true; $result = null; @@ -47,7 +47,7 @@ public function __call($method, $args) } if ($continue) { - $result = call_user_func_array([$this->proxied, $method], $args); + $result = call_user_func_array([$this->proxied, $method], $args ?: []); if (isset($this->afterCall[$method])) { $methods = $this->afterCall[$method]; diff --git a/src/Core/Injector/InjectionCreator.php b/src/Core/Injector/InjectionCreator.php index 0a43bf1363b..99d2b90ebc9 100644 --- a/src/Core/Injector/InjectionCreator.php +++ b/src/Core/Injector/InjectionCreator.php @@ -9,11 +9,11 @@ class InjectionCreator implements Factory { public function create($class, array $params = []) { - if (!class_exists($class)) { + if (!class_exists((string) $class)) { throw new InjectorNotFoundException("Class {$class} does not exist"); } // Ensure there are no string keys as they cannot be unpacked with the `...` operator - $values = array_values($params); + $values = array_values($params ?: []); return new $class(...$values); } } diff --git a/src/Core/Injector/Injector.php b/src/Core/Injector/Injector.php index 7dcfea32434..e1433cafc70 100644 --- a/src/Core/Injector/Injector.php +++ b/src/Core/Injector/Injector.php @@ -419,7 +419,7 @@ public function load($config = []) } // okay, actually include it now we know we're going to use it - if (file_exists($file)) { + if (file_exists((string) $file)) { require_once $file; } @@ -518,13 +518,13 @@ public function convertServiceProperty($value) } // Evaluate service references - if (is_string($value) && strpos($value, '%$') === 0) { - $id = substr($value, 2); + if (is_string($value) && strpos((string) $value, '%$') === 0) { + $id = substr((string) $value, 2); return $this->get($id); } // Evaluate constants surrounded by back ticks - if (preg_match('/^`(?[^`]+)`$/', $value, $matches)) { + if (preg_match('/^`(?[^`]+)`$/', (string) $value, $matches)) { $envValue = Environment::getEnv($matches['name']); if ($envValue !== false) { $value = $envValue; @@ -684,7 +684,7 @@ public function inject($object, $asType = null) $objectMethod, $this->convertServiceProperty( isset($method[1]) ? $method[1] : [] - ) + ) ?: [] ); } } @@ -704,7 +704,7 @@ public function inject($object, $asType = null) /* @var $propertyObject ReflectionProperty */ if ($propertyObject->isPublic() && !$propertyObject->getValue($object)) { $origName = $propertyObject->getName(); - $name = ucfirst($origName); + $name = ucfirst((string) $origName); if ($this->has($name)) { // Pull the name out of the registry $value = $this->get($name); @@ -720,8 +720,8 @@ public function inject($object, $asType = null) foreach ($methods as $methodObj) { /* @var $methodObj ReflectionMethod */ $methName = $methodObj->getName(); - if (strpos($methName, 'set') === 0) { - $pname = substr($methName, 3); + if (strpos((string) $methName, 'set') === 0) { + $pname = substr((string) $methName, 3); if ($this->has($pname)) { // Pull the name out of the registry $value = $this->get($pname); @@ -734,7 +734,7 @@ public function inject($object, $asType = null) $injections = Config::inst()->get(get_class($object), 'dependencies'); // If the type defines some injections, set them here - if ($injections && count($injections)) { + if ($injections && count($injections ?: [])) { foreach ($injections as $property => $value) { // we're checking empty in case it already has a property at this name // this doesn't catch privately set things, but they will only be set by a setter method, @@ -860,7 +860,7 @@ public function getServiceName($name) // okay, check whether we've got a compound name - don't worry about 0 index, cause that's an // invalid name - if (!strpos($name, '.')) { + if (!strpos((string) $name, '.')) { return null; } @@ -918,7 +918,7 @@ public function unregisterObjects($types) foreach ($this->serviceCache as $key => $object) { foreach ($types as $filterClass) { // Prevent destructive flushing - if (strcasecmp($filterClass, 'object') === 0) { + if (strcasecmp((string) $filterClass, 'object') === 0) { throw new InvalidArgumentException("Global unregistration is not allowed"); } if ($object instanceof $filterClass) { @@ -1014,11 +1014,11 @@ protected function getNamedService($name, $asSingleton = true, $constructorArgs protected function normaliseArguments($name, $args = []) { // Allow service names of the form "%$ServiceName" - if (substr($name, 0, 2) == '%$') { - $name = substr($name, 2); + if (substr((string) $name, 0, 2) == '%$') { + $name = substr((string) $name, 2); } - if (strstr($name, '(')) { + if (strstr((string) $name, '(')) { list($name, $extraArgs) = ClassInfo::parse_class_spec($name); if ($args) { $args = array_merge($args, $extraArgs); @@ -1026,7 +1026,7 @@ protected function normaliseArguments($name, $args = []) $args = $extraArgs; } } - $name = trim($name); + $name = trim((string) $name); return [$name, $args]; } @@ -1078,7 +1078,7 @@ public function getServiceSpec($name, $inherit = true) } // Fail over to parent service if allowed - if (!$inherit || !strpos($name, '.')) { + if (!$inherit || !strpos((string) $name, '.')) { return null; } diff --git a/src/Core/Injector/InjectorLoader.php b/src/Core/Injector/InjectorLoader.php index 623a70411ad..89c7b4b5997 100644 --- a/src/Core/Injector/InjectorLoader.php +++ b/src/Core/Injector/InjectorLoader.php @@ -82,7 +82,7 @@ public function popManifest() */ public function countManifests() { - return count($this->manifests); + return count($this->manifests ?: []); } /** diff --git a/src/Core/Injector/SilverStripeServiceConfigurationLocator.php b/src/Core/Injector/SilverStripeServiceConfigurationLocator.php index 0d1b02927b2..ae82ca35588 100644 --- a/src/Core/Injector/SilverStripeServiceConfigurationLocator.php +++ b/src/Core/Injector/SilverStripeServiceConfigurationLocator.php @@ -29,8 +29,8 @@ public function locateConfigFor($name) } // If config is in `%$Source` format then inherit from the named config - if (is_string($config) && stripos($config, '%$') === 0) { - $name = substr($config, 2); + if (is_string($config) && stripos((string) $config, '%$') === 0) { + $name = substr((string) $config, 2); return $this->locateConfigFor($name); } @@ -47,7 +47,7 @@ public function locateConfigFor($name) protected function configFor($name) { // Return cached result - if (array_key_exists($name, $this->configs)) { + if (array_key_exists($name, $this->configs ?: [])) { return $this->configs[$name]; } diff --git a/src/Core/Manifest/ClassContentRemover.php b/src/Core/Manifest/ClassContentRemover.php index 77bbfe0351d..bb4e7f1f5f3 100644 --- a/src/Core/Manifest/ClassContentRemover.php +++ b/src/Core/Manifest/ClassContentRemover.php @@ -23,18 +23,18 @@ public static function remove_class_content($filePath, $cutOffDepth = 1) { // Use PHP's built in method to strip comments and whitespace - $contents = php_strip_whitespace($filePath); + $contents = php_strip_whitespace((string) $filePath); - if (!trim($contents)) { + if (!trim((string) $contents)) { return $contents; } - if (!preg_match('/\b(?:class|interface|trait)/i', $contents)) { + if (!preg_match('/\b(?:class|interface|trait)/i', (string) $contents)) { return ''; } // tokenize the file contents - $tokens = token_get_all($contents); + $tokens = token_get_all((string) $contents); $cleanContents = ''; $depth = 0; $startCounting = false; @@ -81,6 +81,6 @@ public static function remove_class_content($filePath, $cutOffDepth = 1) } // return cleaned class - return trim($cleanContents); + return trim((string) $cleanContents); } } diff --git a/src/Core/Manifest/ClassLoader.php b/src/Core/Manifest/ClassLoader.php index f36c2520df1..69a652cc4bd 100644 --- a/src/Core/Manifest/ClassLoader.php +++ b/src/Core/Manifest/ClassLoader.php @@ -102,7 +102,7 @@ public function loadClass($class) */ public function getItemPath($class) { - foreach (array_reverse($this->manifests) as $manifest) { + foreach (array_reverse($this->manifests ?: []) as $manifest) { /** @var ClassManifest $manifestInst */ $manifestInst = $manifest['instance']; if ($path = $manifestInst->getItemPath($class)) { diff --git a/src/Core/Manifest/ClassManifest.php b/src/Core/Manifest/ClassManifest.php index a09fd06d859..0860d5883af 100644 --- a/src/Core/Manifest/ClassManifest.php +++ b/src/Core/Manifest/ClassManifest.php @@ -332,7 +332,7 @@ public function getVisitor() */ public function getItemPath($name) { - $lowerName = strtolower($name); + $lowerName = strtolower((string) $name); foreach ([ $this->classes, $this->interfaces, @@ -353,7 +353,7 @@ public function getItemPath($name) */ public function getItemName($name) { - $lowerName = strtolower($name); + $lowerName = strtolower((string) $name); foreach ([ $this->classNames, $this->interfaceNames, @@ -429,8 +429,8 @@ public function getDescendantsOf($class) $class = get_class($class); } - $lClass = strtolower($class); - if (array_key_exists($lClass, $this->descendants)) { + $lClass = strtolower((string) $class); + if (array_key_exists($lClass, $this->descendants ?: [])) { return $this->descendants[$lClass]; } @@ -477,8 +477,8 @@ public function getImplementors() */ public function getImplementorsOf($interface) { - $lowerInterface = strtolower($interface); - if (array_key_exists($lowerInterface, $this->implementors)) { + $lowerInterface = strtolower((string) $interface); + if (array_key_exists($lowerInterface, $this->implementors ?: [])) { return $this->implementors[$lowerInterface]; } else { return []; @@ -550,7 +550,7 @@ public function handleFile($basename, $pathname, $includeTests) // files will have changed and TokenisedRegularExpression is quite // slow. A combination of the file name and file contents hash are used, // since just using the datetime lead to problems with upgrading. - $key = preg_replace('/[^a-zA-Z0-9_]/', '_', $basename) . '_' . md5_file($pathname); + $key = preg_replace('/[^a-zA-Z0-9_]/', '_', $basename ?: '') . '_' . md5_file((string) $pathname); // Attempt to load from cache // Note: $classes, $interfaces and $traits arrays have correct-case keys, not lowercase @@ -583,8 +583,8 @@ public function handleFile($basename, $pathname, $includeTests) // Merge raw class data into global list foreach ($classes as $className => $classInfo) { - $lowerClassName = strtolower($className); - if (array_key_exists($lowerClassName, $this->classes)) { + $lowerClassName = strtolower((string) $className); + if (array_key_exists($lowerClassName, $this->classes ?: [])) { throw new Exception(sprintf( 'There are two files containing the "%s" class: "%s" and "%s"', $className, @@ -595,7 +595,7 @@ public function handleFile($basename, $pathname, $includeTests) // Skip if implements TestOnly, but doesn't include tests $lowerInterfaces = array_map('strtolower', $classInfo['interfaces']); - if (!$includeTests && in_array(strtolower(TestOnly::class), $lowerInterfaces)) { + if (!$includeTests && in_array(strtolower(TestOnly::class), $lowerInterfaces ?: [])) { $changed = true; unset($classes[$className]); continue; @@ -607,7 +607,7 @@ public function handleFile($basename, $pathname, $includeTests) // Add to children if ($classInfo['extends']) { foreach ($classInfo['extends'] as $ancestor) { - $lowerAncestor = strtolower($ancestor); + $lowerAncestor = strtolower((string) $ancestor); if (!isset($this->children[$lowerAncestor])) { $this->children[$lowerAncestor] = []; } @@ -619,7 +619,7 @@ public function handleFile($basename, $pathname, $includeTests) // Load interfaces foreach ($classInfo['interfaces'] as $interface) { - $lowerInterface = strtolower($interface); + $lowerInterface = strtolower((string) $interface); if (!isset($this->implementors[$lowerInterface])) { $this->implementors[$lowerInterface] = []; } @@ -629,14 +629,14 @@ public function handleFile($basename, $pathname, $includeTests) // Merge all found interfaces into list foreach ($interfaces as $interfaceName => $interfaceInfo) { - $lowerInterface = strtolower($interfaceName); + $lowerInterface = strtolower((string) $interfaceName); $this->interfaces[$lowerInterface] = $pathname; $this->interfaceNames[$lowerInterface] = $interfaceName; } // Merge all traits foreach ($traits as $traitName => $traitInfo) { - $lowerTrait = strtolower($traitName); + $lowerTrait = strtolower((string) $traitName); $this->traits[$lowerTrait] = $pathname; $this->traitNames[$lowerTrait] = $traitName; } @@ -662,7 +662,7 @@ public function handleFile($basename, $pathname, $includeTests) protected function coalesceDescendants($class) { // Reset descendents to immediate children initially - $lowerClass = strtolower($class); + $lowerClass = strtolower((string) $class); if (empty($this->children[$lowerClass])) { return []; } @@ -736,7 +736,7 @@ protected function validateItemCache($data) } // Detect legacy cache keys (non-associative) $array = $data[$key]; - if (!empty($array) && is_numeric(key($array))) { + if (!empty($array) && is_numeric(key($array ?: []))) { return false; } } diff --git a/src/Core/Manifest/ManifestFileFinder.php b/src/Core/Manifest/ManifestFileFinder.php index 46fa5ea0947..06e3591670a 100644 --- a/src/Core/Manifest/ManifestFileFinder.php +++ b/src/Core/Manifest/ManifestFileFinder.php @@ -49,7 +49,7 @@ public function acceptDir($basename, $pathname, $depth) $inVendor = $this->isInsideVendor($basename, $pathname, $depth); if ($inVendor) { // Skip nested vendor folders (e.g. vendor/silverstripe/framework/vendor) - if ($depth == 4 && basename($pathname) === self::VENDOR_DIR) { + if ($depth == 4 && basename((string) $pathname) === self::VENDOR_DIR) { return false; } @@ -60,7 +60,7 @@ public function acceptDir($basename, $pathname, $depth) // Stop searching if we are in a non-module library $libraryPath = $this->upLevels($pathname, $depth - 3); - $libraryBase = basename($libraryPath); + $libraryBase = basename((string) $libraryPath); if (!$this->isDirectoryModule($libraryBase, $libraryPath, 3)) { return false; } @@ -79,7 +79,7 @@ public function acceptDir($basename, $pathname, $depth) // Skip if test dir inside vendor module with unexpected CI Configuration if ($depth > 3 && $basename === self::TESTS_DIR && $ignoredCIConfig = $this->getOption('ignored_ci_configs')) { $ciLib = $this->findModuleCIPhpConfiguration($basename, $pathname, $depth); - if (in_array($ciLib, $ignoredCIConfig)) { + if (in_array($ciLib, $ignoredCIConfig ?: [])) { return false; } } @@ -97,7 +97,7 @@ public function acceptDir($basename, $pathname, $depth) */ public function isInsideVendor($basename, $pathname, $depth) { - $base = basename($this->upLevels($pathname, $depth - 1)); + $base = basename((string) $this->upLevels($pathname, $depth - 1)); return $base === self::VENDOR_DIR; } @@ -111,7 +111,7 @@ public function isInsideVendor($basename, $pathname, $depth) */ public function isInsideThemes($basename, $pathname, $depth) { - $base = basename($this->upLevels($pathname, $depth - 1)); + $base = basename((string) $this->upLevels($pathname, $depth - 1)); return $base === THEMES_DIR; } @@ -162,8 +162,8 @@ protected function anyParents($basename, $pathname, $depth, $callback) if ($ignored) { return true; } - $pathname = dirname($pathname); - $basename = basename($pathname); + $pathname = dirname((string) $pathname); + $basename = basename((string) $pathname); $depth--; } return false; @@ -211,7 +211,7 @@ protected function upLevels($pathname, $depth) return null; } while ($depth--) { - $pathname = dirname($pathname); + $pathname = dirname((string) $pathname); } return $pathname; } @@ -251,7 +251,7 @@ public function isDirectoryIgnored($basename, $pathname, $depth) // Check if directory name is ignored $ignored = $this->getIgnoredDirs(); - if (in_array($basename, $ignored)) { + if (in_array($basename, $ignored ?: [])) { return true; } @@ -278,8 +278,8 @@ private function findModuleCIPhpConfiguration(string $basename, string $pathname } // We pop the current folder and use the next entry the pathname - $newBasename = basename($pathname); - $newPathname = dirname($pathname); + $newBasename = basename((string) $pathname); + $newPathname = dirname((string) $pathname); $newDepth = $depth - 1; if ($this->isDirectoryModule($newBasename, $newPathname, $newDepth)) { diff --git a/src/Core/Manifest/Module.php b/src/Core/Manifest/Module.php index 8a408fa1329..e6b8e39741a 100644 --- a/src/Core/Manifest/Module.php +++ b/src/Core/Manifest/Module.php @@ -138,13 +138,13 @@ public function getShortName() // Strip from full composer name $composerName = $this->getComposerName(); if ($composerName) { - list(, $name) = explode('/', $composerName); + list(, $name) = explode('/', (string) $composerName); return $name; } } // Base name of directory - return basename($this->path); + return basename((string) $this->path); } /** @@ -182,7 +182,7 @@ public function getRelativePath() if ($this->path === $this->basePath) { return ''; } - return substr($this->path, strlen($this->basePath) + 1); + return substr((string) $this->path, strlen((string) $this->basePath) + 1); } public function __serialize(): array @@ -223,7 +223,7 @@ public function serialize() */ public function unserialize($serialized) { - list($this->path, $this->basePath, $this->composerData) = json_decode($serialized, true); + list($this->path, $this->basePath, $this->composerData) = json_decode((string) $serialized, true); $this->resources = []; } @@ -233,7 +233,7 @@ public function unserialize($serialized) public function activate() { $config = "{$this->path}/_config.php"; - if (file_exists($config)) { + if (file_exists((string) $config)) { requireFile($config); } } @@ -245,9 +245,9 @@ protected function loadComposer() { // Load composer data $path = "{$this->path}/composer.json"; - if (file_exists($path)) { - $content = file_get_contents($path); - $result = json_decode($content, true); + if (file_exists((string) $path)) { + $content = file_get_contents((string) $path); + $result = json_decode((string) $content, true); if (json_last_error()) { $errorMessage = json_last_error_msg(); throw new Exception("$path: $errorMessage"); @@ -433,7 +433,7 @@ private function constraintSatisfies( } // Let's see if we are using an exact version constraint. e.g. ~1.2.3 or 1.2.3 or ~1.2 or 1.2.* - if (preg_match("/^~?$majorVersionFallback(\.(\d+)|\*){0,2}/", $constraint)) { + if (preg_match("/^~?$majorVersionFallback(\.(\d+)|\*){0,2}/", (string) $constraint)) { return true; } diff --git a/src/Core/Manifest/ModuleLoader.php b/src/Core/Manifest/ModuleLoader.php index a27fccb5141..1ab0cca7c17 100644 --- a/src/Core/Manifest/ModuleLoader.php +++ b/src/Core/Manifest/ModuleLoader.php @@ -83,7 +83,7 @@ public function popManifest() */ public function countManifests() { - return count($this->manifests); + return count($this->manifests ?: []); } /** diff --git a/src/Core/Manifest/ModuleManifest.php b/src/Core/Manifest/ModuleManifest.php index 3aca00e8235..185b3beb09f 100644 --- a/src/Core/Manifest/ModuleManifest.php +++ b/src/Core/Manifest/ModuleManifest.php @@ -76,7 +76,7 @@ class ModuleManifest public function __construct($base, CacheFactory $cacheFactory = null) { $this->base = $base; - $this->cacheKey = sha1($base) . '_modules'; + $this->cacheKey = sha1((string) $base) . '_modules'; $this->cacheFactory = $cacheFactory; } @@ -150,7 +150,7 @@ public function activateConfig() $modules = $this->getModules(); // Work in reverse priority, so the higher priority modules get later execution /** @var Module $module */ - foreach (array_reverse($modules) as $module) { + foreach (array_reverse($modules ?: []) as $module) { $module->activate(); } } @@ -206,9 +206,9 @@ public function getModule($name) } // Fall back to lookup by shortname - if (!strstr($name, '/')) { + if (!strstr((string) $name, '/')) { foreach ($this->modules as $module) { - if (strcasecmp($module->getShortName(), $name) === 0) { + if (strcasecmp((string) $module->getShortName(), (string) $name) === 0) { return $module; } } @@ -260,7 +260,7 @@ public function sort() public function getModuleByPath($path) { // Ensure path exists - $path = realpath($path); + $path = realpath((string) $path); if (!$path) { return null; } @@ -273,12 +273,12 @@ public function getModuleByPath($path) foreach ($modules as $module) { // Check if path is in module - $modulePath = realpath($module->getPath()); + $modulePath = realpath((string) $module->getPath()); // if there is a real path if ($modulePath) { // we remove separator to ensure that we are comparing fairly - $modulePath = rtrim($modulePath, DIRECTORY_SEPARATOR); - $path = rtrim($path, DIRECTORY_SEPARATOR); + $modulePath = rtrim((string) $modulePath, DIRECTORY_SEPARATOR); + $path = rtrim((string) $path, DIRECTORY_SEPARATOR); // if the paths are not the same if ($modulePath !== $path) { //add separator to avoid mixing up, for example: @@ -286,7 +286,7 @@ public function getModuleByPath($path) $modulePath .= DIRECTORY_SEPARATOR; $path .= DIRECTORY_SEPARATOR; // if the module path is not the same as the start of the module path being tested - if (stripos($path, $modulePath) !== 0) { + if (stripos((string) $path, (string) $modulePath) !== 0) { // then we need to test the next module continue; } diff --git a/src/Core/Manifest/ModuleResource.php b/src/Core/Manifest/ModuleResource.php index 16e794845ce..5aa116bac56 100644 --- a/src/Core/Manifest/ModuleResource.php +++ b/src/Core/Manifest/ModuleResource.php @@ -110,7 +110,7 @@ public function Link() */ public function exists() { - return file_exists($this->getPath()); + return file_exists((string) $this->getPath()); } /** diff --git a/src/Core/Manifest/ModuleResourceLoader.php b/src/Core/Manifest/ModuleResourceLoader.php index a865c7a4cbf..70e28a63cdd 100644 --- a/src/Core/Manifest/ModuleResourceLoader.php +++ b/src/Core/Manifest/ModuleResourceLoader.php @@ -96,7 +96,7 @@ public static function get_template_global_variables() public function resolveResource($resource) { // String of the form vendor/package:resource. Excludes "http://bla" as that's an absolute URL - if (!preg_match('#^ *(?[^/: ]+/[^/: ]+) *: *(?[^ ]*)$#', $resource, $matches)) { + if (!preg_match('#^ *(?[^/: ]+/[^/: ]+) *: *(?[^ ]*)$#', (string) $resource, $matches)) { return $resource; } $module = $matches['module']; diff --git a/src/Core/Manifest/PrioritySorter.php b/src/Core/Manifest/PrioritySorter.php index 8ac39aee658..7570dece7f1 100644 --- a/src/Core/Manifest/PrioritySorter.php +++ b/src/Core/Manifest/PrioritySorter.php @@ -94,7 +94,7 @@ public function getSortedList() $this->addVariables(); // Find all items that don't have their order specified by the config system - $unspecified = array_diff($this->names, $this->priorities); + $unspecified = array_diff($this->names ?: [], $this->priorities); if (!empty($unspecified)) { $this->includeRest($unspecified); @@ -132,7 +132,7 @@ public function setPriorities(array $priorities) public function setItems(array $items) { $this->items = $items; - $this->names = array_keys($items); + $this->names = array_keys($items ?: []); return $this; } @@ -170,15 +170,15 @@ public function setRestKey($key) protected function addVariables() { // Remove variables from the list - $varValues = array_values($this->variables); - $this->names = array_filter($this->names, function ($name) use ($varValues) { - return !in_array($name, $varValues); + $varValues = array_values($this->variables ?: []); + $this->names = array_filter($this->names ?: [], function ($name) use ($varValues) { + return !in_array($name, $varValues ?: []); }); // Replace variables with their values $this->priorities = array_map(function ($name) { return $this->resolveValue($name); - }, $this->priorities); + }, $this->priorities ?: []); } /** @@ -189,10 +189,10 @@ protected function includeRest(array $list) { $otherItemsIndex = false; if ($this->restKey) { - $otherItemsIndex = array_search($this->restKey, $this->priorities); + $otherItemsIndex = array_search($this->restKey, $this->priorities ?: []); } if ($otherItemsIndex !== false) { - array_splice($this->priorities, $otherItemsIndex, 1, $list); + array_splice($this->priorities, (int) $otherItemsIndex, 1, $list); } else { // Otherwise just jam them on the end $this->priorities = array_merge($this->priorities, $list); diff --git a/src/Core/Manifest/VersionProvider.php b/src/Core/Manifest/VersionProvider.php index 4e05edec74c..2fab51d9d2a 100644 --- a/src/Core/Manifest/VersionProvider.php +++ b/src/Core/Manifest/VersionProvider.php @@ -53,7 +53,7 @@ public function getVersion() $lockModules = $this->getModuleVersionFromComposer(array_keys($modules)); $moduleVersions = []; foreach ($modules as $module => $title) { - if (!array_key_exists($module, $lockModules)) { + if (!array_key_exists($module, $lockModules ?: [])) { continue; } $version = $lockModules[$module]; @@ -152,7 +152,7 @@ private function filterModules(array $modules) { $accountModule = []; foreach ($modules as $module => $value) { - if (!preg_match('#^([a-z0-9\-]+)/([a-z0-9\-]+)$#', $module, $m)) { + if (!preg_match('#^([a-z0-9\-]+)/([a-z0-9\-]+)$#', (string) $module, $m)) { continue; } $account = $m[1]; @@ -189,7 +189,7 @@ public function getModuleVersionFromComposer($modules = []) $lockData = $this->getComposerLock(); if ($lockData && !empty($lockData['packages'])) { foreach ($lockData['packages'] as $package) { - if (in_array($package['name'], $modules) && isset($package['version'])) { + if (in_array($package['name'], $modules ?: []) && isset($package['version'])) { $versions[$package['name']] = $package['version']; } } @@ -206,23 +206,23 @@ public function getModuleVersionFromComposer($modules = []) protected function getComposerLock($cache = true) { $composerLockPath = $this->getComposerLockPath(); - if (!file_exists($composerLockPath)) { + if (!file_exists((string) $composerLockPath)) { return []; } $lockData = []; - $jsonData = file_get_contents($composerLockPath); + $jsonData = file_get_contents((string) $composerLockPath); if ($cache) { $cache = Injector::inst()->get(CacheInterface::class . '.VersionProvider_composerlock'); - $cacheKey = md5($jsonData); + $cacheKey = md5((string) $jsonData); if ($versions = $cache->get($cacheKey)) { - $lockData = json_decode($versions, true); + $lockData = json_decode((string) $versions, true); } } if (empty($lockData) && $jsonData) { - $lockData = json_decode($jsonData, true); + $lockData = json_decode((string) $jsonData, true); if ($cache) { $cache->set($cacheKey, $jsonData); diff --git a/src/Core/Path.php b/src/Core/Path.php index f243f233525..02dac590680 100644 --- a/src/Core/Path.php +++ b/src/Core/Path.php @@ -25,16 +25,16 @@ class Path public static function join(...$parts) { // In case $parts passed as an array in first parameter - if (count($parts) === 1 && is_array($parts[0])) { + if (count($parts ?: []) === 1 && is_array($parts[0])) { $parts = $parts[0]; } // Cleanup and join all parts - $parts = array_filter(array_map('trim', $parts)); + $parts = array_filter(array_map('trim', $parts ?: [])); $fullPath = static::normalise(implode(DIRECTORY_SEPARATOR, $parts)); // Protect against directory traversal vulnerability (OTG-AUTHZ-001) - if (strpos($fullPath, '..') !== false) { + if (strpos((string) $fullPath, '..') !== false) { throw new InvalidArgumentException('Can not collapse relative folders'); } @@ -51,11 +51,11 @@ public static function join(...$parts) */ public static function normalise($path, $relative = false) { - $path = trim(Convert::slashes($path)); + $path = trim((string) Convert::slashes($path)); if ($relative) { - return trim($path, self::TRIM_CHARS); + return trim((string) $path, (string) self::TRIM_CHARS); } else { - return rtrim($path, self::TRIM_CHARS); + return rtrim((string) $path, (string) self::TRIM_CHARS); } } } diff --git a/src/Core/Startup/AbstractConfirmationToken.php b/src/Core/Startup/AbstractConfirmationToken.php index 59f09be57b9..432bb33aa08 100644 --- a/src/Core/Startup/AbstractConfirmationToken.php +++ b/src/Core/Startup/AbstractConfirmationToken.php @@ -61,7 +61,7 @@ public static function prepare_tokens($keys, HTTPRequest $request) */ protected function pathForToken($token) { - return TEMP_PATH . DIRECTORY_SEPARATOR . 'token_' . preg_replace('/[^a-z0-9]+/', '', $token); + return TEMP_PATH . DIRECTORY_SEPARATOR . 'token_' . preg_replace('/[^a-z0-9]+/', '', $token ?: ''); } /** @@ -76,7 +76,7 @@ protected function genToken() $token = $rg->randomToken('md5'); // Store a file in the session save path (safer than /tmp, as open_basedir might limit that) - file_put_contents($this->pathForToken($token), $token); + file_put_contents((string) $this->pathForToken($token), $token); return $token; } @@ -107,9 +107,9 @@ protected function checkToken($token) $file = $this->pathForToken($token); $content = null; - if (file_exists($file)) { - $content = file_get_contents($file); - unlink($file); + if (file_exists((string) $file)) { + $content = file_get_contents((string) $file); + unlink((string) $file); } return $content === $token; diff --git a/src/Core/Startup/ConfirmationTokenChain.php b/src/Core/Startup/ConfirmationTokenChain.php index c2dd28bd33a..59ae73db2f8 100644 --- a/src/Core/Startup/ConfirmationTokenChain.php +++ b/src/Core/Startup/ConfirmationTokenChain.php @@ -155,7 +155,7 @@ public function getRedirectUrlParams() */ protected function redirectURL() { - $params = http_build_query($this->getRedirectUrlParams()); + $params = http_build_query($this->getRedirectUrlParams() ?: []); return Controller::join_links($this->getRedirectUrlBase(), '?' . $params); } diff --git a/src/Core/Startup/DeployFlushDiscoverer.php b/src/Core/Startup/DeployFlushDiscoverer.php index 27cbcde6123..c16df56c9c8 100644 --- a/src/Core/Startup/DeployFlushDiscoverer.php +++ b/src/Core/Startup/DeployFlushDiscoverer.php @@ -88,11 +88,11 @@ protected function getDeployResource() */ protected function getDeployTimestamp($resource) { - if (!file_exists($resource)) { + if (!file_exists((string) $resource)) { return 0; } - return max(filemtime($resource), filectime($resource)); + return max(filemtime((string) $resource), filectime((string) $resource)); } /** diff --git a/src/Core/Startup/ErrorControlChain.php b/src/Core/Startup/ErrorControlChain.php index ded5b0b785c..2aab56b978a 100644 --- a/src/Core/Startup/ErrorControlChain.php +++ b/src/Core/Startup/ErrorControlChain.php @@ -182,7 +182,7 @@ protected function lastErrorWasMemoryExhaustion() { $error = error_get_last(); $message = $error ? $error['message'] : ''; - return stripos($message, 'memory') !== false && stripos($message, 'exhausted') !== false; + return stripos((string) $message, 'memory') !== false && stripos((string) $message, 'exhausted') !== false; } static $transtable = [ @@ -193,7 +193,7 @@ protected function lastErrorWasMemoryExhaustion() protected function translateMemstring($memString) { - $char = strtolower(substr($memString, -1)); + $char = strtolower(substr((string) $memString, -1)); $fact = isset(self::$transtable[$char]) ? self::$transtable[$char] : 1; return ((int)$memString) * $fact; } diff --git a/src/Core/Startup/ErrorControlChainMiddleware.php b/src/Core/Startup/ErrorControlChainMiddleware.php index 9deee43b8e9..f69d9a9f012 100644 --- a/src/Core/Startup/ErrorControlChainMiddleware.php +++ b/src/Core/Startup/ErrorControlChainMiddleware.php @@ -138,9 +138,9 @@ protected function safeReloadWithTokens(HTTPRequest $request, ConfirmationTokenC // Fail and redirect the user to the login page $params = array_merge($request->getVars(), $confirmationTokenChain->params(false)); - $backURL = $confirmationTokenChain->getRedirectUrlBase() . '?' . http_build_query($params); + $backURL = $confirmationTokenChain->getRedirectUrlBase() . '?' . http_build_query($params ?: []); $loginPage = Director::absoluteURL(Security::config()->get('login_url')); - $loginPage .= "?BackURL=" . urlencode($backURL); + $loginPage .= "?BackURL=" . urlencode((string) $backURL); $result = new HTTPResponse(); $result->redirect($loginPage); return $result; diff --git a/src/Core/Startup/ParameterConfirmationToken.php b/src/Core/Startup/ParameterConfirmationToken.php index 02ddcc0bd6b..7b184a6c973 100644 --- a/src/Core/Startup/ParameterConfirmationToken.php +++ b/src/Core/Startup/ParameterConfirmationToken.php @@ -71,13 +71,13 @@ public function __construct($parameterName, HTTPRequest $request) protected function backURLToken(HTTPRequest $request) { $backURL = $request->getVar('BackURL'); - if (!strstr($backURL, '?')) { + if (!strstr((string) $backURL, '?')) { return null; } // Filter backURL if it contains the given request parameter - list(,$query) = explode('?', $backURL); - parse_str($query, $queryArgs); + list(,$query) = explode('?', (string) $backURL); + parse_str((string) $query, $queryArgs); $name = $this->getName(); if (isset($queryArgs[$name])) { return $queryArgs[$name]; @@ -158,7 +158,7 @@ public function getRedirectUrlParams() protected function redirectURL() { - $query = http_build_query($this->getRedirectUrlParams()); + $query = http_build_query($this->getRedirectUrlParams() ?: []); return Controller::join_links($this->getRedirectUrlBase(), '?' . $query); } } diff --git a/src/Core/Startup/RequestFlushDiscoverer.php b/src/Core/Startup/RequestFlushDiscoverer.php index 73f2bb7d5dc..216ed3c36ab 100644 --- a/src/Core/Startup/RequestFlushDiscoverer.php +++ b/src/Core/Startup/RequestFlushDiscoverer.php @@ -51,7 +51,7 @@ protected function lookupRequest() { $request = $this->request; - $getVar = array_key_exists('flush', $request->getVars()); + $getVar = array_key_exists('flush', $request->getVars() ?: []); $devBuild = $request->getURL() === 'dev/build'; // WARNING! diff --git a/src/Core/Startup/URLConfirmationToken.php b/src/Core/Startup/URLConfirmationToken.php index 75bfa8c440a..f9670e9adb3 100644 --- a/src/Core/Startup/URLConfirmationToken.php +++ b/src/Core/Startup/URLConfirmationToken.php @@ -46,7 +46,7 @@ public function __construct($urlToCheck, HTTPRequest $request) $this->request = $request; $this->currentURL = $request->getURL(false); - $this->tokenParameterName = preg_replace('/[^a-z0-9]/i', '', $urlToCheck) . 'token'; + $this->tokenParameterName = preg_replace('/[^a-z0-9]/i', '', $urlToCheck ?: '') . 'token'; $this->urlExistsInBackURL = $this->getURLExistsInBackURL($request); // If the token provided is valid, mark it as such @@ -62,8 +62,8 @@ public function __construct($urlToCheck, HTTPRequest $request) */ protected function getURLExistsInBackURL(HTTPRequest $request) { - $backURL = ltrim($request->getVar('BackURL'), '/'); - return (strpos($backURL, $this->urlToCheck) === 0); + $backURL = ltrim((string) $request->getVar('BackURL'), '/'); + return (strpos((string) $backURL, (string) $this->urlToCheck) === 0); } /** @@ -135,7 +135,7 @@ public function getRedirectUrlParams() protected function redirectURL() { - $query = http_build_query($this->getRedirectUrlParams()); + $query = http_build_query($this->getRedirectUrlParams() ?: []); return Controller::join_links($this->getRedirectUrlBase(), '?' . $query); } } diff --git a/src/Core/TempFolder.php b/src/Core/TempFolder.php index 78d923e80cc..95b956c381e 100644 --- a/src/Core/TempFolder.php +++ b/src/Core/TempFolder.php @@ -22,8 +22,8 @@ public static function getTempFolder($base) // The actual temp folder is a subfolder of getTempParentFolder(), named by username $subfolder = Path::join($parent, static::getTempFolderUsername()); - if (!@file_exists($subfolder)) { - mkdir($subfolder); + if (!@file_exists((string) $subfolder)) { + mkdir((string) $subfolder); } return $subfolder; @@ -53,7 +53,7 @@ public static function getTempFolderUsername() if (!$user) { $user = 'unknown'; } - $user = preg_replace('/[^A-Za-z0-9_\-]/', '', $user); + $user = preg_replace('/[^A-Za-z0-9_\-]/', '', $user ?: ''); return $user; } @@ -70,9 +70,9 @@ protected static function getTempParentFolder($base) { // first, try finding a silverstripe-cache dir built off the base path $localPath = Path::join($base, 'silverstripe-cache'); - if (@file_exists($localPath)) { - if ((fileperms($localPath) & 0777) != 0777) { - @chmod($localPath, 0777); + if (@file_exists((string) $localPath)) { + if ((fileperms((string) $localPath) & 0777) != 0777) { + @chmod((string) $localPath, 0777); } return $localPath; } @@ -83,30 +83,30 @@ protected static function getTempParentFolder($base) 'silverstripe-cache-php' . preg_replace('/[^\w\-\.+]+/', '-', PHP_VERSION) . str_replace([' ', '/', ':', '\\'], '-', $base) ); - if (!@file_exists($tempPath)) { + if (!@file_exists((string) $tempPath)) { $oldUMask = umask(0); - @mkdir($tempPath, 0777); + @mkdir((string) $tempPath, 0777); umask($oldUMask); // if the folder already exists, correct perms } else { - if ((fileperms($tempPath) & 0777) != 0777) { - @chmod($tempPath, 0777); + if ((fileperms((string) $tempPath) & 0777) != 0777) { + @chmod((string) $tempPath, 0777); } } - $worked = @file_exists($tempPath) && @is_writable($tempPath); + $worked = @file_exists((string) $tempPath) && @is_writable((string) $tempPath); // failing to use the system path, attempt to create a local silverstripe-cache dir if (!$worked) { $tempPath = $localPath; - if (!@file_exists($tempPath)) { + if (!@file_exists((string) $tempPath)) { $oldUMask = umask(0); - @mkdir($tempPath, 0777); + @mkdir((string) $tempPath, 0777); umask($oldUMask); } - $worked = @file_exists($tempPath) && @is_writable($tempPath); + $worked = @file_exists((string) $tempPath) && @is_writable((string) $tempPath); } if (!$worked) { diff --git a/src/Dev/Backtrace.php b/src/Dev/Backtrace.php index 8e681d631ae..f549c390b96 100644 --- a/src/Dev/Backtrace.php +++ b/src/Dev/Backtrace.php @@ -98,7 +98,7 @@ public static function filter_backtrace($bt, $ignoredFunctions = null) } } - while ($bt && in_array(self::full_func_name($bt[0]), $defaultIgnoredFunctions)) { + while ($bt && in_array(self::full_func_name($bt[0]), $defaultIgnoredFunctions ?: [])) { array_shift($bt); } @@ -117,7 +117,7 @@ public static function filter_backtrace($bt, $ignoredFunctions = null) } } } else { - if (in_array($bt[$i]['function'], $ignoredArgs)) { + if (in_array($bt[$i]['function'], $ignoredArgs ?: [])) { $match = true; } } @@ -178,7 +178,7 @@ public static function full_func_name($item, $showArgs = false, $argCharLimit = foreach ($item['args'] as $arg) { if (!is_object($arg) || method_exists($arg, '__toString')) { $sarg = is_array($arg) ? 'Array' : strval($arg); - $args[] = (strlen($sarg) > $argCharLimit) ? substr($sarg, 0, $argCharLimit) . '...' : $sarg; + $args[] = (strlen((string) $sarg) > $argCharLimit) ? substr($sarg, 0, $argCharLimit) . '...' : $sarg; } else { $args[] = get_class($arg); } @@ -218,7 +218,7 @@ public static function get_rendered_backtrace($bt, $plainText = false, $ignoredF } else { $name = self::full_func_name($item, true); } - $result .= "
  • " . htmlentities($name, ENT_COMPAT, 'UTF-8') . "\n
    \n"; + $result .= "
  • " . htmlentities((string) $name, ENT_COMPAT, 'UTF-8') . "\n
    \n"; $result .= isset($item['file']) ? htmlentities(basename($item['file']), ENT_COMPAT, 'UTF-8') : ''; $result .= isset($item['line']) ? ":$item[line]" : ''; $result .= "
  • \n"; diff --git a/src/Dev/BulkLoader_Result.php b/src/Dev/BulkLoader_Result.php index 79271c0757f..6aa18480289 100644 --- a/src/Dev/BulkLoader_Result.php +++ b/src/Dev/BulkLoader_Result.php @@ -62,9 +62,10 @@ class BulkLoader_Result implements \Countable * * @return int */ + #[\ReturnTypeWillChange] public function Count() { - return count($this->created) + count($this->updated); + return count($this->created ?: []) + count($this->updated ?: []); } /** @@ -72,7 +73,7 @@ public function Count() */ public function CreatedCount() { - return count($this->created); + return count($this->created ?: []); } /** @@ -80,7 +81,7 @@ public function CreatedCount() */ public function UpdatedCount() { - return count($this->updated); + return count($this->updated ?: []); } /** @@ -88,7 +89,7 @@ public function UpdatedCount() */ public function DeletedCount() { - return count($this->deleted); + return count($this->deleted ?: []); } /** diff --git a/src/Dev/CSSContentParser.php b/src/Dev/CSSContentParser.php index 238a0f0b0af..789c493dcd2 100644 --- a/src/Dev/CSSContentParser.php +++ b/src/Dev/CSSContentParser.php @@ -45,14 +45,14 @@ public function __construct($content) 'utf8' ); $tidy->cleanRepair(); - $tidy = str_replace('xmlns="http://www.w3.org/1999/xhtml"', '', $tidy); - $tidy = str_replace(' ', '', $tidy); + $tidy = str_replace('xmlns="http://www.w3.org/1999/xhtml"', '', $tidy ?: ''); + $tidy = str_replace(' ', '', $tidy ?: ''); } elseif (@shell_exec('which tidy')) { // using tiny through cli - $CLI_content = escapeshellarg($content); + $CLI_content = escapeshellarg((string) $content); $tidy = `echo $CLI_content | tidy --force-output 1 -n -q -utf8 -asxhtml -w 99999 2> /dev/null`; - $tidy = str_replace('xmlns="http://www.w3.org/1999/xhtml"', '', $tidy); - $tidy = str_replace(' ', '', $tidy); + $tidy = str_replace('xmlns="http://www.w3.org/1999/xhtml"', '', $tidy ?: ''); + $tidy = str_replace(' ', '', $tidy ?: ''); } else { // no tidy library found, hence no sanitizing $tidy = $content; @@ -65,7 +65,7 @@ public function __construct($content) return null; }); } - $this->simpleXML = @simplexml_load_string($tidy, 'SimpleXMLElement', LIBXML_NOWARNING); + $this->simpleXML = @simplexml_load_string((string) $tidy, 'SimpleXMLElement', LIBXML_NOWARNING); if (!$this->simpleXML) { throw new Exception('CSSContentParser::__construct(): Could not parse content.' . ' Please check the PHP extension tidy is installed.'); @@ -106,19 +106,19 @@ public function getByXpath($xpath) */ public function selector2xpath($selector) { - $parts = preg_split('/\\s+/', $selector); + $parts = preg_split('/\\s+/', (string) $selector); $xpath = ""; foreach ($parts as $part) { - if (preg_match('/^([A-Za-z][A-Za-z0-9]*)/', $part, $matches)) { + if (preg_match('/^([A-Za-z][A-Za-z0-9]*)/', (string) $part, $matches)) { $xpath .= "//$matches[1]"; } else { $xpath .= "//*"; } $xfilters = []; - if (preg_match('/#([^#.\[]+)/', $part, $matches)) { + if (preg_match('/#([^#.\[]+)/', (string) $part, $matches)) { $xfilters[] = "@id='$matches[1]'"; } - if (preg_match('/\.([^#.\[]+)/', $part, $matches)) { + if (preg_match('/\.([^#.\[]+)/', (string) $part, $matches)) { $xfilters[] = "contains(@class,'$matches[1]')"; } if ($xfilters) { diff --git a/src/Dev/CSVParser.php b/src/Dev/CSVParser.php index 1b23a866194..dd50e73d148 100644 --- a/src/Dev/CSVParser.php +++ b/src/Dev/CSVParser.php @@ -169,7 +169,7 @@ public function provideHeaderRow($headerRow) protected function openFile() { ini_set('auto_detect_line_endings', 1); - $this->fileHandle = fopen($this->filename, 'r'); + $this->fileHandle = fopen((string) $this->filename, 'r'); if ($this->providedHeaderRow) { $this->headerRow = $this->remapHeader($this->providedHeaderRow); @@ -200,8 +200,8 @@ protected function fetchCSVHeader() $srcRow = fgetcsv( $this->fileHandle, 0, - $this->delimiter, - $this->enclosure + (string) $this->delimiter, + (string) $this->enclosure ); $this->headerRow = $this->remapHeader($srcRow); @@ -248,8 +248,8 @@ protected function fetchCSVRow() $srcRow = fgetcsv( $this->fileHandle, 0, - $this->delimiter, - $this->enclosure + (string) $this->delimiter, + (string) $this->enclosure ); if ($srcRow) { @@ -260,12 +260,12 @@ protected function fetchCSVRow() $value = str_replace( ['\\' . $this->enclosure,'\\' . $this->delimiter], [$this->enclosure, $this->delimiter], - $value + $value ?: '' ); // Trim leading tab // [SS-2017-007] Ensure all cells with leading [@=+] have a leading tab - $value = ltrim($value, "\t"); - if (array_key_exists($i, $this->headerRow)) { + $value = ltrim((string) $value, "\t"); + if (array_key_exists($i, $this->headerRow ?: [])) { if ($this->headerRow[$i]) { $row[$this->headerRow[$i]] = $value; } @@ -295,6 +295,7 @@ public function __destruct() /** * @ignore */ + #[\ReturnTypeWillChange] public function rewind() { $this->closeFile(); @@ -304,6 +305,7 @@ public function rewind() /** * @ignore */ + #[\ReturnTypeWillChange] public function current() { return $this->currentRow; @@ -312,6 +314,7 @@ public function current() /** * @ignore */ + #[\ReturnTypeWillChange] public function key() { return $this->rowNum; @@ -320,6 +323,7 @@ public function key() /** * @ignore */ + #[\ReturnTypeWillChange] public function next() { $this->fetchCSVRow(); @@ -330,6 +334,7 @@ public function next() /** * @ignore */ + #[\ReturnTypeWillChange] public function valid() { return $this->currentRow ? true : false; diff --git a/src/Dev/CliDebugView.php b/src/Dev/CliDebugView.php index 14478160b69..324b94d8100 100644 --- a/src/Dev/CliDebugView.php +++ b/src/Dev/CliDebugView.php @@ -69,7 +69,7 @@ public function renderSourceFragment($lines, $errline) foreach ($lines as $offset => $line) { $output .= ($offset == $errline) ? "* " : " "; $output .= str_pad("$offset:", 5); - $output .= wordwrap($line, self::config()->columns, "\n "); + $output .= wordwrap((string) $line, (int) self::config()->columns, "\n "); } $output .= "\n"; @@ -92,7 +92,7 @@ public function renderTrace($trace = null) public function renderParagraph($text) { - return wordwrap($text, self::config()->columns) . "\n\n"; + return wordwrap((string) $text, (int) self::config()->columns) . "\n\n"; } /** @@ -105,10 +105,10 @@ public function renderParagraph($text) */ public function renderInfo($title, $subtitle, $description = null) { - $output = wordwrap(strtoupper($title), self::config()->columns) . "\n"; - $output .= wordwrap($subtitle, self::config()->columns) . "\n"; - $output .= str_repeat('-', min(self::config()->columns, max(strlen($title), strlen($subtitle)))) . "\n"; - $output .= wordwrap($description, self::config()->columns) . "\n\n"; + $output = wordwrap(strtoupper((string) $title), (int) self::config()->columns) . "\n"; + $output .= wordwrap((string) $subtitle, (int) self::config()->columns) . "\n"; + $output .= str_repeat('-', (int) min(self::config()->columns, max(strlen((string) $title), strlen((string) $subtitle)))) . "\n"; + $output .= wordwrap((string) $description, (int) self::config()->columns) . "\n\n"; return $output; } @@ -121,7 +121,7 @@ public function renderVariable($val, $caller) $output .= CLI::text($this->formatCaller($caller), 'blue', null, true); $output .= PHP_EOL . PHP_EOL; if (is_string($val)) { - $output .= wordwrap($val, self::config()->columns); + $output .= wordwrap((string) $val, (int) self::config()->columns); } else { $output .= var_export($val, true); } @@ -186,7 +186,7 @@ public function debugVariableText($val) // Format text if (is_string($val)) { - return wordwrap($val, self::config()->columns); + return wordwrap((string) $val, (int) self::config()->columns); } // Other diff --git a/src/Dev/Constraint/SSListContains.php b/src/Dev/Constraint/SSListContains.php index c5d2a9ebdbf..94cb0495c01 100644 --- a/src/Dev/Constraint/SSListContains.php +++ b/src/Dev/Constraint/SSListContains.php @@ -222,7 +222,7 @@ public function evaluate($other, $description = '', $returnResult = false) } //we have remaining matches? - if (count($this->matches) !== 0) { + if (count($this->matches ?: []) !== 0) { $success = false; $this->hasLeftoverItems = true; } @@ -272,8 +272,8 @@ public function toString() $matchesToString = function ($matches) use ($matchToString) { $matchesAsString = implode(' and ', array_map( $matchToString, - array_keys($matches), - array_values($matches) + array_keys($matches ?: []), + array_values($matches ?: []) )); return '(' . $matchesAsString . ')'; @@ -281,7 +281,7 @@ public function toString() $allMatchesAsString = implode( "\n or ", - array_map($matchesToString, $this->matches) + array_map($matchesToString, $this->matches ?: []) ); diff --git a/src/Dev/Constraint/SSListContainsOnly.php b/src/Dev/Constraint/SSListContainsOnly.php index eb11b53703b..fea7924b28c 100644 --- a/src/Dev/Constraint/SSListContainsOnly.php +++ b/src/Dev/Constraint/SSListContainsOnly.php @@ -66,7 +66,7 @@ public function evaluate($other, $description = '', $returnResult = false): ?boo } //we have remaining matches? - if (!$this->itemNotMatching && count($this->matches) !== 0) { + if (!$this->itemNotMatching && count($this->matches ?: []) !== 0) { $success = false; $this->hasLeftoverItems = true; } diff --git a/src/Dev/Constraint/SSListContainsOnlyMatchingItems.php b/src/Dev/Constraint/SSListContainsOnlyMatchingItems.php index b370cb09481..e71f3a47995 100644 --- a/src/Dev/Constraint/SSListContainsOnlyMatchingItems.php +++ b/src/Dev/Constraint/SSListContainsOnlyMatchingItems.php @@ -188,6 +188,6 @@ public function evaluate($other, $description = '', $returnResult = false) */ public function toString() { - return 'contains only Objects where "' . key($this->match) . '" is "' . current($this->match) . '"'; + return 'contains only Objects where "' . key($this->match ?: []) . '" is "' . current($this->match ?: []) . '"'; } } diff --git a/src/Dev/Constraint/ViewableDataContains.php b/src/Dev/Constraint/ViewableDataContains.php index 88776f4c7f9..b8a19b00234 100644 --- a/src/Dev/Constraint/ViewableDataContains.php +++ b/src/Dev/Constraint/ViewableDataContains.php @@ -201,6 +201,6 @@ public function evaluate($other, $description = '', $returnResult = false) */ public function toString() { - return 'contains only Objects where "' . key($this->match) . '" is "' . current($this->match) . '"'; + return 'contains only Objects where "' . key($this->match ?: []) . '" is "' . current($this->match ?: []) . '"'; } } diff --git a/src/Dev/CsvBulkLoader.php b/src/Dev/CsvBulkLoader.php index 07b12b867e4..555b6595668 100644 --- a/src/Dev/CsvBulkLoader.php +++ b/src/Dev/CsvBulkLoader.php @@ -91,8 +91,8 @@ protected function processAll($filepath, $preview = false) $tabExtractor = function ($row, $rowOffset) { foreach ($row as &$item) { // [SS-2017-007] Ensure all cells with leading tab and then [@=+] have the tab removed on import - if (preg_match("/^\t[\-@=\+]+.*/", $item)) { - $item = ltrim($item, "\t"); + if (preg_match("/^\t[\-@=\+]+.*/", (string) $item)) { + $item = ltrim((string) $item, "\t"); } } return $row; @@ -107,8 +107,8 @@ protected function processAll($filepath, $preview = false) if ($column == $renamedColumn) { continue; } - if (array_key_exists($column, $row)) { - if (strpos($renamedColumn, '_ignore_') !== 0) { + if (array_key_exists($column, $row ?: [])) { + if (strpos((string) $renamedColumn, '_ignore_') !== 0) { $row[$renamedColumn] = $row[$column]; } unset($row[$column]); @@ -157,7 +157,7 @@ protected function getNormalisedColumnMap() { $map = []; foreach ($this->columnMap as $column => $newColumn) { - if (strpos($newColumn, "->") === 0) { + if (strpos((string) $newColumn, "->") === 0) { $map[$column] = $column; } elseif (is_null($newColumn)) { // the column map must consist of unique scalar values @@ -192,14 +192,14 @@ protected function splitFile($path, $lines = null) $new = $this->getNewSplitFileName(); - $to = fopen($new, 'w+'); - $from = fopen($path, 'r'); + $to = fopen((string) $new, 'w+'); + $from = fopen((string) $path, 'r'); $header = null; if ($this->hasHeaderRow) { $header = fgets($from); - fwrite($to, $header); + fwrite($to, (string) $header); } $files = []; @@ -208,7 +208,7 @@ protected function splitFile($path, $lines = null) $count = 0; while (!feof($from)) { - fwrite($to, fgets($from)); + fwrite($to, (string) fgets($from)); $count++; @@ -218,11 +218,11 @@ protected function splitFile($path, $lines = null) // get a new temporary file name, to write the next lines to $new = $this->getNewSplitFileName(); - $to = fopen($new, 'w+'); + $to = fopen((string) $new, 'w+'); if ($this->hasHeaderRow) { // add the headers to the new file - fwrite($to, $header); + fwrite($to, (string) $header); } $files[] = $new; @@ -244,7 +244,7 @@ protected function splitFile($path, $lines = null) protected function getNewSplitFileName() { Deprecation::notice('5.0', 'getNewSplitFileName is deprecated, please name your files yourself'); - return TEMP_PATH . DIRECTORY_SEPARATOR . uniqid(str_replace('\\', '_', static::class), true) . '.csv'; + return TEMP_PATH . DIRECTORY_SEPARATOR . uniqid((string) str_replace('\\', '_', static::class), true) . '.csv'; } /** @@ -271,7 +271,7 @@ protected function processChunk($filepath, $preview = false) // same callback $map = []; foreach ($this->columnMap as $k => $v) { - if (strpos($v, "->") === 0) { + if (strpos((string) $v, "->") === 0) { $map[$k] = $k; } else { $map[$k] = $v; @@ -347,9 +347,9 @@ protected function processRecord($record, $columnMap, &$results, $preview = fals $obj->write(); $obj->flushCache(); // avoid relation caching confusion } - } elseif (strpos($fieldName, '.') !== false) { + } elseif (strpos((string) $fieldName, '.') !== false) { // we have a relation column with dot notation - [$relationName, $columnName] = explode('.', $fieldName); + [$relationName, $columnName] = explode('.', (string) $fieldName); // always gives us an component (either empty or existing) $relationObj = $obj->getComponent($relationName); if (!$preview) { diff --git a/src/Dev/Debug.php b/src/Dev/Debug.php index de62fe76bc6..f8c2d080bfb 100644 --- a/src/Dev/Debug.php +++ b/src/Dev/Debug.php @@ -179,17 +179,17 @@ protected static function supportsHTML(HTTPRequest $request = null) $accepted = $request->getAcceptMimetypes(false); // Explicit opt in - if (in_array('text/html', $accepted)) { + if (in_array('text/html', $accepted ?: [])) { return true; }; // Implicit opt-out - if (in_array('application/json', $accepted)) { + if (in_array('application/json', $accepted ?: [])) { return false; } // Fallback to wildcard comparison - if (in_array('*/*', $accepted)) { + if (in_array('*/*', $accepted ?: [])) { return true; } return false; diff --git a/src/Dev/DebugView.php b/src/Dev/DebugView.php index fd0f7792273..79b7efd5b3a 100644 --- a/src/Dev/DebugView.php +++ b/src/Dev/DebugView.php @@ -124,12 +124,12 @@ class DebugView */ public function Breadcrumbs() { - $basePath = str_replace(Director::protocolAndHost(), '', Director::absoluteBaseURL()); + $basePath = str_replace(Director::protocolAndHost() ?: '', '', Director::absoluteBaseURL() ?: ''); $relPath = parse_url( - substr($_SERVER['REQUEST_URI'], strlen($basePath), strlen($_SERVER['REQUEST_URI'])), + substr($_SERVER['REQUEST_URI'], strlen((string) $basePath), strlen($_SERVER['REQUEST_URI'])), PHP_URL_PATH ); - $parts = explode('/', $relPath); + $parts = explode('/', (string) $relPath); $base = Director::absoluteBaseURL(); $pathPart = ""; $pathLinks = []; @@ -277,9 +277,9 @@ public function renderFooter() public function renderError($httpRequest, $errno, $errstr, $errfile, $errline) { $errorType = isset(self::$error_types[$errno]) ? self::$error_types[$errno] : self::$unknown_error; - $httpRequestEnt = htmlentities($httpRequest, ENT_COMPAT, 'UTF-8'); + $httpRequestEnt = htmlentities((string) $httpRequest, ENT_COMPAT, 'UTF-8'); if (ini_get('html_errors')) { - $errstr = strip_tags($errstr); + $errstr = strip_tags((string) $errstr); } else { $errstr = Convert::raw2xml($errstr); } @@ -304,7 +304,7 @@ public function renderSourceFragment($lines, $errline) $output = '

    Source

    '; $output .= '
    ';
             foreach ($lines as $offset => $line) {
    -            $line = htmlentities($line, ENT_COMPAT, 'UTF-8');
    +            $line = htmlentities((string) $line, ENT_COMPAT, 'UTF-8');
                 if ($offset == $errline) {
                     $output .= "$offset $line";
                 } else {
    @@ -370,7 +370,7 @@ public function renderVariable($val, $caller)
             $output = '
    ';
             $output .= "" . $this->formatCaller($caller) . " - \n";
             if (is_string($val)) {
    -            $output .= wordwrap($val, self::config()->columns);
    +            $output .= wordwrap((string) $val, (int) self::config()->columns);
             } else {
                 $output .= var_export($val, true);
             }
    diff --git a/src/Dev/Deprecation.php b/src/Dev/Deprecation.php
    index 6a025c3168a..615d2d424f1 100644
    --- a/src/Dev/Deprecation.php
    +++ b/src/Dev/Deprecation.php
    @@ -180,10 +180,10 @@ public static function notice($atVersion, $string = '', $scope = Deprecation::SC
             $backtrace = null;
     
             // If you pass #.#, assume #.#.0
    -        if (preg_match('/^[0-9]+\.[0-9]+$/', $atVersion)) {
    +        if (preg_match('/^[0-9]+\.[0-9]+$/', (string) $atVersion)) {
                 $atVersion .= '.0';
             }
    -        if (preg_match('/^[0-9]+\.[0-9]+$/', $checkVersion)) {
    +        if (preg_match('/^[0-9]+\.[0-9]+$/', (string) $checkVersion)) {
                 $checkVersion .= '.0';
             }
     
    @@ -203,7 +203,7 @@ public static function notice($atVersion, $string = '', $scope = Deprecation::SC
             }
     
             // Check the version against the notice version
    -        if ($checkVersion && version_compare($checkVersion, $atVersion, '>=')) {
    +        if ($checkVersion && version_compare((string) $checkVersion, (string) $atVersion, '>=')) {
                 // Get the calling scope
                 if ($scope == Deprecation::SCOPE_METHOD) {
                     if (!$backtrace) {
    @@ -226,16 +226,16 @@ public static function notice($atVersion, $string = '', $scope = Deprecation::SC
                 }
     
                 // Then raise the notice
    -            if (substr($string, -1) != '.') {
    +            if (substr((string) $string, -1) != '.') {
                     $string .= ".";
                 }
     
                 $string .= " Called from " . self::get_called_method_from_trace($backtrace, 2) . '.';
     
                 if ($caller) {
    -                user_error($caller . ' is deprecated.' . ($string ? ' ' . $string : ''), $level);
    +                user_error($caller . ' is deprecated.' . ($string ? ' ' . $string : ''), (int) $level);
                 } else {
    -                user_error($string, $level);
    +                user_error((string) $string, (int) $level);
                 }
             }
         }
    diff --git a/src/Dev/DevConfigController.php b/src/Dev/DevConfigController.php
    index 71e57e75f01..358bd702b8c 100644
    --- a/src/Dev/DevConfigController.php
    +++ b/src/Dev/DevConfigController.php
    @@ -43,7 +43,7 @@ public function index()
             $subtitle = "Config manifest";
     
             if (Director::is_cli()) {
    -            $body .= sprintf("\n%s\n\n", strtoupper($subtitle));
    +            $body .= sprintf("\n%s\n\n", strtoupper((string) $subtitle));
                 $body .= Yaml::dump(Config::inst()->getAll(), 99, 2, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
             } else {
                 $renderer = DebugView::create();
    @@ -73,9 +73,9 @@ public function audit()
             $subtitle = "Missing Config property definitions";
     
             foreach ($this->array_keys_recursive(Config::inst()->getAll(), 2) as $className => $props) {
    -            $props = array_keys($props);
    +            $props = array_keys($props ?: []);
     
    -            if (!count($props)) {
    +            if (!count($props ?: [])) {
                     // We can skip this entry
                     continue;
                 }
    @@ -89,7 +89,7 @@ public function audit()
                     $defined = false;
                     // Check ancestry (private properties don't inherit natively)
                     foreach (ClassInfo::ancestry($className) as $cn) {
    -                    if (property_exists($cn, $prop)) {
    +                    if (property_exists($cn, (string) $prop)) {
                             $defined = true;
                             break;
                         }
    @@ -104,12 +104,12 @@ public function audit()
                 }
             }
     
    -        $output = count($missing)
    +        $output = count($missing ?: [])
                 ? implode("\n", $missing)
                 : "All configured properties are defined\n";
     
             if (Director::is_cli()) {
    -            $body .= sprintf("\n%s\n\n", strtoupper($subtitle));
    +            $body .= sprintf("\n%s\n\n", strtoupper((string) $subtitle));
                 $body .= $output;
             } else {
                 $renderer = DebugView::create();
    @@ -142,7 +142,7 @@ private function array_keys_recursive($array, $maxdepth = 20, $depth = 0, $array
         {
             if ($depth < $maxdepth) {
                 $depth++;
    -            $keys = array_keys($array);
    +            $keys = array_keys($array ?: []);
     
                 foreach ($keys as $key) {
                     if (!is_array($array[$key])) {
    diff --git a/src/Dev/DevelopmentAdmin.php b/src/Dev/DevelopmentAdmin.php
    index 8bc81fabd1b..ca7ba1fb437 100644
    --- a/src/Dev/DevelopmentAdmin.php
    +++ b/src/Dev/DevelopmentAdmin.php
    @@ -86,8 +86,8 @@ protected function init()
             }
     
             // Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957)
    -        $requestedDevBuild = (stripos($this->getRequest()->getURL(), 'dev/build') === 0)
    -            && (stripos($this->getRequest()->getURL(), 'dev/build/defaults') === false);
    +        $requestedDevBuild = (stripos((string) $this->getRequest()->getURL(), 'dev/build') === 0)
    +            && (stripos((string) $this->getRequest()->getURL(), 'dev/build/defaults') === false);
     
             // We allow access to this controller regardless of live-status or ADMIN permission only
             // if on CLI.  Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
    @@ -152,7 +152,7 @@ public function runRegisteredController(HTTPRequest $request)
                 $controllerClass = $reg[$baseUrlPart]['controller'];
             }
     
    -        if ($controllerClass && class_exists($controllerClass)) {
    +        if ($controllerClass && class_exists((string) $controllerClass)) {
                 return $controllerClass::create();
             }
     
    diff --git a/src/Dev/FixtureBlueprint.php b/src/Dev/FixtureBlueprint.php
    index 604c7bd65fc..f85f69126cd 100644
    --- a/src/Dev/FixtureBlueprint.php
    +++ b/src/Dev/FixtureBlueprint.php
    @@ -172,8 +172,8 @@ public function createObject($identifier, $data = null, $fixtures = null)
                                     $extrafields = [];
                                     if (is_array($relVal)) {
                                         // Item is either first row, or key in yet another nested array
    -                                    $item = key($relVal);
    -                                    if (is_array($relVal[$item]) && count($relVal) === 1) {
    +                                    $item = key($relVal ?: []);
    +                                    if (is_array($relVal[$item]) && count($relVal ?: []) === 1) {
                                             // Extra fields from nested array
                                             $extrafields = $relVal[$item];
                                         } else {
    @@ -194,13 +194,13 @@ public function createObject($identifier, $data = null, $fixtures = null)
                             } else {
                                 $items = is_array($fieldVal)
                                 ? $fieldVal
    -                            : preg_split('/ *, */', trim($fieldVal));
    +                            : preg_split('/ *, */', trim((string) $fieldVal));
     
                                 $parsedItems = [];
                                 foreach ($items as $item) {
                                     // Check for correct format: =>..
                                     // Ignore if the item has already been replaced with a numeric DB identifier
    -                                if (!is_numeric($item) && !preg_match('/^=>[^\.]+\.[^\.]+/', $item)) {
    +                                if (!is_numeric($item) && !preg_match('/^=>[^\.]+\.[^\.]+/', (string) $item)) {
                                         throw new InvalidArgumentException(sprintf(
                                             'Invalid format for relation "%s" on class "%s" ("%s")',
                                             $fieldName,
    @@ -219,7 +219,7 @@ public function createObject($identifier, $data = null, $fixtures = null)
                                 }
                             }
                         } else {
    -                        $hasOneField = preg_replace('/ID$/', '', $fieldName);
    +                        $hasOneField = preg_replace('/ID$/', '', $fieldName ?: '');
                             if ($className = $schema->hasOneComponent($class, $hasOneField)) {
                                 $obj->{$hasOneField . 'ID'} = $this->parseValue($fieldVal, $fixtures, $fieldClass);
                                 // Inject class for polymorphic relation
    @@ -233,7 +233,7 @@ public function createObject($identifier, $data = null, $fixtures = null)
                 $obj->write();
     
                 // If LastEdited was set in the fixture, set it here
    -            if ($data && array_key_exists('LastEdited', $data)) {
    +            if ($data && array_key_exists('LastEdited', $data ?: [])) {
                     $this->overrideField($obj, 'LastEdited', $data['LastEdited'], $fixtures);
                 }
             } catch (Exception $e) {
    @@ -282,7 +282,7 @@ public function getClass()
          */
         public function addCallback($type, $callback)
         {
    -        if (!array_key_exists($type, $this->callbacks)) {
    +        if (!array_key_exists($type, $this->callbacks ?: [])) {
                 throw new InvalidArgumentException(sprintf('Invalid type "%s"', $type));
             }
     
    @@ -308,7 +308,7 @@ public function removeCallback($type, $callback)
         protected function invokeCallbacks($type, $args = [])
         {
             foreach ($this->callbacks[$type] as $callback) {
    -            call_user_func_array($callback, $args);
    +            call_user_func_array($callback, $args ?: []);
             }
         }
     
    @@ -324,9 +324,9 @@ protected function invokeCallbacks($type, $args = [])
          */
         protected function parseValue($value, $fixtures = null, &$class = null)
         {
    -        if (substr($value, 0, 2) == '=>') {
    +        if (substr((string) $value, 0, 2) == '=>') {
                 // Parse a dictionary reference - used to set foreign keys
    -            list($class, $identifier) = explode('.', substr($value, 2), 2);
    +            list($class, $identifier) = explode('.', substr((string) $value, 2), 2);
     
                 if ($fixtures && !isset($fixtures[$class][$identifier])) {
                     throw new InvalidArgumentException(sprintf(
    diff --git a/src/Dev/FixtureFactory.php b/src/Dev/FixtureFactory.php
    index 09812c04ada..aac1d566c4b 100644
    --- a/src/Dev/FixtureFactory.php
    +++ b/src/Dev/FixtureFactory.php
    @@ -174,9 +174,9 @@ public function get($class, $identifier)
             }
     
             // If the class doesn't exist, look for a table instead
    -        if (!class_exists($class)) {
    +        if (!class_exists((string) $class)) {
                 $tableNames = DataObject::getSchema()->getTableNames();
    -            $potential = array_search($class, $tableNames);
    +            $potential = array_search($class, $tableNames ?: []);
                 if (!$potential) {
                     throw new \LogicException("'$class' is neither a class nor a table name");
                 }
    @@ -206,11 +206,11 @@ public function getFixtures()
          */
         public function clear($limitToClass = null, $metadata = false)
         {
    -        $classes = ($limitToClass) ? [$limitToClass] : array_keys($this->fixtures);
    +        $classes = ($limitToClass) ? [$limitToClass] : array_keys($this->fixtures ?: []);
             foreach ($classes as $class) {
                 $ids = $this->fixtures[$class];
                 foreach ($ids as $id => $dbId) {
    -                if (class_exists($class)) {
    +                if (class_exists((string) $class)) {
                         $instance = DataObject::get($class)->byId($dbId);
                         if ($instance) {
                             $instance->delete();
    @@ -256,10 +256,10 @@ public function getBlueprint($name)
          */
         protected function parseValue($value)
         {
    -        if (substr($value, 0, 2) == '=>') {
    +        if (substr((string) $value, 0, 2) == '=>') {
                 // Parse a dictionary reference - used to set foreign keys
    -            if (strpos($value, '.') !== false) {
    -                list($class, $identifier) = explode('.', substr($value, 2), 2);
    +            if (strpos((string) $value, '.') !== false) {
    +                list($class, $identifier) = explode('.', substr((string) $value, 2), 2);
                 } else {
                     throw new \LogicException("Bad fixture lookup identifier: " . $value);
                 }
    diff --git a/src/Dev/FunctionalTest.php b/src/Dev/FunctionalTest.php
    index 0dbe683c8b3..56e01586e7f 100644
    --- a/src/Dev/FunctionalTest.php
    +++ b/src/Dev/FunctionalTest.php
    @@ -750,7 +750,7 @@ public function assertPartialMatchBySelector($selector, $expectedMatches, $messa
             $message = $message ?:
             "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
                 . implode("'\n'", $expectedMatches) . "'\n\n"
    -            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
    +            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals ?: [])) . "'";
     
             foreach ($expectedMatches as $match) {
                 $this->assertTrue(isset($actuals[$match]), $message);
    @@ -780,7 +780,7 @@ public function assertExactMatchBySelector($selector, $expectedMatches, $message
             $actuals = [];
             if ($items) {
                 foreach ($items as $item) {
    -                $actuals[] = trim(preg_replace('/\s+/', ' ', (string)$item));
    +                $actuals[] = trim((string) preg_replace('/\s+/', ' ', (string)$item));
                 }
             }
     
    @@ -823,7 +823,7 @@ public function assertPartialHTMLMatchBySelector($selector, $expectedMatches, $m
             $message = $message ?:
                     "Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
                     . implode("'\n'", $expectedMatches) . "'\n\n"
    -            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'";
    +            . "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals ?: [])) . "'";
     
             foreach ($expectedMatches as $match) {
                 $this->assertTrue(isset($actuals[$match]), $message);
    diff --git a/src/Dev/Install/DatabaseAdapterRegistry.php b/src/Dev/Install/DatabaseAdapterRegistry.php
    index e2632125d1f..7c4015dfbd8 100644
    --- a/src/Dev/Install/DatabaseAdapterRegistry.php
    +++ b/src/Dev/Install/DatabaseAdapterRegistry.php
    @@ -72,7 +72,7 @@ class DatabaseAdapterRegistry implements Flushable
         public static function register($config)
         {
             // Validate config
    -        $missing = array_diff(['title', 'class', 'helperClass', 'supported'], array_keys($config));
    +        $missing = array_diff(['title', 'class', 'helperClass', 'supported'], array_keys($config ?: []));
             if ($missing) {
                 throw new InvalidArgumentException(
                     "Missing database helper config keys: '" . implode("', '", $missing) . "'"
    @@ -256,6 +256,6 @@ public static function getDatabaseConfigurationHelper($databaseClass)
     
             // Construct
             $class = $adapters[$databaseClass]['helperClass'];
    -        return (class_exists($class)) ? new $class() : null;
    +        return (class_exists((string) $class)) ? new $class() : null;
         }
     }
    diff --git a/src/Dev/Install/MySQLDatabaseConfigurationHelper.php b/src/Dev/Install/MySQLDatabaseConfigurationHelper.php
    index 74b0500266f..5d4453c858e 100644
    --- a/src/Dev/Install/MySQLDatabaseConfigurationHelper.php
    +++ b/src/Dev/Install/MySQLDatabaseConfigurationHelper.php
    @@ -173,7 +173,7 @@ public function requireDatabaseVersion($databaseConfig)
             $success = false;
             $error = '';
             if ($version) {
    -            $success = version_compare($version, '5.0', '>=');
    +            $success = version_compare((string) $version, '5.0', '>=');
                 if (!$success) {
                     $error = "Your MySQL server version is $version. It's recommended you use at least MySQL 5.0.";
                 }
    @@ -213,13 +213,13 @@ public function checkValidDatabaseName($database)
         {
     
             // Reject filename unsafe characters (cross platform)
    -        if (preg_match('/[\\\\\/\?%\*\:\|"\<\>\.]+/', $database)) {
    +        if (preg_match('/[\\\\\/\?%\*\:\|"\<\>\.]+/', (string) $database)) {
                 return false;
             }
     
             // Restricted to characters in the ASCII and Extended ASCII range
             // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
    -        return preg_match('/^[\x{0001}-\x{FFFF}]+$/u', $database);
    +        return preg_match('/^[\x{0001}-\x{FFFF}]+$/u', (string) $database);
         }
     
         /**
    @@ -239,7 +239,7 @@ public function checkDatabasePermissionGrant($database, $permission, $grant)
             }
     
             // Escape all valid database patterns (permission must exist on all tables)
    -        $sqlDatabase = addcslashes($database, '_%'); // See http://dev.mysql.com/doc/refman/5.7/en/string-literals.html
    +        $sqlDatabase = addcslashes((string) $database, '_%'); // See http://dev.mysql.com/doc/refman/5.7/en/string-literals.html
             $dbPattern = sprintf(
                 '((%s)|(%s)|(%s)|(%s))',
                 preg_quote("\"$sqlDatabase\".*"), // Regexp escape sql-escaped db identifier
    @@ -248,7 +248,7 @@ public function checkDatabasePermissionGrant($database, $permission, $grant)
                 preg_quote('*.*')
             );
             $expression = '/GRANT[ ,\w]+((ALL PRIVILEGES)|(' . $permission . '(?! ((VIEW)|(ROUTINE)))))[ ,\w]+ON ' . $dbPattern . '/i';
    -        return preg_match($expression, $grant);
    +        return preg_match((string) $expression, (string) $grant);
         }
     
         /**
    @@ -277,7 +277,7 @@ public function requireDatabaseOrCreatePermissions($databaseConfig)
             $conn = $this->createConnection($databaseConfig, $error);
             if ($conn) {
                 $list = $this->column($conn->query("SHOW DATABASES"));
    -            if (in_array($databaseConfig['database'], $list)) {
    +            if (in_array($databaseConfig['database'], $list ?: [])) {
                     $success = true;
                     $alreadyExists = true;
                 } else {
    diff --git a/src/Dev/SapphireTest.php b/src/Dev/SapphireTest.php
    index 479a08311f8..898e6b35f1e 100644
    --- a/src/Dev/SapphireTest.php
    +++ b/src/Dev/SapphireTest.php
    @@ -1902,7 +1902,7 @@ protected function getCurrentAbsolutePath()
                 throw new LogicException('getItemPath returned null for ' . static::class
                     . '. Try adding flush=1 to the test run.');
             }
    -        return dirname($filename);
    +        return dirname((string) $filename);
         }
     
         /**
    @@ -1912,8 +1912,8 @@ protected function getCurrentRelativePath()
         {
             $base = Director::baseFolder();
             $path = $this->getCurrentAbsolutePath();
    -        if (substr($path, 0, strlen($base)) == $base) {
    -            $path = preg_replace('/^\/*/', '', substr($path, strlen($base)));
    +        if (substr((string) $path, 0, strlen((string) $base)) == $base) {
    +            $path = preg_replace('/^\/*/', '', substr((string) $path, strlen((string) $base)));
             }
             return $path;
         }
    @@ -2255,7 +2255,7 @@ public function assertDOSAllMatch($match, SS_List $dataObjectSet)
          */
         protected static function normaliseSQL($sql)
         {
    -        return trim(preg_replace('/\s+/m', ' ', $sql));
    +        return trim((string) preg_replace('/\s+/m', ' ', $sql ?: ''));
         }
     
         /**
    @@ -2353,7 +2353,7 @@ public static function start()
                 $request = CLIRequestBuilder::createFromEnvironment();
     
                 $app = new HTTPApplication($kernel);
    -            $flush = array_key_exists('flush', $request->getVars());
    +            $flush = array_key_exists('flush', $request->getVars() ?: []);
     
                 // Custom application
                 $res = $app->execute($request, function (HTTPRequest $request) {
    @@ -2378,7 +2378,7 @@ public static function start()
                 // Allow flush from the command line in the absence of HTTPApplication's special sauce
                 $flush = false;
                 foreach ($_SERVER['argv'] as $arg) {
    -                if (preg_match('/^(--)?flush(=1)?$/', $arg)) {
    +                if (preg_match('/^(--)?flush(=1)?$/', (string) $arg)) {
                         $flush = true;
                     }
                 }
    @@ -2536,8 +2536,8 @@ public function logOut()
         protected function useTestTheme($themeBaseDir, $theme, $callback)
         {
             Config::nest();
    -        if (strpos($themeBaseDir, BASE_PATH) === 0) {
    -            $themeBaseDir = substr($themeBaseDir, strlen(BASE_PATH));
    +        if (strpos((string) $themeBaseDir, BASE_PATH) === 0) {
    +            $themeBaseDir = substr((string) $themeBaseDir, strlen(BASE_PATH));
             }
             SSViewer::config()->update('theme_enabled', true);
             SSViewer::set_themes([$themeBaseDir . '/themes/' . $theme, '$default']);
    @@ -2565,7 +2565,7 @@ protected function getFixturePaths()
     
             return array_map(function ($fixtureFilePath) {
                 return $this->resolveFixturePath($fixtureFilePath);
    -        }, $fixtureFiles);
    +        }, $fixtureFiles ?: []);
         }
     
         /**
    @@ -2596,7 +2596,7 @@ public static function getExtraControllers()
         protected function resolveFixturePath($fixtureFilePath)
         {
             // support loading via composer name path.
    -        if (strpos($fixtureFilePath, ':') !== false) {
    +        if (strpos((string) $fixtureFilePath, ':') !== false) {
                 return ModuleResourceLoader::singleton()->resolvePath($fixtureFilePath);
             }
     
    @@ -2650,7 +2650,7 @@ protected function getExtraRoutes()
             foreach ($this->getExtraControllers() as $class) {
                 $controllerInst = Controller::singleton($class);
                 $link = Director::makeRelative($controllerInst->Link());
    -            $route = rtrim($link, '/') . '//$Action/$ID/$OtherID';
    +            $route = rtrim((string) $link, '/') . '//$Action/$ID/$OtherID';
                 $rules[$route] = $class;
             }
             return $rules;
    diff --git a/src/Dev/State/ExtensionTestState.php b/src/Dev/State/ExtensionTestState.php
    index 29c4dd09c31..bbba4dba028 100644
    --- a/src/Dev/State/ExtensionTestState.php
    +++ b/src/Dev/State/ExtensionTestState.php
    @@ -47,14 +47,14 @@ public function setUpOnce($class)
             /** @var string|DataObject $dataClass */
             // Remove any illegal extensions that are present
             foreach ($class::getIllegalExtensions() as $dataClass => $extensions) {
    -            if (!class_exists($dataClass)) {
    +            if (!class_exists((string) $dataClass)) {
                     continue;
                 }
                 if ($extensions === '*') {
                     $extensions = $dataClass::get_extensions();
                 }
                 foreach ($extensions as $extension) {
    -                if (!class_exists($extension) || !$dataClass::has_extension($extension)) {
    +                if (!class_exists((string) $extension) || !$dataClass::has_extension($extension)) {
                         continue;
                     }
                     if (!isset($this->extensionsToReapply[$dataClass])) {
    @@ -68,12 +68,12 @@ public function setUpOnce($class)
     
             // Add any required extensions that aren't present
             foreach ($class::getRequiredExtensions() as $dataClass => $extensions) {
    -            if (!class_exists($dataClass)) {
    +            if (!class_exists((string) $dataClass)) {
                     throw new LogicException("Test {$class} requires dataClass {$dataClass} which doesn't exist");
                 }
                 foreach ($extensions as $extension) {
                     $extension = Extension::get_classname_without_arguments($extension);
    -                if (!class_exists($extension)) {
    +                if (!class_exists((string) $extension)) {
                         throw new LogicException("Test {$class} requires extension {$extension} which doesn't exist");
                     }
                     if (!$dataClass::has_extension($extension)) {
    diff --git a/src/Dev/State/FixtureTestState.php b/src/Dev/State/FixtureTestState.php
    index 80ac22e4fd4..2b9c8c4f6d6 100644
    --- a/src/Dev/State/FixtureTestState.php
    +++ b/src/Dev/State/FixtureTestState.php
    @@ -124,8 +124,8 @@ public function tearDownOnce($class)
          */
         public function getFixtureFactory($class)
         {
    -        $testClass = strtolower($class);
    -        if (array_key_exists($testClass, $this->fixtureFactories)) {
    +        $testClass = strtolower((string) $class);
    +        if (array_key_exists($testClass, $this->fixtureFactories ?: [])) {
                 return $this->fixtureFactories[$testClass];
             }
             return false;
    @@ -151,7 +151,7 @@ protected function getFixturePaths($fixtures, SapphireTest $test)
         {
             return array_map(function ($fixtureFilePath) use ($test) {
                 return $this->resolveFixturePath($fixtureFilePath, $test);
    -        }, $fixtures);
    +        }, $fixtures ?: []);
         }
     
         /**
    @@ -220,7 +220,7 @@ protected function getTestAbsolutePath(SapphireTest $test)
                 throw new LogicException('getItemPath returned null for ' . static::class
                     . '. Try adding flush=1 to the test run.');
             }
    -        return dirname($filename);
    +        return dirname((string) $filename);
         }
     
         /**
    @@ -265,7 +265,7 @@ protected function testNeedsDB(SapphireTest $test)
          */
         protected function resetFixtureFactory($class)
         {
    -        $class = strtolower($class);
    +        $class = strtolower((string) $class);
             $this->fixtureFactories[$class] = Injector::inst()->create(FixtureFactory::class);
             $this->loaded[$class] = false;
         }
    diff --git a/src/Dev/State/SapphireTestState.php b/src/Dev/State/SapphireTestState.php
    index f8dbcd3a4fd..62425bd8335 100644
    --- a/src/Dev/State/SapphireTestState.php
    +++ b/src/Dev/State/SapphireTestState.php
    @@ -30,7 +30,7 @@ public function getStates()
         public function getStateByName($name)
         {
             $states = $this->getStates();
    -        if (array_key_exists($name, $states)) {
    +        if (array_key_exists($name, $states ?: [])) {
                 return $states[$name];
             }
             return false;
    @@ -43,7 +43,7 @@ public function getStateByName($name)
          */
         public function getStateByClass($class)
         {
    -        $lClass = strtolower($class);
    +        $lClass = strtolower((string) $class);
             foreach ($this->getStates() as $state) {
                 if ($lClass === strtolower(get_class($state))) {
                     return $state;
    @@ -73,7 +73,7 @@ public function tearDown(SapphireTest $test)
         {
             // Tear down in reverse order
             /** @var TestState $state */
    -        foreach (array_reverse($this->states) as $state) {
    +        foreach (array_reverse($this->states ?: []) as $state) {
                 $state->tearDown($test);
             }
         }
    @@ -94,7 +94,7 @@ public function tearDownOnce($class)
         {
             // Tear down in reverse order
             /** @var TestState $state */
    -        foreach (array_reverse($this->states) as $state) {
    +        foreach (array_reverse($this->states ?: []) as $state) {
                 $state->tearDownOnce($class);
             }
         }
    diff --git a/src/Dev/TaskRunner.php b/src/Dev/TaskRunner.php
    index bb911d7126b..223c6450b28 100644
    --- a/src/Dev/TaskRunner.php
    +++ b/src/Dev/TaskRunner.php
    @@ -150,7 +150,7 @@ protected function getTasks()
     
                 $singleton = BuildTask::singleton($class);
                 $description = $singleton->getDescription();
    -            $description = trim($description);
    +            $description = trim((string) $description);
     
                 $desc = (Director::is_cli())
                     ? Convert::html2raw($description)
    @@ -202,7 +202,7 @@ protected function addCssToHeader($header)
     
                 // inject CSS into the heaader
                 $element = sprintf('', $path);
    -            $header = str_replace('', $element . '', $header);
    +            $header = str_replace('', $element . '', $header ?: '');
             }
     
             return $header;
    diff --git a/src/Dev/Tasks/MigrateFileTask.php b/src/Dev/Tasks/MigrateFileTask.php
    index fffa7622607..576ef72247b 100644
    --- a/src/Dev/Tasks/MigrateFileTask.php
    +++ b/src/Dev/Tasks/MigrateFileTask.php
    @@ -76,7 +76,7 @@ public function run($request)
             $subtasks = !empty($args['only']) ? explode(',', $args['only']) : $this->defaultSubtasks;
     
             $subtask = 'move-files';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(FileMigrationHelper::class)) {
                     $this->logger->error("No file migration helper detected");
                 } else {
    @@ -95,7 +95,7 @@ public function run($request)
             }
     
             $subtask = 'migrate-folders';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(FolderMigrationHelper::class)) {
                     $this->logger->error("No folder migration helper detected");
                 } else {
    @@ -114,7 +114,7 @@ public function run($request)
             }
     
             $subtask = 'move-thumbnails';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(LegacyThumbnailMigrationHelper::class)) {
                     $this->logger->error("LegacyThumbnailMigrationHelper not found");
                 } else {
    @@ -139,7 +139,7 @@ public function run($request)
             }
     
             $subtask = 'generate-cms-thumbnails';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(ImageThumbnailHelper::class)) {
                     $this->logger->error("ImageThumbnailHelper not found");
                 } else {
    @@ -164,7 +164,7 @@ public function run($request)
             }
     
             $subtask = 'fix-folder-permissions';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(FixFolderPermissionsHelper::class)) {
                     $this->logger->error("FixFolderPermissionsHelper not found");
                 } else {
    @@ -190,7 +190,7 @@ public function run($request)
             }
     
             $subtask = 'fix-secureassets';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(SecureAssetsMigrationHelper::class)) {
                     $this->logger->error("SecureAssetsMigrationHelper not found");
                 } else {
    @@ -205,7 +205,7 @@ public function run($request)
                         ->setLogger($this->logger)
                         ->run($this->getStore());
     
    -                if (count($paths) > 0) {
    +                if (count($paths ?: []) > 0) {
                         $this->logger->info(sprintf("Repaired %d folders broken folder restrictions", count($paths)));
                     } else {
                         $this->logger->info("No folders required fixes");
    @@ -216,7 +216,7 @@ public function run($request)
             }
     
             $subtask = 'normalise-access';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(NormaliseAccessMigrationHelper::class)) {
                     $this->logger->error("No normalise access migration helper detected");
                 } else {
    @@ -235,7 +235,7 @@ public function run($request)
             }
     
             $subtask = 'relocate-userform-uploads-2020-9280';
    -        if (in_array($subtask, $subtasks)) {
    +        if (in_array($subtask, $subtasks ?: [])) {
                 if (!class_exists(RecoverUploadLocationsHelper::class)) {
                     $this->logger->error("No UserForms helper detected");
                 } else {
    @@ -296,8 +296,8 @@ protected function validateArgs($args)
             if (!empty($args['only'])) {
                 $only = explode(',', $args['only']);
     
    -            $diff = array_diff($only, $this->defaultSubtasks);
    -            $diff = array_diff($diff, $this->optInSubtasks);
    +            $diff = array_diff($only ?: [], $this->defaultSubtasks);
    +            $diff = array_diff($diff ?: [], $this->optInSubtasks);
     
                 if ($diff) {
                     throw new \InvalidArgumentException('Invalid subtasks detected: ' . implode(', ', $diff));
    diff --git a/src/Dev/TestMailer.php b/src/Dev/TestMailer.php
    index 667454ecd14..04535343f3a 100644
    --- a/src/Dev/TestMailer.php
    +++ b/src/Dev/TestMailer.php
    @@ -113,7 +113,7 @@ public function findEmail($to, $from = null, $subject = null, $content = null)
                             $value = $this->normaliseSpaces($value);
                         }
                         if ($value[0] === '/') {
    -                        $matched = preg_match($value, $emailValue);
    +                        $matched = preg_match((string) $value, (string) $emailValue);
                         } else {
                             $matched = ($value === $emailValue);
                         }
    @@ -135,6 +135,6 @@ public function findEmail($to, $from = null, $subject = null, $content = null)
          */
         private function normaliseSpaces(string $value)
         {
    -        return str_replace([', ', '; '], [',', ';'], $value);
    +        return str_replace([', ', '; '], [',', ';'], $value ?: '');
         }
     }
    diff --git a/src/Dev/TestSession.php b/src/Dev/TestSession.php
    index 68fa685fdaa..5e40d248808 100644
    --- a/src/Dev/TestSession.php
    +++ b/src/Dev/TestSession.php
    @@ -235,7 +235,7 @@ public function submitForm($formID, $button = null, $data = [])
                 $url = Director::makeRelative($form->getAction()->asString());
     
                 $postVars = [];
    -            parse_str($submission->_encode(), $postVars);
    +            parse_str((string) $submission->_encode(), $postVars);
                 return $this->post($url, $postVars);
             } else {
                 user_error("TestSession::submitForm called when there is no form loaded."
    @@ -252,7 +252,7 @@ public function followRedirection()
         {
             if ($this->lastResponse->getHeader('Location')) {
                 $url = Director::makeRelative($this->lastResponse->getHeader('Location'));
    -            $url = strtok($url, '#');
    +            $url = strtok((string) $url, '#');
                 return $this->get($url);
             }
         }
    diff --git a/src/Dev/Validation/RelationValidationService.php b/src/Dev/Validation/RelationValidationService.php
    index 2cff1926e95..450adb77225 100644
    --- a/src/Dev/Validation/RelationValidationService.php
    +++ b/src/Dev/Validation/RelationValidationService.php
    @@ -108,7 +108,7 @@ public function validateRelations(): array
         public function executeValidation(): void
         {
             $errors = $this->validateRelations();
    -        $count = count($errors);
    +        $count = count($errors ?: []);
     
             if ($count === 0) {
                 return;
    @@ -231,7 +231,7 @@ protected function matchRules(string $class, string $rule): bool
                     continue;
                 }
     
    -            if (mb_strpos($class, $pattern) === 0) {
    +            if (mb_strpos((string) $class, (string) $pattern) === 0) {
                     // Classname prefix matches the pattern
                     return true;
                 }
    @@ -295,7 +295,7 @@ protected function validateHasOne(string $class): void
                     continue;
                 }
     
    -            if (mb_strpos($relationData, '.') !== false) {
    +            if (mb_strpos((string) $relationData, '.') !== false) {
                     $this->logError(
                         $class,
                         $relationName,
    @@ -395,7 +395,7 @@ protected function validateBelongsTo(string $class): void
                 $relatedObject = DataObject::singleton($relatedClass);
                 $relatedRelations = (array) $relatedObject->config()->uninherited('has_one');
     
    -            if (array_key_exists($relatedRelation, $relatedRelations)) {
    +            if (array_key_exists($relatedRelation, $relatedRelations ?: [])) {
                     continue;
                 }
     
    @@ -444,7 +444,7 @@ protected function validateHasMany(string $class): void
                 $relatedObject = DataObject::singleton($relatedClass);
                 $relatedRelations = (array) $relatedObject->config()->uninherited('has_one');
     
    -            if (array_key_exists($relatedRelation, $relatedRelations)) {
    +            if (array_key_exists($relatedRelation, $relatedRelations ?: [])) {
                     continue;
                 }
     
    @@ -556,7 +556,7 @@ protected function validateBelongsManyMany(string $class): void
                 $relatedObject = DataObject::singleton($relatedClass);
                 $relatedRelations = (array) $relatedObject->config()->uninherited('many_many');
     
    -            if (array_key_exists($relatedRelation, $relatedRelations)) {
    +            if (array_key_exists($relatedRelation, $relatedRelations ?: [])) {
                     continue;
                 }
     
    @@ -570,13 +570,13 @@ protected function validateBelongsManyMany(string $class): void
          */
         protected function parsePlainRelation(string $relationData): ?array
         {
    -        if (mb_strpos($relationData, '.') === false) {
    +        if (mb_strpos((string) $relationData, '.') === false) {
                 return null;
             }
     
    -        $segments = explode('.', $relationData);
    +        $segments = explode('.', (string) $relationData);
     
    -        if (count($segments) !== 2) {
    +        if (count($segments ?: []) !== 2) {
                 return null;
             }
     
    @@ -597,7 +597,7 @@ protected function parseManyManyRelation($relationData): ?string
         {
             if (is_array($relationData)) {
                 foreach (['through', 'to'] as $key) {
    -                if (!array_key_exists($key, $relationData)) {
    +                if (!array_key_exists($key, $relationData ?: [])) {
                         return null;
                     }
                 }
    @@ -612,7 +612,7 @@ protected function parseManyManyRelation($relationData): ?string
                 $throughObject = DataObject::singleton($through);
                 $throughRelations = (array) $throughObject->config()->uninherited('has_one');
     
    -            if (!array_key_exists($to, $throughRelations)) {
    +            if (!array_key_exists($to, $throughRelations ?: [])) {
                     return null;
                 }
     
    diff --git a/src/Dev/YamlFixture.php b/src/Dev/YamlFixture.php
    index 3071c134b20..04bf595e84e 100644
    --- a/src/Dev/YamlFixture.php
    +++ b/src/Dev/YamlFixture.php
    @@ -91,14 +91,14 @@ class YamlFixture
          */
         public function __construct($fixture)
         {
    -        if (false !== strpos($fixture, "\n")) {
    +        if (false !== strpos((string) $fixture, "\n")) {
                 $this->fixtureString = $fixture;
             } else {
                 if (!Director::is_absolute($fixture)) {
                     $fixture = Director::baseFolder() . '/' . $fixture;
                 }
     
    -            if (!file_exists($fixture)) {
    +            if (!file_exists((string) $fixture)) {
                     throw new InvalidArgumentException('YamlFixture::__construct(): Fixture path "' . $fixture
                         . '" not found');
                 }
    @@ -136,11 +136,11 @@ public function writeInto(FixtureFactory $factory)
             if (isset($this->fixtureString)) {
                 $fixtureContent = $parser->parse($this->fixtureString);
             } else {
    -            if (!file_exists($this->fixtureFile) || is_dir($this->fixtureFile)) {
    +            if (!file_exists((string) $this->fixtureFile) || is_dir((string) $this->fixtureFile)) {
                     return;
                 }
     
    -            $contents = file_get_contents($this->fixtureFile);
    +            $contents = file_get_contents((string) $this->fixtureFile);
                 $fixtureContent = $parser->parse($contents);
     
                 if (!$fixtureContent) {
    diff --git a/src/Forms/CheckboxSetField.php b/src/Forms/CheckboxSetField.php
    index 2d814595a21..bf88c0f09ed 100644
    --- a/src/Forms/CheckboxSetField.php
    +++ b/src/Forms/CheckboxSetField.php
    @@ -82,10 +82,10 @@ public function getOptions()
                 $itemID = Convert::raw2htmlid("{$formID}_{$itemValue}");
                 $odd = !$odd;
                 $extraClass = $odd ? 'odd' : 'even';
    -            $extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $itemValue);
    +            $extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $itemValue ?: '');
     
    -            $itemChecked = in_array($itemValue, $selectedValues) || in_array($itemValue, $defaultItems);
    -            $itemDisabled = $this->isDisabled() || in_array($itemValue, $disabledItems);
    +            $itemChecked = in_array($itemValue, $selectedValues ?: []) || in_array($itemValue, $defaultItems ?: []);
    +            $itemDisabled = $this->isDisabled() || in_array($itemValue, $disabledItems ?: []);
     
                 $options->push(new ArrayData([
                     'ID' => $itemID,
    diff --git a/src/Forms/CompositeField.php b/src/Forms/CompositeField.php
    index 5ea266af63a..ea6e40dd9d1 100644
    --- a/src/Forms/CompositeField.php
    +++ b/src/Forms/CompositeField.php
    @@ -146,7 +146,7 @@ public function getName()
             if ($count === 1) {
                 $compositeTitle .= 'Group';
             }
    -        return preg_replace("/[^a-zA-Z0-9]+/", "", $compositeTitle);
    +        return preg_replace("/[^a-zA-Z0-9]+/", "", $compositeTitle ?: '');
         }
     
         /**
    diff --git a/src/Forms/CompositeValidator.php b/src/Forms/CompositeValidator.php
    index 3135e2cf6e4..a5b04cb2ea0 100644
    --- a/src/Forms/CompositeValidator.php
    +++ b/src/Forms/CompositeValidator.php
    @@ -49,7 +49,7 @@ class CompositeValidator extends Validator
          */
         public function __construct(array $validators = [])
         {
    -        $this->validators = array_values($validators);
    +        $this->validators = array_values($validators ?: []);
     
             parent::__construct();
         }
    @@ -216,7 +216,7 @@ public function canBeCached(): bool
          */
         protected function removeValidatorByKey(int $key): CompositeValidator
         {
    -        if (!array_key_exists($key, $this->validators)) {
    +        if (!array_key_exists($key, $this->validators ?: [])) {
                 throw new InvalidArgumentException(
                     sprintf('Key "%s" does not exist in $validators array', $key)
                 );
    diff --git a/src/Forms/ConfirmedPasswordField.php b/src/Forms/ConfirmedPasswordField.php
    index 5cd8ece1e57..b30ae05ee21 100644
    --- a/src/Forms/ConfirmedPasswordField.php
    +++ b/src/Forms/ConfirmedPasswordField.php
    @@ -194,7 +194,7 @@ public function Field($properties = [])
                 $field->setDisabled($this->isDisabled());
                 $field->setReadonly($this->isReadonly());
     
    -            if (count($this->attributes)) {
    +            if (count($this->attributes ?: [])) {
                     foreach ($this->attributes as $name => $value) {
                         $field->setAttribute($name, $value);
                     }
    @@ -310,7 +310,7 @@ public function setRightTitle($title)
         public function setChildrenTitles($titles)
         {
             $expectedChildren = $this->getRequireExistingPassword() ? 3 : 2;
    -        if (is_array($titles) && count($titles) === $expectedChildren) {
    +        if (is_array($titles) && count($titles ?: []) === $expectedChildren) {
                 foreach ($this->getChildren() as $field) {
                     if (isset($titles[0])) {
                         /** @var FormField $field */
    @@ -476,7 +476,7 @@ public function validate($validator)
                     );
                 }
                 $limitRegex = '/^.' . $limit . '$/';
    -            if (!empty($value) && !preg_match($limitRegex, $value)) {
    +            if (!empty($value) && !preg_match((string) $limitRegex, (string) $value)) {
                     $validator->validationError(
                         $name,
                         $errorMsg,
    @@ -488,7 +488,7 @@ public function validate($validator)
             }
     
             if ($this->getRequireStrongPassword()) {
    -            if (!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/', $value)) {
    +            if (!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/', (string) $value)) {
                     $validator->validationError(
                         $name,
                         _t(
    diff --git a/src/Forms/CurrencyField.php b/src/Forms/CurrencyField.php
    index 5e2555c704b..6e6fbfaa84b 100644
    --- a/src/Forms/CurrencyField.php
    +++ b/src/Forms/CurrencyField.php
    @@ -28,7 +28,7 @@ public function setValue($value, $data = null)
                 $value = 0.00;
             }
             $this->value = DBCurrency::config()->uninherited('currency_symbol')
    -            . number_format((double)preg_replace('/[^0-9.\-]/', '', $value), 2);
    +            . number_format((double)preg_replace('/[^0-9.\-]/', '', $value ?: ''), 2);
             return $this;
         }
         /**
    @@ -38,7 +38,7 @@ public function setValue($value, $data = null)
         public function dataValue()
         {
             if ($this->value) {
    -            return preg_replace('/[^0-9.\-]/', '', $this->value);
    +            return preg_replace('/[^0-9.\-]/', '', $this->value ?: '');
             }
             return 0.00;
         }
    @@ -58,9 +58,9 @@ public function performReadonlyTransformation()
     
         public function validate($validator)
         {
    -        $currencySymbol = preg_quote(DBCurrency::config()->uninherited('currency_symbol'));
    +        $currencySymbol = preg_quote((string) DBCurrency::config()->uninherited('currency_symbol'));
             $regex = '/^\s*(\-?' . $currencySymbol . '?|' . $currencySymbol . '\-?)?(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?\s*$/';
    -        if (!empty($this->value) && !preg_match($regex, $this->value)) {
    +        if (!empty($this->value) && !preg_match((string) $regex, (string) $this->value)) {
                 $validator->validationError(
                     $this->name,
                     _t('SilverStripe\\Forms\\Form.VALIDCURRENCY', "Please enter a valid currency"),
    diff --git a/src/Forms/CurrencyField_Disabled.php b/src/Forms/CurrencyField_Disabled.php
    index 80ef0c25676..52537118f7e 100644
    --- a/src/Forms/CurrencyField_Disabled.php
    +++ b/src/Forms/CurrencyField_Disabled.php
    @@ -24,7 +24,7 @@ public function Field($properties = [])
             if ($this->value) {
                 $val = Convert::raw2xml($this->value);
                 $val = DBCurrency::config()->get('currency_symbol')
    -                . number_format(preg_replace('/[^0-9.-]/', '', $val), 2);
    +                . number_format((float) preg_replace('/[^0-9.-]/', '', $val ?: ''), 2);
                 $valforInput = Convert::raw2att($val);
             } else {
                 $valforInput = '';
    diff --git a/src/Forms/CurrencyField_Readonly.php b/src/Forms/CurrencyField_Readonly.php
    index e96c90083bf..e5495fb4919 100644
    --- a/src/Forms/CurrencyField_Readonly.php
    +++ b/src/Forms/CurrencyField_Readonly.php
    @@ -22,7 +22,7 @@ public function Field($properties = [])
             $currencySymbol = DBCurrency::config()->get('currency_symbol');
             if ($this->value) {
                 $val = Convert::raw2xml($this->value);
    -            $val = $currencySymbol . number_format(preg_replace('/[^0-9.-]/', '', $val), 2);
    +            $val = $currencySymbol . number_format((float) preg_replace('/[^0-9.-]/', '', $val ?: ''), 2);
                 $valforInput = Convert::raw2att($val);
             } else {
                 $val = '' . $currencySymbol . '0.00';
    diff --git a/src/Forms/DateField.php b/src/Forms/DateField.php
    index f9563fc9038..5aecff99604 100644
    --- a/src/Forms/DateField.php
    +++ b/src/Forms/DateField.php
    @@ -388,7 +388,7 @@ public function validate($validator)
             // Check min date
             $min = $this->getMinDate();
             if ($min) {
    -            $oops = strtotime($this->value) < strtotime($min);
    +            $oops = strtotime((string) $this->value) < strtotime((string) $min);
                 if ($oops) {
                     $validator->validationError(
                         $this->name,
    @@ -413,7 +413,7 @@ public function validate($validator)
             // Check max date
             $max = $this->getMaxDate();
             if ($max) {
    -            $oops = strtotime($this->value) > strtotime($max);
    +            $oops = strtotime((string) $this->value) > strtotime((string) $max);
                 if ($oops) {
                     $validator->validationError(
                         $this->name,
    @@ -570,7 +570,7 @@ protected function tidyInternal($date)
             $timestamp = $formatter->parse($date);
             if ($timestamp === false) {
                 // Fallback to strtotime
    -            $timestamp = strtotime($date, DBDatetime::now()->getTimestamp());
    +            $timestamp = strtotime((string) $date, DBDatetime::now()->getTimestamp());
                 if ($timestamp === false) {
                     return null;
                 }
    diff --git a/src/Forms/DatetimeField.php b/src/Forms/DatetimeField.php
    index c7d86a7281c..a756905c53d 100644
    --- a/src/Forms/DatetimeField.php
    +++ b/src/Forms/DatetimeField.php
    @@ -411,7 +411,7 @@ public function tidyInternal($datetime)
             $timestamp = $formatter->parse($datetime);
             if ($timestamp === false) {
                 // Fallback to strtotime
    -            $timestamp = strtotime($datetime, DBDatetime::now()->getTimestamp());
    +            $timestamp = strtotime((string) $datetime, DBDatetime::now()->getTimestamp());
                 if ($timestamp === false) {
                     return null;
                 }
    @@ -584,7 +584,7 @@ public function validate($validator)
             // Check min date (in server timezone)
             $min = $this->getMinDatetime();
             if ($min) {
    -            $oops = strtotime($this->value) < strtotime($min);
    +            $oops = strtotime((string) $this->value) < strtotime((string) $min);
                 if ($oops) {
                     $validator->validationError(
                         $this->name,
    @@ -609,7 +609,7 @@ public function validate($validator)
             // Check max date (in server timezone)
             $max = $this->getMaxDatetime();
             if ($max) {
    -            $oops = strtotime($this->value) > strtotime($max);
    +            $oops = strtotime((string) $this->value) > strtotime((string) $max);
                 if ($oops) {
                     $validator->validationError(
                         $this->name,
    diff --git a/src/Forms/EmailField.php b/src/Forms/EmailField.php
    index be432e1e677..38498de959e 100644
    --- a/src/Forms/EmailField.php
    +++ b/src/Forms/EmailField.php
    @@ -29,14 +29,14 @@ public function Type()
          */
         public function validate($validator)
         {
    -        $this->value = trim($this->value);
    +        $this->value = trim((string) $this->value);
     
             $pattern = '^[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$';
     
             // Escape delimiter characters.
    -        $safePattern = str_replace('/', '\\/', $pattern);
    +        $safePattern = str_replace('/', '\\/', $pattern ?: '');
     
    -        if ($this->value && !preg_match('/' . $safePattern . '/i', $this->value)) {
    +        if ($this->value && !preg_match('/' . $safePattern . '/i', (string) $this->value)) {
                 $validator->validationError(
                     $this->name,
                     _t('SilverStripe\\Forms\\EmailField.VALIDATION', 'Please enter an email address'),
    diff --git a/src/Forms/FieldGroup.php b/src/Forms/FieldGroup.php
    index ed14f4419b1..407171fe5cc 100644
    --- a/src/Forms/FieldGroup.php
    +++ b/src/Forms/FieldGroup.php
    @@ -120,7 +120,7 @@ public function getName()
                 return parent::getName();
             }
     
    -        return preg_replace("/[^a-zA-Z0-9]+/", "", $this->title);
    +        return preg_replace("/[^a-zA-Z0-9]+/", "", $this->title ?: '');
         }
     
         /**
    @@ -160,7 +160,7 @@ public function getMessage()
             foreach ($dataFields as $subfield) {
                 $message = $subfield->obj('Message')->forTemplate();
                 if ($message) {
    -                $messages[] = rtrim($message, ".");
    +                $messages[] = rtrim((string) $message, ".");
                 }
             }
     
    diff --git a/src/Forms/FieldList.php b/src/Forms/FieldList.php
    index 7df1b80ada8..26ccf2eb1be 100644
    --- a/src/Forms/FieldList.php
    +++ b/src/Forms/FieldList.php
    @@ -144,7 +144,7 @@ public function saveableFields()
          */
         public function dataFieldNames()
         {
    -        return array_keys($this->dataFields());
    +        return array_keys($this->dataFields() ?: []);
         }
     
         /**
    @@ -359,7 +359,7 @@ public function removeByName($fieldName, $dataFieldOnly = false)
                 }
     
                 if (($childName == $fieldName) && (!$dataFieldOnly || $child->hasData())) {
    -                array_splice($this->items, $i, 1);
    +                array_splice($this->items, (int) $i, 1);
                     break;
                 } elseif ($child instanceof CompositeField) {
                     $child->removeByName($fieldName, $dataFieldOnly);
    @@ -436,8 +436,8 @@ public function hasTabSet()
          */
         public function findTab($tabName)
         {
    -        $parts = explode('.', $tabName);
    -        $last_idx = count($parts) - 1;
    +        $parts = explode('.', (string) $tabName);
    +        $last_idx = count($parts ?: []) - 1;
     
             $currentPointer = $this;
     
    @@ -465,8 +465,8 @@ public function findTab($tabName)
          */
         public function findOrMakeTab($tabName, $title = null)
         {
    -        $parts = explode('.', $tabName);
    -        $last_idx = count($parts) - 1;
    +        $parts = explode('.', (string) $tabName);
    +        $last_idx = count($parts ?: []) - 1;
             // We could have made this recursive, but I've chosen to keep all the logic code within FieldList rather than
             // add it to TabSet and Tab too.
             $currentPointer = $this;
    @@ -512,16 +512,16 @@ public function findOrMakeTab($tabName, $title = null)
         public function fieldByName($name)
         {
             $fullName = $name;
    -        if (strpos($name, '.') !== false) {
    -            list($name, $remainder) = explode('.', $name, 2);
    +        if (strpos((string) $name, '.') !== false) {
    +            list($name, $remainder) = explode('.', (string) $name, 2);
             } else {
                 $remainder = null;
             }
     
             foreach ($this as $child) {
    -            if (trim($fullName) == trim($child->getName()) || $fullName == $child->id) {
    +            if (trim((string) $fullName) == trim((string) $child->getName()) || $fullName == $child->id) {
                     return $child;
    -            } elseif (trim($name) == trim($child->getName()) || $name == $child->id) {
    +            } elseif (trim((string) $name) == trim((string) $child->getName()) || $name == $child->id) {
                     if ($remainder) {
                         if ($child instanceof CompositeField) {
                             return $child->fieldByName($remainder);
    @@ -552,7 +552,7 @@ public function dataFieldByName($name)
         {
             if ($dataFields = $this->dataFields()) {
                 foreach ($dataFields as $child) {
    -                if (trim($name) == trim($child->getName()) || $name == $child->id) {
    +                if (trim((string) $name) == trim((string) $child->getName()) || $name == $child->id) {
                         return $child;
                     }
                 }
    @@ -582,7 +582,7 @@ public function insertBefore($name, $item, $appendIfMissing = true)
             $i = 0;
             foreach ($this as $child) {
                 if ($name == $child->getName() || $name == $child->id) {
    -                array_splice($this->items, $i, 0, [$item]);
    +                array_splice($this->items, (int) $i, 0, [$item]);
                     return $item;
                 } elseif ($child instanceof CompositeField) {
                     $ret = $child->insertBefore($name, $item, false);
    diff --git a/src/Forms/FileField.php b/src/Forms/FileField.php
    index 6a6b54b4973..aa868677432 100644
    --- a/src/Forms/FileField.php
    +++ b/src/Forms/FileField.php
    @@ -120,7 +120,7 @@ protected function getAcceptFileTypes()
                 }
             }
     
    -        return array_unique($accept);
    +        return array_unique($accept ?: []);
         }
     
         /**
    @@ -181,8 +181,8 @@ public function validate($validator)
             // $_FILES super-global so it will be stored as $_FILES['mutli_file_syntax']
             // multi-file uploads, which are not officially supported by Silverstripe, though may be
             // implemented in custom code, so we should still ensure they are at least validated
    -        $isMultiFileUpload = strpos($this->name, '[') !== false;
    -        $fieldName = preg_replace('#\[(.*?)\]$#', '', $this->name);
    +        $isMultiFileUpload = strpos((string) $this->name, '[') !== false;
    +        $fieldName = preg_replace('#\[(.*?)\]$#', '', $this->name ?: '');
     
             if (!isset($_FILES[$fieldName])) {
                 return true;
    diff --git a/src/Forms/Form.php b/src/Forms/Form.php
    index 4f4816f7156..0f491d23bfb 100644
    --- a/src/Forms/Form.php
    +++ b/src/Forms/Form.php
    @@ -433,7 +433,7 @@ public function getSessionValidationResult()
         {
             $resultData = $this->getSession()->get("FormInfo.{$this->FormName()}.result");
             if (isset($resultData)) {
    -            return unserialize($resultData);
    +            return unserialize((string) $resultData);
             }
             return null;
         }
    @@ -525,7 +525,7 @@ public function setFieldMessage(
         public function castingHelper($field)
         {
             // Override casting for field message
    -        if (strcasecmp($field, 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
    +        if (strcasecmp((string) $field, 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
                 return $helper;
             }
             return parent::castingHelper($field);
    @@ -699,7 +699,7 @@ public function actionIsValidationExempt($action)
             if ($action->getValidationExempt()) {
                 return true;
             }
    -        if (in_array($action->actionName(), $this->getValidationExemptActions())) {
    +        if (in_array($action->actionName(), $this->getValidationExemptActions() ?: [])) {
                 return true;
             }
             return false;
    @@ -724,7 +724,7 @@ public function getExtraFields()
             $this->securityTokenAdded = true;
     
             // add the "real" HTTP method if necessary (for PUT, DELETE and HEAD)
    -        if (strtoupper($this->FormMethod()) != $this->FormHttpMethod()) {
    +        if (strtoupper((string) $this->FormMethod()) != $this->FormHttpMethod()) {
                 $methodField = new HiddenField('_method', '', $this->FormHttpMethod());
                 $methodField->setForm($this);
                 $extraFields->push($methodField);
    @@ -1017,7 +1017,7 @@ public function FormMethod()
          */
         public function setFormMethod($method, $strict = null)
         {
    -        $this->formMethod = strtoupper($method);
    +        $this->formMethod = strtoupper((string) $method);
             if ($strict !== null) {
                 $this->setStrictFormMethodCheck($strict);
             }
    @@ -1378,7 +1378,7 @@ public function loadDataFrom($data, $mergeStrategy = 0, $fieldList = null)
                 $name = $field->getName();
     
                 // Skip fields that have been excluded
    -            if ($fieldList && !in_array($name, $fieldList)) {
    +            if ($fieldList && !in_array($name, $fieldList ?: [])) {
                     continue;
                 }
     
    @@ -1394,7 +1394,7 @@ public function loadDataFrom($data, $mergeStrategy = 0, $fieldList = null)
     
                 if (is_object($data)) {
                     // Allow dot-syntax traversal of has-one relations fields
    -                if (strpos($name, '.') !== false) {
    +                if (strpos((string) $name, '.') !== false) {
                         $exists = (
                             $data->hasMethod('relField')
                         );
    @@ -1419,21 +1419,21 @@ public function loadDataFrom($data, $mergeStrategy = 0, $fieldList = null)
     
                 // Regular array access. Note that dot-syntax not supported here
                 } elseif (is_array($data)) {
    -                if (array_key_exists($name, $data)) {
    +                if (array_key_exists($name, $data ?: [])) {
                         $exists = true;
                         $val = $data[$name];
                     // PHP turns the '.'s in POST vars into '_'s
    -                } elseif (array_key_exists($altName = str_replace('.', '_', $name), $data)) {
    +                } elseif (array_key_exists($altName = str_replace('.', '_', $name ?: ''), $data ?: [])) {
                         $exists = true;
                         $val = $data[$altName];
    -                } elseif (preg_match_all('/(.*)\[(.*)\]/U', $name, $matches)) {
    +                } elseif (preg_match_all('/(.*)\[(.*)\]/U', (string) $name, $matches)) {
                         // If field is in array-notation we need to access nested data
                         //discard first match which is just the whole string
                         array_shift($matches);
                         $keys = array_pop($matches);
                         $name = array_shift($matches);
                         $name = array_shift($name);
    -                    if (array_key_exists($name, $data)) {
    +                    if (array_key_exists($name, $data ?: [])) {
                             $tmpData = &$data[$name];
                             // drill down into the data array looking for the corresponding value
                             foreach ($keys as $arrayKey) {
    @@ -1491,7 +1491,7 @@ public function saveInto(DataObjectInterface $dataObject, $fieldList = null)
             if ($dataFields) {
                 foreach ($dataFields as $field) {
                 // Skip fields that have been excluded
    -                if ($fieldList && is_array($fieldList) && !in_array($field->getName(), $fieldList)) {
    +                if ($fieldList && is_array($fieldList) && !in_array($field->getName(), $fieldList ?: [])) {
                         continue;
                     }
     
    @@ -1707,7 +1707,7 @@ public function getSecurityToken()
          */
         public function extraClass()
         {
    -        return implode(' ', array_unique($this->extraClasses));
    +        return implode(' ', array_unique($this->extraClasses ?: []));
         }
     
         /**
    @@ -1720,7 +1720,7 @@ public function extraClass()
         public function hasExtraClass($class)
         {
             //split at white space
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
             foreach ($classes as $class) {
                 if (!isset($this->extraClasses[$class])) {
                     return false;
    @@ -1740,7 +1740,7 @@ public function hasExtraClass($class)
         public function addExtraClass($class)
         {
             //split at white space
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
             foreach ($classes as $class) {
                 //add classes one by one
                 $this->extraClasses[$class] = $class;
    @@ -1758,7 +1758,7 @@ public function addExtraClass($class)
         public function removeExtraClass($class)
         {
             //split at white space
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
             foreach ($classes as $class) {
                 //unset one by one
                 unset($this->extraClasses[$class]);
    diff --git a/src/Forms/FormAction.php b/src/Forms/FormAction.php
    index ae9b32627e6..17e620525e3 100644
    --- a/src/Forms/FormAction.php
    +++ b/src/Forms/FormAction.php
    @@ -125,7 +125,7 @@ public function setIcon($icon)
          */
         public function actionName()
         {
    -        return substr($this->name, 7);
    +        return substr((string) $this->name, 7);
         }
     
         /**
    diff --git a/src/Forms/FormField.php b/src/Forms/FormField.php
    index ecd9ede9a20..a74c3a2fc87 100644
    --- a/src/Forms/FormField.php
    +++ b/src/Forms/FormField.php
    @@ -302,11 +302,11 @@ class FormField extends RequestHandler
         public static function name_to_label($fieldName)
         {
             // Handle dot delimiters
    -        if (strpos($fieldName, '.') !== false) {
    -            $parts = explode('.', $fieldName);
    +        if (strpos((string) $fieldName, '.') !== false) {
    +            $parts = explode('.', (string) $fieldName);
                 // Ensure that any letter following a dot is uppercased, so that the regex below can break it up
                 // into words
    -            $label = implode(array_map('ucfirst', $parts));
    +            $label = implode(array_map('ucfirst', $parts ?: []));
             } else {
                 $label = $fieldName;
             }
    @@ -315,13 +315,13 @@ public static function name_to_label($fieldName)
             // version of itself then the remaining lowercase letters.
             $labelWithSpaces = preg_replace_callback('/([A-Z])([a-z]+)/', function ($matches) {
                 return ' ' . strtolower($matches[1]) . $matches[2];
    -        }, $label);
    +        }, $label ?: '');
     
             // Add a space before any capital letter block that is at the end of the string
    -        $labelWithSpaces = preg_replace('/([a-z])([A-Z]+)$/', '$1 $2', $labelWithSpaces);
    +        $labelWithSpaces = preg_replace('/([a-z])([A-Z]+)$/', '$1 $2', $labelWithSpaces ?: '');
     
             // The first letter should be uppercase
    -        return ucfirst(trim($labelWithSpaces));
    +        return ucfirst(trim((string) $labelWithSpaces));
         }
     
         /**
    @@ -469,9 +469,9 @@ public function saveInto(DataObjectInterface $record)
             $fieldName = $this->name;
     
             // Allow for dot syntax
    -        if (($pos = strrpos($this->name, '.')) !== false) {
    -            $relation = substr($this->name, 0, $pos);
    -            $fieldName = substr($this->name, $pos + 1);
    +        if (($pos = strrpos((string) $this->name, '.')) !== false) {
    +            $relation = substr((string) $this->name, 0, $pos);
    +            $fieldName = substr((string) $this->name, $pos + 1);
                 $component = $record->relObject($relation);
             }
     
    @@ -574,7 +574,7 @@ public function extraClass()
             if ($this->extraClasses) {
                 $classes = array_merge(
                     $classes,
    -                array_values($this->extraClasses)
    +                array_values($this->extraClasses ?: [])
                 );
             }
     
    @@ -603,7 +603,7 @@ public function extraClass()
         public function hasExtraClass($class)
         {
             //split at white space
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
             foreach ($classes as $class) {
                 if (!isset($this->extraClasses[$class])) {
                     return false;
    @@ -623,7 +623,7 @@ public function hasExtraClass($class)
          */
         public function addExtraClass($class)
         {
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
     
             foreach ($classes as $class) {
                 $this->extraClasses[$class] = $class;
    @@ -641,7 +641,7 @@ public function addExtraClass($class)
          */
         public function removeExtraClass($class)
         {
    -        $classes = preg_split('/\s+/', $class);
    +        $classes = preg_split('/\s+/', (string) $class);
     
             foreach ($classes as $class) {
                 unset($this->extraClasses[$class]);
    @@ -794,7 +794,7 @@ public function securityTokenEnabled()
         public function castingHelper($field)
         {
             // Override casting for field message
    -        if (strcasecmp($field, 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
    +        if (strcasecmp((string) $field, 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
                 return $helper;
             }
             return parent::castingHelper($field);
    @@ -921,7 +921,7 @@ public function Field($properties = [])
     
             $this->extend('onBeforeRender', $context, $properties);
     
    -        if (count($properties)) {
    +        if (count($properties ?: [])) {
                 $context = $context->customise($properties);
             }
     
    @@ -929,7 +929,7 @@ public function Field($properties = [])
     
             // Trim whitespace from the result, so that trailing newlines are suppressed. Works for strings and HTMLText values
             if (is_string($result)) {
    -            $result = trim($result);
    +            $result = trim((string) $result);
             } elseif ($result instanceof DBField) {
                 $result->setValue(trim($result->getValue()));
             }
    @@ -956,7 +956,7 @@ public function FieldHolder($properties = [])
     
             $this->extend('onBeforeRenderHolder', $context, $properties);
     
    -        if (count($properties)) {
    +        if (count($properties ?: [])) {
                 $context = $this->customise($properties);
             }
     
    @@ -974,7 +974,7 @@ public function SmallFieldHolder($properties = [])
         {
             $context = $this;
     
    -        if (count($properties)) {
    +        if (count($properties ?: [])) {
                 $context = $this->customise($properties);
             }
     
    @@ -1198,8 +1198,8 @@ public function transform(FormTransformation $transformation)
          */
         public function hasClass($class)
         {
    -        $classes = explode(' ', strtolower($this->extraClass()));
    -        return in_array(strtolower(trim($class)), $classes);
    +        $classes = explode(' ', strtolower((string) $this->extraClass()));
    +        return in_array(strtolower(trim((string) $class)), $classes ?: []);
         }
     
         /**
    @@ -1216,7 +1216,7 @@ public function hasClass($class)
         public function Type()
         {
             $type = new ReflectionClass($this);
    -        return strtolower(preg_replace('/Field$/', '', $type->getShortName()));
    +        return strtolower((string) preg_replace('/Field$/', '', $type->getShortName() ?: ''));
         }
     
         /**
    @@ -1420,7 +1420,7 @@ public function getSchemaComponent()
         public function setSchemaData($schemaData = [])
         {
             $defaults = $this->getSchemaData();
    -        $this->schemaData = array_merge($this->schemaData, array_intersect_key($schemaData, $defaults));
    +        $this->schemaData = array_merge($this->schemaData, array_intersect_key($schemaData ?: [], $defaults));
             return $this;
         }
     
    @@ -1432,7 +1432,7 @@ public function setSchemaData($schemaData = [])
         public function getSchemaData()
         {
             $defaults = $this->getSchemaDataDefaults();
    -        return array_replace_recursive($defaults, array_intersect_key($this->schemaData, $defaults));
    +        return array_replace_recursive($defaults ?: [], array_intersect_key($this->schemaData ?: [], $defaults));
         }
     
         /**
    @@ -1496,7 +1496,7 @@ public function getSchemaDataDefaults()
         public function setSchemaState($schemaState = [])
         {
             $defaults = $this->getSchemaState();
    -        $this->schemaState = array_merge($this->schemaState, array_intersect_key($schemaState, $defaults));
    +        $this->schemaState = array_merge($this->schemaState, array_intersect_key($schemaState ?: [], $defaults));
             return $this;
         }
     
    @@ -1508,7 +1508,7 @@ public function setSchemaState($schemaState = [])
         public function getSchemaState()
         {
             $defaults = $this->getSchemaStateDefaults();
    -        return array_merge($defaults, array_intersect_key($this->schemaState, $defaults));
    +        return array_merge($defaults, array_intersect_key($this->schemaState ?: [], $defaults));
         }
     
         /**
    diff --git a/src/Forms/FormRequestHandler.php b/src/Forms/FormRequestHandler.php
    index ee40828d8f8..1393d69fed6 100644
    --- a/src/Forms/FormRequestHandler.php
    +++ b/src/Forms/FormRequestHandler.php
    @@ -131,7 +131,7 @@ public function httpSubmission($request)
             }
     
             // Ensure we only process saveable fields (non structural, readonly, or disabled)
    -        $allowedFields = array_keys($this->form->Fields()->saveableFields());
    +        $allowedFields = array_keys($this->form->Fields()->saveableFields() ?: []);
     
             // Populate the form
             $this->form->loadDataFrom($vars, true, $allowedFields);
    @@ -165,17 +165,17 @@ public function httpSubmission($request)
             // Determine the action button clicked
             $funcName = null;
             foreach ($vars as $paramName => $paramVal) {
    -            if (substr($paramName, 0, 7) == 'action_') {
    +            if (substr((string) $paramName, 0, 7) == 'action_') {
                     // Break off querystring arguments included in the action
    -                if (strpos($paramName, '?') !== false) {
    -                    list($paramName, $paramVars) = explode('?', $paramName, 2);
    +                if (strpos((string) $paramName, '?') !== false) {
    +                    list($paramName, $paramVars) = explode('?', (string) $paramName, 2);
                         $newRequestParams = [];
    -                    parse_str($paramVars, $newRequestParams);
    +                    parse_str((string) $paramVars, $newRequestParams);
                         $vars = array_merge((array)$vars, (array)$newRequestParams);
                     }
     
                     // Cleanup action_, _x and _y from image fields
    -                $funcName = preg_replace(['/^action_/','/_x$|_y$/'], '', $paramName);
    +                $funcName = preg_replace(['/^action_/','/_x$|_y$/'], '', $paramName ?: '');
                     break;
                 }
             }
    @@ -375,7 +375,7 @@ protected function getAjaxErrorResponse(ValidationResult $result)
         {
             // Ajax form submissions accept json encoded errors by default
             $acceptType = $this->getRequest()->getHeader('Accept');
    -        if (strpos($acceptType, 'application/json') !== false) {
    +        if (strpos((string) $acceptType, 'application/json') !== false) {
                 // Send validation errors back as JSON with a flag at the start
                 $response = new HTTPResponse(json_encode($result->getMessages()));
                 $response->addHeader('Content-Type', 'application/json');
    @@ -473,7 +473,7 @@ protected function getAllActions()
             $actions = $this->form->Actions()->dataFields();
     
             $fieldsAndActions = array_merge($fields, $actions);
    -        $actions = array_filter($fieldsAndActions, function ($fieldOrAction) {
    +        $actions = array_filter($fieldsAndActions ?: [], function ($fieldOrAction) {
                 return $fieldOrAction instanceof FormAction;
             });
     
    diff --git a/src/Forms/FormScaffolder.php b/src/Forms/FormScaffolder.php
    index 8b35404d3b4..67854668275 100644
    --- a/src/Forms/FormScaffolder.php
    +++ b/src/Forms/FormScaffolder.php
    @@ -83,7 +83,7 @@ public function getFieldList()
             // Add logical fields directly specified in db config
             foreach ($this->obj->config()->get('db') as $fieldName => $fieldType) {
                 // Skip restricted fields
    -            if ($this->restrictFields && !in_array($fieldName, $this->restrictFields)) {
    +            if ($this->restrictFields && !in_array($fieldName, $this->restrictFields ?: [])) {
                     continue;
                 }
     
    @@ -112,7 +112,7 @@ public function getFieldList()
             // add has_one relation fields
             if ($this->obj->hasOne()) {
                 foreach ($this->obj->hasOne() as $relationship => $component) {
    -                if ($this->restrictFields && !in_array($relationship, $this->restrictFields)) {
    +                if ($this->restrictFields && !in_array($relationship, $this->restrictFields ?: [])) {
                         continue;
                     }
                     $fieldName = $component === 'SilverStripe\\ORM\\DataObject'
    diff --git a/src/Forms/FormTemplateHelper.php b/src/Forms/FormTemplateHelper.php
    index a487ac44aff..a8231eeada1 100644
    --- a/src/Forms/FormTemplateHelper.php
    +++ b/src/Forms/FormTemplateHelper.php
    @@ -39,7 +39,7 @@ public function generateFormID($form)
             }
     
             $reflection = new ReflectionClass($form);
    -        $shortName = str_replace(['.', '/'], '', $form->getName());
    +        $shortName = str_replace(['.', '/'], '', $form->getName() ?: '');
             return Convert::raw2htmlid($reflection->getShortName() . '_' . $shortName);
         }
     
    @@ -62,7 +62,7 @@ public function generateFieldHolderID($field)
         public function generateFieldID($field)
         {
             // Don't include '.'s in IDs, they confused JavaScript
    -        $name = str_replace('.', '_', $field->getName());
    +        $name = str_replace('.', '_', $field->getName() ?: '');
     
             if ($form = $field->getForm()) {
                 return sprintf(
    diff --git a/src/Forms/FormTransformation.php b/src/Forms/FormTransformation.php
    index 9b64a1c1e63..741484f1782 100644
    --- a/src/Forms/FormTransformation.php
    +++ b/src/Forms/FormTransformation.php
    @@ -44,16 +44,16 @@ public function transform(FormField $field)
                 function ($name) {
                     return ClassInfo::shortName($name);
                 },
    -            array_values(ClassInfo::ancestry($this))
    +            array_values(ClassInfo::ancestry($this) ?: [])
             ));
             $fieldClasses = array_reverse(array_map(
                 function ($name) {
                     return ClassInfo::shortName($name);
                 },
    -            array_values(ClassInfo::ancestry($field))
    +            array_values(ClassInfo::ancestry($field) ?: [])
             ));
     
    -        $len = max(sizeof($transNames), sizeof($fieldClasses));
    +        $len = max(sizeof($transNames ?: []), sizeof($fieldClasses ?: []));
             for ($i=0; $i<$len; $i++) {
                 // This is lets fieldClasses be longer than transNames
                 if (!empty($transNames[$i])) {
    diff --git a/src/Forms/GridField/GridField.php b/src/Forms/GridField/GridField.php
    index ae25e1ec379..4802007c331 100644
    --- a/src/Forms/GridField/GridField.php
    +++ b/src/Forms/GridField/GridField.php
    @@ -263,7 +263,7 @@ public function performReadonlyTransformation()
             $allowedComponents = $this->getReadonlyComponents();
             foreach ($this->getConfig()->getComponents() as $component) {
                 // if a component doesn't exist, remove it from the readonly version.
    -            if (!in_array(get_class($component), $allowedComponents)) {
    +            if (!in_array(get_class($component), $allowedComponents ?: [])) {
                     $copyConfig->removeComponent($component);
                 }
             }
    @@ -354,18 +354,18 @@ public function getCastedValue($value, $castingDefinition)
                 $castingDefinition = array_shift($castingDefinition);
             }
     
    -        if (strpos($castingDefinition, '->') === false) {
    +        if (strpos((string) $castingDefinition, '->') === false) {
                 $castingFieldType = $castingDefinition;
                 $castingField = DBField::create_field($castingFieldType, $value);
     
    -            return call_user_func_array([$castingField, 'XML'], $castingParams);
    +            return call_user_func_array([$castingField, 'XML'], $castingParams ?: []);
             }
     
    -        list($castingFieldType, $castingMethod) = explode('->', $castingDefinition);
    +        list($castingFieldType, $castingMethod) = explode('->', (string) $castingDefinition);
     
             $castingField = DBField::create_field($castingFieldType, $value);
     
    -        return call_user_func_array([$castingField, $castingMethod], $castingParams);
    +        return call_user_func_array([$castingField, $castingMethod], $castingParams ?: []);
         }
     
         /**
    @@ -471,7 +471,7 @@ public function FieldHolder($properties = [])
     
                     if ($fragments) {
                         foreach ($fragments as $fragmentKey => $fragmentValue) {
    -                        $fragmentKey = strtolower($fragmentKey);
    +                        $fragmentKey = strtolower((string) $fragmentKey);
     
                             if (!isset($content[$fragmentKey])) {
                                 $content[$fragmentKey] = '';
    @@ -484,7 +484,7 @@ public function FieldHolder($properties = [])
             }
     
             foreach ($content as $contentKey => $contentValue) {
    -            $content[$contentKey] = trim($contentValue);
    +            $content[$contentKey] = trim((string) $contentValue);
             }
     
             // Replace custom fragments and check which fragments are defined. Circular dependencies
    @@ -501,16 +501,16 @@ public function FieldHolder($properties = [])
             // TODO: Break the below into separate reducer methods
     
             // Continue looping if any placeholders exist
    -        while (array_filter($content, function ($value) {
    -            return preg_match(self::FRAGMENT_REGEX, $value);
    +        while (array_filter($content ?: [], function ($value) {
    +            return preg_match((string) self::FRAGMENT_REGEX, (string) $value);
             })) {
                 foreach ($content as $contentKey => $contentValue) {
                     // Skip if this specific content has no placeholders
    -                if (!preg_match_all(self::FRAGMENT_REGEX, $contentValue, $matches)) {
    +                if (!preg_match_all((string) self::FRAGMENT_REGEX, (string) $contentValue, $matches)) {
                         continue;
                     }
                     foreach ($matches[1] as $match) {
    -                    $fragmentName = strtolower($match);
    +                    $fragmentName = strtolower((string) $match);
                         $fragmentDefined[$fragmentName] = true;
     
                         $fragment = '';
    @@ -522,7 +522,7 @@ public function FieldHolder($properties = [])
                         // If the fragment still has a fragment definition in it, when we should defer
                         // this item until later.
     
    -                    if (preg_match(self::FRAGMENT_REGEX, $fragment, $matches)) {
    +                    if (preg_match((string) self::FRAGMENT_REGEX, (string) $fragment, $matches)) {
                             if (isset($fragmentDeferred[$contentKey]) && $fragmentDeferred[$contentKey] > 5) {
                                 throw new LogicException(sprintf(
                                     'GridField HTML fragment "%s" and "%s" appear to have a circular dependency.',
    @@ -545,7 +545,7 @@ public function FieldHolder($properties = [])
                         } else {
                             $content[$contentKey] = preg_replace(
                                 sprintf('/\$DefineFragment\(%s\)/i', $fragmentName),
    -                            $fragment,
    +                            $fragment ?: '',
                                 $content[$contentKey]
                             );
                         }
    @@ -565,7 +565,7 @@ public function FieldHolder($properties = [])
                 }
             }
     
    -        $total = count($list);
    +        $total = count($list ?: []);
     
             if ($total > 0) {
                 $rows = [];
    @@ -632,7 +632,7 @@ public function FieldHolder($properties = [])
             $this->addExtraClass('ss-gridfield grid-field field');
     
             $fieldsetAttributes = array_diff_key(
    -            $this->getAttributes(),
    +            $this->getAttributes() ?: [],
                 [
                     'value' => false,
                     'type' => false,
    @@ -997,7 +997,7 @@ public function getColumnCount()
                 $this->buildColumnDispatch();
             }
     
    -        return count($this->columnDispatch);
    +        return count($this->columnDispatch ?: []);
         }
     
         /**
    @@ -1062,7 +1062,7 @@ public function gridFieldAlterAction($data, $form, HTTPRequest $request)
             $store = Injector::inst()->create(StateStore::class . '.' . $this->getName());
     
             foreach ($data as $dataKey => $dataValue) {
    -            if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $dataKey, $matches)) {
    +            if (preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', (string) $dataKey, $matches)) {
                     $stateChange = $store->load($matches[1]);
     
                     $actionName = $stateChange['actionName'];
    @@ -1104,13 +1104,13 @@ public function gridFieldAlterAction($data, $form, HTTPRequest $request)
          */
         public function handleAlterAction($actionName, $arguments, $data)
         {
    -        $actionName = strtolower($actionName);
    +        $actionName = strtolower((string) $actionName);
     
             foreach ($this->getComponents() as $component) {
                 if ($component instanceof GridField_ActionProvider) {
                     $actions = array_map('strtolower', (array) $component->getActions($this));
     
    -                if (in_array($actionName, $actions)) {
    +                if (in_array($actionName, $actions ?: [])) {
                         return $component->handleAction($this, $actionName, $arguments, $data);
                     }
                 }
    diff --git a/src/Forms/GridField/GridFieldAddExistingAutocompleter.php b/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
    index 6bec9d026d5..84f74f2fb50 100644
    --- a/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
    +++ b/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
    @@ -241,7 +241,7 @@ public function doSearch($gridField, $request)
     
             $params = [];
             foreach ($searchFields as $searchField) {
    -            $name = (strpos($searchField, ':') !== false) ? $searchField : "$searchField:StartsWith";
    +            $name = (strpos((string) $searchField, ':') !== false) ? $searchField : "$searchField:StartsWith";
                 $params[$name] = $searchStr;
             }
     
    @@ -342,12 +342,12 @@ public function scaffoldSearchFields($dataClass)
             if ($fieldSpecs = $obj->searchableFields()) {
                 $customSearchableFields = $obj->config()->get('searchable_fields');
                 foreach ($fieldSpecs as $name => $spec) {
    -                if (is_array($spec) && array_key_exists('filter', $spec)) {
    +                if (is_array($spec) && array_key_exists('filter', $spec ?: [])) {
                         // The searchableFields() spec defaults to PartialMatch,
                         // so we need to check the original setting.
                         // If the field is defined $searchable_fields = array('MyField'),
                         // then default to StartsWith filter, which makes more sense in this context.
    -                    if (!$customSearchableFields || array_search($name, $customSearchableFields)) {
    +                    if (!$customSearchableFields || array_search($name, $customSearchableFields ?: [])) {
                             $filter = 'StartsWith';
                         } else {
                             $filterName = $spec['filter'];
    @@ -356,12 +356,12 @@ public function scaffoldSearchFields($dataClass)
                                 $filterName = get_class($filterName);
                             }
                             // It can be a fully qualified class name
    -                        if (strpos($filterName, '\\') !== false) {
    -                            $filterNameParts = explode("\\", $filterName);
    +                        if (strpos((string) $filterName, '\\') !== false) {
    +                            $filterNameParts = explode("\\", (string) $filterName);
                                 // We expect an alias matching the class name without namespace, see #coresearchaliases
                                 $filterName = array_pop($filterNameParts);
                             }
    -                        $filter = preg_replace('/Filter$/', '', $filterName);
    +                        $filter = preg_replace('/Filter$/', '', $filterName ?: '');
                         }
                         $fields[] = "{$name}:{$filter}";
                     } else {
    @@ -397,7 +397,7 @@ public function getPlaceholderText($dataClass)
                 $labels = [];
                 if ($searchFields) {
                     foreach ($searchFields as $searchField) {
    -                    $searchField = explode(':', $searchField);
    +                    $searchField = explode(':', (string) $searchField);
                         $label = singleton($dataClass)->fieldLabel($searchField[0]);
                         if ($label) {
                             $labels[] = $label;
    diff --git a/src/Forms/GridField/GridFieldDataColumns.php b/src/Forms/GridField/GridFieldDataColumns.php
    index 39ee1ecfe43..e3373db5d4b 100644
    --- a/src/Forms/GridField/GridFieldDataColumns.php
    +++ b/src/Forms/GridField/GridFieldDataColumns.php
    @@ -38,13 +38,13 @@ class GridFieldDataColumns extends AbstractGridFieldComponent implements GridFie
          */
         public function augmentColumns($gridField, &$columns)
         {
    -        $baseColumns = array_keys($this->getDisplayFields($gridField));
    +        $baseColumns = array_keys($this->getDisplayFields($gridField) ?: []);
     
             foreach ($baseColumns as $col) {
                 $columns[] = $col;
             }
     
    -        $columns = array_unique($columns);
    +        $columns = array_unique($columns ?: []);
         }
     
         /**
    @@ -55,7 +55,7 @@ public function augmentColumns($gridField, &$columns)
          */
         public function getColumnsHandled($gridField)
         {
    -        return array_keys($this->getDisplayFields($gridField));
    +        return array_keys($this->getDisplayFields($gridField) ?: []);
         }
     
         /**
    @@ -154,7 +154,7 @@ public function getColumnContent($gridField, $record, $columnName)
         {
             // Find the data column for the given named column
             $columns = $this->getDisplayFields($gridField);
    -        $columnInfo = array_key_exists($columnName, $columns) ? $columns[$columnName] : null;
    +        $columnInfo = array_key_exists($columnName, $columns ?: []) ? $columns[$columnName] : null;
     
             // Allow callbacks
             if (is_array($columnInfo) && isset($columnInfo['callback'])) {
    @@ -222,12 +222,12 @@ public function getColumnMetadata($gridField, $column)
          */
         protected function getValueFromRelation($record, $columnName)
         {
    -        $fieldNameParts = explode('.', $columnName);
    +        $fieldNameParts = explode('.', (string) $columnName);
             $tmpItem = clone($record);
    -        for ($idx = 0; $idx < sizeof($fieldNameParts); $idx++) {
    +        for ($idx = 0; $idx < sizeof($fieldNameParts ?: []); $idx++) {
                 $methodName = $fieldNameParts[$idx];
                 // Last mmethod call from $columnName return what that method is returning
    -            if ($idx == sizeof($fieldNameParts) - 1) {
    +            if ($idx == sizeof($fieldNameParts ?: []) - 1) {
                     return $tmpItem->XML_val($methodName);
                 }
                 // else get the object from this $methodName
    @@ -247,20 +247,20 @@ protected function getValueFromRelation($record, $columnName)
         protected function castValue($gridField, $fieldName, $value)
         {
             // If a fieldCasting is specified, we assume the result is safe
    -        if (array_key_exists($fieldName, $this->fieldCasting)) {
    +        if (array_key_exists($fieldName, $this->fieldCasting ?: [])) {
                 $value = $gridField->getCastedValue($value, $this->fieldCasting[$fieldName]);
             } elseif (is_object($value)) {
                 // If the value is an object, we do one of two things
                 if (method_exists($value, 'Nice')) {
                     // If it has a "Nice" method, call that & make sure the result is safe
    -                $value = nl2br(Convert::raw2xml($value->Nice()));
    +                $value = nl2br((string) Convert::raw2xml($value->Nice()));
                 } else {
                     // Otherwise call forTemplate - the result of this should already be safe
                     $value = $value->forTemplate();
                 }
             } else {
                 // Otherwise, just treat as a text string & make sure the result is safe
    -            $value = nl2br(Convert::raw2xml($value));
    +            $value = nl2br((string) Convert::raw2xml($value));
             }
     
             return $value;
    @@ -276,7 +276,7 @@ protected function castValue($gridField, $fieldName, $value)
          */
         protected function formatValue($gridField, $item, $fieldName, $value)
         {
    -        if (!array_key_exists($fieldName, $this->fieldFormatting)) {
    +        if (!array_key_exists($fieldName, $this->fieldFormatting ?: [])) {
                 return $value;
             }
     
    @@ -284,9 +284,9 @@ protected function formatValue($gridField, $item, $fieldName, $value)
             if (!is_string($spec) && is_callable($spec)) {
                 return $spec($value, $item);
             } else {
    -            $format = str_replace('$value', "__VAL__", $spec);
    -            $format = preg_replace('/\$([A-Za-z0-9-_]+)/', '$item->$1', $format);
    -            $format = str_replace('__VAL__', '$value', $format);
    +            $format = str_replace('$value', "__VAL__", $spec ?: '');
    +            $format = preg_replace('/\$([A-Za-z0-9-_]+)/', '$item->$1', $format ?: '');
    +            $format = str_replace('__VAL__', '$value', $format ?: '');
                 eval('$value = "' . $format . '";');
                 return $value;
             }
    @@ -306,7 +306,7 @@ protected function escapeValue($gridField, $value)
             }
     
             foreach ($escape as $search => $replace) {
    -            $value = str_replace($search, $replace, $value);
    +            $value = str_replace($search ?: '', $replace ?: '', $value ?: '');
             }
             return $value;
         }
    diff --git a/src/Forms/GridField/GridFieldDeleteAction.php b/src/Forms/GridField/GridFieldDeleteAction.php
    index e5bc0434991..b1473d33cf6 100644
    --- a/src/Forms/GridField/GridFieldDeleteAction.php
    +++ b/src/Forms/GridField/GridFieldDeleteAction.php
    @@ -96,7 +96,7 @@ public function getExtraData($gridField, $record, $columnName)
          */
         public function augmentColumns($gridField, &$columns)
         {
    -        if (!in_array('Actions', $columns)) {
    +        if (!in_array('Actions', $columns ?: [])) {
                 $columns[] = 'Actions';
             }
         }
    diff --git a/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php b/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php
    index ee04715d640..68f526a2403 100644
    --- a/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php
    +++ b/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php
    @@ -523,7 +523,7 @@ public function doSave($data, $form)
             $this->saveFormIntoRecord($data, $form);
     
             $link = '"'
    -            . htmlspecialchars($this->record->Title, ENT_QUOTES)
    +            . htmlspecialchars((string) $this->record->Title, ENT_QUOTES)
                 . '"';
             $message = _t(
                 'SilverStripe\\Forms\\GridField\\GridFieldDetailForm.Saved',
    @@ -583,7 +583,7 @@ private function getAdjacentRecordID($offset)
             $limitOffset = max(0, $itemsPerPage * ($currentPage-1) -1);
     
             $map = $list->limit($limit, $limitOffset)->column('ID');
    -        $index = array_search($this->record->ID, $map);
    +        $index = array_search($this->record->ID, $map ?: []);
             return isset($map[$index+$offset]) ? $map[$index+$offset] : false;
         }
     
    diff --git a/src/Forms/GridField/GridFieldEditButton.php b/src/Forms/GridField/GridFieldEditButton.php
    index aac826507af..6e771e6a2c7 100644
    --- a/src/Forms/GridField/GridFieldEditButton.php
    +++ b/src/Forms/GridField/GridFieldEditButton.php
    @@ -85,7 +85,7 @@ public function getUrl($gridField, $record, $columnName, $addState = true)
          */
         public function augmentColumns($gridField, &$columns)
         {
    -        if (!in_array('Actions', $columns)) {
    +        if (!in_array('Actions', $columns ?: [])) {
                 $columns[] = 'Actions';
             }
         }
    @@ -167,7 +167,7 @@ public function getColumnContent($gridField, $record, $columnName)
          */
         public function getExtraClass()
         {
    -        return implode(' ', array_keys($this->extraClass));
    +        return implode(' ', array_keys($this->extraClass ?: []));
         }
     
         /**
    diff --git a/src/Forms/GridField/GridFieldExportButton.php b/src/Forms/GridField/GridFieldExportButton.php
    index 1954eb00628..40a751d7a51 100644
    --- a/src/Forms/GridField/GridFieldExportButton.php
    +++ b/src/Forms/GridField/GridFieldExportButton.php
    @@ -192,7 +192,7 @@ public function generateExportFileData($gridField)
                 // determine the CSV headers. If a field is callable (e.g. anonymous function) then use the
                 // source name as the header instead
                 foreach ($csvColumns as $columnSource => $columnHeader) {
    -                if (is_array($columnHeader) && array_key_exists('title', $columnHeader)) {
    +                if (is_array($columnHeader) && array_key_exists('title', $columnHeader ?: [])) {
                         $headers[] = $columnHeader['title'];
                     } else {
                         $headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader;
    @@ -242,9 +242,9 @@ public function generateExportFileData($gridField)
                             }
     
                             $value = $columnHeader($relObj);
    -                    } elseif ($gridFieldColumnsComponent && array_key_exists($columnSource, $columnsHandled)) {
    +                    } elseif ($gridFieldColumnsComponent && array_key_exists($columnSource, $columnsHandled ?: [])) {
                             $value = strip_tags(
    -                            $gridFieldColumnsComponent->getColumnContent($gridField, $item, $columnSource)
    +                            (string) $gridFieldColumnsComponent->getColumnContent($gridField, $item, $columnSource)
                             );
                         } else {
                             $value = $gridField->getDataFieldValue($item, $columnSource);
    diff --git a/src/Forms/GridField/GridFieldFilterHeader.php b/src/Forms/GridField/GridFieldFilterHeader.php
    index 9bd3bf37a98..1bbe0c601e0 100755
    --- a/src/Forms/GridField/GridFieldFilterHeader.php
    +++ b/src/Forms/GridField/GridFieldFilterHeader.php
    @@ -284,7 +284,7 @@ public function getSearchFieldSchema(GridField $gridField)
     
             $context = $this->getSearchContext($gridField);
             $params = $gridField->getRequest()->postVar('filter') ?: [];
    -        if (array_key_exists($gridField->getName(), $params)) {
    +        if (array_key_exists($gridField->getName(), $params ?: [])) {
                 $params = $params[$gridField->getName()];
             }
             if ($context->getSearchParams()) {
    @@ -302,7 +302,7 @@ public function getSearchFieldSchema(GridField $gridField)
             if (!$this->useLegacyFilterHeader && !empty($filters)) {
                 $filters = array_combine(array_map(function ($key) {
                     return 'Search__' . $key;
    -            }, array_keys($filters)), $filters);
    +            }, array_keys($filters ?: [])), $filters ?: []);
             }
     
             $searchAction = GridField_FormAction::create($gridField, 'filter', false, 'filter', null);
    @@ -352,7 +352,7 @@ public function getSearchForm(GridField $gridField)
             foreach ($columns as $columnField) {
                 $metadata = $gridField->getColumnMetadata($columnField);
                 // Get the field name, without any modifications
    -            $name = explode('.', $columnField);
    +            $name = explode('.', (string) $columnField);
                 $title = $metadata['title'];
                 $field = $searchFields->fieldByName($name[0]);
     
    @@ -465,7 +465,7 @@ public function getLegacyFilterHeader(GridField $gridField)
                     );
                 }
     
    -            if ($currentColumn == count($columns)) {
    +            if ($currentColumn == count($columns ?: [])) {
                     $fields->push(
                         GridField_FormAction::create($gridField, 'filter', false, 'filter', null)
                             ->addExtraClass('btn font-icon-search btn--no-text btn--icon-large grid-field__filter-submit ss-gridfield-button-filter')
    diff --git a/src/Forms/GridField/GridFieldGroupDeleteAction.php b/src/Forms/GridField/GridFieldGroupDeleteAction.php
    index 36721002cb9..7fc506156ba 100644
    --- a/src/Forms/GridField/GridFieldGroupDeleteAction.php
    +++ b/src/Forms/GridField/GridFieldGroupDeleteAction.php
    @@ -93,11 +93,11 @@ protected function canUnlink($record)
                 && Permission::checkMember($record, 'ADMIN')
             ) {
                 $adminGroups = array_intersect(
    -                $record->Groups()->column(),
    +                $record->Groups()->column() ?: [],
                     Permission::get_groups_by_permission('ADMIN')->column()
                 );
     
    -            if (count($adminGroups) === 1 && array_search($this->groupID, $adminGroups) !== false) {
    +            if (count($adminGroups ?: []) === 1 && array_search($this->groupID, $adminGroups ?: []) !== false) {
                     return false;
                 }
             }
    diff --git a/src/Forms/GridField/GridFieldSortableHeader.php b/src/Forms/GridField/GridFieldSortableHeader.php
    index 7255cc8b584..94a0b388915 100644
    --- a/src/Forms/GridField/GridFieldSortableHeader.php
    +++ b/src/Forms/GridField/GridFieldSortableHeader.php
    @@ -127,7 +127,7 @@ public function getHTMLFragments($gridField)
             foreach ($columns as $columnField) {
                 $currentColumn++;
                 $metadata = $gridField->getColumnMetadata($columnField);
    -            $fieldName = str_replace('.', '-', $columnField);
    +            $fieldName = str_replace('.', '-', $columnField ?: '');
                 $title = $metadata['title'];
     
                 if (isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) {
    @@ -136,12 +136,12 @@ public function getHTMLFragments($gridField)
     
                 $allowSort = ($title && $list->canSortBy($columnField));
     
    -            if (!$allowSort && strpos($columnField, '.') !== false) {
    +            if (!$allowSort && strpos((string) $columnField, '.') !== false) {
                     // we have a relation column with dot notation
                     // @see DataObject::relField for approximation
    -                $parts = explode('.', $columnField);
    +                $parts = explode('.', (string) $columnField);
                     $tmpItem = singleton($list->dataClass());
    -                for ($idx = 0; $idx < sizeof($parts); $idx++) {
    +                for ($idx = 0; $idx < sizeof($parts ?: []); $idx++) {
                         $methodName = $parts[$idx];
                         if ($tmpItem instanceof SS_List) {
                             // It's impossible to sort on a HasManyList/ManyManyList
    @@ -154,7 +154,7 @@ public function getHTMLFragments($gridField)
                         ) {
                             // Else, if we've found a database field at the end of the chain, we can sort on it.
                             // If a method is applied further to this field (E.g. 'Cost.Currency') then don't try to sort.
    -                        $allowSort = $idx === sizeof($parts) - 1;
    +                        $allowSort = $idx === sizeof($parts ?: []) - 1;
                             break;
                         } else {
                             // If neither method nor field, then unable to sort
    @@ -187,7 +187,7 @@ public function getHTMLFragments($gridField)
                         }
                     }
                 } else {
    -                if ($currentColumn == count($columns)) {
    +                if ($currentColumn == count($columns ?: [])) {
                         $filter = $gridField->getConfig()->getComponentByType(GridFieldFilterHeader::class);
     
                         if ($filter && $filter->useLegacyFilterHeader && $filter->canFilterAnyColumns($gridField)) {
    diff --git a/src/Forms/GridField/GridFieldViewButton.php b/src/Forms/GridField/GridFieldViewButton.php
    index 9848f7a25cc..22ee5e4e845 100644
    --- a/src/Forms/GridField/GridFieldViewButton.php
    +++ b/src/Forms/GridField/GridFieldViewButton.php
    @@ -49,7 +49,7 @@ public function getUrl($gridField, $record, $columnName)
     
         public function augmentColumns($field, &$columns)
         {
    -        if (!in_array('Actions', $columns)) {
    +        if (!in_array('Actions', $columns ?: [])) {
                 $columns[] = 'Actions';
             }
         }
    diff --git a/src/Forms/GridField/GridField_ActionMenu.php b/src/Forms/GridField/GridField_ActionMenu.php
    index caf535b573c..0f90ed967a3 100644
    --- a/src/Forms/GridField/GridField_ActionMenu.php
    +++ b/src/Forms/GridField/GridField_ActionMenu.php
    @@ -12,7 +12,7 @@ class GridField_ActionMenu extends AbstractGridFieldComponent implements GridFie
     {
         public function augmentColumns($gridField, &$columns)
         {
    -        if (!in_array('Actions', $columns)) {
    +        if (!in_array('Actions', $columns ?: [])) {
                 $columns[] = 'Actions';
             }
         }
    @@ -86,7 +86,7 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
                     $actions = $item->getActions($gridField);
                 }
     
    -            if (in_array($actionName, $actions)) {
    +            if (in_array($actionName, $actions ?: [])) {
                     $item->handleAction($gridField, $actionName, $arguments, $data);
                 }
             }
    diff --git a/src/Forms/GridField/GridField_FormAction.php b/src/Forms/GridField/GridField_FormAction.php
    index 789b34c9ae9..23d73748780 100644
    --- a/src/Forms/GridField/GridField_FormAction.php
    +++ b/src/Forms/GridField/GridField_FormAction.php
    @@ -69,7 +69,7 @@ public function __construct(GridField $gridField, $name, $title, $actionName, $a
          */
         public function nameEncode($value)
         {
    -        return (string)preg_replace_callback('/[^\w]/', [$this, '_nameEncode'], $value);
    +        return (string)preg_replace_callback('/[^\w]/', [$this, '_nameEncode'], $value ?: '');
         }
     
         /**
    diff --git a/src/Forms/GridField/GridState.php b/src/Forms/GridField/GridState.php
    index 2a5efe7ba0d..059ed2a082b 100644
    --- a/src/Forms/GridField/GridState.php
    +++ b/src/Forms/GridField/GridState.php
    @@ -51,7 +51,7 @@ public static function array_to_object($d)
             if (is_array($d)) {
                 return (object) array_map(function ($item) {
                     return GridState::array_to_object($item);
    -            }, $d);
    +            }, $d ?: []);
             }
     
             return $d;
    @@ -60,7 +60,7 @@ public static function array_to_object($d)
         public function setValue($value, $data = null)
         {
             // Apply the value on top of the existing defaults
    -        $data = json_decode($value, true);
    +        $data = json_decode((string) $value, true);
             if ($data) {
                 $this->mergeValues($this->getData(), $data);
             }
    diff --git a/src/Forms/GridField/GridState_Data.php b/src/Forms/GridField/GridState_Data.php
    index e73da7a7004..be4e7c4b4ea 100644
    --- a/src/Forms/GridField/GridState_Data.php
    +++ b/src/Forms/GridField/GridState_Data.php
    @@ -63,7 +63,7 @@ public function initDefaults(array $defaults): void
          */
         public function getData($name, $default = null)
         {
    -        if (!array_key_exists($name, $this->data)) {
    +        if (!array_key_exists($name, $this->data ?: [])) {
                 $this->data[$name] = $default;
             } else {
                 if (is_array($this->data[$name])) {
    @@ -129,7 +129,7 @@ public function getChangesArray(): array
                 } else {
                     $value = $v;
                     // Check if we have a default value for this key and if it matches our current value
    -                if (array_key_exists($k, $this->defaults) && $this->defaults[$k] === $value) {
    +                if (array_key_exists($k, $this->defaults ?: []) && $this->defaults[$k] === $value) {
                         continue;
                     }
                 }
    diff --git a/src/Forms/HTMLEditor/HTMLEditorSanitiser.php b/src/Forms/HTMLEditor/HTMLEditorSanitiser.php
    index 9cc194de190..2fb705a7261 100644
    --- a/src/Forms/HTMLEditor/HTMLEditorSanitiser.php
    +++ b/src/Forms/HTMLEditor/HTMLEditorSanitiser.php
    @@ -70,7 +70,7 @@ public function __construct(HTMLEditorConfig $config)
          */
         protected function patternToRegex($str)
         {
    -        return '/^' . preg_replace('/([?+*])/', '.$1', $str) . '$/';
    +        return '/^' . preg_replace('/([?+*])/', '.$1', $str ?: '') . '$/';
         }
     
         /**
    @@ -87,8 +87,8 @@ protected function addValidElements($validElements)
             $attrRuleRegExp = '/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/';
             $hasPatternsRegExp = '/[*?+]/';
     
    -        foreach (explode(',', $validElements) as $validElement) {
    -            if (preg_match($elementRuleRegExp, $validElement, $matches)) {
    +        foreach (explode(',', (string) $validElements) as $validElement) {
    +            if (preg_match((string) $elementRuleRegExp, (string) $validElement, $matches)) {
                     $prefix = isset($matches[1]) ? $matches[1] : null;
                     $elementName = isset($matches[2]) ? $matches[2] : null;
                     $outputName = isset($matches[3]) ? $matches[3] : null;
    @@ -114,8 +114,8 @@ protected function addValidElements($validElements)
     
                     // Attributes defined
                     if ($attrData) {
    -                    foreach (explode('|', $attrData) as $attr) {
    -                        if (preg_match($attrRuleRegExp, $attr, $matches)) {
    +                    foreach (explode('|', (string) $attrData) as $attr) {
    +                        if (preg_match((string) $attrRuleRegExp, (string) $attr, $matches)) {
                                 $attr = new stdClass();
     
                                 $attrType = isset($matches[1]) ? $matches[1] : null;
    @@ -144,12 +144,12 @@ protected function addValidElements($validElements)
                                         $attr->forcedValue = $value;
                                     } elseif ($prefix === '<') {
                                         // Required values
    -                                    $attr->validValues = explode('?', $value);
    +                                    $attr->validValues = explode('?', (string) $value);
                                     }
                                 }
     
                                 // Check for attribute patterns
    -                            if (preg_match($hasPatternsRegExp, $attrName)) {
    +                            if (preg_match((string) $hasPatternsRegExp, (string) $attrName)) {
                                     $attr->pattern = $this->patternToRegex($attrName);
                                     $element->attributePatterns[] = $attr;
                                 } else {
    @@ -171,7 +171,7 @@ protected function addValidElements($validElements)
                     }
     
                     // Add pattern or exact element
    -                if (preg_match($hasPatternsRegExp, $elementName)) {
    +                if (preg_match((string) $hasPatternsRegExp, (string) $elementName)) {
                         $element->pattern = $this->patternToRegex($elementName);
                         $this->elementPatterns[] = $element;
                     } else {
    @@ -192,7 +192,7 @@ protected function getRuleForElement($tag)
                 return $this->elements[$tag];
             }
             foreach ($this->elementPatterns as $element) {
    -            if (preg_match($element->pattern, $tag)) {
    +            if (preg_match((string) $element->pattern, (string) $tag)) {
                     return $element;
                 }
             }
    @@ -212,7 +212,7 @@ protected function getRuleForAttribute($elementRule, $name)
                 return $elementRule->attributes[$name];
             }
             foreach ($elementRule->attributePatterns as $attribute) {
    -            if (preg_match($attribute->pattern, $name)) {
    +            if (preg_match((string) $attribute->pattern, (string) $name)) {
                     return $attribute;
                 }
             }
    @@ -271,7 +271,7 @@ protected function attributeMatchesRule($attr, $rule = null)
             }
     
             // If the rule has a set of valid values, check them to see if this attribute is one
    -        if (isset($rule->validValues) && !in_array($attr->value, $rule->validValues)) {
    +        if (isset($rule->validValues) && !in_array($attr->value, $rule->validValues ?: [])) {
                 return false;
             }
     
    diff --git a/src/Forms/HTMLEditor/TinyMCECombinedGenerator.php b/src/Forms/HTMLEditor/TinyMCECombinedGenerator.php
    index a037cb35565..2189963be45 100644
    --- a/src/Forms/HTMLEditor/TinyMCECombinedGenerator.php
    +++ b/src/Forms/HTMLEditor/TinyMCECombinedGenerator.php
    @@ -124,7 +124,7 @@ public function generateContent(TinyMCEConfig $config)
             }
     
             // Process source files
    -        $files = array_filter($files);
    +        $files = array_filter($files ?: []);
             $libResource = $this->resolveRelativeResource($tinymceDir, 'tinymce');
             $libContent = $this->getFileContents($libResource);
     
    @@ -165,7 +165,7 @@ public function generateContent(TinyMCEConfig $config)
                     return Director::makeRelative($path->getURL());
                 }
                 return $path;
    -        }, $files);
    +        }, $files ?: []);
     
             // Join list of paths
             $filesList = Convert::raw2js(implode(',', $fileURLS));
    @@ -189,14 +189,14 @@ protected function getFileContents($file)
             } else {
                 $path = Director::baseFolder() . '/' . $file;
             }
    -        if (!file_exists($path)) {
    +        if (!file_exists((string) $path)) {
                 return null;
             }
    -        $content = file_get_contents($path);
    +        $content = file_get_contents((string) $path);
     
             // Remove UTF-8 BOM
    -        if (substr($content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf)) {
    -            $content = substr($content, 3);
    +        if (substr((string) $content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf)) {
    +            $content = substr((string) $content, 3);
             }
     
             return $content;
    @@ -227,12 +227,12 @@ protected function checkName(TinyMCEConfig $config)
          */
         public function generateFilename(TinyMCEConfig $config)
         {
    -        $hash = substr(sha1(json_encode($config->getAttributes())), 0, 10);
    +        $hash = substr(sha1((string) json_encode($config->getAttributes())), 0, 10);
             $name = $this->checkName($config);
             $url = str_replace(
                 ['{name}', '{hash}'],
                 [$name, $hash],
    -            $this->config()->get('filename_base')
    +            $this->config()->get('filename_base') ?: ''
             );
             return $url;
         }
    @@ -246,7 +246,7 @@ public function generateFilename(TinyMCEConfig $config)
          */
         public static function flush()
         {
    -        $dir = dirname(static::config()->get('filename_base'));
    +        $dir = dirname((string) static::config()->get('filename_base'));
             static::singleton()->getAssetHandler()->removeContent($dir);
         }
     
    @@ -265,7 +265,7 @@ protected function resolveRelativeResource($base, $resource)
                 if ($base instanceof ModuleResource) {
                     $next = $base->getRelativeResource($resource . $ext);
                 } else {
    -                $next = rtrim($base, '/') . '/' . $resource . $ext;
    +                $next = rtrim((string) $base, '/') . '/' . $resource . $ext;
                 }
                 // Ensure resource exists
                 if ($this->resourceExists($next)) {
    @@ -289,7 +289,7 @@ protected function resourceExists($resource)
             if ($resource instanceof ModuleResource) {
                 return $resource->exists();
             }
    -        $base = rtrim(Director::baseFolder(), '/');
    +        $base = rtrim((string) Director::baseFolder(), '/');
             return file_exists($base . '/' . $resource);
         }
     }
    diff --git a/src/Forms/HTMLEditor/TinyMCEConfig.php b/src/Forms/HTMLEditor/TinyMCEConfig.php
    index 62a839229ba..b5cf9b7e4ae 100644
    --- a/src/Forms/HTMLEditor/TinyMCEConfig.php
    +++ b/src/Forms/HTMLEditor/TinyMCEConfig.php
    @@ -414,8 +414,8 @@ public function getAttributes()
         public function enablePlugins($plugin)
         {
             $plugins = func_get_args();
    -        if (is_array(current($plugins))) {
    -            $plugins = current($plugins);
    +        if (is_array(current($plugins ?: []))) {
    +            $plugins = current($plugins ?: []);
             }
             foreach ($plugins as $name => $path) {
                 // if plugins are passed without a path
    @@ -423,7 +423,7 @@ public function enablePlugins($plugin)
                     $name = $path;
                     $path = null;
                 }
    -            if (!array_key_exists($name, $this->plugins)) {
    +            if (!array_key_exists($name, $this->plugins ?: [])) {
                     $this->plugins[$name] = $path;
                 }
             }
    @@ -438,8 +438,8 @@ public function enablePlugins($plugin)
         public function disablePlugins($plugin)
         {
             $plugins = func_get_args();
    -        if (is_array(current($plugins))) {
    -            $plugins = current($plugins);
    +        if (is_array(current($plugins ?: []))) {
    +            $plugins = current($plugins ?: []);
             }
             foreach ($plugins as $name) {
                 unset($this->plugins[$name]);
    @@ -485,7 +485,7 @@ public function getInternalPlugins()
          */
         public function getButtons()
         {
    -        return array_filter($this->buttons);
    +        return array_filter($this->buttons ?: []);
         }
     
         /**
    @@ -541,7 +541,7 @@ public function addButtonsToLine($line, $buttons)
         protected function modifyButtons($name, $offset, $del = 0, $add = null)
         {
             foreach ($this->buttons as &$buttons) {
    -            if (($idx = array_search($name, $buttons)) !== false) {
    +            if (($idx = array_search($name, $buttons ?: [])) !== false) {
                     if ($add) {
                         array_splice($buttons, $idx + $offset, $del, $add);
                     } else {
    @@ -659,7 +659,7 @@ protected function getConfig()
             $settings['toolbar'] = [];
             foreach ($buttons as $rowButtons) {
                 $row = implode(' ', $rowButtons);
    -            if (count($buttons) > 1) {
    +            if (count($buttons ?: []) > 1) {
                     $settings['toolbar'][] = $row;
                 } else {
                     $settings['toolbar'] = $row;
    diff --git a/src/Forms/HTMLReadonlyField.php b/src/Forms/HTMLReadonlyField.php
    index 36c83bd956e..813ebe61037 100644
    --- a/src/Forms/HTMLReadonlyField.php
    +++ b/src/Forms/HTMLReadonlyField.php
    @@ -34,6 +34,6 @@ public function Field($properties = [])
          */
         public function ValueEntities()
         {
    -        return htmlentities($this->Value(), ENT_COMPAT, 'UTF-8');
    +        return htmlentities((string) $this->Value(), ENT_COMPAT, 'UTF-8');
         }
     }
    diff --git a/src/Forms/ListboxField.php b/src/Forms/ListboxField.php
    index 0b6b8a56e99..937e46208d3 100644
    --- a/src/Forms/ListboxField.php
    +++ b/src/Forms/ListboxField.php
    @@ -87,10 +87,10 @@ public function getOptions()
             $options = [];
             $selectedValue = $this->getValueArray();
             foreach ($this->getSource() as $itemValue => $title) {
    -            $itemSelected = in_array($itemValue, $selectedValue)
    -                || in_array($itemValue, $this->getDefaultItems());
    +            $itemSelected = in_array($itemValue, $selectedValue ?: [])
    +                || in_array($itemValue, $this->getDefaultItems() ?: []);
                 $itemDisabled = $this->isDisabled()
    -                || in_array($itemValue, $this->getDisabledItems());
    +                || in_array($itemValue, $this->getDisabledItems() ?: []);
                 $options[] = new ArrayData([
                     'Title' => $title,
                     'Value' => $itemValue,
    diff --git a/src/Forms/LookupField.php b/src/Forms/LookupField.php
    index 33c160e6e08..de3556ad65d 100644
    --- a/src/Forms/LookupField.php
    +++ b/src/Forms/LookupField.php
    @@ -51,8 +51,8 @@ public function Field($properties = [])
             }
     
             if ($mapped) {
    -            $attrValue = implode(', ', array_values($mapped));
    -            $inputValue = implode(', ', array_values($values));
    +            $attrValue = implode(', ', array_values($mapped ?: []));
    +            $inputValue = implode(', ', array_values($values ?: []));
             } else {
                 $attrValue = '(' . _t('SilverStripe\\Forms\\FormField.NONE', 'none') . ')';
                 $inputValue = '';
    diff --git a/src/Forms/MoneyField.php b/src/Forms/MoneyField.php
    index cb5e7a5450b..c9105b78f51 100644
    --- a/src/Forms/MoneyField.php
    +++ b/src/Forms/MoneyField.php
    @@ -89,11 +89,11 @@ protected function buildCurrencyField()
             // Validate allowed currencies
             $currencyValue = $this->fieldCurrency ? $this->fieldCurrency->dataValue() : null;
             $allowedCurrencies = $this->getAllowedCurrencies();
    -        if (count($allowedCurrencies) === 1) {
    +        if (count($allowedCurrencies ?: []) === 1) {
                 // Hidden field for single currency
                 $field = HiddenField::create("{$name}[Currency]");
                 reset($allowedCurrencies);
    -            $currencyValue = key($allowedCurrencies);
    +            $currencyValue = key($allowedCurrencies ?: []);
             } elseif ($allowedCurrencies) {
                 // Dropdown field for multiple currencies
                 $field = DropdownField::create(
    @@ -153,7 +153,7 @@ public function setValue($value, $data = null)
             // Convert string to array
             // E.g. `44.00 NZD`
             if (is_string($value) &&
    -            preg_match('/^(?[\\d\\.]+)( (?\w{3}))?$/i', $value, $matches)
    +            preg_match('/^(?[\\d\\.]+)( (?\w{3}))?$/i', (string) $value, $matches)
             ) {
                 $currency = isset($matches['currency']) ? strtoupper($matches['currency']) : null;
                 $value = [
    @@ -274,7 +274,7 @@ public function setAllowedCurrencies($currencies)
             } elseif (!is_array($currencies)) {
                 throw new InvalidArgumentException("Invalid currency list");
             } elseif (!ArrayLib::is_associative($currencies)) {
    -            $currencies = array_combine($currencies, $currencies);
    +            $currencies = array_combine($currencies ?: [], $currencies ?: []);
             }
     
             $this->allowedCurrencies = $currencies;
    @@ -326,7 +326,7 @@ public function validate($validator)
             // Validate currency
             $currencies = $this->getAllowedCurrencies();
             $currency = $this->fieldCurrency->dataValue();
    -        if ($currency && $currencies && !in_array($currency, $currencies)) {
    +        if ($currency && $currencies && !in_array($currency, $currencies ?: [])) {
                 $validator->validationError(
                     $this->getName(),
                     _t(
    diff --git a/src/Forms/MultiSelectField.php b/src/Forms/MultiSelectField.php
    index 6c34e7d62c8..25b399e149f 100644
    --- a/src/Forms/MultiSelectField.php
    +++ b/src/Forms/MultiSelectField.php
    @@ -96,7 +96,7 @@ public function loadFrom(DataObjectInterface $record)
             // Detect DB relation or field
             if ($relation instanceof Relation) {
                 // Load ids from relation
    -            $value = array_values($relation->getIDList());
    +            $value = array_values($relation->getIDList() ?: []);
                 parent::setValue($value);
             } elseif ($record->hasField($fieldName)) {
                 // Load dataValue from field... a CSV for DBMultiEnum
    @@ -176,7 +176,7 @@ protected function stringDecode($value)
             }
     
             // If json deserialisation fails, then fallover to legacy format
    -        $result = json_decode($value, true);
    +        $result = json_decode((string) $value, true);
             if ($result !== false) {
                 return $result;
             }
    @@ -200,9 +200,9 @@ protected function csvEncode($value)
                 ',',
                 array_map(
                     function ($x) {
    -                    return str_replace(',', '', $x);
    +                    return str_replace(',', '', $x ?: '');
                     },
    -                array_values($value)
    +                array_values($value ?: [])
                 )
             );
         }
    @@ -220,7 +220,7 @@ protected function csvDecode($value)
                 return [];
             }
     
    -        return preg_split('/\s*,\s*/', trim($value));
    +        return preg_split('/\s*,\s*/', trim((string) $value));
         }
     
         /**
    @@ -237,7 +237,7 @@ public function validate($validator)
             // Filter out selected values not in the data source
             $self = $this;
             $invalidValues = array_filter(
    -            $values,
    +            $values ?: [],
                 function ($userValue) use ($self, $validValues) {
                     foreach ($validValues as $formValue) {
                         if ($self->isSelectedValue($formValue, $userValue)) {
    diff --git a/src/Forms/NullableField.php b/src/Forms/NullableField.php
    index 25c601e192c..500fe7b0428 100644
    --- a/src/Forms/NullableField.php
    +++ b/src/Forms/NullableField.php
    @@ -138,7 +138,7 @@ public function setValue($value, $data = null)
         {
             $id = $this->getIsNullId();
     
    -        if (is_array($data) && array_key_exists($id, $data) && $data[$id]) {
    +        if (is_array($data) && array_key_exists($id, $data ?: []) && $data[$id]) {
                 $value = null;
             }
     
    diff --git a/src/Forms/NumericField.php b/src/Forms/NumericField.php
    index 65392a9543b..ae9e007a283 100644
    --- a/src/Forms/NumericField.php
    +++ b/src/Forms/NumericField.php
    @@ -104,11 +104,11 @@ protected function getNumberType()
         public function setSubmittedValue($value, $data = null)
         {
             // Save original value in case parse fails
    -        $value = trim($value);
    +        $value = trim((string) $value);
             $this->originalValue = $value;
     
             // Empty string is no-number (not 0)
    -        if (strlen($value) === 0) {
    +        if (strlen((string) $value) === 0) {
                 $this->value = null;
                 return $this;
             }
    @@ -118,7 +118,7 @@ public function setSubmittedValue($value, $data = null)
             $parsed = 0;
             $this->value = $formatter->parse($value, $this->getNumberType(), $parsed); // Note: may store literal `false` for invalid values
             // Ensure that entire string is parsed
    -        if ($parsed < strlen($value)) {
    +        if ($parsed < strlen((string) $value)) {
                 $this->value = false;
             }
             return $this;
    @@ -154,7 +154,7 @@ public function setValue($value, $data = null)
          */
         protected function cast($value)
         {
    -        if (strlen($value) === 0) {
    +        if (strlen((string) $value) === 0) {
                 return null;
             }
             if ($this->getScale() === 0) {
    diff --git a/src/Forms/PopoverField.php b/src/Forms/PopoverField.php
    index 0ec7589b7a6..187fcb34efd 100644
    --- a/src/Forms/PopoverField.php
    +++ b/src/Forms/PopoverField.php
    @@ -101,7 +101,7 @@ public function setPlacement($placement)
         {
             $valid = ['top', 'right', 'bottom', 'left'];
     
    -        if (!in_array($placement, $valid)) {
    +        if (!in_array($placement, $valid ?: [])) {
                 throw new InvalidArgumentException(
                     'Invalid placement value. Valid: top, left, bottom, right'
                 );
    diff --git a/src/Forms/ReadonlyField.php b/src/Forms/ReadonlyField.php
    index fcfe3107270..e4fef000bf8 100644
    --- a/src/Forms/ReadonlyField.php
    +++ b/src/Forms/ReadonlyField.php
    @@ -59,7 +59,7 @@ public function Type()
         public function castingHelper($field)
         {
             // Get dynamic cast for 'Value' field
    -        if (strcasecmp($field, 'Value') === 0) {
    +        if (strcasecmp((string) $field, 'Value') === 0) {
                 return $this->getValueCast();
             }
     
    diff --git a/src/Forms/RequiredFields.php b/src/Forms/RequiredFields.php
    index 3b693a3c230..2f974b0845c 100644
    --- a/src/Forms/RequiredFields.php
    +++ b/src/Forms/RequiredFields.php
    @@ -113,11 +113,11 @@ public function php($data)
                     if ($formField instanceof FileField && isset($value['error']) && $value['error']) {
                         $error = true;
                     } else {
    -                    $error = (count($value)) ? false : true;
    +                    $error = (count($value ?: [])) ? false : true;
                     }
                 } else {
                     // assume a string or integer
    -                $error = (strlen($value)) ? false : true;
    +                $error = (strlen((string) $value)) ? false : true;
                 }
     
                 if ($formField && $error) {
    @@ -213,7 +213,7 @@ public function fieldIsRequired($fieldName)
          */
         public function getRequired()
         {
    -        return array_values($this->required);
    +        return array_values($this->required ?: []);
         }
     
         /**
    @@ -221,6 +221,6 @@ public function getRequired()
          */
         public function canBeCached(): bool
         {
    -        return count($this->getRequired()) === 0;
    +        return count($this->getRequired() ?: []) === 0;
         }
     }
    diff --git a/src/Forms/Schema/FormSchema.php b/src/Forms/Schema/FormSchema.php
    index 4adc1ca8b04..9dbc668ecaf 100644
    --- a/src/Forms/Schema/FormSchema.php
    +++ b/src/Forms/Schema/FormSchema.php
    @@ -52,12 +52,12 @@ class FormSchema
         public function getMultipartSchema($schemaParts, $schemaID, Form $form = null, ValidationResult $result = null)
         {
             if (!is_array($schemaParts)) {
    -            $schemaParts = preg_split('#\s*,\s*#', $schemaParts) ?: [];
    +            $schemaParts = preg_split('#\s*,\s*#', (string) $schemaParts) ?: [];
             }
    -        $wantSchema = in_array('schema', $schemaParts);
    -        $wantState = in_array('state', $schemaParts);
    -        $wantErrors = in_array('errors', $schemaParts);
    -        $auto = in_array('auto', $schemaParts);
    +        $wantSchema = in_array('schema', $schemaParts ?: []);
    +        $wantState = in_array('state', $schemaParts ?: []);
    +        $wantErrors = in_array('errors', $schemaParts ?: []);
    +        $auto = in_array('auto', $schemaParts ?: []);
     
             // Require ID
             if (empty($schemaID)) {
    diff --git a/src/Forms/SelectField.php b/src/Forms/SelectField.php
    index 6d4477aafc1..0c110e2d24e 100644
    --- a/src/Forms/SelectField.php
    +++ b/src/Forms/SelectField.php
    @@ -100,7 +100,7 @@ protected function isDisabledValue($value)
             if ($this->isDisabled()) {
                 return true;
             }
    -        return in_array($value, $this->getDisabledItems());
    +        return in_array($value, $this->getDisabledItems() ?: []);
         }
     
         public function getAttributes()
    @@ -118,7 +118,7 @@ public function getAttributes()
          */
         protected function getSourceValues()
         {
    -        return array_keys($this->getSource());
    +        return array_keys($this->getSource() ?: []);
         }
     
         /**
    @@ -130,9 +130,9 @@ protected function getSourceValues()
          */
         public function getValidValues()
         {
    -        $valid = array_diff($this->getSourceValues(), $this->getDisabledItems());
    +        $valid = array_diff($this->getSourceValues() ?: [], $this->getDisabledItems());
             // Renumber indexes from 0
    -        return array_values($valid);
    +        return array_values($valid ?: []);
         }
     
         /**
    @@ -195,7 +195,7 @@ protected function getListValues($values)
     
             // Direct array
             if (is_array($values)) {
    -            return array_values($values);
    +            return array_values($values ?: []);
             }
     
             // Extract lists
    diff --git a/src/Forms/SelectionGroup.php b/src/Forms/SelectionGroup.php
    index 3ff48eece66..749d53c8739 100644
    --- a/src/Forms/SelectionGroup.php
    +++ b/src/Forms/SelectionGroup.php
    @@ -54,8 +54,8 @@ public function __construct($name, $items, $value = null)
                     $selectionItems[] = $item;
                 } else {
                     // Convert legacy format
    -                if (strpos($key, '//') !== false) {
    -                    list($key,$title) = explode('//', $key, 2);
    +                if (strpos((string) $key, '//') !== false) {
    +                    list($key,$title) = explode('//', (string) $key, 2);
                     } else {
                         $title = null;
                     }
    diff --git a/src/Forms/SingleLookupField.php b/src/Forms/SingleLookupField.php
    index b17cc8ed0e5..bd2690bb3f8 100644
    --- a/src/Forms/SingleLookupField.php
    +++ b/src/Forms/SingleLookupField.php
    @@ -29,7 +29,7 @@ protected function valueToLabel()
             $source = $this->getSource();
             $source = ($source instanceof Map) ? $source->toArray() : $source;
     
    -        if (array_key_exists($value, $source)) {
    +        if (array_key_exists($value, $source ?: [])) {
                 return $source[$value];
             }
     
    diff --git a/src/Forms/SingleSelectField.php b/src/Forms/SingleSelectField.php
    index 7d0a218196d..3d03201aacb 100644
    --- a/src/Forms/SingleSelectField.php
    +++ b/src/Forms/SingleSelectField.php
    @@ -133,7 +133,7 @@ public function validate($validator)
             $selected = $this->Value();
             $validValues = $this->getValidValues();
     
    -        if (strlen($selected)) {
    +        if (strlen((string) $selected)) {
                 // Use selection rules to check which are valid
                 foreach ($validValues as $formValue) {
                     if ($this->isSelectedValue($formValue, $selected)) {
    @@ -141,7 +141,7 @@ public function validate($validator)
                     }
                 }
             } else {
    -            if ($this->getHasEmptyDefault() || !$validValues || in_array('', $validValues)) {
    +            if ($this->getHasEmptyDefault() || !$validValues || in_array('', $validValues ?: [])) {
                     // Check empty value
                     return true;
                 }
    diff --git a/src/Forms/TabSet.php b/src/Forms/TabSet.php
    index f8aabcf63ab..7a80a81299e 100644
    --- a/src/Forms/TabSet.php
    +++ b/src/Forms/TabSet.php
    @@ -79,7 +79,7 @@ public function __construct($name, $titleOrTab = null, $tabs = null)
             }
     
             // Normalise children list
    -        if (count($tabs) === 1 && (is_array($tabs[0]) || $tabs[0] instanceof FieldList)) {
    +        if (count($tabs ?: []) === 1 && (is_array($tabs[0]) || $tabs[0] instanceof FieldList)) {
                 $tabs = $tabs[0];
             }
     
    diff --git a/src/Forms/TextField.php b/src/Forms/TextField.php
    index f1e171b78c8..5e7b694627d 100644
    --- a/src/Forms/TextField.php
    +++ b/src/Forms/TextField.php
    @@ -141,7 +141,7 @@ public function InternallyLabelledField()
          */
         public function validate($validator)
         {
    -        if (!is_null($this->maxLength) && mb_strlen($this->value) > $this->maxLength) {
    +        if (!is_null($this->maxLength) && mb_strlen((string) $this->value) > $this->maxLength) {
                 $name = strip_tags($this->Title() ? $this->Title() : $this->getName());
                 $validator->validationError(
                     $this->name,
    diff --git a/src/Forms/TextareaField.php b/src/Forms/TextareaField.php
    index a4023221ceb..a08923673ad 100644
    --- a/src/Forms/TextareaField.php
    +++ b/src/Forms/TextareaField.php
    @@ -174,6 +174,6 @@ public function Type()
          */
         public function ValueEntities()
         {
    -        return htmlentities($this->Value(), ENT_COMPAT, 'UTF-8');
    +        return htmlentities((string) $this->Value(), ENT_COMPAT, 'UTF-8');
         }
     }
    diff --git a/src/Forms/TimeField.php b/src/Forms/TimeField.php
    index 748ea49e5df..8d196c0e87e 100644
    --- a/src/Forms/TimeField.php
    +++ b/src/Forms/TimeField.php
    @@ -457,7 +457,7 @@ protected function tidyInternal($time)
             $timestamp = $formatter->parse($time);
             if ($timestamp === false) {
                 // Fallback to strtotime
    -            $timestamp = strtotime($time, DBDatetime::now()->getTimestamp());
    +            $timestamp = strtotime((string) $time, DBDatetime::now()->getTimestamp());
                 if ($timestamp === false) {
                     return null;
                 }
    @@ -498,7 +498,7 @@ protected function withTimezone($timezone, $callback)
             $currentTimezone = date_default_timezone_get();
             try {
                 if ($timezone) {
    -                date_default_timezone_set($timezone);
    +                date_default_timezone_set((string) $timezone);
                 }
                 return $callback();
             } finally {
    diff --git a/src/Forms/Tip.php b/src/Forms/Tip.php
    index 508e3451c09..e501b92a574 100644
    --- a/src/Forms/Tip.php
    +++ b/src/Forms/Tip.php
    @@ -83,7 +83,7 @@ public function getImportanceLevel(): string
          */
         public function setImportanceLevel(string $importance_level): self
         {
    -        if (!in_array($importance_level, self::IMPORTANCE_LEVELS)) {
    +        if (!in_array($importance_level, self::IMPORTANCE_LEVELS ?: [])) {
                 throw new InvalidArgumentException(
                     'Provided importance level must be defined in Tip::IMPORTANCE_LEVELS'
                 );
    diff --git a/src/Forms/ToggleCompositeField.php b/src/Forms/ToggleCompositeField.php
    index 5aecc5f3a1f..be6aef6000f 100644
    --- a/src/Forms/ToggleCompositeField.php
    +++ b/src/Forms/ToggleCompositeField.php
    @@ -46,7 +46,7 @@ public function FieldHolder($properties = [])
         {
             $context = $this;
     
    -        if (count($properties)) {
    +        if (count($properties ?: [])) {
                 $context = $this->customise($properties);
             }
     
    diff --git a/src/Forms/TreeDropdownField.php b/src/Forms/TreeDropdownField.php
    index a6f21ec14bf..d5c552dcdfb 100644
    --- a/src/Forms/TreeDropdownField.php
    +++ b/src/Forms/TreeDropdownField.php
    @@ -242,7 +242,7 @@ public function __construct(
             $this->setShowSearch($showSearch);
     
             // Extra settings for Folders
    -        if (strcasecmp($sourceObject, Folder::class) === 0) {
    +        if (strcasecmp((string) $sourceObject, Folder::class) === 0) {
                 $this->setChildrenMethod('ChildFolders');
                 $this->setNumChildrenMethod('numChildFolders');
             }
    @@ -514,7 +514,7 @@ public function tree(HTTPRequest $request)
     
             // Allow to pass values to be selected within the ajax request
             $value = $request->requestVar('forceValue') ?: $this->value;
    -        if ($value && ($values = preg_split('/,\s*/', $value))) {
    +        if ($value && ($values = preg_split('/,\s*/', (string) $value))) {
                 foreach ($values as $value) {
                     if (!$value || $value == 'unchanged') {
                         continue;
    @@ -724,7 +724,7 @@ protected function flattenChildrenArray($children, $parentTitles = [])
                 $child['contextString'] = ($contextString !== '') ? $contextString . '/' : '';
                 unset($child['children']);
     
    -            if (!$this->search || in_array($child['id'], $this->realSearchIds)) {
    +            if (!$this->search || in_array($child['id'], $this->realSearchIds ?: [])) {
                     $output[] = $child;
                 }
                 $output = array_merge($output, $this->flattenChildrenArray($grandChildren, $childTitles));
    diff --git a/src/Forms/TreeMultiselectField.php b/src/Forms/TreeMultiselectField.php
    index 516acf6bee7..ae6818fccc2 100644
    --- a/src/Forms/TreeMultiselectField.php
    +++ b/src/Forms/TreeMultiselectField.php
    @@ -104,7 +104,7 @@ public function getSchemaStateDefaults()
             $data['data']['valueObjects'] = $values;
     
             // cannot rely on $this->value as this could be a many-many relationship
    -        $value = array_column($values, 'id');
    +        $value = array_column($values ?: [], 'id');
             if ($value) {
                 sort($value);
                 $data['value'] = $value;
    @@ -151,13 +151,13 @@ public function getItems()
             $ids = [];
     
             if (is_string($value)) {
    -            $ids = preg_split("#\s*,\s*#", trim($value));
    +            $ids = preg_split("#\s*,\s*#", trim((string) $value));
             } elseif (is_array($value)) {
    -            $ids = array_values($value);
    +            $ids = array_values($value ?: []);
             }
     
             // Filter out empty strings
    -        $ids = array_filter($ids);
    +        $ids = array_filter($ids ?: []);
     
             // No value
             if (empty($ids)) {
    @@ -208,7 +208,7 @@ public function Field($properties = [])
             $items = $this->getItems();
             $emptyTitle = _t('SilverStripe\\Forms\\DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown');
     
    -        if ($items && count($items)) {
    +        if ($items && count($items ?: [])) {
                 foreach ($items as $item) {
                     $idArray[] = $item->ID;
                     $titleArray[] = ($item instanceof ViewableData)
    @@ -314,7 +314,7 @@ protected function objectForKey($key)
             if ($this->getKeyField() === 'ID' && $key === 'unchanged') {
                 $key = null;
             } elseif (is_string($key)) {
    -            $key = preg_split('/\s*,\s*/', trim($key));
    +            $key = preg_split('/\s*,\s*/', trim((string) $key));
             }
     
             return parent::objectForKey($key);
    diff --git a/src/Logging/DebugViewFriendlyErrorFormatter.php b/src/Logging/DebugViewFriendlyErrorFormatter.php
    index 857fbfb70d5..13cf8d05dab 100644
    --- a/src/Logging/DebugViewFriendlyErrorFormatter.php
    +++ b/src/Logging/DebugViewFriendlyErrorFormatter.php
    @@ -157,7 +157,7 @@ private function addContactAdministratorInfo()
                 return 'Contact an administrator: ' . Email::obfuscate($adminEmail);
             }
     
    -        if (!is_array($adminEmail) || !count($adminEmail)) {
    +        if (!is_array($adminEmail) || !count($adminEmail ?: [])) {
                 return null;
             }
     
    diff --git a/src/Logging/DetailedErrorFormatter.php b/src/Logging/DetailedErrorFormatter.php
    index 187c85357f5..9432117d3ec 100644
    --- a/src/Logging/DetailedErrorFormatter.php
    +++ b/src/Logging/DetailedErrorFormatter.php
    @@ -39,7 +39,7 @@ public function format(array $record)
                     // call to the fatal error handler and is not useful, so exclude it entirely
                     $i = $this->findInTrace($trace, $context['file'], $context['line']);
                     if ($i !== null) {
    -                    $context['trace'] = array_slice($trace, $i);
    +                    $context['trace'] = array_slice($trace ?: [], (int) $i);
                     } else {
                         $context['trace'] = null;
                     }
    @@ -57,7 +57,7 @@ public function format(array $record)
     
         public function formatBatch(array $records)
         {
    -        return implode("\n", array_map([$this, 'format'], $records));
    +        return implode("\n", array_map([$this, 'format'], $records ?: []));
         }
     
         /**
    @@ -105,15 +105,15 @@ protected function output($errno, $errstr, $errfile, $errline, $errcontext)
             $output = $reporter->renderHeader();
             $output .= $reporter->renderError($httpRequest, $errno, $errstr, $errfile, $errline);
     
    -        if (file_exists($errfile)) {
    -            $lines = file($errfile);
    +        if (file_exists((string) $errfile)) {
    +            $lines = file((string) $errfile);
     
                 // Make the array 1-based
                 array_unshift($lines, "");
                 unset($lines[0]);
     
                 $offset = $errline-10;
    -            $lines = array_slice($lines, $offset, 16, true);
    +            $lines = array_slice($lines ?: [], (int) $offset, 16, true);
                 $output .= $reporter->renderSourceFragment($lines, $errline);
             }
             $output .= $reporter->renderTrace($errcontext);
    diff --git a/src/ORM/ArrayLib.php b/src/ORM/ArrayLib.php
    index 296a202164d..645ec70e30e 100644
    --- a/src/ORM/ArrayLib.php
    +++ b/src/ORM/ArrayLib.php
    @@ -71,7 +71,7 @@ public static function invert($arr)
          */
         public static function valuekey($arr)
         {
    -        return array_combine($arr, $arr);
    +        return array_combine($arr ?: [], $arr ?: []);
         }
     
         /**
    @@ -97,7 +97,7 @@ public static function array_values_recursive($array)
         public static function filter_keys($arr, $keys)
         {
             foreach ($arr as $key => $v) {
    -            if (!in_array($key, $keys)) {
    +            if (!in_array($key, $keys ?: [])) {
                     unset($arr[$key]);
                 }
             }
    @@ -119,7 +119,7 @@ public static function is_associative($array)
         {
             $isAssociative = !empty($array)
                 && is_array($array)
    -            && ($array !== array_values($array));
    +            && ($array !== array_values($array ?: []));
     
             return $isAssociative;
         }
    @@ -142,7 +142,7 @@ public static function in_array_recursive($needle, $haystack, $strict = false)
                 return false;
             }
     
    -        if (in_array($needle, $haystack, $strict)) {
    +        if (in_array($needle, $haystack ?: [], (bool) $strict)) {
                 return true;
             } else {
                 foreach ($haystack as $obj) {
    @@ -170,7 +170,7 @@ public static function array_map_recursive($f, $array)
                 return is_array($v) ? ArrayLib::array_map_recursive($f, $v) : call_user_func($f, $v);
             };
     
    -        return array_map($applyOrRecurse, $array);
    +        return array_map($applyOrRecurse, $array ?: []);
         }
     
         /**
    @@ -190,7 +190,7 @@ public static function array_merge_recursive($array)
             $arrays = func_get_args();
             $merged = [];
     
    -        if (count($arrays) == 1) {
    +        if (count($arrays ?: []) == 1) {
                 return $array;
             }
     
    @@ -210,7 +210,7 @@ public static function array_merge_recursive($array)
                 }
     
                 foreach ($array as $key => $value) {
    -                if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) {
    +                if (is_array($value) && array_key_exists($key, $merged ?: []) && is_array($merged[$key])) {
                         $merged[$key] = ArrayLib::array_merge_recursive($merged[$key], $value);
                     } else {
                         $merged[$key] = $value;
    @@ -262,11 +262,11 @@ public static function iterateVolatile(array &$list)
             // Keyed by already-iterated items
             $iterated = [];
             // Get all items not yet iterated
    -        while ($items = array_diff_key($list, $iterated)) {
    +        while ($items = array_diff_key($list ?: [], $iterated)) {
                 // Yield all results
                 foreach ($items as $key => $value) {
                     // Skip items removed by a prior step
    -                if (array_key_exists($key, $list)) {
    +                if (array_key_exists($key, $list ?: [])) {
                         // Ensure we yield from the source list
                         $iterated[$key] = true;
                         yield $key => $list[$key];
    diff --git a/src/ORM/ArrayList.php b/src/ORM/ArrayList.php
    index 8911f3326b4..a581763635d 100644
    --- a/src/ORM/ArrayList.php
    +++ b/src/ORM/ArrayList.php
    @@ -40,7 +40,7 @@ class ArrayList extends ViewableData implements SS_List, Filterable, Sortable, L
          */
         public function __construct(array $items = [])
         {
    -        $this->items = array_values($items);
    +        $this->items = array_values($items ?: []);
             parent::__construct();
         }
     
    @@ -61,7 +61,7 @@ public function dataClass()
             if ($this->dataClass) {
                 return $this->dataClass;
             }
    -        if (count($this->items) > 0) {
    +        if (count($this->items ?: []) > 0) {
                 return get_class($this->items[0]);
             }
             return null;
    @@ -84,9 +84,10 @@ public function setDataClass($class)
          *
          * @return int
          */
    +    #[\ReturnTypeWillChange]
         public function count()
         {
    -        return count($this->items);
    +        return count($this->items ?: []);
         }
     
         /**
    @@ -105,13 +106,14 @@ public function exists()
          *
          * @return ArrayIterator
          */
    +    #[\ReturnTypeWillChange]
         public function getIterator()
         {
             $items = array_map(
                 function ($item) {
                     return is_array($item) ? new ArrayData($item) : $item;
                 },
    -            $this->items
    +            $this->items ?: []
             );
             return new ArrayIterator($items);
         }
    @@ -206,11 +208,11 @@ public function limit($length, $offset = 0)
                     );
                 }
     
    -            $length = count($this->items);
    +            $length = count($this->items ?: []);
             }
     
             $list = clone $this;
    -        $list->items = array_slice($this->items, $offset, $length);
    +        $list->items = array_slice($this->items ?: [], (int) $offset, $length);
     
             return $list;
         }
    @@ -240,7 +242,7 @@ public function remove($item)
                 }
             }
             if ($renumberKeys) {
    -            $this->items = array_values($this->items);
    +            $this->items = array_values($this->items ?: []);
             }
         }
     
    @@ -289,7 +291,7 @@ public function removeDuplicates($field = 'ID')
             foreach ($this->items as $key => $item) {
                 $value = $this->extractValue($item, $field);
     
    -            if (array_key_exists($value, $seen)) {
    +            if (array_key_exists($value, $seen ?: [])) {
                     $renumberKeys = true;
                     unset($this->items[$key]);
                 }
    @@ -298,7 +300,7 @@ public function removeDuplicates($field = 'ID')
             }
     
             if ($renumberKeys) {
    -            $this->items = array_values($this->items);
    +            $this->items = array_values($this->items ?: []);
             }
     
             return $this;
    @@ -427,7 +429,7 @@ public function column($colName = 'ID')
          */
         public function columnUnique($colName = 'ID')
         {
    -        return array_unique($this->column($colName));
    +        return array_unique($this->column($colName) ?: []);
         }
     
         /**
    @@ -449,7 +451,7 @@ public function canSortBy($by)
         public function reverse()
         {
             $list = clone $this;
    -        $list->items = array_reverse($this->items);
    +        $list->items = array_reverse($this->items ?: []);
     
             return $list;
         }
    @@ -471,7 +473,7 @@ protected function parseSortColumn($column, $direction = null)
     
             // Parse column specification, considering possible ansi sql quoting
             // Note that table prefix is allowed, but discarded
    -        if (preg_match('/^("?(?[^"\s]+)"?\\.)?"?(?[^"\s]+)"?(\s+(?((asc)|(desc))(ending)?))?$/i', $column, $match)) {
    +        if (preg_match('/^("?(?
    [^"\s]+)"?\\.)?"?(?[^"\s]+)"?(\s+(?((asc)|(desc))(ending)?))?$/i', (string) $column, $match)) { $column = $match['column']; if (empty($direction) && !empty($match['direction'])) { $direction = $match['direction']; @@ -481,9 +483,9 @@ protected function parseSortColumn($column, $direction = null) } // Parse sort direction specification - if (empty($direction) || preg_match('/^asc(ending)?$/i', $direction)) { + if (empty($direction) || preg_match('/^asc(ending)?$/i', (string) $direction)) { $direction = SORT_ASC; - } elseif (preg_match('/^desc(ending)?$/i', $direction)) { + } elseif (preg_match('/^desc(ending)?$/i', (string) $direction)) { $direction = SORT_DESC; } else { throw new InvalidArgumentException("Invalid sort() direction"); @@ -509,19 +511,19 @@ public function sort() { $args = func_get_args(); - if (count($args)==0) { + if (count($args ?: [])==0) { return $this; } - if (count($args)>2) { + if (count($args ?: [])>2) { throw new InvalidArgumentException('This method takes zero, one or two arguments'); } $columnsToSort = []; // One argument and it's a string - if (count($args)==1 && is_string($args[0])) { + if (count($args ?: [])==1 && is_string($args[0])) { list($column, $direction) = $this->parseSortColumn($args[0]); $columnsToSort[$column] = $direction; - } elseif (count($args)==2) { + } elseif (count($args ?: [])==2) { list($column, $direction) = $this->parseSortColumn($args[0], $args[1]); $columnsToSort[$column] = $direction; } elseif (is_array($args[0])) { @@ -536,7 +538,7 @@ public function sort() // Store the original keys of the items as a sort fallback, so we can preserve the original order in the event // that array_multisort is unable to work out a sort order for them. This also prevents array_multisort trying // to inspect object properties which can result in errors with circular dependencies - $originalKeys = array_keys($this->items); + $originalKeys = array_keys($this->items ?: []); // This the main sorting algorithm that supports infinite sorting params $multisortArgs = []; @@ -549,7 +551,7 @@ public function sort() $sortDirection[$column] = $direction; // We need to subtract every value into a temporary array for sorting foreach ($this->items as $index => $item) { - $values[$column][] = strtolower($this->extractValue($item, $column)); + $values[$column][] = strtolower((string) $this->extractValue($item, $column)); } // PHP 5.3 requires below arguments to be reference when using array_multisort together // with call_user_func_array @@ -568,7 +570,7 @@ public function sort() $list = clone $this; // As the last argument we pass in a reference to the items that all the sorting will be applied upon $multisortArgs[] = &$list->items; - call_user_func_array('array_multisort', $multisortArgs); + call_user_func_array('array_multisort', $multisortArgs ?: []); return $list; } @@ -600,7 +602,7 @@ public function canFilterBy($by) $firstRecord = $this->first(); - return is_array($firstRecord) ? array_key_exists($by, $firstRecord) : property_exists($by, $firstRecord); + return is_array($firstRecord) ? array_key_exists($by, $firstRecord) : property_exists($by, (string) $firstRecord); } /** @@ -624,7 +626,7 @@ public function filter() foreach ($this->items as $item) { $keepItem = true; foreach ($keepUs as $column => $value) { - if ((is_array($value) && !in_array($this->extractValue($item, $column), $value)) + if ((is_array($value) && !in_array($this->extractValue($item, $column), $value ?: [])) || (!is_array($value) && $this->extractValue($item, $column) != $value) ) { $keepItem = false; @@ -676,7 +678,7 @@ public function filterAny() } $list = clone $this; - $list->items = array_unique($itemsToKeep, SORT_REGULAR); + $list->items = array_unique($itemsToKeep ?: [], SORT_REGULAR); return $list; } @@ -692,20 +694,20 @@ public function filterAny() protected function normaliseFilterArgs($column, $value = null) { $args = func_get_args(); - if (count($args) > 2) { + if (count($args ?: []) > 2) { throw new InvalidArgumentException('filter takes one array or two arguments'); } - if (count($args) === 1 && !is_array($args[0])) { + if (count($args ?: []) === 1 && !is_array($args[0])) { throw new InvalidArgumentException('filter takes one array or two arguments'); } $keepUs = []; - if (count($args) === 2) { + if (count($args ?: []) === 2) { $keepUs[$args[0]] = $args[1]; } - if (count($args) === 1 && is_array($args[0])) { + if (count($args ?: []) === 1 && is_array($args[0])) { foreach ($args[0] as $key => $val) { $keepUs[$key] = $val; } @@ -722,7 +724,7 @@ protected function normaliseFilterArgs($column, $value = null) */ public function byIDs($ids) { - $ids = array_map('intval', $ids); // sanitize + $ids = array_map('intval', $ids ?: []); // sanitize return $this->filter('ID', $ids); } @@ -780,23 +782,23 @@ public function exclude() { $removeUs = $this->normaliseFilterArgs(...func_get_args()); - $hitsRequiredToRemove = count($removeUs); + $hitsRequiredToRemove = count($removeUs ?: []); $matches = []; foreach ($removeUs as $column => $excludeValue) { foreach ($this->items as $key => $item) { if (!is_array($excludeValue) && $this->extractValue($item, $column) == $excludeValue) { $matches[$key] = isset($matches[$key]) ? $matches[$key] + 1 : 1; - } elseif (is_array($excludeValue) && in_array($this->extractValue($item, $column), $excludeValue)) { + } elseif (is_array($excludeValue) && in_array($this->extractValue($item, $column), $excludeValue ?: [])) { $matches[$key] = isset($matches[$key]) ? $matches[$key] + 1 : 1; } } } - $keysToRemove = array_keys($matches, $hitsRequiredToRemove); + $keysToRemove = array_keys($matches ?: [], $hitsRequiredToRemove); $itemsToKeep = []; foreach ($this->items as $key => $value) { - if (!in_array($key, $keysToRemove)) { + if (!in_array($key, $keysToRemove ?: [])) { $itemsToKeep[] = $value; } } @@ -817,9 +819,10 @@ protected function shouldExclude($item, $args) * @param mixed $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { - return array_key_exists($offset, $this->items); + return array_key_exists($offset, $this->items ?: []); } /** @@ -828,6 +831,7 @@ public function offsetExists($offset) * @param mixed $offset * @return DataObject */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($this->offsetExists($offset)) { @@ -842,6 +846,7 @@ public function offsetGet($offset) * @param mixed $offset * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if ($offset === null) { @@ -856,6 +861,7 @@ public function offsetSet($offset, $value) * * @param mixed $offset */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->items[$offset]); @@ -878,7 +884,7 @@ protected function extractValue($item, $key) return $item->{$key}; } - if (array_key_exists($key, $item)) { + if (array_key_exists($key, $item ?: [])) { return $item[$key]; } diff --git a/src/ORM/Connect/DBConnector.php b/src/ORM/Connect/DBConnector.php index 50f83a36bee..6d7b42927f7 100644 --- a/src/ORM/Connect/DBConnector.php +++ b/src/ORM/Connect/DBConnector.php @@ -63,7 +63,7 @@ protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, // in code, such as credential checking during installation throw new DatabaseException($msg, 0, null, $sql, $parameters); } else { - user_error($msg, $errorLevel); + user_error((string) $msg, (int) $errorLevel); } } @@ -116,14 +116,14 @@ public function isQueryWrite($sql) */ protected function isQueryType($sql, $type) { - if (!preg_match('/^(?\w+)\b/', $sql, $matches)) { + if (!preg_match('/^(?\w+)\b/', (string) $sql, $matches)) { return false; } $operation = $matches['operation']; if (is_array($type)) { - return in_array(strtolower($operation), $type); + return in_array(strtolower((string) $operation), $type ?: []); } - return strcasecmp($sql, $type) === 0; + return strcasecmp((string) $sql, (string) $type) === 0; } /** diff --git a/src/ORM/Connect/DBQueryBuilder.php b/src/ORM/Connect/DBQueryBuilder.php index 47769f6226b..a495386d5fc 100644 --- a/src/ORM/Connect/DBQueryBuilder.php +++ b/src/ORM/Connect/DBQueryBuilder.php @@ -171,7 +171,7 @@ protected function buildSelectFragment(SQLSelect $query, array &$parameters) foreach ($select as $alias => $field) { // Don't include redundant aliases. $fieldAlias = "\"{$alias}\""; - if ($alias === $field || substr($field, -strlen($fieldAlias)) === $fieldAlias) { + if ($alias === $field || substr((string) $field, -strlen((string) $fieldAlias)) === $fieldAlias) { $clauses[] = $field; } else { $clauses[] = "$field AS $fieldAlias"; diff --git a/src/ORM/Connect/DBSchemaManager.php b/src/ORM/Connect/DBSchemaManager.php index 3332f46904b..81badf3f371 100644 --- a/src/ORM/Connect/DBSchemaManager.php +++ b/src/ORM/Connect/DBSchemaManager.php @@ -376,7 +376,7 @@ public function requireTable( $tableOptionsChanged = false; // Check for DB constant on the schema class $dbIDName = sprintf('%s::ID', static::class); - $dbID = defined($dbIDName) ? constant($dbIDName) : null; + $dbID = defined((string) $dbIDName) ? constant($dbIDName) : null; if ($dbID && isset($options[$dbID])) { if (preg_match('/ENGINE=([^\s]*)/', $options[$dbID], $alteredEngineMatches)) { $alteredEngine = $alteredEngineMatches[1]; @@ -400,11 +400,11 @@ public function requireTable( foreach ($fieldSchema as $fieldName => $fieldSpec) { //Is this an array field? $arrayValue = ''; - if (strpos($fieldSpec, '[') !== false) { + if (strpos((string) $fieldSpec, '[') !== false) { //If so, remove it and store that info separately - $pos = strpos($fieldSpec, '['); - $arrayValue = substr($fieldSpec, $pos); - $fieldSpec = substr($fieldSpec, 0, $pos); + $pos = strpos((string) $fieldSpec, '['); + $arrayValue = substr((string) $fieldSpec, (int) $pos); + $fieldSpec = substr((string) $fieldSpec, 0, $pos); } /** @var DBField $fieldObj */ @@ -535,11 +535,11 @@ protected function explodeColumnString($spec) { // Remove any leading/trailing brackets and outlying modifiers // E.g. 'unique (Title, "QuotedColumn");' => 'Title, "QuotedColumn"' - $containedSpec = preg_replace('/(.*\(\s*)|(\s*\).*)/', '', $spec); + $containedSpec = preg_replace('/(.*\(\s*)|(\s*\).*)/', '', $spec ?: ''); // Split potentially quoted modifiers // E.g. 'Title, "QuotedColumn"' => ['Title', 'QuotedColumn'] - return preg_split('/"?\s*,\s*"?/', trim($containedSpec, '(") ')); + return preg_split('/"?\s*,\s*"?/', trim((string) $containedSpec, '(") ')); } /** @@ -581,7 +581,7 @@ protected function determineIndexType($spec) // check array spec if (is_array($spec) && isset($spec['type'])) { return $spec['type']; - } elseif (!is_array($spec) && preg_match('/(?\w+)\s*\(/', $spec, $matchType)) { + } elseif (!is_array($spec) && preg_match('/(?\w+)\s*\(/', (string) $spec, $matchType)) { return strtolower($matchType['type']); } else { return 'index'; @@ -604,10 +604,10 @@ protected function convertIndexSpec($indexSpec) { // Return already converted spec if (!is_array($indexSpec) - || !array_key_exists('type', $indexSpec) - || !array_key_exists('columns', $indexSpec) + || !array_key_exists('type', $indexSpec ?: []) + || !array_key_exists('columns', $indexSpec ?: []) || !is_array($indexSpec['columns']) - || array_key_exists('value', $indexSpec) + || array_key_exists('value', $indexSpec ?: []) ) { throw new \InvalidArgumentException( sprintf( @@ -642,7 +642,7 @@ public function hasField($tableName, $fieldName) return false; } $fields = $this->fieldList($tableName); - return array_key_exists($fieldName, $fields); + return array_key_exists($fieldName, $fields ?: []); } /** @@ -677,7 +677,7 @@ public function requireField($table, $field, $spec) // collations. // TODO: move this to the MySQLDatabase file, or drop it altogether? if (!$this->database->supportsCollations()) { - $spec = preg_replace('/ *character set [^ ]+( collate [^ ]+)?( |$)/', '\\2', $spec); + $spec = preg_replace('/ *character set [^ ]+( collate [^ ]+)?( |$)/', '\\2', $spec ?: ''); } if (!isset($this->tableList[strtolower($table)])) { @@ -719,23 +719,23 @@ public function requireField($table, $field, $spec) // If enums/sets are being modified, then we need to fix existing data in the table. // Update any records where the enum is set to a legacy value to be set to the default. $enumValuesExpr = "/^(enum|set)\\s*\\(['\"](?[^'\"]+)['\"]\\).*/i"; - if (preg_match($enumValuesExpr, $specValue, $specMatches) - && preg_match($enumValuesExpr, $spec_orig, $oldMatches) + if (preg_match((string) $enumValuesExpr, (string) $specValue, $specMatches) + && preg_match((string) $enumValuesExpr, (string) $spec_orig, $oldMatches) ) { $new = preg_split("/'\\s*,\\s*'/", $specMatches['values']); $old = preg_split("/'\\s*,\\s*'/", $oldMatches['values']); $holder = []; foreach ($old as $check) { - if (!in_array($check, $new)) { + if (!in_array($check, $new ?: [])) { $holder[] = $check; } } - if (count($holder)) { + if (count($holder ?: [])) { // Get default pre-escaped for SQL. We just use this directly, as we don't have a real way to // de-encode SQL values - $default = explode('default ', $spec_orig); + $default = explode('default ', (string) $spec_orig); $defaultSQL = isset($default[1]) ? $default[1] : 'NULL'; // Reset to default any value in that is in the old enum, but not the new one $placeholders = DB::placeholders($holder); @@ -764,7 +764,7 @@ public function requireField($table, $field, $spec) public function dontRequireField($table, $fieldName) { $fieldList = $this->fieldList($table); - if (array_key_exists($fieldName, $fieldList)) { + if (array_key_exists($fieldName, $fieldList ?: [])) { $suffix = ''; while (isset($fieldList[strtolower("_obsolete_{$fieldName}$suffix")])) { $suffix = $suffix @@ -808,7 +808,7 @@ public function alterationMessage($message, $type = "") default: $sign = ' '; } - $message = strip_tags($message); + $message = strip_tags((string) $message); echo " $sign $message\n"; } else { switch ($type) { @@ -862,7 +862,7 @@ public function fixTableCase($tableName) { // Check if table exists $tables = $this->tableList(); - if (!array_key_exists(strtolower($tableName), $tables)) { + if (!array_key_exists(strtolower((string) $tableName), $tables ?: [])) { return; } diff --git a/src/ORM/Connect/Database.php b/src/ORM/Connect/Database.php index c7ef09be1ff..32591beba46 100644 --- a/src/ORM/Connect/Database.php +++ b/src/ORM/Connect/Database.php @@ -236,7 +236,7 @@ protected function benchmarkQuery($sql, $callback, $parameters = []) $displaySql = false; foreach (self::$whitelist_array as $query => $searchType) { $fullQuery = ($searchType === self::FULL_QUERY && $query === $sql); - $partialQuery = ($searchType === self::PARTIAL_QUERY && mb_strpos($sql, $query) !== false); + $partialQuery = ($searchType === self::PARTIAL_QUERY && mb_strpos((string) $sql, (string) $query) !== false); if (!$fullQuery && !$partialQuery) { continue; } @@ -348,7 +348,7 @@ public function escapeIdentifier($value, $separator = '.') { // Split string into components if (!is_array($value)) { - $value = explode($separator, $value); + $value = explode((string) $separator, (string) $value); } // Implode quoted column @@ -478,7 +478,7 @@ public function nullCheckClause($field, $isNull) $clause = $isNull ? "%s IS NULL" : "%s IS NOT NULL"; - return sprintf($clause, $field); + return sprintf((string) $clause, $field); } /** @@ -906,7 +906,7 @@ public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERRO // Check DB creation permission if (!$create) { if ($errorLevel !== false) { - user_error("Attempted to connect to non-existing database \"$name\"", $errorLevel); + user_error("Attempted to connect to non-existing database \"$name\"", (int) $errorLevel); } // Unselect database $this->connector->unloadDatabase(); diff --git a/src/ORM/Connect/MySQLDatabase.php b/src/ORM/Connect/MySQLDatabase.php index 7f5d3547711..3e06744e2cd 100644 --- a/src/ORM/Connect/MySQLDatabase.php +++ b/src/ORM/Connect/MySQLDatabase.php @@ -184,15 +184,15 @@ public function searchEngine( ) { $pageClass = SiteTree::class; $fileClass = File::class; - if (!class_exists($pageClass)) { + if (!class_exists((string) $pageClass)) { throw new Exception('MySQLDatabase->searchEngine() requires "SiteTree" class'); } - if (!class_exists($fileClass)) { + if (!class_exists((string) $fileClass)) { throw new Exception('MySQLDatabase->searchEngine() requires "File" class'); } $keywords = $this->escapeString($keywords); - $htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8'); + $htmlEntityKeywords = htmlentities((string) $keywords, ENT_NOQUOTES, 'UTF-8'); $extraFilters = [$pageClass => '', $fileClass => '']; @@ -218,7 +218,7 @@ public function searchEngine( // by checking for its existence first $fileTable = DataObject::getSchema()->tableName($fileClass); $fields = $this->getSchemaManager()->fieldList($fileTable); - if (array_key_exists('ShowInSearch', $fields)) { + if (array_key_exists('ShowInSearch', $fields ?: [])) { $extraFilters[$fileClass] .= " AND ShowInSearch <> 0"; } @@ -237,8 +237,8 @@ public function searchEngine( // We make the relevance search by converting a boolean mode search into a normal one $booleanChars = ['*', '+', '@', '-', '(', ')', '<', '>']; - $relevanceKeywords = str_replace($booleanChars, '', $keywords); - $htmlEntityRelevanceKeywords = str_replace($booleanChars, '', $htmlEntityKeywords); + $relevanceKeywords = str_replace($booleanChars ?: '', '', $keywords ?: ''); + $htmlEntityRelevanceKeywords = str_replace($booleanChars ?: '', '', $htmlEntityKeywords ?: ''); $relevance[$pageClass] = "MATCH (Title, MenuTitle, Content, MetaDescription) " . "AGAINST ('$relevanceKeywords') " . "+ MATCH (Title, MenuTitle, Content, MetaDescription) AGAINST ('$htmlEntityRelevanceKeywords')"; @@ -444,16 +444,16 @@ public function comparisonClause( public function formattedDatetimeClause($date, $format) { - preg_match_all('/%(.)/', $format, $matches); + preg_match_all('/%(.)/', (string) $format, $matches); foreach ($matches[1] as $match) { if (array_search($match, ['Y', 'm', 'd', 'H', 'i', 's', 'U']) === false) { user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING); } } - if (preg_match('/^now$/i', $date)) { + if (preg_match('/^now$/i', (string) $date)) { $date = "NOW()"; - } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) { + } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', (string) $date)) { $date = "'$date'"; } @@ -466,11 +466,11 @@ public function formattedDatetimeClause($date, $format) public function datetimeIntervalClause($date, $interval) { - $interval = preg_replace('/(year|month|day|hour|minute|second)s/i', '$1', $interval); + $interval = preg_replace('/(year|month|day|hour|minute|second)s/i', '$1', $interval ?: ''); - if (preg_match('/^now$/i', $date)) { + if (preg_match('/^now$/i', (string) $date)) { $date = "NOW()"; - } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date)) { + } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', (string) $date)) { $date = "'$date'"; } @@ -480,15 +480,15 @@ public function datetimeIntervalClause($date, $interval) public function datetimeDifferenceClause($date1, $date2) { // First date format - if (preg_match('/^now$/i', $date1)) { + if (preg_match('/^now$/i', (string) $date1)) { $date1 = "NOW()"; - } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date1)) { + } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', (string) $date1)) { $date1 = "'$date1'"; } // Second date format - if (preg_match('/^now$/i', $date2)) { + if (preg_match('/^now$/i', (string) $date2)) { $date2 = "NOW()"; - } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', $date2)) { + } elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/i', (string) $date2)) { $date2 = "'$date2'"; } diff --git a/src/ORM/Connect/MySQLQuery.php b/src/ORM/Connect/MySQLQuery.php index f1dbfefd289..03a70c1add9 100644 --- a/src/ORM/Connect/MySQLQuery.php +++ b/src/ORM/Connect/MySQLQuery.php @@ -75,7 +75,7 @@ public function nextRecord() if (!isset($this->columns[$i])) { throw new DatabaseException("Can't get metadata for column $i"); } - if (in_array($this->columns[$i]->type, $floatTypes)) { + if (in_array($this->columns[$i]->type, $floatTypes ?: [])) { $value = (float)$value; } $data[$this->columns[$i]->name] = $value; diff --git a/src/ORM/Connect/MySQLQueryBuilder.php b/src/ORM/Connect/MySQLQueryBuilder.php index da7791c13d1..a6a48f53e83 100644 --- a/src/ORM/Connect/MySQLQueryBuilder.php +++ b/src/ORM/Connect/MySQLQueryBuilder.php @@ -40,7 +40,7 @@ public function buildLimitFragment(SQLSelect $query, array &$parameters) } // Assert that the array version provides the 'limit' key - if (!array_key_exists('limit', $limit) || ($limit['limit'] !== null && ! is_numeric($limit['limit']))) { + if (!array_key_exists('limit', $limit ?: []) || ($limit['limit'] !== null && ! is_numeric($limit['limit']))) { throw new InvalidArgumentException( 'MySQLQueryBuilder::buildLimitSQL(): Wrong format for $limit: ' . var_export($limit, true) ); diff --git a/src/ORM/Connect/MySQLSchemaManager.php b/src/ORM/Connect/MySQLSchemaManager.php index fd02b38cafa..d4f9c164a7f 100644 --- a/src/ORM/Connect/MySQLSchemaManager.php +++ b/src/ORM/Connect/MySQLSchemaManager.php @@ -180,7 +180,7 @@ public function hasTable($table) { // MySQLi doesn't like parameterised queries for some queries // underscores need to be escaped in a SHOW TABLES LIKE query - $sqlTable = str_replace('_', '\\_', $this->database->quoteString($table)); + $sqlTable = str_replace('_', '\\_', $this->database->quoteString($table) ?: ''); return (bool) ($this->query("SHOW TABLES LIKE $sqlTable")->value()); } @@ -197,7 +197,7 @@ public function databaseList() public function databaseExists($name) { // MySQLi doesn't like parameterised queries for some queries - $sqlName = addcslashes($this->database->quoteString($name), '%_'); + $sqlName = addcslashes((string) $this->database->quoteString($name), '%_'); return !!($this->query("SHOW DATABASES LIKE $sqlName")->value()); } @@ -234,7 +234,7 @@ public function alterField($tableName, $fieldName, $fieldSpec) public function renameField($tableName, $oldName, $newName) { $fieldList = $this->fieldList($tableName); - if (array_key_exists($oldName, $fieldList)) { + if (array_key_exists($oldName, $fieldList ?: [])) { $this->query("ALTER TABLE \"$tableName\" CHANGE \"$oldName\" \"$newName\" " . $fieldList[$oldName]); } } @@ -251,11 +251,11 @@ private function shouldUseIntegerWidth() return $forceWidth; } $v = $this->database->getVersion(); - if (false !== strpos($v, 'MariaDB')) { + if (false !== strpos((string) $v, 'MariaDB')) { // MariaDB is included in the version string: https://mariadb.com/kb/en/version/ return true; } - return version_compare($v, '8.0.17', '<'); + return version_compare((string) $v, '8.0.17', '<'); } public function fieldList($table) @@ -391,7 +391,7 @@ public function enumValuesForField($tableName, $fieldName) $classes = []; foreach ($matches[0] as $value) { - $classes[] = stripslashes(trim($value, "'")); + $classes[] = stripslashes(trim((string) $value, "'")); } return $classes; } @@ -461,10 +461,10 @@ public function decimal($values) // Fix format of default value to match precision if (isset($values['default']) && is_numeric($values['default'])) { - $decs = strpos($precision, ',') !== false + $decs = strpos((string) $precision, ',') !== false ? (int) substr($precision, strpos($precision, ',') + 1) : 0; - $values['default'] = number_format($values['default'], $decs, '.', ''); + $values['default'] = number_format($values['default'], (int) $decs, '.', ''); } else { unset($values['default']); } diff --git a/src/ORM/Connect/MySQLStatement.php b/src/ORM/Connect/MySQLStatement.php index d79cf94c822..1b33bc1ce00 100644 --- a/src/ORM/Connect/MySQLStatement.php +++ b/src/ORM/Connect/MySQLStatement.php @@ -79,7 +79,7 @@ protected function bind() // Buffer all results $this->statement->store_result(); - call_user_func_array([$this->statement, 'bind_result'], $variables); + call_user_func_array([$this->statement, 'bind_result'], $variables ?: []); } /** @@ -129,7 +129,7 @@ public function nextRecord() $row = []; foreach ($this->boundValues as $key => $value) { $floatTypes = [MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE, MYSQLI_TYPE_DECIMAL, MYSQLI_TYPE_NEWDECIMAL]; - if (in_array($this->types[$key], $floatTypes)) { + if (in_array($this->types[$key], $floatTypes ?: [])) { $value = (float)$value; } $row[$key] = $value; diff --git a/src/ORM/Connect/MySQLiConnector.php b/src/ORM/Connect/MySQLiConnector.php index 8f2c9107976..901bc452000 100644 --- a/src/ORM/Connect/MySQLiConnector.php +++ b/src/ORM/Connect/MySQLiConnector.php @@ -91,9 +91,9 @@ public function connect($parameters, $selectDB = false) } // Set SSL parameters if they exist. All parameters are required. - if (array_key_exists('ssl_key', $parameters) && - array_key_exists('ssl_cert', $parameters) && - array_key_exists('ssl_ca', $parameters)) { + if (array_key_exists('ssl_key', $parameters ?: []) && + array_key_exists('ssl_cert', $parameters ?: []) && + array_key_exists('ssl_ca', $parameters ?: [])) { $this->dbConn->ssl_set( $parameters['ssl_key'], $parameters['ssl_cert'], @@ -198,7 +198,7 @@ public function parsePreparedParameters($parameters, &$blobs) $types = ''; $values = []; $blobs = []; - $parametersCount = count($parameters); + $parametersCount = count($parameters ?: []); for ($index = 0; $index < $parametersCount; $index++) { $value = $parameters[$index]; $phpType = gettype($value); @@ -258,7 +258,7 @@ public function bindParameters(mysqli_stmt $statement, array $parameters) // Because mysqli_stmt::bind_param arguments must be passed by reference // we need to do a bit of hackery $boundNames = []; - $parametersCount = count($parameters); + $parametersCount = count($parameters ?: []); for ($i = 0; $i < $parametersCount; $i++) { $boundName = "param$i"; $$boundName = $parameters[$i]; diff --git a/src/ORM/Connect/PDOConnector.php b/src/ORM/Connect/PDOConnector.php index 84f4b0026a9..5a39e2987c0 100644 --- a/src/ORM/Connect/PDOConnector.php +++ b/src/ORM/Connect/PDOConnector.php @@ -118,7 +118,7 @@ public function getOrPrepareStatement($sql) $statementHandle = ($statement === false) ? false : new PDOStatementHandle($statement); // Only cache select statements - if (preg_match('/^(\s*)select\b/i', $sql)) { + if (preg_match('/^(\s*)select\b/i', (string) $sql)) { $this->cachedStatements[$sql] = $statementHandle; } return $statementHandle; @@ -212,17 +212,17 @@ public function connect($parameters, $selectDB = false) } // Set SSL options if they are defined - if (array_key_exists('ssl_key', $parameters) && - array_key_exists('ssl_cert', $parameters) + if (array_key_exists('ssl_key', $parameters ?: []) && + array_key_exists('ssl_cert', $parameters ?: []) ) { $options[PDO::MYSQL_ATTR_SSL_KEY] = $parameters['ssl_key']; $options[PDO::MYSQL_ATTR_SSL_CERT] = $parameters['ssl_cert']; - if (array_key_exists('ssl_ca', $parameters)) { + if (array_key_exists('ssl_ca', $parameters ?: [])) { $options[PDO::MYSQL_ATTR_SSL_CA] = $parameters['ssl_ca']; } // use default cipher if not provided $options[PDO::MYSQL_ATTR_SSL_CIPHER] = - array_key_exists('ssl_cipher', $parameters) ? + array_key_exists('ssl_cipher', $parameters ?: []) ? $parameters['ssl_cipher'] : self::config()->get('ssl_cipher_default'); } @@ -278,7 +278,7 @@ public function escapeString($value) // Since the PDO library quotes the value, we should remove this to maintain // consistency with MySQLDatabase::escapeString - if (preg_match('/^\'(?.*)\'$/', $value, $matches)) { + if (preg_match('/^\'(?.*)\'$/', (string) $value, $matches)) { $value = $matches['value']; } return $value; @@ -383,7 +383,7 @@ public function getPDOParamType($phpType) public function bindParameters(PDOStatement $statement, $parameters) { // Bind all parameters - $parameterCount = count($parameters); + $parameterCount = count($parameters ?: []); for ($index = 0; $index < $parameterCount; $index++) { $value = $parameters[$index]; $phpType = gettype($value); diff --git a/src/ORM/Connect/PDOQuery.php b/src/ORM/Connect/PDOQuery.php index 718fd6f8b7c..026f6510490 100644 --- a/src/ORM/Connect/PDOQuery.php +++ b/src/ORM/Connect/PDOQuery.php @@ -34,7 +34,7 @@ public function seek($row) public function numRecords() { - return count($this->results); + return count($this->results ?: []); } public function nextRecord() diff --git a/src/ORM/Connect/PDOStatementHandle.php b/src/ORM/Connect/PDOStatementHandle.php index 4eae21fef14..9ad81cd632a 100644 --- a/src/ORM/Connect/PDOStatementHandle.php +++ b/src/ORM/Connect/PDOStatementHandle.php @@ -85,7 +85,7 @@ function ($rowArray) { } return $row; }, - $this->statement->fetchAll(PDO::FETCH_NUM) + $this->statement->fetchAll(PDO::FETCH_NUM) ?: [] ); } diff --git a/src/ORM/Connect/Query.php b/src/ORM/Connect/Query.php index 122de024b95..af9e0c3a540 100644 --- a/src/ORM/Connect/Query.php +++ b/src/ORM/Connect/Query.php @@ -170,6 +170,7 @@ public function table() * * @return void */ + #[\ReturnTypeWillChange] public function rewind() { if ($this->queryHasBegun && $this->numRecords() > 0) { @@ -184,6 +185,7 @@ public function rewind() * * @return array */ + #[\ReturnTypeWillChange] public function current() { if (!$this->currentRecord) { @@ -209,6 +211,7 @@ public function first() * * @return int */ + #[\ReturnTypeWillChange] public function key() { return $this->rowNum; @@ -220,6 +223,7 @@ public function key() * * @return array */ + #[\ReturnTypeWillChange] public function next() { $this->queryHasBegun = true; @@ -233,6 +237,7 @@ public function next() * * @return bool */ + #[\ReturnTypeWillChange] public function valid() { if (!$this->queryHasBegun) { diff --git a/src/ORM/Connect/TempDatabase.php b/src/ORM/Connect/TempDatabase.php index 4a5a30b5a4a..77506553aa1 100644 --- a/src/ORM/Connect/TempDatabase.php +++ b/src/ORM/Connect/TempDatabase.php @@ -58,8 +58,8 @@ protected function isDBTemp($name) { $prefix = Environment::getEnv('SS_DATABASE_PREFIX') ?: 'ss_'; $result = preg_match( - sprintf('/^%stmpdb_[0-9]+_[0-9]+$/i', preg_quote($prefix, '/')), - $name + sprintf('/^%stmpdb_[0-9]+_[0-9]+$/i', preg_quote((string) $prefix, '/')), + (string) $name ); return $result === 1; } diff --git a/src/ORM/DB.php b/src/ORM/DB.php index 1b45c1c81c8..15e2cc302cb 100644 --- a/src/ORM/DB.php +++ b/src/ORM/DB.php @@ -268,7 +268,7 @@ public static function valid_alternative_database_name($name) $prefix = Environment::getEnv('SS_DATABASE_PREFIX') ?: 'ss_'; $pattern = strtolower(sprintf('/^%stmpdb\d{7}$/', $prefix)); - return (bool)preg_match($pattern, $name); + return (bool)preg_match((string) $pattern, (string) $name); } /** @@ -365,7 +365,7 @@ public static function query($sql, $errorLevel = E_USER_ERROR) public static function placeholders($input, $join = ', ') { if (is_array($input)) { - $number = count($input); + $number = count($input ?: []); } elseif (is_numeric($input)) { $number = intval($input); } else { @@ -374,7 +374,7 @@ public static function placeholders($input, $join = ', ') if ($number === 0) { return null; } - return implode($join, array_fill(0, $number, '?')); + return implode($join ?: '', array_fill(0, (int) $number, '?')); } /** @@ -385,10 +385,10 @@ public static function placeholders($input, $join = ', ') */ public static function inline_parameters($sql, $parameters) { - $segments = preg_split('/\?/', $sql); + $segments = preg_split('/\?/', (string) $sql); $joined = ''; $inString = false; - $numSegments = count($segments); + $numSegments = count($segments ?: []); for ($i = 0; $i < $numSegments; $i++) { $input = $segments[$i]; // Append next segment @@ -399,10 +399,10 @@ public static function inline_parameters($sql, $parameters) } // check string escape on previous fragment // Remove escaped backslashes, count them! - $input = preg_replace('/\\\\\\\\/', '', $input); + $input = preg_replace('/\\\\\\\\/', '', $input ?: ''); // Count quotes - $totalQuotes = substr_count($input, "'"); // Includes double quote escaped quotes - $escapedQuotes = substr_count($input, "\\'"); + $totalQuotes = substr_count((string) $input, "'"); // Includes double quote escaped quotes + $escapedQuotes = substr_count((string) $input, "\\'"); if ((($totalQuotes - $escapedQuotes) % 2) !== 0) { $inString = !$inString; } diff --git a/src/ORM/DataExtension.php b/src/ORM/DataExtension.php index 4fbf3f211d3..077431d2557 100644 --- a/src/ORM/DataExtension.php +++ b/src/ORM/DataExtension.php @@ -264,8 +264,8 @@ public function updateSummaryFields(&$fields) if ($summary_fields) { // if summary_fields were passed in numeric array, // convert to an associative array - if ($summary_fields && array_key_exists(0, $summary_fields)) { - $summary_fields = array_combine(array_values($summary_fields), array_values($summary_fields)); + if ($summary_fields && array_key_exists(0, $summary_fields ?: [])) { + $summary_fields = array_combine(array_values($summary_fields ?: []), array_values($summary_fields ?: [])); } if ($summary_fields) { $fields = array_merge($fields, $summary_fields); diff --git a/src/ORM/DataList.php b/src/ORM/DataList.php index dff1db476a0..464a7810262 100644 --- a/src/ORM/DataList.php +++ b/src/ORM/DataList.php @@ -250,7 +250,7 @@ public function canSortBy($fieldName) public function canFilterBy($fieldName) { $model = singleton($this->dataClass); - $relations = explode(".", $fieldName); + $relations = explode(".", (string) $fieldName); // First validate the relationships $fieldName = array_pop($relations); foreach ($relations as $r) { @@ -330,7 +330,7 @@ public function sort() list($col, $dir) = func_get_args(); // Validate direction - if (!in_array(strtolower($dir), ['desc', 'asc'])) { + if (!in_array(strtolower((string) $dir), ['desc', 'asc'])) { user_error('Second argument to sort must be either ASC or DESC'); } @@ -387,7 +387,7 @@ public function filter() { // Validate and process arguments $arguments = func_get_args(); - switch (sizeof($arguments)) { + switch (sizeof($arguments ?: [])) { case 1: $filters = $arguments[0]; @@ -526,7 +526,7 @@ public function applyRelation($field, &$columnName = null, $linearOnly = false) } // Simple fields without relations are mapped directly - if (strpos($field, '.') === false) { + if (strpos((string) $field, '.') === false) { $columnName = '"' . $field . '"'; return $this; } @@ -555,7 +555,7 @@ function (DataQuery $query) use ($field, &$columnName, $linearOnly) { */ protected function isValidRelationName($field) { - return preg_match('/^[A-Z0-9._]+$/i', $field); + return preg_match('/^[A-Z0-9._]+$/i', (string) $field); } /** @@ -568,7 +568,7 @@ protected function isValidRelationName($field) protected function createSearchFilter($filter, $value) { // Field name is always the first component - $fieldArgs = explode(':', $filter); + $fieldArgs = explode(':', (string) $filter); $fieldName = array_shift($fieldArgs); // Inspect type of second argument to determine context @@ -582,7 +582,7 @@ protected function createSearchFilter($filter, $value) // Whether this is a valid modifier on the default filter, or a filter itself. /** @var SearchFilter $defaultFilterInstance */ $defaultFilterInstance = Injector::inst()->get('DataListFilter.default'); - if (in_array(strtolower($secondArg), $defaultFilterInstance->getSupportedModifiers())) { + if (in_array(strtolower((string) $secondArg), $defaultFilterInstance->getSupportedModifiers() ?: [])) { // Treat second (and any subsequent) argument as modifiers, using default filter $filterServiceName = 'DataListFilter.default'; array_unshift($modifiers, $secondArg); @@ -866,6 +866,7 @@ public function getQueryParams() * * @return ArrayIterator */ + #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->toArray()); @@ -876,6 +877,7 @@ public function getIterator() * * @return int */ + #[\ReturnTypeWillChange] public function count() { return $this->dataQuery->count(); @@ -1248,6 +1250,7 @@ public function reverse() * @param mixed $key * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($key) { return ($this->limit(1, $key)->first() != null); @@ -1261,6 +1264,7 @@ public function offsetExists($key) * @param mixed $key * @return DataObject */ + #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->limit(1, $key)->first(); @@ -1272,6 +1276,7 @@ public function offsetGet($key) * @param mixed $key * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($key, $value) { throw new \BadMethodCallException("Can't alter items in a DataList using array-access"); @@ -1282,6 +1287,7 @@ public function offsetSet($key, $value) * * @param mixed $key */ + #[\ReturnTypeWillChange] public function offsetUnset($key) { throw new \BadMethodCallException("Can't alter items in a DataList using array-access"); diff --git a/src/ORM/DataObject.php b/src/ORM/DataObject.php index a21f55211a3..d2d0e04bb87 100644 --- a/src/ORM/DataObject.php +++ b/src/ORM/DataObject.php @@ -464,8 +464,8 @@ private function hydrate(array $record, bool $mustHaveID) ); foreach ($fields as $field => $fieldSpec) { - $fieldClass = strtok($fieldSpec, "."); - if (!array_key_exists($field, $record)) { + $fieldClass = strtok((string) $fieldSpec, "."); + if (!array_key_exists($field, $record ?: [])) { $this->record[$field . '_Lazy'] = $fieldClass; } } @@ -479,7 +479,7 @@ private function hydrate(array $record, bool $mustHaveID) // If a corresponding lazy-load field exists, remove it as the value has been provided $lazyName = $field . '_Lazy'; - if (array_key_exists($lazyName, $this->record)) { + if (array_key_exists($lazyName, $this->record ?: [])) { unset($this->record[$lazyName]); } } @@ -519,7 +519,7 @@ public function duplicate($doWrite = true, $relations = null) $relations = 'many_many'; } Deprecation::notice('5.0', 'Use cascade_duplicates config instead of providing a string to duplicate()'); - $relations = array_keys($this->config()->get($relations)) ?: []; + $relations = array_keys($this->config()->get($relations) ?: []) ?: []; } // Get duplicates @@ -527,7 +527,7 @@ public function duplicate($doWrite = true, $relations = null) $relations = $this->config()->get('cascade_duplicates'); // Remove any duplicate entries before duplicating them if (is_array($relations)) { - $relations = array_unique($relations); + $relations = array_unique($relations ?: []); } } @@ -756,7 +756,7 @@ public function getClassName() */ public function setClassName($className) { - $className = trim($className); + $className = trim((string) $className); if (!$className || !is_subclass_of($className, self::class)) { return $this; } @@ -832,7 +832,7 @@ public function defineMethods() } } if ($belongsTo = $this->belongsTo()) { - foreach (array_keys($belongsTo) as $relationship) { + foreach (array_keys($belongsTo ?: []) as $relationship) { $this->addWrapperMethod($relationship, 'getComponent'); } } @@ -907,10 +907,10 @@ public function singular_name() if ($name) { return $name; } - return ucwords(trim(strtolower(preg_replace( + return ucwords(trim(strtolower((string) preg_replace( '/_?([A-Z])/', ' $1', - ClassInfo::shortName($this) + ClassInfo::shortName($this) ?: '' )))); } @@ -944,8 +944,8 @@ public function plural_name() } $name = $this->singular_name(); //if the penultimate character is not a vowel, replace "y" with "ies" - if (preg_match('/[^aeiou]y$/i', $name)) { - $name = substr($name, 0, -1) . 'ie'; + if (preg_match('/[^aeiou]y$/i', (string) $name)) { + $name = substr((string) $name, 0, -1) . 'ie'; } return ucfirst($name . 's'); } @@ -1017,7 +1017,7 @@ public function data() public function toMap() { $this->loadLazyFields(); - return array_filter($this->record, function ($val) { + return array_filter($this->record ?: [], function ($val) { return $val !== null; }); } @@ -1054,8 +1054,8 @@ public function update($data) { foreach ($data as $key => $value) { // Implement dot syntax for updates - if (strpos($key, '.') !== false) { - $relations = explode('.', $key); + if (strpos((string) $key, '.') !== false) { + $relations = explode('.', (string) $key); $fieldName = array_pop($relations); /** @var static $relObj */ $relObj = $this; @@ -1067,7 +1067,7 @@ public function update($data) $parentObj = $relObj; $relObj = $relObj->$relation(); // If the intermediate relationship objects haven't been created, then write them - if ($i < sizeof($relations) - 1 && !$relObj->ID || (!$relObj->ID && $parentObj !== $this)) { + if ($i < sizeof($relations ?: []) - 1 && !$relObj->ID || (!$relObj->ID && $parentObj !== $this)) { $relObj->write(); $relatedFieldName = $relation . "ID"; $parentObj->$relatedFieldName = $relObj->ID; @@ -1227,7 +1227,7 @@ public function forceChange() $this->loadLazyFields(); // Populate the null values in record so that they actually get written - foreach (array_keys(static::getSchema()->fieldSpecs(static::class)) as $fieldName) { + foreach (array_keys(static::getSchema()->fieldSpecs(static::class) ?: []) as $fieldName) { if (!isset($this->record[$fieldName])) { $this->record[$fieldName] = null; } @@ -1355,7 +1355,7 @@ protected function onAfterDelete() */ public function populateDefaults() { - $classes = array_reverse(ClassInfo::ancestry($this)); + $classes = array_reverse(ClassInfo::ancestry($this) ?: []); foreach ($classes as $class) { $defaults = Config::inst()->get($class, 'defaults', Config::UNINHERITED); @@ -1964,7 +1964,7 @@ public function setComponent($componentName, $item) // For belongs_to, add to has_one on other component $joinField = $schema->getRemoteJoinField(static::class, $componentName, 'belongs_to', $polymorphic); if (!$polymorphic) { - $joinField = substr($joinField, 0, -2); + $joinField = substr((string) $joinField, 0, -2); } $item->setComponent($joinField, $this); } @@ -2051,8 +2051,8 @@ public function getRelationClass($relationName) $remoteClass = $candidates[$relationName]; // If dot notation is present, extract just the first part that contains the class. - if (($fieldPos = strpos($remoteClass, '.')) !== false) { - return substr($remoteClass, 0, $fieldPos); + if (($fieldPos = strpos((string) $remoteClass, '.')) !== false) { + return substr((string) $remoteClass, 0, $fieldPos); } // Otherwise just return the class @@ -2120,7 +2120,7 @@ public function inferReciprocalComponent($remoteClass, $remoteRelation) if ($class === self::class) { return null; } - if (!is_a($this, $class, true)) { + if (!is_a($this, (string) $class, true)) { throw new InvalidArgumentException(sprintf( "Relation %s on %s does not refer to objects of type %s", $remoteRelation, @@ -2287,7 +2287,7 @@ public function belongsTo($classOnly = true) { $belongsTo = (array)$this->config()->get('belongs_to'); if ($belongsTo && $classOnly) { - return preg_replace('/(.+)?\..+/', '$1', $belongsTo); + return preg_replace('/(.+)?\..+/', '$1', $belongsTo ?: ''); } else { return $belongsTo ? $belongsTo : []; } @@ -2305,7 +2305,7 @@ public function hasMany($classOnly = true) { $hasMany = (array)$this->config()->get('has_many'); if ($hasMany && $classOnly) { - return preg_replace('/(.+)?\..+/', '$1', $hasMany); + return preg_replace('/(.+)?\..+/', '$1', $hasMany ?: ''); } else { return $hasMany ? $hasMany : []; } @@ -2444,7 +2444,7 @@ public function scaffoldSearchFields($_params = null) continue; } - if (strstr($fieldName, '.')) { + if (strstr((string) $fieldName, '.')) { $field->setName(str_replace('.', '__', $fieldName)); } $field->setTitle($spec['title']); @@ -2667,7 +2667,7 @@ protected function loadLazyFields($class = null) $loaded = []; foreach ($this->record as $key => $value) { - if (strlen($key) > 5 && substr($key, -5) == '_Lazy' && !array_key_exists($value, $loaded)) { + if (strlen((string) $key) > 5 && substr((string) $key, -5) == '_Lazy' && !array_key_exists($value, $loaded ?: [])) { $this->loadLazyFields($value); $loaded[$value] = $value; } @@ -2715,7 +2715,7 @@ protected function loadLazyFields($class = null) // Load the data into record if ($newData) { foreach ($newData as $k => $v) { - if (in_array($k, $columns)) { + if (in_array($k, $columns ?: [])) { $this->record[$k] = $v; $this->original[$k] = $v; unset($this->record[$k . '_Lazy']); @@ -2761,7 +2761,7 @@ public function getChangedFields($databaseFieldsOnly = false, $changeLevel = sel // Update the changed array with references to changed obj-fields foreach ($this->record as $k => $v) { // Prevents DBComposite infinite looping on isChanged - if (is_array($databaseFieldsOnly) && !in_array($k, $databaseFieldsOnly)) { + if (is_array($databaseFieldsOnly) && !in_array($k, $databaseFieldsOnly ?: [])) { continue; } if (is_object($v) && method_exists($v, 'isChanged') && $v->isChanged()) { @@ -2772,8 +2772,8 @@ public function getChangedFields($databaseFieldsOnly = false, $changeLevel = sel // If change was forced, then derive change data from $this->record if ($this->changeForced && $changeLevel <= self::CHANGE_STRICT) { $changed = array_combine( - array_keys($this->record), - array_fill(0, count($this->record), self::CHANGE_STRICT) + array_keys($this->record ?: []), + array_fill(0, count($this->record ?: []), self::CHANGE_STRICT) ); // @todo Find better way to allow versioned to write a new version after forceChange unset($changed['Version']); @@ -2782,10 +2782,10 @@ public function getChangedFields($databaseFieldsOnly = false, $changeLevel = sel } if (is_array($databaseFieldsOnly)) { - $fields = array_intersect_key($changed, array_flip($databaseFieldsOnly)); + $fields = array_intersect_key($changed ?: [], array_flip($databaseFieldsOnly ?: [])); } elseif ($databaseFieldsOnly) { $fieldsSpecs = static::getSchema()->fieldSpecs(static::class); - $fields = array_intersect_key($changed, $fieldsSpecs); + $fields = array_intersect_key($changed ?: [], $fieldsSpecs); } else { $fields = $changed; } @@ -2829,7 +2829,7 @@ public function isChanged($fieldName = null, $changeLevel = self::CHANGE_STRICT) if (!isset($fieldName)) { return !empty($changed); } else { - return array_key_exists($fieldName, $changed); + return array_key_exists($fieldName, $changed ?: []); } } @@ -2845,7 +2845,7 @@ public function setField($fieldName, $val) { $this->objCacheClear(); //if it's a has_one component, destroy the cache - if (substr($fieldName, -2) == 'ID') { + if (substr((string) $fieldName, -2) == 'ID') { unset($this->components[substr($fieldName, 0, -2)]); } @@ -2900,14 +2900,14 @@ public function setField($fieldName, $val) } // if a field is not existing or has strictly changed - if (!array_key_exists($fieldName, $this->original) || $this->original[$fieldName] !== $val) { + if (!array_key_exists($fieldName, $this->original ?: []) || $this->original[$fieldName] !== $val) { // TODO Add check for php-level defaults which are not set in the db // TODO Add check for hidden input-fields (readonly) which are not set in the db // At the very least, the type has changed $this->changed[$fieldName] = self::CHANGE_STRICT; - if ((!array_key_exists($fieldName, $this->original) && $val) - || (array_key_exists($fieldName, $this->original) && $this->original[$fieldName] != $val) + if ((!array_key_exists($fieldName, $this->original ?: []) && $val) + || (array_key_exists($fieldName, $this->original ?: []) && $this->original[$fieldName] != $val) ) { // Value has changed as well, not just the type $this->changed[$fieldName] = self::CHANGE_VALUE; @@ -2984,8 +2984,8 @@ public function hasField($field) { $schema = static::getSchema(); return ( - array_key_exists($field, $this->record) - || array_key_exists($field, $this->components) + array_key_exists($field, $this->record ?: []) + || array_key_exists($field, $this->components ?: []) || $schema->fieldSpec(static::class, $field) || $schema->unaryComponent(static::class, $field) || $this->hasMethod("get{$field}") @@ -3026,8 +3026,8 @@ public function can($perm, $member = null, $context = []) return true; } - if (is_string($perm) && method_exists($this, 'can' . ucfirst($perm))) { - $method = 'can' . ucfirst($perm); + if (is_string($perm) && method_exists($this, 'can' . ucfirst((string) $perm))) { + $method = 'can' . ucfirst((string) $perm); return $this->$method($member); } @@ -3065,7 +3065,7 @@ public function extendedCan($methodName, $member, $context = []) $results = $this->extend($methodName, $member, $context); if ($results && is_array($results)) { // Remove NULLs - $results = array_filter($results, function ($v) { + $results = array_filter($results ?: [], function ($v) { return !is_null($v); }); // If there are any non-NULL responses, then return the lowest one of them. @@ -3182,9 +3182,9 @@ public function dbObject($fieldName) return $value; } - $pos = strpos($helper, '.'); - $class = substr($helper, 0, $pos); - $spec = substr($helper, $pos + 1); + $pos = strpos((string) $helper, '.'); + $class = substr((string) $helper, 0, $pos); + $spec = substr((string) $helper, $pos + 1); /** @var DBField $obj */ $table = $schema->tableName($class); @@ -3216,7 +3216,7 @@ public function relObject($fieldPath) $component = $this; // Parse all relations - foreach (explode('.', $fieldPath) as $relation) { + foreach (explode('.', (string) $fieldPath) as $relation) { if (!$component) { return null; } @@ -3252,9 +3252,9 @@ public function relField($fieldName) { // Navigate to relative parent using relObject() if needed $component = $this; - if (($pos = strrpos($fieldName, '.')) !== false) { - $relation = substr($fieldName, 0, $pos); - $fieldName = substr($fieldName, $pos + 1); + if (($pos = strrpos((string) $fieldName, '.')) !== false) { + $relation = substr((string) $fieldName, 0, $pos); + $fieldName = substr((string) $fieldName, $pos + 1); $component = $this->relObject($relation); } @@ -3278,20 +3278,20 @@ public function relField($fieldName) public function getReverseAssociation($className) { if (is_array($this->manyMany())) { - $many_many = array_flip($this->manyMany()); - if (array_key_exists($className, $many_many)) { + $many_many = array_flip($this->manyMany() ?: []); + if (array_key_exists($className, $many_many ?: [])) { return $many_many[$className]; } } if (is_array($this->hasMany())) { - $has_many = array_flip($this->hasMany()); - if (array_key_exists($className, $has_many)) { + $has_many = array_flip($this->hasMany() ?: []); + if (array_key_exists($className, $has_many ?: [])) { return $has_many[$className]; } } if (is_array($this->hasOne())) { - $has_one = array_flip($this->hasOne()); - if (array_key_exists($className, $has_one)) { + $has_one = array_flip($this->hasOne() ?: []); + if (array_key_exists($className, $has_one ?: [])) { return $has_one[$className]; } } @@ -3351,8 +3351,8 @@ public static function get( if ($sort) { $result = $result->sort($sort); } - if ($limit && strpos($limit, ',') !== false) { - $limitArguments = explode(',', $limit); + if ($limit && strpos((string) $limit, ',') !== false) { + $limitArguments = explode(',', (string) $limit); $result = $result->limit($limitArguments[1], $limitArguments[0]); } elseif ($limit) { $result = $result->limit($limit); @@ -3606,7 +3606,7 @@ public function requireTable() } if ($fields) { - $hasAutoIncPK = get_parent_class($this) === self::class; + $hasAutoIncPK = get_parent_class($this ?: '') === self::class; DB::require_table( $table, $fields, @@ -3630,7 +3630,7 @@ public function requireTable() $tableOrClass = $manyManyComponent['join']; // Skip if backed by actual class - if (class_exists($tableOrClass)) { + if (class_exists((string) $tableOrClass)) { continue; } @@ -3718,7 +3718,7 @@ public function searchableFields() // fallback to summary fields (unless empty array is explicitly specified) if (!$fields && !is_array($fields)) { - $summaryFields = array_keys($this->summaryFields()); + $summaryFields = array_keys($this->summaryFields() ?: []); $fields = []; // remove the custom getters as the search should not include them @@ -3728,8 +3728,8 @@ public function searchableFields() $spec = $name; // Extract field name in case this is a method called on a field (e.g. "Date.Nice") - if (($fieldPos = strpos($name, '.')) !== false) { - $name = substr($name, 0, $fieldPos); + if (($fieldPos = strpos((string) $name, '.')) !== false) { + $name = substr((string) $name, 0, $fieldPos); } if ($schema->fieldSpec($this, $name)) { @@ -3823,7 +3823,7 @@ public function fieldLabels($includerelations = true) // get all translated static properties as defined in i18nCollectStatics() $ancestry = ClassInfo::ancestry(static::class); - $ancestry = array_reverse($ancestry); + $ancestry = array_reverse($ancestry ?: []); if ($ancestry) { foreach ($ancestry as $ancestorClass) { if ($ancestorClass === ViewableData::class) { @@ -4218,7 +4218,7 @@ public function provideI18nEntities() // Best guess for a/an rule. Better guesses require overriding in subclasses $pluralName = $this->plural_name(); $singularName = $this->singular_name(); - $conjunction = preg_match('/^[aeiou]/i', $singularName) ? 'An ' : 'A '; + $conjunction = preg_match('/^[aeiou]/i', (string) $singularName) ? 'An ' : 'A '; return [ static::class . '.SINGULARNAME' => $this->singular_name(), static::class . '.PLURALNAME' => $pluralName, diff --git a/src/ORM/DataObjectSchema.php b/src/ORM/DataObjectSchema.php index 9e85e0dfbed..7f510ea31e1 100644 --- a/src/ORM/DataObjectSchema.php +++ b/src/ORM/DataObjectSchema.php @@ -145,7 +145,7 @@ public function tableName($class) public function baseDataClass($class) { $current = $class; - while ($next = get_parent_class($current)) { + while ($next = get_parent_class($current ?: '')) { if ($next === DataObject::class) { // Only use ClassInfo::class_name() to format the class if we've not used get_parent_class() return ($current === $class) ? ClassInfo::class_name($current) : $current; @@ -261,16 +261,16 @@ public function fieldSpec($classOrInstance, $fieldName, $options = 0) public function tableClass($table) { $tables = $this->getTableNames(); - $class = array_search($table, $tables, true); + $class = array_search($table, $tables ?: [], true); if ($class) { return $class; } // If there is no class for this table, strip table modifiers (e.g. _Live / _Versions) // from the end and re-attempt a search. - if (preg_match('/^(?.+)(_[^_]+)$/i', $table, $matches)) { + if (preg_match('/^(?.+)(_[^_]+)$/i', (string) $table, $matches)) { $table = $matches['class']; - $class = array_search($table, $tables, true); + $class = array_search($table, $tables ?: [], true); if ($class) { return $class; } @@ -294,7 +294,7 @@ protected function cacheTableNames() $table = $this->buildTableName($class); // Check for conflicts - $conflict = array_search($table, $this->tableNames, true); + $conflict = array_search($table, $this->tableNames ?: [], true); if ($conflict) { throw new LogicException( "Multiple classes (\"{$class}\", \"{$conflict}\") map to the same table: \"{$table}\"" @@ -323,12 +323,12 @@ protected function buildTableName($class) return $table; } - if (strpos($class, '\\') === false) { + if (strpos((string) $class, '\\') === false) { return $class; } $separator = DataObjectSchema::config()->uninherited('table_namespace_separator'); - $table = str_replace('\\', $separator, trim($class, '\\')); + $table = str_replace('\\', $separator ?: '', trim((string) $class, '\\')); if (!ClassInfo::classImplements($class, TestOnly::class) && $this->classHasTable($class)) { DBSchemaManager::showTableNameWarning($table, $class); @@ -361,7 +361,7 @@ public function databaseFields($class, $aggregated = true) // Recursively merge $parentFields = $this->databaseFields(get_parent_class($class)); - return array_merge($fields, array_diff_key($parentFields, $fields)); + return array_merge($fields, array_diff_key($parentFields ?: [], $fields)); } /** @@ -446,7 +446,7 @@ public function compositeFields($class, $aggregated = true) // Recursively merge $parentFields = $this->compositeFields(get_parent_class($class)); - return array_merge($compositeFields, array_diff_key($parentFields, $compositeFields)); + return array_merge($compositeFields, array_diff_key($parentFields ?: [], $compositeFields)); } /** @@ -481,7 +481,7 @@ protected function cacheDatabaseFields($class) // Ensure fixed fields appear at the start $fixedFields = DataObject::config()->uninherited('fixed_fields'); - if (get_parent_class($class) === DataObject::class) { + if (get_parent_class($class ?: '') === DataObject::class) { // Merge fixed with ClassName spec and custom db fields $dbFields = $fixedFields; } else { @@ -491,7 +491,7 @@ protected function cacheDatabaseFields($class) // Check each DB value as either a field or composite field $db = Config::inst()->get($class, 'db', Config::UNINHERITED) ?: []; foreach ($db as $fieldName => $fieldSpec) { - $fieldClass = strtok($fieldSpec, '('); + $fieldClass = strtok((string) $fieldSpec, '('); if (singleton($fieldClass) instanceof DBComposite) { $compositeFields[$fieldName] = $fieldSpec; } else { @@ -520,7 +520,7 @@ protected function cacheDatabaseFields($class) } // Prevent field-less tables with only 'ID' - if (count($dbFields) < 2) { + if (count($dbFields ?: []) < 2) { $dbFields = []; } @@ -536,7 +536,7 @@ protected function cacheDatabaseFields($class) */ protected function cacheDatabaseIndexes($class) { - if (!array_key_exists($class, $this->databaseIndexes)) { + if (!array_key_exists($class, $this->databaseIndexes ?: [])) { $this->databaseIndexes[$class] = array_merge( $this->buildSortDatabaseIndexes($class), $this->cacheDefaultDatabaseIndexes($class), @@ -554,7 +554,7 @@ protected function cacheDatabaseIndexes($class) */ protected function cacheDefaultDatabaseIndexes($class) { - if (array_key_exists($class, $this->defaultDatabaseIndexes)) { + if (array_key_exists($class, $this->defaultDatabaseIndexes ?: [])) { return $this->defaultDatabaseIndexes[$class]; } $this->defaultDatabaseIndexes[$class] = []; @@ -584,7 +584,7 @@ protected function buildCustomDatabaseIndexes($class) $indexes = []; $classIndexes = Config::inst()->get($class, 'indexes', Config::UNINHERITED) ?: []; foreach ($classIndexes as $indexName => $indexSpec) { - if (array_key_exists($indexName, $indexes)) { + if (array_key_exists($indexName, $indexes ?: [])) { throw new InvalidArgumentException(sprintf( 'Index named "%s" already exists on class %s', $indexName, @@ -627,13 +627,13 @@ protected function buildSortDatabaseIndexes($class) $indexes = []; if ($sort && is_string($sort)) { - $sort = preg_split('/,(?![^()]*+\\))/', $sort); + $sort = preg_split('/,(?![^()]*+\\))/', (string) $sort); foreach ($sort as $value) { try { list ($table, $column) = $this->parseSortColumn(trim($value)); - $table = trim($table, '"'); - $column = trim($column, '"'); - if ($table && strtolower($table) !== strtolower(self::tableName($class))) { + $table = trim((string) $table, '"'); + $column = trim((string) $column, '"'); + if ($table && strtolower((string) $table) !== strtolower((string) self::tableName($class))) { continue; } if ($this->databaseField($class, $column, false)) { @@ -660,7 +660,7 @@ protected function parseSortColumn($column) { // Parse column specification, considering possible ansi sql quoting // Note that table prefix is allowed, but discarded - if (preg_match('/^("?(?
    [^"\s]+)"?\\.)?"?(?[^"\s]+)"?(\s+(?((asc)|(desc))(ending)?))?$/i', $column, $match)) { + if (preg_match('/^("?(?
    [^"\s]+)"?\\.)?"?(?[^"\s]+)"?(\s+(?((asc)|(desc))(ending)?))?$/i', (string) $column, $match)) { $table = $match['table']; $column = $match['column']; } else { @@ -718,7 +718,7 @@ public function classForField($candidateClass, $fieldName) if (isset($fields[$fieldName])) { return $candidateClass; } - $candidateClass = get_parent_class($candidateClass); + $candidateClass = get_parent_class($candidateClass ?: ''); } return null; } @@ -796,12 +796,12 @@ protected function parseBelongsManyManyComponent($parentClass, $component, $spec { $childClass = $specification; $relationName = null; - if (strpos($specification, '.') !== false) { - list($childClass, $relationName) = explode('.', $specification, 2); + if (strpos((string) $specification, '.') !== false) { + list($childClass, $relationName) = explode('.', (string) $specification, 2); } // Check child class exists - if (!class_exists($childClass)) { + if (!class_exists((string) $childClass)) { throw new LogicException( "belongs_many_many relation {$parentClass}.{$component} points to " . "{$childClass} which does not exist" @@ -856,7 +856,7 @@ public function manyManyExtraFieldsForComponent($class, $component) ); return $this->manyManyExtraFieldsForComponent($belongs['childClass'], $belongs['relationName']); } - $class = get_parent_class($class); + $class = get_parent_class($class ?: ''); } return null; } @@ -880,7 +880,7 @@ public function hasManyComponent($class, $component, $classOnly = true) // Remove has_one specifier if given $hasMany = $hasMany[$component]; - $hasManyClass = strtok($hasMany, '.'); + $hasManyClass = strtok((string) $hasMany, '.'); // Validate $this->checkRelationClass($class, $component, $hasManyClass, 'has_many'); @@ -927,7 +927,7 @@ public function belongsToComponent($class, $component, $classOnly = true) // Remove has_one specifier if given $belongsTo = $belongsTo[$component]; - $belongsToClass = strtok($belongsTo, '.'); + $belongsToClass = strtok((string) $belongsTo, '.'); // Validate $this->checkRelationClass($class, $component, $belongsToClass, 'belongs_to'); @@ -1065,8 +1065,8 @@ public function getRemoteJoinField($class, $component, $type = 'has_many', &$pol // If presented with an explicit field name (using dot notation) then extract field name $remoteField = null; - if (strpos($remoteClass, '.') !== false) { - list($remoteClass, $remoteField) = explode('.', $remoteClass); + if (strpos((string) $remoteClass, '.') !== false) { + list($remoteClass, $remoteField) = explode('.', (string) $remoteClass); } // Reference remote has_one to check against @@ -1076,9 +1076,9 @@ public function getRemoteJoinField($class, $component, $type = 'has_many', &$pol // with the same type as the current class if (empty($remoteField)) { // look for remote has_one joins on this class or any parent classes - $remoteRelationsMap = array_flip($remoteRelations); - foreach (array_reverse(ClassInfo::ancestry($class)) as $ancestryClass) { - if (array_key_exists($ancestryClass, $remoteRelationsMap)) { + $remoteRelationsMap = array_flip($remoteRelations ?: []); + foreach (array_reverse(ClassInfo::ancestry($class) ?: []) as $ancestryClass) { + if (array_key_exists($ancestryClass, $remoteRelationsMap ?: [])) { $remoteField = $remoteRelationsMap[$ancestryClass]; break; } @@ -1196,7 +1196,7 @@ protected function checkManyManyJoinClass($parentClass, $component, $specificati ); } $joinClass = $specification['through']; - if (!class_exists($joinClass)) { + if (!class_exists((string) $joinClass)) { throw new InvalidArgumentException( "many_many relation {$parentClass}.{$component} has through class \"{$joinClass}\" which does not exist" ); @@ -1224,7 +1224,7 @@ protected function checkRelationClass($class, $component, $relationClass, $type) "{$type} relation {$class}.{$component} is not a class name" ); } - if (!class_exists($relationClass)) { + if (!class_exists((string) $relationClass)) { throw new InvalidArgumentException( "{$type} relation {$class}.{$component} references class {$relationClass} which doesn't exist" ); diff --git a/src/ORM/DataQuery.php b/src/ORM/DataQuery.php index 30042330c5f..32568d67b7a 100644 --- a/src/ORM/DataQuery.php +++ b/src/ORM/DataQuery.php @@ -124,7 +124,7 @@ public function removeFilterOn($fieldExpression) // If given a parameterised condition extract only the condition if (is_array($fieldExpression)) { reset($fieldExpression); - $fieldExpression = key($fieldExpression); + $fieldExpression = key($fieldExpression ?: []); } $where = $this->query->getWhere(); @@ -140,7 +140,7 @@ public function removeFilterOn($fieldExpression) // iteration to extract the predicate and parameters foreach ($condition as $predicate => $parameters) { // @see SQLSelect::addWhere for why this is required here - if (strpos($predicate, $fieldExpression) !== false) { + if (strpos((string) $predicate, (string) $fieldExpression) !== false) { unset($where[$i]); $matched = true; } @@ -233,13 +233,13 @@ public function getFinalisedQuery($queriedColumns = null) // Check for any columns in the form '"Column" = ?' or '"Table"."Column"' = ? if (preg_match_all( '/(?:"(?
    [^"]+)"\.)?"(?[^"]+)"(?:[^\.]|$)/', - $where, + (string) $where, $matches, PREG_SET_ORDER )) { foreach ($matches as $match) { $column = $match['column']; - if (!in_array($column, $queriedColumns)) { + if (!in_array($column, $queriedColumns ?: [])) { $queriedColumns[] = $column; } } @@ -258,11 +258,11 @@ public function getFinalisedQuery($queriedColumns = null) // Restrict queried columns to that on the selected table $tableFields = $schema->databaseFields($tableClass, false); unset($tableFields['ID']); - $selectColumns = array_intersect($queriedColumns, array_keys($tableFields)); + $selectColumns = array_intersect($queriedColumns ?: [], array_keys($tableFields ?: [])); } // If this is a subclass without any explicitly requested columns, omit this from the query - if (!in_array($tableClass, $ancestorClasses) && empty($selectColumns)) { + if (!in_array($tableClass, $ancestorClasses ?: []) && empty($selectColumns)) { continue; } @@ -288,7 +288,7 @@ public function getFinalisedQuery($queriedColumns = null) foreach ($this->collidingFields as $collisionField => $collisions) { $caseClauses = []; foreach ($collisions as $collision) { - if (preg_match('/^"(?
    [^"]+)"\./', $collision, $matches)) { + if (preg_match('/^"(?
    [^"]+)"\./', (string) $collision, $matches)) { $collisionTable = $matches['table']; $collisionClass = $schema->tableClass($collisionTable); if ($collisionClass) { @@ -362,22 +362,22 @@ protected function ensureSelectContainsOrderbyColumns($query, $originalSelect = // don't touch functions in the ORDER BY or public function calls // selected as fields - if (strpos($k, '(') !== false) { + if (strpos((string) $k, '(') !== false) { continue; } - $col = str_replace('"', '', trim($k)); - $parts = explode('.', $col); + $col = str_replace('"', '', trim((string) $k)); + $parts = explode('.', (string) $col); // Pull through SortColumn references from the originalSelect variables - if (preg_match('/_SortColumn/', $col)) { + if (preg_match('/_SortColumn/', (string) $col)) { if (isset($originalSelect[$col])) { $query->selectField($originalSelect[$col], $col); } continue; } - if (count($parts) == 1) { + if (count($parts ?: []) == 1) { // Get expression for sort value $qualCol = "\"{$parts[0]}\""; $table = DataObject::getSchema()->tableForField($this->dataClass(), $parts[0]); @@ -394,9 +394,9 @@ protected function ensureSelectContainsOrderbyColumns($query, $originalSelect = // To-do: Remove this if block once SQLSelect::$select has been refactored to store getSelect() // format internally; then this check can be part of selectField() $selects = $query->getSelect(); - if (!isset($selects[$col]) && !in_array($qualCol, $selects)) { + if (!isset($selects[$col]) && !in_array($qualCol, $selects ?: [])) { // Use the original select if possible. - if (array_key_exists($col, $originalSelect)) { + if (array_key_exists($col, $originalSelect ?: [])) { $query->selectField($originalSelect[$col], $col); } else { $query->selectField($qualCol); @@ -405,7 +405,7 @@ protected function ensureSelectContainsOrderbyColumns($query, $originalSelect = } else { $qualCol = '"' . implode('"."', $parts) . '"'; - if (!in_array($qualCol, $query->getSelect())) { + if (!in_array($qualCol, $query->getSelect() ?: [])) { unset($newOrderby[$k]); // Find the first free "_SortColumnX" slot @@ -605,7 +605,7 @@ protected function selectColumnsFromTable(SQLSelect &$query, $tableClass, $colum $compositeFields = $schema->compositeFields($tableClass, false); unset($databaseFields['ID']); foreach ($databaseFields as $k => $v) { - if ((is_null($columns) || in_array($k, $columns)) && !isset($compositeFields[$k])) { + if ((is_null($columns) || in_array($k, $columns ?: [])) && !isset($compositeFields[$k])) { // Update $collidingFields if necessary $expressionForField = $query->expressionForField($k); $quotedField = $schema->sqlColumnForField($tableClass, $k); @@ -620,7 +620,7 @@ protected function selectColumnsFromTable(SQLSelect &$query, $tableClass, $colum } } foreach ($compositeFields as $k => $v) { - if ((is_null($columns) || in_array($k, $columns)) && $v) { + if ((is_null($columns) || in_array($k, $columns ?: [])) && $v) { $tableName = $schema->tableName($tableClass); $dbO = Injector::inst()->create($v, $k); $dbO->setTable($tableName); @@ -829,7 +829,7 @@ public static function applyRelationPrefix($relation) return null; } if (is_string($relation)) { - $relation = explode(".", $relation); + $relation = explode(".", (string) $relation); } return strtolower(implode('_', $relation)) . '_'; } @@ -855,7 +855,7 @@ public function applyRelation($relation, $linearOnly = false) } if (is_string($relation)) { - $relation = explode(".", $relation); + $relation = explode(".", (string) $relation); } $modelClass = $this->dataClass; @@ -980,7 +980,7 @@ protected function joinHasManyRelation( // Add join clause to the component's ancestry classes so that the search filter could search on // its ancestor fields. $ancestry = ClassInfo::ancestry($foreignClass, true); - $ancestry = array_reverse($ancestry); + $ancestry = array_reverse($ancestry ?: []); foreach ($ancestry as $ancestor) { $ancestorTable = $schema->tableName($ancestor); if ($ancestorTable !== $foreignTable) { @@ -1041,7 +1041,7 @@ protected function joinHasOneRelation( // its ancestor fields. $ancestry = ClassInfo::ancestry($foreignClass, true); if (!empty($ancestry)) { - $ancestry = array_reverse($ancestry); + $ancestry = array_reverse($ancestry ?: []); foreach ($ancestry as $ancestor) { $ancestorTable = $schema->tableName($ancestor); if ($ancestorTable !== $foreignBaseTable) { @@ -1080,7 +1080,7 @@ protected function joinManyManyRelationship( ) { $schema = DataObject::getSchema(); - if (class_exists($relationClassOrTable)) { + if (class_exists((string) $relationClassOrTable)) { // class is provided $relationTable = $schema->tableName($relationClassOrTable); $relationTableUpdated = $this->getJoinTableName($relationClassOrTable, $relationTable); @@ -1118,7 +1118,7 @@ protected function joinManyManyRelationship( // Add join clause to the component's ancestry classes so that the search filter could search on // its ancestor fields. $ancestry = ClassInfo::ancestry($componentClass, true); - $ancestry = array_reverse($ancestry); + $ancestry = array_reverse($ancestry ?: []); foreach ($ancestry as $ancestor) { $ancestorTable = $schema->tableName($ancestor); if ($ancestorTable !== $componentBaseTable) { @@ -1163,7 +1163,7 @@ public function selectFromTable($table, $fields) { $fieldExpressions = array_map(function ($item) use ($table) { return Convert::symbol2sql("{$table}.{$item}"); - }, $fields); + }, $fields ?: []); $this->query->setSelect($fieldExpressions); @@ -1181,7 +1181,7 @@ public function addSelectFromTable($table, $fields) { $fieldExpressions = array_map(function ($item) use ($table) { return Convert::symbol2sql("{$table}.{$item}"); - }, $fields); + }, $fields ?: []); $this->query->addSelect($fieldExpressions); @@ -1340,11 +1340,11 @@ public function pushQueryManipulator(DataQueryManipulator $manipulator) private function validateColumnField($field, SQLSelect $query) { // standard column - nothing to process here - if (strpos($field, '.') === false) { + if (strpos((string) $field, '.') === false) { return false; } - $fieldData = explode('.', $field); + $fieldData = explode('.', (string) $field); $tablePrefix = str_replace('"', '', $fieldData[0]); // check if related table is available diff --git a/src/ORM/DataQuery_SubGroup.php b/src/ORM/DataQuery_SubGroup.php index 46d21124f97..225cfb9f410 100644 --- a/src/ORM/DataQuery_SubGroup.php +++ b/src/ORM/DataQuery_SubGroup.php @@ -61,6 +61,6 @@ public function conditionSQL(&$parameters) // Allow database to manage joining of conditions $sql = DB::get_conn()->getQueryBuilder()->buildWhereFragment($this->whereQuery, $parameters); - return preg_replace('/^\s*WHERE\s*/i', '', $sql); + return preg_replace('/^\s*WHERE\s*/i', '', $sql ?: ''); } } diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index 0f4e60ae8bd..541e06b0d77 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -95,7 +95,7 @@ public function groupedDataClasses() $allClasses = get_declared_classes(); $rootClasses = []; foreach ($allClasses as $class) { - if (get_parent_class($class) == DataObject::class) { + if (get_parent_class($class ?: '') == DataObject::class) { $rootClasses[$class] = []; } } @@ -104,7 +104,7 @@ public function groupedDataClasses() foreach ($allClasses as $class) { if (!isset($rootClasses[$class]) && is_subclass_of($class, DataObject::class)) { foreach ($rootClasses as $rootClass => $dummy) { - if (is_subclass_of($class, $rootClass)) { + if (is_subclass_of($class, (string) $rootClass)) { $rootClasses[$rootClass][] = $class; break; } @@ -135,7 +135,7 @@ public function build() // If this code is being run outside of a dev/build or without a ?flush query string param, // the class manifest hasn't been flushed, so do it here $request = $this->getRequest(); - if (!array_key_exists('flush', $request->getVars()) && strpos($request->getURL(), 'dev/build') !== 0) { + if (!array_key_exists('flush', $request->getVars() ?: []) && strpos((string) $request->getURL(), 'dev/build') !== 0) { ClassLoader::inst()->getManifest()->regenerate(false); } @@ -209,10 +209,10 @@ public static function lastBuilt() $file = TEMP_PATH . DIRECTORY_SEPARATOR . 'database-last-generated-' - . str_replace(['\\', '/', ':'], '.', Director::baseFolder()); + . str_replace(['\\', '/', ':'], '.', Director::baseFolder() ?: ''); - if (file_exists($file)) { - return filemtime($file); + if (file_exists((string) $file)) { + return filemtime((string) $file); } return null; } @@ -273,7 +273,7 @@ public function doBuild($quiet = false, $populate = true, $testMode = false) // Check to ensure that the re-instated SS_DATABASE_SUFFIX functionality won't unexpectedly // rename the database. To be removed for SS5 if ($suffix = Environment::getEnv('SS_DATABASE_SUFFIX')) { - $previousName = preg_replace("/{$suffix}$/", '', $database); + $previousName = preg_replace("/{$suffix}$/", '', $database ?: ''); if (!isset($_GET['force_suffix_rename']) && DB::get_conn()->databaseExists($previousName)) { throw new DatabaseException( @@ -366,7 +366,7 @@ public function doBuild($quiet = false, $populate = true, $testMode = false) foreach ($dataClasses as $dataClass) { // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness // Test_ indicates that it's the data class is part of testing system - if (strpos($dataClass, 'Test_') === false && class_exists($dataClass)) { + if (strpos((string) $dataClass, 'Test_') === false && class_exists((string) $dataClass)) { if (!$quiet) { if (Director::is_cli()) { echo " * $dataClass\n"; @@ -387,7 +387,7 @@ public function doBuild($quiet = false, $populate = true, $testMode = false) touch(TEMP_PATH . DIRECTORY_SEPARATOR . 'database-last-generated-' - . str_replace(['\\', '/', ':'], '.', Director::baseFolder())); + . str_replace(['\\', '/', ':'], '.', Director::baseFolder() ?: '')); if (isset($_REQUEST['from_installer'])) { echo "OK"; @@ -443,12 +443,12 @@ protected function updateLegacyClassNameField($dataClass, $fieldName, $mapping) $currentClassNameList = DB::query("SELECT DISTINCT(\"{$fieldName}\") FROM \"{$table}\"")->column(); // Get all invalid classes for this field - $invalidClasses = array_intersect($currentClassNameList, array_keys($mapping)); + $invalidClasses = array_intersect($currentClassNameList ?: [], array_keys($mapping ?: [])); if (!$invalidClasses) { return; } - $numberClasses = count($invalidClasses); + $numberClasses = count($invalidClasses ?: []); DB::alteration_message( "Correcting obsolete {$fieldName} values for {$numberClasses} outdated types", 'obsolete' @@ -527,7 +527,7 @@ public function cleanup() { $baseClasses = []; foreach (ClassInfo::subclassesFor(DataObject::class) as $class) { - if (get_parent_class($class) == DataObject::class) { + if (get_parent_class($class ?: '') == DataObject::class) { $baseClasses[] = $class; } } @@ -559,7 +559,7 @@ public function cleanup() $subclassTable = $schema->tableName($subclass); $id = $record['ID']; if (($record['ClassName'] != $subclass) - && (!is_subclass_of($record['ClassName'], $subclass)) + && (!is_subclass_of($record['ClassName'], (string) $subclass)) && isset($recordExists[$subclass][$id]) ) { $sql = "DELETE FROM \"$subclassTable\" WHERE \"ID\" = ?"; diff --git a/src/ORM/FieldType/DBBoolean.php b/src/ORM/FieldType/DBBoolean.php index bc74dc9a08a..183c44cb646 100644 --- a/src/ORM/FieldType/DBBoolean.php +++ b/src/ORM/FieldType/DBBoolean.php @@ -85,7 +85,7 @@ public function prepValueForDB($value) return 0; } if (is_string($value)) { - switch (strtolower($value)) { + switch (strtolower((string) $value)) { case 'false': case 'f': return 0; diff --git a/src/ORM/FieldType/DBClassName.php b/src/ORM/FieldType/DBClassName.php index 183fcf9dd21..cf794c87e64 100644 --- a/src/ORM/FieldType/DBClassName.php +++ b/src/ORM/FieldType/DBClassName.php @@ -140,7 +140,7 @@ public function getEnum() $classNames = ClassInfo::subclassesFor($this->getBaseClass()); $dataobject = strtolower(DataObject::class); unset($classNames[$dataobject]); - return array_values($classNames); + return array_values($classNames ?: []); } public function setValue($value, $record = null, $markChanged = true) @@ -163,7 +163,7 @@ public function getDefault() // Allow classes to set default class $baseClass = $this->getBaseClass(); $defaultClass = Config::inst()->get($baseClass, 'default_classname'); - if ($defaultClass && class_exists($defaultClass)) { + if ($defaultClass && class_exists((string) $defaultClass)) { return $defaultClass; } diff --git a/src/ORM/FieldType/DBCurrency.php b/src/ORM/FieldType/DBCurrency.php index a6962f3e007..90b4ec66a37 100644 --- a/src/ORM/FieldType/DBCurrency.php +++ b/src/ORM/FieldType/DBCurrency.php @@ -35,7 +35,7 @@ public function __construct($name = null, $wholeSize = 9, $decimalSize = 2, $def */ public function Nice() { - $val = $this->config()->currency_symbol . number_format(abs($this->value), 2); + $val = $this->config()->currency_symbol . number_format((float) abs($this->value ?: 0.0), 2); if ($this->value < 0) { return "($val)"; } @@ -48,7 +48,7 @@ public function Nice() */ public function Whole() { - $val = $this->config()->currency_symbol . number_format(abs($this->value), 0); + $val = $this->config()->currency_symbol . number_format((float) abs($this->value ?: 0.0), 0); if ($this->value < 0) { return "($val)"; } @@ -60,7 +60,7 @@ public function setValue($value, $record = null, $markChanged = true) $matches = null; if (is_numeric($value)) { $this->value = $value; - } elseif (preg_match('/-?\$?[0-9,]+(.[0-9]+)?([Ee][0-9]+)?/', $value, $matches)) { + } elseif (preg_match('/-?\$?[0-9,]+(.[0-9]+)?([Ee][0-9]+)?/', (string) $value, $matches)) { $this->value = str_replace(['$', ',', $this->config()->currency_symbol], '', $matches[0]); } else { $this->value = 0; diff --git a/src/ORM/FieldType/DBDate.php b/src/ORM/FieldType/DBDate.php index 3fe6c696e92..2f2cbef9ae6 100644 --- a/src/ORM/FieldType/DBDate.php +++ b/src/ORM/FieldType/DBDate.php @@ -76,7 +76,7 @@ protected function parseDate($value) if (is_null($value)) { return null; } - $source = strtotime($value); // convert string to timestamp + $source = strtotime((string) $value); // convert string to timestamp } if ($value === false) { return false; @@ -275,8 +275,8 @@ public function Format($format) } // Replace {o} with ordinal representation of day of the month - if (strpos($format, '{o}') !== false) { - $format = str_replace('{o}', "'{$this->DayOfMonth(true)}'", $format); + if (strpos((string) $format, '{o}') !== false) { + $format = str_replace('{o}', "'{$this->DayOfMonth(true)}'", $format ?: ''); } $formatter = $this->getCustomFormatter($locale, $format); @@ -291,7 +291,7 @@ public function Format($format) public function getTimestamp() { if ($this->value) { - return strtotime($this->value); + return strtotime((string) $this->value); } return 0; } @@ -525,7 +525,7 @@ public function requireField() */ public function InPast() { - return strtotime($this->value) < DBDatetime::now()->getTimestamp(); + return strtotime((string) $this->value) < DBDatetime::now()->getTimestamp(); } /** @@ -534,7 +534,7 @@ public function InPast() */ public function InFuture() { - return strtotime($this->value) > DBDatetime::now()->getTimestamp(); + return strtotime((string) $this->value) > DBDatetime::now()->getTimestamp(); } /** @@ -561,7 +561,7 @@ public function IsToday() */ public function modify(string $adjustment): self { - $modifiedTime = strtotime($adjustment, $this->getTimestamp()); + $modifiedTime = strtotime((string) $adjustment, $this->getTimestamp()); return $this->setValue($modifiedTime); } @@ -572,7 +572,7 @@ public function modify(string $adjustment): self */ public function URLDate() { - return rawurlencode($this->Format(self::ISO_DATE, self::ISO_LOCALE)); + return rawurlencode((string) $this->Format(self::ISO_DATE, self::ISO_LOCALE)); } public function scaffoldFormField($title = null, $params = null) @@ -598,7 +598,7 @@ protected function fixInputDate($value) return null; } // Validate date - if (!checkdate($month, $day, $year)) { + if (!checkdate((int) $month, (int) $day, (int) $year)) { throw new InvalidArgumentException( "Invalid date: '$value'. Use " . self::ISO_DATE . " to prevent this error." ); @@ -619,7 +619,7 @@ protected function explodeDateString($value) // split on known delimiters (. / -) if (!preg_match( '#^(?\\d+)[-/\\.](?\\d+)[-/\\.](?\\d+)(?