<?php

/**
 * Implements only the subset of filter_var() used by Phan.
 *
 * @author Tyson Andre
 */
if (!function_exists('filter_var')) {
$define_polyfill_const = function(string $name, int $value) {
    if (!defined($name)) {
        define($name, $value);
    }
};

$define_polyfill_const('FILTER_CALLBACK', 1024);
$define_polyfill_const('FILTER_DEFAULT', 516);
$define_polyfill_const('FILTER_FLAG_ALLOW_FRACTION', 4096);
$define_polyfill_const('FILTER_FLAG_ALLOW_HEX', 2);
$define_polyfill_const('FILTER_FLAG_ALLOW_OCTAL', 1);
$define_polyfill_const('FILTER_FLAG_ALLOW_SCIENTIFIC', 16384);
$define_polyfill_const('FILTER_FLAG_ALLOW_THOUSAND', 8192);
$define_polyfill_const('FILTER_FLAG_EMAIL_UNICODE', 1048576);
$define_polyfill_const('FILTER_FLAG_EMPTY_STRING_NULL', 256);
$define_polyfill_const('FILTER_FLAG_ENCODE_AMP', 64);
$define_polyfill_const('FILTER_FLAG_ENCODE_HIGH', 32);
$define_polyfill_const('FILTER_FLAG_ENCODE_LOW', 16);
$define_polyfill_const('FILTER_FLAG_HOSTNAME', 1048576);
$define_polyfill_const('FILTER_FLAG_HOST_REQUIRED', 131072);
$define_polyfill_const('FILTER_FLAG_IPV4', 1048576);
$define_polyfill_const('FILTER_FLAG_IPV6', 2097152);
$define_polyfill_const('FILTER_FLAG_NONE', 0);
$define_polyfill_const('FILTER_FLAG_NO_ENCODE_QUOTES', 128);
$define_polyfill_const('FILTER_FLAG_NO_PRIV_RANGE', 8388608);
$define_polyfill_const('FILTER_FLAG_NO_RES_RANGE', 4194304);
$define_polyfill_const('FILTER_FLAG_PATH_REQUIRED', 262144);
$define_polyfill_const('FILTER_FLAG_QUERY_REQUIRED', 524288);
$define_polyfill_const('FILTER_FLAG_SCHEME_REQUIRED', 65536);
$define_polyfill_const('FILTER_FLAG_STRIP_BACKTICK', 512);
$define_polyfill_const('FILTER_FLAG_STRIP_HIGH', 8);
$define_polyfill_const('FILTER_FLAG_STRIP_LOW', 4);
$define_polyfill_const('FILTER_FORCE_ARRAY', 67108864);
$define_polyfill_const('FILTER_NULL_ON_FAILURE', 134217728);
$define_polyfill_const('FILTER_REQUIRE_ARRAY', 16777216);
$define_polyfill_const('FILTER_REQUIRE_SCALAR', 33554432);
$define_polyfill_const('FILTER_SANITIZE_ADD_SLASHES', 523);
$define_polyfill_const('FILTER_SANITIZE_EMAIL', 517);
$define_polyfill_const('FILTER_SANITIZE_ENCODED', 514);
$define_polyfill_const('FILTER_SANITIZE_FULL_SPECIAL_CHARS', 522);
$define_polyfill_const('FILTER_SANITIZE_MAGIC_QUOTES', 521);
$define_polyfill_const('FILTER_SANITIZE_NUMBER_FLOAT', 520);
$define_polyfill_const('FILTER_SANITIZE_NUMBER_INT', 519);
$define_polyfill_const('FILTER_SANITIZE_SPECIAL_CHARS', 515);
$define_polyfill_const('FILTER_SANITIZE_STRING', 513);
$define_polyfill_const('FILTER_SANITIZE_STRIPPED', 513);
$define_polyfill_const('FILTER_SANITIZE_URL', 518);
$define_polyfill_const('FILTER_UNSAFE_RAW', 516);
$define_polyfill_const('FILTER_VALIDATE_BOOLEAN', 258);
$define_polyfill_const('FILTER_VALIDATE_DOMAIN', 277);
$define_polyfill_const('FILTER_VALIDATE_EMAIL', 274);
$define_polyfill_const('FILTER_VALIDATE_FLOAT', 259);
$define_polyfill_const('FILTER_VALIDATE_INT', 257);
$define_polyfill_const('FILTER_VALIDATE_IP', 275);
$define_polyfill_const('FILTER_VALIDATE_MAC', 276);
$define_polyfill_const('FILTER_VALIDATE_REGEXP', 272);
$define_polyfill_const('FILTER_VALIDATE_URL', 273);
/**
 *
 * @throws AssertionError for unimplemented filters
 *
 * @phan-file-suppress PhanNativePHPSyntaxCheckPlugin
 * @suppress PhanRedefineFunctionInternal
 * @return mixed
 */
function filter_var($value, int $filter = FILTER_DEFAULT, $options = null) {
    switch ($filter) {
        case FILTER_DEFAULT:
            if (is_array($value)) {
                return false;
            }
            if (is_object($value)) {
                if (!method_exists($value, '__toString')) {
                    return false;
                }
            }
            return (string) $value;
        case FILTER_VALIDATE_INT:
            if (is_int($value)) {
                return $value;
            }
            if (is_array($value)) {
                return false;
            }
            if (is_object($value)) {
                if (!method_exists($value, '__toString')) {
                    return false;
                }
            }
            // handle "\v\n\r\t  0123  "
            $value = trim((string)$value, " \t\r\n\v");
            if ($options !== null) {
                if (!is_int($options)) {
                    throw new AssertionError("filter_var() polyfill: unsupported type for options with FILTER_VALIDATE_INT");
                }
                // Octal and hex are supported by the real filter_var() for non-negative numbers
                if ($options & FILTER_FLAG_ALLOW_OCTAL) {
                    if (preg_match('/^0[0-7]*$/iD', $value)) {
                        // @phan-suppress-next-line PhanPossiblyFalseTypeArgumentInternal
                        $result = octdec(substr($value, 2));
                        return is_int($result) ? $result : false;
                    }
                }
                if ($options & FILTER_FLAG_ALLOW_HEX) {
                    if (preg_match('/^0x[0-9a-f]+$/iD', $value)) {
                        // @phan-suppress-next-line PhanPossiblyFalseTypeArgumentInternal
                        $result = hexdec(substr($value, 2));
                        return is_int($result) ? $result : false;
                    }
                }
            }
            if (!preg_match('/^[-+]?(0|[1-9][0-9]*)$/D', $value)) {
                return false;
            }
            $result = (int)$value;
            if ((string)$result !== $value) {
                return false;
            }
            return $result;
        case FILTER_VALIDATE_FLOAT:
            if ($options !== null) {
                throw new AssertionError("filter_var() polyfill: Unsupported \$options");
            }
            if (!is_numeric($value)) {
                return false;
            }
            return (float)$value;
        case FILTER_VALIDATE_IP:
            if ($options !== null) {
                throw new AssertionError("filter_var() polyfill: Unsupported \$options");
            }
            if (is_array($value)) {
                return false;
            }
            if (is_object($value)) {
                if (!method_exists($value, '__toString')) {
                    return false;
                }
            }
            $result = (string)$value;
            // TODO: This is incomplete and doesn't bother validating integer ranges or details.
            if (strpos($result, ':')) {
                // ipv6
                if (!preg_match('/^[0-9a-f:.]+\.)$/i', $result)) {
                    return false;
                }
                return $result;
            } else {
                // ipv4
                if (!preg_match('/^([0-9]+\.){3}[0-9]+$/D', $result)) {
                    return false;
                }
                return $result;
            }
        default:
            throw new AssertionError("filter_var() polyfill: Unsupported filter $filter");
    }
}
} /* end function_exists check */
