Add type hints and use class constants instead of protected properties (continued)

This commit is contained in:
Kijin Sung 2023-10-03 02:55:24 +09:00
parent b6e8d41be8
commit b373dc94c0
5 changed files with 76 additions and 67 deletions

View file

@ -13,18 +13,18 @@ class MIME
* This method returns the MIME type of a file, or false on error. * This method returns the MIME type of a file, or false on error.
* *
* @param string $filename * @param string $filename
* @return array|false * @return ?string
*/ */
public static function getContentType($filename) public static function getContentType(string $filename): ?string
{ {
$filename = rtrim($filename, '/\\'); $filename = rtrim($filename, '/\\');
if (Storage::exists($filename) && @is_file($filename) && @is_readable($filename)) if (Storage::exists($filename) && @is_file($filename) && @is_readable($filename))
{ {
if (function_exists('mime_content_type')) if (function_exists('mime_content_type'))
{ {
return @mime_content_type($filename) ?: false; return @mime_content_type($filename) ?: null;
} }
elseif (($image = @getimagesize($filename)) && $image['mime']) elseif (($image = @getimagesize($filename)) && !empty($image['mime']))
{ {
return $image['mime']; return $image['mime'];
} }
@ -35,7 +35,7 @@ class MIME
} }
else else
{ {
return false; return null;
} }
} }
@ -45,10 +45,10 @@ class MIME
* @param string $extension * @param string $extension
* @return string * @return string
*/ */
public static function getTypeByExtension($extension) public static function getTypeByExtension(string $extension): string
{ {
$extension = strtolower($extension); $extension = strtolower($extension);
return array_key_exists($extension, self::$_types) ? self::$_types[$extension][0] : self::$_default; return array_key_exists($extension, self::TYPES) ? self::TYPES[$extension][0] : self::DEFAULT_TYPE;
} }
/** /**
@ -57,41 +57,41 @@ class MIME
* @param string $filename * @param string $filename
* @return string * @return string
*/ */
public static function getTypeByFilename($filename) public static function getTypeByFilename(string $filename): string
{ {
$extension = strrchr($filename, '.'); $extension = strrchr($filename, '.');
if ($extension === false) return self::$_default; if ($extension === false) return self::DEFAULT_TYPE;
$extension = strtolower(substr($extension, 1)); $extension = strtolower(substr($extension, 1));
return array_key_exists($extension, self::$_types) ? self::$_types[$extension][0] : self::$_default; return array_key_exists($extension, self::TYPES) ? self::TYPES[$extension][0] : self::DEFAULT_TYPE;
} }
/** /**
* Get the most common extension for the given MIME type. * Get the most common extension for the given MIME type.
* *
* @param string $type * @param string $type
* @return string|false * @return ?string
*/ */
public static function getExtensionByType($type) public static function getExtensionByType(string $type): ?string
{ {
foreach (self::$_types as $extension => $mimes) foreach (self::TYPES as $extension => $mimes)
{ {
foreach ($mimes as $mime) foreach ($mimes as $mime)
{ {
if (!strncasecmp($type, $mime, strlen($type))) return $extension; if (!strncasecmp($type, $mime, strlen($type))) return $extension;
} }
} }
return false; return null;
} }
/** /**
* The default MIME type for unknown extensions. * The default MIME type for unknown extensions.
*/ */
protected static $_default = 'application/octet-stream'; protected const DEFAULT_TYPE = 'application/octet-stream';
/** /**
* The list of known MIME types. * The list of known MIME types.
*/ */
protected static $_types = array( protected const TYPES = array(
// Text-based document formats. // Text-based document formats.
'html' => ['text/html'], 'html' => ['text/html'],

View file

@ -10,8 +10,8 @@ class Pagination
/** /**
* Count style constants. * Count style constants.
*/ */
const COUNT_STYLE_NORMAL = 1; public const COUNT_STYLE_NORMAL = 1;
const COUNT_STYLE_CONTINUOUS = 2; public const COUNT_STYLE_CONTINUOUS = 2;
/** /**
* Calculate the number of pages. * Calculate the number of pages.
@ -21,11 +21,11 @@ class Pagination
* @param int $minimum (optional) * @param int $minimum (optional)
* @return int * @return int
*/ */
public static function countPages($total_items, $items_per_page, $minimum = 1) public static function countPages(int $total_items, int $items_per_page, int $minimum = 1): int
{ {
if (!$items_per_page) if (!$items_per_page)
{ {
return (int)$minimum; return $minimum;
} }
else else
{ {
@ -37,11 +37,13 @@ class Pagination
* Create HTML for pagination. * Create HTML for pagination.
* *
* @param string $base_url ($PAGE will be replaced with the page number) * @param string $base_url ($PAGE will be replaced with the page number)
* @param int $current_page
* @param int $total_pages * @param int $total_pages
* @param int $current_page (optional)
* @param int $count (optional) * @param int $count (optional)
* @param int $count_style (optional)
* @return string
*/ */
public static function createLinks($base_url, $total_pages, $current_page, $count = 10, $count_style = self::COUNT_STYLE_NORMAL) public static function createLinks(string $base_url, int $total_pages, int $current_page = 1, int $count = 10, int $count_style = self::COUNT_STYLE_NORMAL): string
{ {
// Only integers are allowed here. // Only integers are allowed here.
$current_page = (int)$current_page; $current_page = (int)$current_page;
@ -146,7 +148,7 @@ class Pagination
* @param string $content * @param string $content
* @return string * @return string
*/ */
protected static function _composeLink($target_url, $content) protected static function _composeLink(string $target_url, string $content): string
{ {
return '<a href="' . escape($target_url) . '">' . $content . '</a>'; return '<a href="' . escape($target_url) . '">' . $content . '</a>';
} }

View file

@ -41,7 +41,7 @@ class Password
* @param callable $callback * @param callable $callback
* @return void * @return void
*/ */
public static function addAlgorithm($name, $signature, $callback) public static function addAlgorithm(string $name, string $signature, callable $callback): void
{ {
self::$_algorithm_signatures[$name] = $signature; self::$_algorithm_signatures[$name] = $signature;
self::$_algorithm_callbacks[$name] = $callback; self::$_algorithm_callbacks[$name] = $callback;
@ -53,7 +53,7 @@ class Password
* @param array|string $algos * @param array|string $algos
* @return bool * @return bool
*/ */
public static function isValidAlgorithm($algos) public static function isValidAlgorithm($algos): bool
{ {
$hash_algos = hash_algos(); $hash_algos = hash_algos();
$algos = is_array($algos) ? $algos : explode(',', $algos); $algos = is_array($algos) ? $algos : explode(',', $algos);
@ -77,7 +77,7 @@ class Password
* *
* @return array * @return array
*/ */
public static function getSupportedAlgorithms() public static function getSupportedAlgorithms(): array
{ {
$retval = array(); $retval = array();
if (defined('\PASSWORD_ARGON2ID')) if (defined('\PASSWORD_ARGON2ID'))
@ -104,7 +104,7 @@ class Password
* *
* @return string * @return string
*/ */
public static function getBestSupportedAlgorithm() public static function getBestSupportedAlgorithm(): string
{ {
// Return the first algorithm in the list, except argon2id. // Return the first algorithm in the list, except argon2id.
$algos = self::getSupportedAlgorithms(); $algos = self::getSupportedAlgorithms();
@ -112,7 +112,7 @@ class Password
{ {
unset($algos['argon2id']); unset($algos['argon2id']);
} }
return key($algos); return array_first_key($algos);
} }
/** /**
@ -120,11 +120,11 @@ class Password
* *
* @return string * @return string
*/ */
public static function getDefaultAlgorithm() public static function getDefaultAlgorithm(): string
{ {
if (class_exists('\MemberModel')) if (class_exists('\MemberModel'))
{ {
$config = @\MemberModel::getInstance()->getMemberConfig(); $config = \MemberModel::getInstance()->getMemberConfig();
$algorithm = $config->password_hashing_algorithm ?? ''; $algorithm = $config->password_hashing_algorithm ?? '';
if (strval($algorithm) === '') if (strval($algorithm) === '')
{ {
@ -148,7 +148,7 @@ class Password
* *
* @return string * @return string
*/ */
public static function getBackwardCompatibleAlgorithm() public static function getBackwardCompatibleAlgorithm(): string
{ {
$algorithm = self::getDefaultAlgorithm(); $algorithm = self::getDefaultAlgorithm();
if (!in_array($algorithm, ['bcrypt', 'pbkdf2', 'sha1', 'md5'])) if (!in_array($algorithm, ['bcrypt', 'pbkdf2', 'sha1', 'md5']))
@ -170,11 +170,11 @@ class Password
* *
* @return int * @return int
*/ */
public static function getWorkFactor() public static function getWorkFactor(): int
{ {
if (class_exists('\MemberModel')) if (class_exists('\MemberModel'))
{ {
$config = @\MemberModel::getInstance()->getMemberConfig(); $config = \MemberModel::getInstance()->getMemberConfig();
$work_factor = $config->password_hashing_work_factor ?? 10; $work_factor = $config->password_hashing_work_factor ?? 10;
if (!$work_factor || $work_factor < 4 || $work_factor > 31) if (!$work_factor || $work_factor < 4 || $work_factor > 31)
{ {
@ -195,7 +195,7 @@ class Password
* @param int $length * @param int $length
* @return string * @return string
*/ */
public static function getRandomPassword($length = 16) public static function getRandomPassword(int $length = 16): string
{ {
while(true) while(true)
{ {
@ -222,11 +222,11 @@ class Password
* On error, false will be returned. * On error, false will be returned.
* *
* @param string $password * @param string $password
* @param string|array $algos (optional) * @param string|array|null $algos (optional)
* @param string $salt (optional) * @param string|null $salt (optional)
* @return string|false * @return string
*/ */
public static function hashPassword($password, $algos = null, $salt = null) public static function hashPassword(string $password, $algos = null, ?string $salt = null): string
{ {
// If the algorithm is null, use the default algorithm. // If the algorithm is null, use the default algorithm.
if ($algos === null) if ($algos === null)
@ -246,13 +246,19 @@ class Password
// argon2id (must be used last) // argon2id (must be used last)
case 'argon2id': case 'argon2id':
$hashchain = self::argon2id($hashchain, self::getWorkFactor()); $hashchain = self::argon2id($hashchain, self::getWorkFactor());
if ($hashchain[0] === '*') return false; if ($hashchain[0] === '*')
{
throw new Exception('Failed to hash password using ' . $algo);
}
return $hashchain; return $hashchain;
// bcrypt (must be used last) // bcrypt (must be used last)
case 'bcrypt': case 'bcrypt':
$hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor()); $hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor());
if ($hashchain[0] === '*') return false; if ($hashchain[0] === '*')
{
throw new Exception('Failed to hash password using ' . $algo);
}
return $hashchain; return $hashchain;
// PBKDF2 (must be used last) // PBKDF2 (must be used last)
@ -286,7 +292,7 @@ class Password
else else
{ {
$match = $phpass->CheckPassword($hashchain, $salt); $match = $phpass->CheckPassword($hashchain, $salt);
return $match ? $salt : false; return $match ? $salt : '';
} }
// Drupal's SHA-512 based algorithm (must be used last) // Drupal's SHA-512 based algorithm (must be used last)
@ -346,7 +352,7 @@ class Password
} }
else else
{ {
return false; throw new Exception('Failed to hash password using ' . $algo);
} }
} }
} }
@ -362,10 +368,10 @@ class Password
* *
* @param string $password * @param string $password
* @param string $hash * @param string $hash
* @param array|string $algos * @param array|string|null $algos
* @return bool * @return bool
*/ */
public static function checkPassword($password, $hash, $algos = null) public static function checkPassword(string $password, string $hash, $algos = null): bool
{ {
if ($algos === null) if ($algos === null)
{ {
@ -400,7 +406,7 @@ class Password
* @param string $hash * @param string $hash
* @return array * @return array
*/ */
public static function checkAlgorithm($hash) public static function checkAlgorithm(string $hash): array
{ {
$candidates = array(); $candidates = array();
foreach (self::$_algorithm_signatures as $name => $signature) foreach (self::$_algorithm_signatures as $name => $signature)
@ -416,7 +422,7 @@ class Password
* @param string $hash * @param string $hash
* @return int * @return int
*/ */
public static function checkWorkFactor($hash) public static function checkWorkFactor(string $hash): int
{ {
if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches)) if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches))
{ {
@ -439,7 +445,7 @@ class Password
* @param int $work_factor (optional) * @param int $work_factor (optional)
* @return string * @return string
*/ */
public static function argon2id($password, $work_factor = 10) public static function argon2id(string $password, int $work_factor = 10): string
{ {
if (!defined('\PASSWORD_ARGON2ID')) if (!defined('\PASSWORD_ARGON2ID'))
{ {
@ -483,7 +489,7 @@ class Password
* @param int $work_factor (optional) * @param int $work_factor (optional)
* @return string * @return string
*/ */
public static function bcrypt($password, $salt = null, $work_factor = 10) public static function bcrypt(string $password, ?string $salt = null, int $work_factor = 10): string
{ {
if ($salt === null) if ($salt === null)
{ {
@ -504,7 +510,7 @@ class Password
* @param int $iterations_padding (optional) * @param int $iterations_padding (optional)
* @return string * @return string
*/ */
public static function pbkdf2($password, $salt = null, $algorithm = 'sha512', $iterations = 16384, $length = 24, $iterations_padding = 7) public static function pbkdf2(string $password, ?string $salt = null, string $algorithm = 'sha512', int $iterations = 16384, int $length = 24, int $iterations_padding = 7): string
{ {
if ($salt === null) if ($salt === null)
{ {
@ -541,7 +547,7 @@ class Password
* @param string $password * @param string $password
* @return int * @return int
*/ */
public static function countEntropyBits($password) public static function countEntropyBits(string $password): int
{ {
// An empty string has no entropy. // An empty string has no entropy.
@ -566,7 +572,7 @@ class Password
{ {
if (preg_match($regex, $password)) if (preg_match($regex, $password))
{ {
return log(pow($entropy, strlen($password)), 2); return (int)round(log(pow($entropy, strlen($password)), 2));
} }
} }

View file

@ -12,9 +12,9 @@ class Security
* *
* @param string $input * @param string $input
* @param string $type * @param string $type
* @return string|false * @return string
*/ */
public static function sanitize($input, $type) public static function sanitize(string $input, string $type): string
{ {
switch ($type) switch ($type)
{ {
@ -38,8 +38,9 @@ class Security
if (!utf8_check($input)) return false; if (!utf8_check($input)) return false;
return Filters\FilenameFilter::clean($input); return Filters\FilenameFilter::clean($input);
// Unknown filters return false. // Unknown filters.
default: return false; default:
throw new Exception('Unknown filter type for sanitize: ' . $type);
} }
} }
@ -48,9 +49,9 @@ class Security
* *
* @param string $plaintext * @param string $plaintext
* @param string $key (optional) * @param string $key (optional)
* @return string|false * @return string
*/ */
public static function encrypt($plaintext, $key = null) public static function encrypt(string $plaintext, ?string $key = null): string
{ {
// Get the encryption key. // Get the encryption key.
$key = $key ?: config('crypto.encryption_key'); $key = $key ?: config('crypto.encryption_key');
@ -67,7 +68,7 @@ class Security
* @param string $key (optional) * @param string $key (optional)
* @return string|false * @return string|false
*/ */
public static function decrypt($ciphertext, $key = null) public static function decrypt(string $ciphertext, ?string $key = null)
{ {
// Get the encryption key. // Get the encryption key.
$key = $key ?: config('crypto.encryption_key'); $key = $key ?: config('crypto.encryption_key');
@ -90,7 +91,7 @@ class Security
* @param string $string * @param string $string
* @return string * @return string
*/ */
public static function createSignature($string) public static function createSignature(string $string): string
{ {
$key = config('crypto.authentication_key'); $key = config('crypto.authentication_key');
$salt = self::getRandom(8, 'alnum'); $salt = self::getRandom(8, 'alnum');
@ -105,7 +106,7 @@ class Security
* @param string $signature * @param string $signature
* @return bool * @return bool
*/ */
public static function verifySignature($string, $signature) public static function verifySignature(string $string, string $signature): bool
{ {
if(strlen($signature) !== 40) if(strlen($signature) !== 40)
{ {
@ -125,7 +126,7 @@ class Security
* @param string $format * @param string $format
* @return string * @return string
*/ */
public static function getRandom($length = 32, $format = 'alnum') public static function getRandom(int $length = 32, string $format = 'alnum'): string
{ {
// Find out how many bytes of entropy we really need. // Find out how many bytes of entropy we really need.
switch($format) switch($format)
@ -233,7 +234,7 @@ class Security
* @param int $max * @param int $max
* @return int * @return int
*/ */
public static function getRandomNumber($min = 0, $max = 0x7fffffff) public static function getRandomNumber(int $min = 0, int $max = \PHP_INT_MAX): int
{ {
if (function_exists('random_int')) if (function_exists('random_int'))
{ {
@ -253,7 +254,7 @@ class Security
* *
* @return string * @return string
*/ */
public static function getRandomUUID() public static function getRandomUUID(): string
{ {
$randpool = self::getRandom(16, 'binary'); $randpool = self::getRandom(16, 'binary');
$randpool[6] = chr(ord($randpool[6]) & 0x0f | 0x40); $randpool[6] = chr(ord($randpool[6]) & 0x0f | 0x40);
@ -268,7 +269,7 @@ class Security
* @param string $b * @param string $b
* @return bool * @return bool
*/ */
public static function compareStrings($a, $b) public static function compareStrings(string $a, string $b): bool
{ {
if(function_exists('hash_equals')) if(function_exists('hash_equals'))
{ {
@ -293,7 +294,7 @@ class Security
* @param string $referer (optional) * @param string $referer (optional)
* @return bool * @return bool
*/ */
public static function checkCSRF($referer = null) public static function checkCSRF(?string $referer = null): bool
{ {
$check_csrf_token = config('security.check_csrf_token') ? true : false; $check_csrf_token = config('security.check_csrf_token') ? true : false;
if ($token = isset($_SERVER['HTTP_X_CSRF_TOKEN']) ? $_SERVER['HTTP_X_CSRF_TOKEN'] : null) if ($token = isset($_SERVER['HTTP_X_CSRF_TOKEN']) ? $_SERVER['HTTP_X_CSRF_TOKEN'] : null)
@ -342,7 +343,7 @@ class Security
* @param string $xml (optional) * @param string $xml (optional)
* @return bool * @return bool
*/ */
public static function checkXXE($xml = null) public static function checkXXE(?string $xml = null): bool
{ {
// Stop if there is no XML content. // Stop if there is no XML content.
if (!$xml) if (!$xml)

View file

@ -21,6 +21,6 @@ class MIMETest extends \Codeception\TestCase\Test
$this->assertEquals('ogv', Rhymix\Framework\MIME::getExtensionByType('video/ogg')); $this->assertEquals('ogv', Rhymix\Framework\MIME::getExtensionByType('video/ogg'));
$this->assertEquals('mp4', Rhymix\Framework\MIME::getExtensionByType('audio/mp4')); $this->assertEquals('mp4', Rhymix\Framework\MIME::getExtensionByType('audio/mp4'));
$this->assertEquals('mp4', Rhymix\Framework\MIME::getExtensionByType('video/mp4')); $this->assertEquals('mp4', Rhymix\Framework\MIME::getExtensionByType('video/mp4'));
$this->assertFalse(Rhymix\Framework\MIME::getExtensionByType('application/octet-stream')); $this->assertNull(Rhymix\Framework\MIME::getExtensionByType('application/octet-stream'));
} }
} }