' . esc(print_r($value, true)) . '' : esc($value); } - foreach ($request->getHeaders() as $header => $value) + foreach ($request->getHeaders() as $value) { if (empty($value)) { @@ -415,6 +415,7 @@ public function respond() return; } + // @codeCoverageIgnoreStart $request = Services::request(); // If the request contains '?debugbar then we're @@ -459,6 +460,7 @@ public function respond() http_response_code(404); exit; // Exit here is needed to avoid load the index page } + // @codeCoverageIgnoreEnd } /** diff --git a/system/Debug/Toolbar/Collectors/Config.php b/system/Debug/Toolbar/Collectors/Config.php index 1ae809cdfbcb..82ef63475af6 100644 --- a/system/Debug/Toolbar/Collectors/Config.php +++ b/system/Debug/Toolbar/Collectors/Config.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; +use CodeIgniter\CodeIgniter; use Config\App; use Config\Services; -use CodeIgniter\CodeIgniter; /** * Debug toolbar configuration diff --git a/system/Debug/Toolbar/Collectors/Events.php b/system/Debug/Toolbar/Collectors/Events.php index 285d87d8e709..41c9f008c87d 100644 --- a/system/Debug/Toolbar/Collectors/Events.php +++ b/system/Debug/Toolbar/Collectors/Events.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; use CodeIgniter\View\RendererInterface; +use Config\Services; /** * Views collector @@ -111,7 +111,7 @@ protected function formatTimelineData(): array $rows = $this->viewer->getPerformanceData(); - foreach ($rows as $name => $info) + foreach ($rows as $info) { $data[] = [ 'name' => 'View: ' . $info['view'], diff --git a/system/Debug/Toolbar/Collectors/Views.php b/system/Debug/Toolbar/Collectors/Views.php index 8bcec5698340..bf0c1108cb07 100644 --- a/system/Debug/Toolbar/Collectors/Views.php +++ b/system/Debug/Toolbar/Collectors/Views.php @@ -39,8 +39,8 @@ namespace CodeIgniter\Debug\Toolbar\Collectors; -use Config\Services; use CodeIgniter\View\RendererInterface; +use Config\Services; /** * Views collector @@ -126,7 +126,7 @@ protected function formatTimelineData(): array $rows = $this->viewer->getPerformanceData(); - foreach ($rows as $name => $info) + foreach ($rows as $info) { $data[] = [ 'name' => 'View: ' . $info['view'], diff --git a/system/Debug/Toolbar/Views/toolbarloader.js.php b/system/Debug/Toolbar/Views/toolbarloader.js.php index 4ecdeeb16968..af6933810063 100644 --- a/system/Debug/Toolbar/Views/toolbarloader.js.php +++ b/system/Debug/Toolbar/Views/toolbarloader.js.php @@ -75,9 +75,11 @@ function newXHR() { var debugbarTime = realXHR.getResponseHeader('Debugbar-Time'); if (debugbarTime) { var h2 = document.querySelector('#ci-history > h2'); - h2.innerHTML = 'History You have new debug data. '; - var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); - badge.className += ' active'; + if(h2) { + h2.innerHTML = 'History You have new debug data. '; + var badge = document.querySelector('a[data-tab="ci-history"] > span > .badge'); + badge.className += ' active'; + } } } }, false); diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 1f047f6e1ae4..4c2d3337ea66 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -38,10 +38,8 @@ namespace CodeIgniter\Encryption; -use Config\Encryption as EncryptionConfig; -use CodeIgniter\Encryption\Exceptions\EncryptionException; use CodeIgniter\Config\BaseConfig; -use Config\Services; +use CodeIgniter\Encryption\Exceptions\EncryptionException; /** * CodeIgniter Encryption Manager diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index 72b6023656a1..50622d1f0d9f 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -64,7 +64,7 @@ class OpenSSLHandler extends BaseHandler * * @param BaseConfig $config * - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function __construct(BaseConfig $config = null) { @@ -77,7 +77,7 @@ public function __construct(BaseConfig $config = null) * @param string $data Input data * @param array $params Over-ridden parameters, specifically the key * @return string - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function encrypt($data, $params = null) { @@ -114,9 +114,8 @@ public function encrypt($data, $params = null) $result = $iv . $data; $hmacKey = \hash_hmac($this->digest, $result, $secret, true); - $result = $hmacKey . $result; - return $result; + return $hmacKey . $result; } // -------------------------------------------------------------------- @@ -127,7 +126,7 @@ public function encrypt($data, $params = null) * @param string $data Encrypted data * @param array $params Over-ridden parameters, specifically the key * @return string - * @throws \CodeIgniter\Encryption\EncryptionException + * @throws \CodeIgniter\Encryption\Exceptions\EncryptionException */ public function decrypt($data, $params = null) { @@ -145,7 +144,7 @@ public function decrypt($data, $params = null) } if (empty($this->key)) { - throw EncryptionException::forStarterKeyNeeded(); + throw EncryptionException::forNeedsStarterKey(); } // derive a secret key diff --git a/system/Entity.php b/system/Entity.php index 94153d0b9a15..1ccd6d298bde 100644 --- a/system/Entity.php +++ b/system/Entity.php @@ -39,14 +39,13 @@ namespace CodeIgniter; -use CodeIgniter\Exceptions\EntityException; -use CodeIgniter\I18n\Time; use CodeIgniter\Exceptions\CastException; +use CodeIgniter\I18n\Time; /** * Entity encapsulation, for use with CodeIgniter\Model */ -class Entity +class Entity implements \JsonSerializable { /** * Maps names used in sets and gets against unique @@ -165,7 +164,7 @@ public function toArray(bool $onlyChanged = false, bool $cast = true): array // allow our magic methods a chance to do their thing. foreach ($this->attributes as $key => $value) { - if (substr($key, 0, 1) === '_') + if (strpos($key, '_') === 0) { continue; } @@ -353,7 +352,7 @@ public function __set(string $key, $value = null) if (array_key_exists($key, $this->casts)) { - $isNullable = substr($this->casts[$key], 0, 1) === '?'; + $isNullable = strpos($this->casts[$key], '?') === 0; $castTo = $isNullable ? substr($this->casts[$key], 1) : $this->casts[$key]; } @@ -530,7 +529,7 @@ protected function mutateDate($value) protected function castAs($value, string $type) { - if (substr($type, 0, 1) === '?') + if (strpos($type, '?') === 0) { if ($value === null) { @@ -570,17 +569,15 @@ protected function castAs($value, string $type) $value = (array)$value; break; case 'json': - $value = $this->castAsJson($value, false); + $value = $this->castAsJson($value); break; case 'json-array': $value = $this->castAsJson($value, true); break; case 'datetime': - return new \DateTime($value); - break; + return $this->mutateDate($value); case 'timestamp': return strtotime($value); - break; } return $value; @@ -614,4 +611,15 @@ private function castAsJson($value, bool $asArray = false) } return $tmp; } + + /** + * Support for json_encode() + * + * @return array|mixed + * @throws \Exception + */ + public function jsonSerialize() + { + return $this->toArray(); + } } diff --git a/system/Exceptions/CastException.php b/system/Exceptions/CastException.php index a5bb06594e2e..ddcb7178e844 100644 --- a/system/Exceptions/CastException.php +++ b/system/Exceptions/CastException.php @@ -19,22 +19,17 @@ public static function forInvalidJsonFormatException(int $error) switch($error) { case JSON_ERROR_DEPTH: - throw new static(lang('Cast.jsonErrorDepth')); - break; + return new static(lang('Cast.jsonErrorDepth')); case JSON_ERROR_STATE_MISMATCH: - throw new static(lang('Cast.jsonErrorStateMismatch')); - break; + return new static(lang('Cast.jsonErrorStateMismatch')); case JSON_ERROR_CTRL_CHAR: - throw new static(lang('Cast.jsonErrorCtrlChar')); - break; + return new static(lang('Cast.jsonErrorCtrlChar')); case JSON_ERROR_SYNTAX: - throw new static(lang('Cast.jsonErrorSyntax')); - break; + return new static(lang('Cast.jsonErrorSyntax')); case JSON_ERROR_UTF8: - throw new static(lang('Cast.jsonErrorUtf8')); - break; + return new static(lang('Cast.jsonErrorUtf8')); default: - throw new static(lang('Cast.jsonErrorUnknown')); + return new static(lang('Cast.jsonErrorUnknown')); } } diff --git a/system/Exceptions/ConfigException.php b/system/Exceptions/ConfigException.php index dc36377e59fa..964c52127579 100644 --- a/system/Exceptions/ConfigException.php +++ b/system/Exceptions/ConfigException.php @@ -16,6 +16,6 @@ class ConfigException extends CriticalError public static function forDisabledMigrations() { - throw new static(lang('Migrations.disabled')); + return new static(lang('Migrations.disabled')); } } diff --git a/system/Files/File.php b/system/Files/File.php index 8b5fc2ed2930..1f5979588eba 100644 --- a/system/Files/File.php +++ b/system/Files/File.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Files; -use SplFileInfo; use CodeIgniter\Files\Exceptions\FileException; use CodeIgniter\Files\Exceptions\FileNotFoundException; +use SplFileInfo; /** * Wrapper for PHP's built-in SplFileInfo, with goodies. diff --git a/system/Filters/Filters.php b/system/Filters/Filters.php index 853e135a5b53..451a2dabc787 100644 --- a/system/Filters/Filters.php +++ b/system/Filters/Filters.php @@ -39,9 +39,9 @@ namespace CodeIgniter\Filters; use CodeIgniter\Config\BaseConfig; +use CodeIgniter\Filters\Exceptions\FilterException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; -use CodeIgniter\Filters\Exceptions\FilterException; /** * Filters @@ -150,47 +150,59 @@ public function run(string $uri, string $position = 'before') throw FilterException::forNoAlias($alias); } - $class = new $this->config->aliases[$alias](); - - if (! $class instanceof FilterInterface) + if (is_array($this->config->aliases[$alias])) { - throw FilterException::forIncorrectInterface(get_class($class)); + $classNames = $this->config->aliases[$alias]; + } + else + { + $classNames = [$this->config->aliases[$alias]]; } - if ($position === 'before') + foreach ($classNames as $className) { - $result = $class->before($this->request, $this->arguments[$alias] ?? null); + $class = new $className(); - if ($result instanceof RequestInterface) + if (! $class instanceof FilterInterface) { - $this->request = $result; - continue; + throw FilterException::forIncorrectInterface(get_class($class)); } - // If the response object was sent back, - // then send it and quit. - if ($result instanceof ResponseInterface) + if ($position === 'before') { - // short circuit - bypass any other filters - return $result; - } + $result = $class->before($this->request, $this->arguments[$alias] ?? null); - // Ignore an empty result - if (empty($result)) - { - continue; - } + if ($result instanceof RequestInterface) + { + $this->request = $result; + continue; + } - return $result; - } - elseif ($position === 'after') - { - $result = $class->after($this->request, $this->response); + // If the response object was sent back, + // then send it and quit. + if ($result instanceof ResponseInterface) + { + // short circuit - bypass any other filters + return $result; + } + + // Ignore an empty result + if (empty($result)) + { + continue; + } - if ($result instanceof ResponseInterface) + return $result; + } + elseif ($position === 'after') { - $this->response = $result; - continue; + $result = $class->after($this->request, $this->response); + + if ($result instanceof ResponseInterface) + { + $this->response = $result; + continue; + } } } } diff --git a/system/Filters/Honeypot.php b/system/Filters/Honeypot.php index aea08c3c4a1d..a5807e42bcdd 100644 --- a/system/Filters/Honeypot.php +++ b/system/Filters/Honeypot.php @@ -38,10 +38,10 @@ namespace CodeIgniter\Filters; +use CodeIgniter\Honeypot\Exceptions\HoneypotException; use CodeIgniter\HTTP\RequestInterface; use CodeIgniter\HTTP\ResponseInterface; use Config\Services; -use CodeIgniter\Honeypot\Exceptions\HoneypotException; /** * Honeypot filter diff --git a/system/Format/JSONFormatter.php b/system/Format/JSONFormatter.php index 50caf3930f9d..823f3ca240e6 100644 --- a/system/Format/JSONFormatter.php +++ b/system/Format/JSONFormatter.php @@ -56,13 +56,13 @@ class JSONFormatter implements FormatterInterface */ public function format($data) { - $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES; + $options = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR; $options = ENVIRONMENT === 'production' ? $options : $options | JSON_PRETTY_PRINT; $result = json_encode($data, $options, 512); - if (json_last_error() !== JSON_ERROR_NONE) + if ( ! in_array(json_last_error(), [JSON_ERROR_NONE, JSON_ERROR_RECURSION])) { throw FormatException::forInvalidJSON(json_last_error_msg()); } diff --git a/system/HTTP/CURLRequest.php b/system/HTTP/CURLRequest.php index c5506accbfeb..d1a8e94428d1 100644 --- a/system/HTTP/CURLRequest.php +++ b/system/HTTP/CURLRequest.php @@ -536,9 +536,7 @@ protected function applyMethod(string $method, array $curl_options): array // Have content? if ($size === null || $size > 0) { - $curl_options = $this->applyBody($curl_options); - - return $curl_options; + return $this->applyBody($curl_options); } if ($method === 'PUT' || $method === 'POST') diff --git a/system/HTTP/ContentSecurityPolicy.php b/system/HTTP/ContentSecurityPolicy.php index 713312f9e5bf..910f700287cb 100644 --- a/system/HTTP/ContentSecurityPolicy.php +++ b/system/HTTP/ContentSecurityPolicy.php @@ -136,7 +136,7 @@ class ContentSecurityPolicy * * @var string */ - protected $reportURI = null; + protected $reportURI; /** * Used for security enforcement diff --git a/system/HTTP/IncomingRequest.php b/system/HTTP/IncomingRequest.php index 0b363cdb5502..b7c04dfba89a 100755 --- a/system/HTTP/IncomingRequest.php +++ b/system/HTTP/IncomingRequest.php @@ -427,7 +427,7 @@ public function getPostGet($index = null, $filter = null, $flags = null) // Use $_POST directly here, since filter_has_var only // checks the initial POST data, not anything that might // have been added since. - return isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : $this->getGet($index, $filter, $flags); + return isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : (isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : $this->getPost()); } //-------------------------------------------------------------------- @@ -446,7 +446,7 @@ public function getGetPost($index = null, $filter = null, $flags = null) // Use $_GET directly here, since filter_has_var only // checks the initial GET data, not anything that might // have been added since. - return isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : $this->getPost($index, $filter, $flags); + return isset($_GET[$index]) ? $this->getGet($index, $filter, $flags) : (isset($_POST[$index]) ? $this->getPost($index, $filter, $flags) : $this->getGet()); } //-------------------------------------------------------------------- @@ -616,7 +616,6 @@ protected function detectURI(string $protocol, string $baseURL) $this->uri->setScheme(parse_url($baseURL, PHP_URL_SCHEME)); $this->uri->setHost(parse_url($baseURL, PHP_URL_HOST)); $this->uri->setPort(parse_url($baseURL, PHP_URL_PORT)); - $this->uri->resolveRelativeURI(parse_url($baseURL, PHP_URL_PATH)); // Ensure we have any query vars $this->uri->setQuery($_SERVER['QUERY_STRING'] ?? ''); @@ -722,7 +721,7 @@ protected function parseRequestURI(): string $query = $parts['query'] ?? ''; $uri = $parts['path'] ?? ''; - if (isset($_SERVER['SCRIPT_NAME'][0])) + if (isset($_SERVER['SCRIPT_NAME'][0]) && pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_EXTENSION) === 'php') { // strip the script name from the beginning of the URI if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) diff --git a/system/HTTP/Message.php b/system/HTTP/Message.php index 1afb1d5258d5..6d7a5cce8dbc 100644 --- a/system/HTTP/Message.php +++ b/system/HTTP/Message.php @@ -316,7 +316,9 @@ public function appendHeader(string $name, string $value) { $orig_name = $this->getHeaderName($name); - $this->headers[$orig_name]->appendValue($value); + array_key_exists($orig_name, $this->headers) + ? $this->headers[$orig_name]->appendValue($value) + : $this->setHeader($name, $value); return $this; } diff --git a/system/HTTP/Negotiate.php b/system/HTTP/Negotiate.php index 6ac2d06b7bc4..74aa4706227d 100644 --- a/system/HTTP/Negotiate.php +++ b/system/HTTP/Negotiate.php @@ -179,7 +179,7 @@ public function encoding(array $supported = []): string */ public function language(array $supported): string { - return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language')); + return $this->getBestMatch($supported, $this->request->getHeaderLine('accept-language'), false, false, true); } //-------------------------------------------------------------------- @@ -198,10 +198,11 @@ public function language(array $supported): string * @param boolean $enforceTypes If TRUE, will compare media types and sub-types. * @param boolean $strictMatch If TRUE, will return empty string on no match. * If FALSE, will return the first supported element. + * @param boolean $matchLocales If TRUE, will match locale sub-types to a broad type (fr-FR = fr) * * @return string Best match */ - protected function getBestMatch(array $supported, string $header = null, bool $enforceTypes = false, bool $strictMatch = false): string + protected function getBestMatch(array $supported, string $header = null, bool $enforceTypes = false, bool $strictMatch = false, bool $matchLocales = false): string { if (empty($supported)) { @@ -232,7 +233,7 @@ protected function getBestMatch(array $supported, string $header = null, bool $e // If an acceptable value is supported, return it foreach ($supported as $available) { - if ($this->match($accept, $available, $enforceTypes)) + if ($this->match($accept, $available, $enforceTypes, $matchLocales)) { return $available; } @@ -337,12 +338,14 @@ public function parseHeader(string $header): array /** * Match-maker * - * @param array $acceptable - * @param string $supported - * @param boolean $enforceTypes + * @param array $acceptable + * @param string $supported + * @param boolean $enforceTypes + * @param boolean $matchLocales + * * @return boolean */ - protected function match(array $acceptable, string $supported, bool $enforceTypes = false): bool + protected function match(array $acceptable, string $supported, bool $enforceTypes = false, $matchLocales = false): bool { $supported = $this->parseHeader($supported); if (is_array($supported) && count($supported) === 1) @@ -363,6 +366,12 @@ protected function match(array $acceptable, string $supported, bool $enforceType return $this->matchTypes($acceptable, $supported); } + // Do we need to match locales against broader locales? + if ($matchLocales) + { + return $this->matchLocales($acceptable, $supported); + } + return false; } @@ -409,8 +418,14 @@ protected function matchParameters(array $acceptable, array $supported): bool */ public function matchTypes(array $acceptable, array $supported): bool { - list($aType, $aSubType) = explode('/', $acceptable['value']); - list($sType, $sSubType) = explode('/', $supported['value']); + [ + $aType, + $aSubType, + ] = explode('/', $acceptable['value']); + [ + $sType, + $sSubType, + ] = explode('/', $supported['value']); // If the types don't match, we're done. if ($aType !== $sType) @@ -429,4 +444,25 @@ public function matchTypes(array $acceptable, array $supported): bool } //-------------------------------------------------------------------- + + /** + * Will match locales against their broader pairs, so that fr-FR would + * match a supported localed of fr + * + * @param array $acceptable + * @param array $supported + * + * @return boolean + */ + public function matchLocales(array $acceptable, array $supported): bool + { + $aBroad = mb_strpos($acceptable['value'], '-') > 0 + ? mb_substr($acceptable['value'], 0, mb_strpos($acceptable['value'], '-')) + : $acceptable['value']; + $sBroad = mb_strpos($supported['value'], '-') > 0 + ? mb_substr($supported['value'], 0, mb_strpos($supported['value'], '-')) + : $supported['value']; + + return strtolower($aBroad) === strtolower($sBroad); + } } diff --git a/system/HTTP/Response.php b/system/HTTP/Response.php index a6d3c5ec3cd5..0d8320bb72a9 100644 --- a/system/HTTP/Response.php +++ b/system/HTTP/Response.php @@ -40,10 +40,10 @@ namespace CodeIgniter\HTTP; -use Config\App; -use Config\Format; use CodeIgniter\HTTP\Exceptions\HTTPException; use CodeIgniter\Pager\PagerInterface; +use Config\App; +use Config\Format; /** * Representation of an outgoing, getServer-side response. @@ -986,6 +986,7 @@ public function deleteCookie(string $name = '', string $domain = '', string $pat $name = $prefix . $name; + $cookieHasFlag = false; foreach ($this->cookies as &$cookie) { if ($cookie['name'] === $name) @@ -1000,11 +1001,16 @@ public function deleteCookie(string $name = '', string $domain = '', string $pat } $cookie['value'] = ''; $cookie['expires'] = ''; - + $cookieHasFlag = true; break; } } + if (! $cookieHasFlag) + { + $this->setCookie($name, '', '', $domain, $path, $prefix); + } + return $this; } diff --git a/system/HTTP/URI.php b/system/HTTP/URI.php index 001b0a96de68..09aa3cdc2eeb 100644 --- a/system/HTTP/URI.php +++ b/system/HTTP/URI.php @@ -566,7 +566,7 @@ public static function createURIString(string $scheme = null, string $authority $uri .= $authority; } - if ($path) + if ($path !== '') { $uri .= substr($uri, -1, 1) !== '/' ? '/' . ltrim($path, '/') : $path; } @@ -597,7 +597,12 @@ public function setAuthority(string $str) { $parts = parse_url($str); - if (empty($parts['host']) && ! empty($parts['path'])) + if (! isset($parts['path'])) + { + $parts['path'] = $this->getPath(); + } + + if (empty($parts['host']) && $parts['path'] !== '') { $parts['host'] = $parts['path']; unset($parts['path']); @@ -705,7 +710,9 @@ public function setPath(string $path) { $this->path = $this->filterPath($path); - $this->segments = explode('/', $this->path); + $tempPath = trim($this->path, '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); return $this; } @@ -721,7 +728,9 @@ public function refreshPath() { $this->path = $this->filterPath(implode('/', $this->segments)); - $this->segments = explode('/', $this->path); + $tempPath = trim($this->path, '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); return $this; } @@ -913,7 +922,7 @@ protected function applyParts(array $parts) { $this->user = $parts['user']; } - if (! empty($parts['path'])) + if (isset($parts['path']) && $parts['path'] !== '') { $this->path = $this->filterPath($parts['path']); } @@ -953,9 +962,11 @@ protected function applyParts(array $parts) } // Populate our segments array - if (! empty($parts['path'])) + if (isset($parts['path']) && $parts['path'] !== '') { - $this->segments = explode('/', trim($parts['path'], '/')); + $tempPath = trim($parts['path'], '/'); + + $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath); } } @@ -1048,14 +1059,14 @@ public function resolveRelativeURI(string $uri) */ protected function mergePaths(URI $base, URI $reference): string { - if (! empty($base->getAuthority()) && empty($base->getPath())) + if (! empty($base->getAuthority()) && $base->getPath() === '') { return '/' . ltrim($reference->getPath(), '/ '); } $path = explode('/', $base->getPath()); - if (empty($path[0])) + if ($path[0] === '') { unset($path[0]); } @@ -1082,7 +1093,7 @@ protected function mergePaths(URI $base, URI $reference): string */ public function removeDotSegments(string $path): string { - if (empty($path) || $path === '/') + if ($path === '' || $path === '/') { return $path; } @@ -1091,7 +1102,7 @@ public function removeDotSegments(string $path): string $input = explode('/', $path); - if (empty($input[0])) + if ($input[0] === '') { unset($input[0]); $input = array_values($input); diff --git a/system/HTTP/UserAgent.php b/system/HTTP/UserAgent.php index 6e5b5965a8a7..c6873fd5b45b 100644 --- a/system/HTTP/UserAgent.php +++ b/system/HTTP/UserAgent.php @@ -51,7 +51,7 @@ class UserAgent * * @var string */ - protected $agent = null; + protected $agent; /** * Flag for if the user-agent belongs to a browser diff --git a/system/Helpers/array_helper.php b/system/Helpers/array_helper.php index 9c5d8f6e5120..c114df9fa97c 100644 --- a/system/Helpers/array_helper.php +++ b/system/Helpers/array_helper.php @@ -79,7 +79,7 @@ function _array_search_dot(array $indexes, array $array) ? array_shift($indexes) : null; - if (empty($currentIndex) || (! isset($array[$currentIndex]) && $currentIndex !== '*')) + if ((empty($currentIndex) && intval($currentIndex) !== 0) || (! isset($array[$currentIndex]) && $currentIndex !== '*')) { return null; } @@ -90,7 +90,7 @@ function _array_search_dot(array $indexes, array $array) // If $array has more than 1 item, we have to loop over each. if (is_array($array)) { - foreach ($array as $key => $value) + foreach ($array as $value) { $answer = _array_search_dot($indexes, $value); diff --git a/system/Helpers/cookie_helper.php b/system/Helpers/cookie_helper.php index d85f644d0994..50ab39272a2d 100755 --- a/system/Helpers/cookie_helper.php +++ b/system/Helpers/cookie_helper.php @@ -96,9 +96,8 @@ function get_cookie($index, bool $xssClean = false) $request = \Config\Services::request(); $filter = true === $xssClean ? FILTER_SANITIZE_STRING : null; - $cookie = $request->getCookie($prefix . $index, $filter); - return $cookie; + return $request->getCookie($prefix . $index, $filter); } } diff --git a/system/Helpers/date_helper.php b/system/Helpers/date_helper.php index 6cf362ad49b5..1951d2535350 100644 --- a/system/Helpers/date_helper.php +++ b/system/Helpers/date_helper.php @@ -96,8 +96,7 @@ function timezone_select(string $class = '', string $default = '', int $what = \ $selected = ($timezone === $default) ? 'selected' : ''; $buffer .= "" . PHP_EOL; } - $buffer .= '' . PHP_EOL; - return $buffer; + return $buffer . ('' . PHP_EOL); } } diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 1ead233b47ff..2556e86ade2a 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -209,45 +209,54 @@ function delete_files(string $path, bool $del_dir = false, bool $htdocs = false, * Reads the specified directory and builds an array containing the filenames. * Any sub-folders contained within the specified path are read as well. * - * @param string $source_dir Path to source - * @param boolean $include_path Whether to include the path as part of the filename - * @param boolean $recursion Internal variable to determine recursion status - do not use in calls + * @param string $source_dir Path to source + * @param boolean|null $include_path Whether to include the path as part of the filename; false for no path, null for a relative path, true for full path + * @param boolean $hidden Whether to include hidden files (files beginning with a period) * * @return array */ - function get_filenames(string $source_dir, bool $include_path = false, bool $recursion = false): array + function get_filenames(string $source_dir, ?bool $include_path = false, bool $hidden = false): array { - static $fileData = []; + $files = []; + + $source_dir = realpath($source_dir) ?: $source_dir; + $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; try { - $fp = opendir($source_dir); - // reset the array and make sure $source_dir has a trailing slash on the initial call - if ($recursion === false) + foreach (new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST + ) as $name => $object) { - $fileData = []; - $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - } + $basename = pathinfo($name, PATHINFO_BASENAME); - while (false !== ($file = readdir($fp))) - { - if (is_dir($source_dir . $file) && $file[0] !== '.') + if (! $hidden && $basename[0] === '.') { - get_filenames($source_dir . $file . DIRECTORY_SEPARATOR, $include_path, true); + continue; } - elseif ($file[0] !== '.') + elseif ($include_path === false) { - $fileData[] = ($include_path === true) ? $source_dir . $file : $file; + $files[] = $basename; + } + elseif (is_null($include_path)) + { + $files[] = str_replace($source_dir, '', $name); + } + else + { + $files[] = $name; } } - - closedir($fp); - return $fileData; } - catch (\Exception $fe) + catch (\Throwable $e) { return []; } + + sort($files); + + return $files; } } diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 82c809ce3fa4..e505e65b70b8 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -69,6 +69,12 @@ function form_open(string $action = '', $attributes = [], array $hidden = []): s } // If an action is not a full URL then turn it into one elseif (strpos($action, '://') === false) { + // If an action has {locale} + if (strpos($action, '{locale}') !== false) + { + $action = str_replace('{locale}', Services::request()->getLocale(), $action); + } + $action = site_url($action); } @@ -180,7 +186,7 @@ function form_hidden($name, $value = '', bool $recursing = false): string if (! is_array($value)) { - $form .= '\n"; + $form .= '\n"; } else { @@ -408,7 +414,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): { $sel = in_array($optgroup_key, $selected) ? ' selected="selected"' : ''; $form .= '\n"; + . $optgroup_val . "\n"; } $form .= "\n"; } @@ -416,7 +422,7 @@ function form_dropdown($data = '', $options = [], $selected = [], $extra = ''): { $form .= '\n"; + . $val . "\n"; } } @@ -645,9 +651,7 @@ function form_datalist(string $name, string $value, array $options): string $out .= "