1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216:
<?php
namespace esperecyan\webidl\lib;
/** @internal */
class IntegerType
{
use Utility;
/** @var int PHP がサポートする整数型の最小値。 */
private static $phpIntMin = ~PHP_INT_MAX;
/**
* 与えられた値が整数型に変換可能であれば真を返します。
*
* 次の型の値が整数型に変換可能であるとみなされます。
* 論理型。整数型。浮動小数点型。文字列型。リソース型。オブジェクト型のうち、GMP のインスタンス。
* @param mixed $value
* @return bool
*/
public static function isIntegerCastable($value)
{
return is_scalar($value) || is_resource($value) || $value instanceof \GMP;
}
/**
* 与えられた値を整数型に変換して返します。
* @link https://triple-underscore.github.io/WebIDL-ja.html#es-integer-types Web IDL (第2版 — 日本語訳)
* @param bool|int|float|string|resource|\GMP $value
* @param string $type byte、octet、short、unsigned short、long、unsigned long、long long、unsigned long long
* @param int|float $min 浮動小数点型で正確に扱える整数の範囲よりも、整数型で扱える整数の範囲が狭ければ (整数型が32bitである環境なら) 浮動小数点数。
* @param int|float $max 浮動小数点型で正確に扱える整数の範囲よりも、整数型で扱える整数の範囲が狭ければ (整数型が32bitである環境なら) 浮動小数点数。
* @param int $bits
* @param booelan $signed
* @param string $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int|float 整数型の範囲を超える場合は浮動小数点数。
* @throws \InvalidArgumentException 配列、NULL が与えられた場合。または、GMP 以外のオブジェクトが与えられた場合。
* @throws \DomainException $extendedAttribute が [EnforceRange]、かつ与えられたの値が $min 〜 $max に収まらなかった場合。
*/
private static function toInteger($value, $type, $min, $max, $bits, $signed, $extendedAttribute = null)
{
/** @var string 要求される型。 */
$expectedType = sprintf(
'%s (an integer in the range of %s to %s)',
$type,
is_float($min) ? number_format($min, 0, '', '') : $min,
is_float($max) ? number_format($max, 0, '', '') : $max
);
if (!self::isIntegerCastable($value)) {
throw new \InvalidArgumentException(ErrorMessageCreator::create($value, $expectedType));
}
if ($value instanceof \GMP || is_resource($value) && get_resource_type($value) === 'GMP integer') {
// GMP数であれば、あらかじめ文字列に変換しておく
$value = gmp_strval($value);
}
/** @var int|float 与えられた値の数値表現。整数型の範囲を超える場合は浮動小数点数。整数値となる場合、小数部があれば0方向へ丸められる。 */
$number = is_float($value) || (float)$value < self::$phpIntMin || (float)$value > PHP_INT_MAX
? (float)$value
: (int)$value;
if ($extendedAttribute === '[EnforceRange]') {
/** @var int|float 与えられた値の整数表現。整数型の範囲を超える場合は浮動小数点数。 */
$integer = self::roundTowardZero($number);
if (!is_finite($number) || $integer < $min || $integer > $max) {
throw new \DomainException(ErrorMessageCreator::create($value, $expectedType));
}
} elseif (!is_nan($number) && $extendedAttribute === '[Clamp]') {
$number = min($max, max($number, $min));
$integer = is_float($number) ? round($number, 0, PHP_ROUND_HALF_EVEN) : $number;
} elseif (!is_finite($number)) {
$integer = 0;
} else {
$integer = self::modulo(self::roundTowardZero($number), pow(2, $bits));
if ($signed && $integer >= pow(2, $bits - 1)
&& !(PHP_INT_SIZE === 8 && $bits === 64 && $integer <= PHP_INT_MAX)) {
$integer -= pow(2, $bits);
}
}
return is_float($integer) && $integer >= self::$phpIntMin && $integer <= PHP_INT_MAX
? (int)$integer
: $integer;
}
/**
* 与えられた数値を0の方向に丸めた整数を返します。
* @param float|int $value
* @return float|int
*/
private static function roundTowardZero($value)
{
return is_float($value) ? ($value >= 0 ? 1 : -1) * floor(abs($value)) : $value;
}
/**
* 正の剰余を返します。
* @link https://www.ecma-international.org/ecma-262/7.0/index.html#sec-algorithm-conventions ECMAScript 2015 Language Specification – ECMA-262 6th Edition
* @param int|float $x
* @param int|float $y
* @return int|float
*/
private static function modulo($x, $y)
{
return ($x < $y ? $x : (is_int($y) ? $x % $y : fmod($x, $y))) + ($x < 0 ? $y : 0);
}
/**
* 与えられた値を −128〜127 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-byte WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int
*/
public static function toByte($value, $extendedAttribute = null)
{
return self::toInteger($value, 'byte', -128, 127, 8, true, $extendedAttribute);
}
/**
* 与えられた値を 0〜255 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-octet WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int
*/
public static function toOctet($value, $extendedAttribute = null)
{
return self::toInteger($value, 'octet', 0, 255, 8, false, $extendedAttribute);
}
/**
* 与えられた値を −32768〜32767 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-short WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int
*/
public static function toShort($value, $extendedAttribute = null)
{
return self::toInteger($value, 'short', -32768, 32767, 16, true, $extendedAttribute);
}
/**
* 与えられた値を 0〜65535 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-unsigned-short WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int
*/
public static function toUnsignedShort($value, $extendedAttribute = null)
{
return self::toInteger($value, 'unsigned short', 0, 65535, 16, false, $extendedAttribute);
}
/**
* 与えられた値を −2147483648〜2147483647 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-long WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int
*/
public static function toLong($value, $extendedAttribute = null)
{
return self::toInteger($value, 'long', -2147483648, 2147483647, 32, true, $extendedAttribute);
}
/**
* 与えられた値を 0〜4294967295 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-unsigned-long WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int|float 32bit版のPHP、またはWindows版のPHP 5.6以前において、整数型の範囲を超える場合は浮動小数点数。
*/
public static function toUnsignedLong($value, $extendedAttribute = null)
{
return self::toInteger($value, 'unsigned long', 0, 4294967295, 32, false, $extendedAttribute);
}
/**
* 与えられた値を −9223372036854775808〜9223372036854775807 の範囲の整数に変換して返します。
* 32bit版のPHP、またはWindows版のPHP 5.6以前では、−9007199254740991〜9007199254740991 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-long WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int|float 32bit版のPHP、またはWindows版のPHP 5.6以前において、整数型の範囲を超える場合は浮動小数点数。
*/
public static function toLongLong($value, $extendedAttribute = null)
{
$longLongMin = self::$phpIntMin > -9007199254740991 ? -9007199254740991 : ~9223372036854775807;
$longLongMax = PHP_INT_MAX < 9007199254740991 ? 9007199254740991 : 9223372036854775807;
return self::toInteger($value, 'long long', $longLongMin, $longLongMax, 64, true, $extendedAttribute);
}
/**
* 与えられた値を 0〜9223372036854775807 の範囲の整数に変換して返します。
* 32bit版のPHP、またはWindows版のPHP 5.6以前では、0〜9007199254740991 の範囲の整数に変換して返します。
* @link https://www.w3.org/TR/WebIDL-1/#idl-unsigned-long-long WebIDL Level 1
* @param bool|int|float|string|resource|\GMP $value
* @param string|null $extendedAttribute 拡張属性。[EnforceRange] か [Clamp] のいずれか。
* @return int|float 32bit版のPHP、またはWindows版のPHP 5.6以前において、整数型の範囲を超える場合は浮動小数点数。
*/
public static function toUnsignedLongLong($value, $extendedAttribute = null)
{
if (PHP_INT_MAX < 9007199254740991) {
$unsignedLongLongMax = 9007199254740991;
} elseif (PHP_INT_MAX < 18446744073709551615) {
$unsignedLongLongMax = PHP_INT_MAX;
} else {
$unsignedLongLongMax = 18446744073709551615;
}
return self::toInteger($value, 'unsigned long long', 0, $unsignedLongLongMax, 64, false, $extendedAttribute);
}
}