public static function blocks_check($data, $blocks)
	{
		if (! ReflectionTypeHint::isValid()) return false;

		if (is_array($data))
		{
			foreach ($data as $k => &$v)
			{
				if (! self::blocks_check($k, $blocks) || ! self::blocks_check($v, $blocks)) return false;
			}
			return true;
		}

		if (is_string($data))
		{
			$chars = self::str_split($data);
			if ($chars === false) return false; #broken UTF-8
			unset($data); #memory free
			$skip = array(); #save to cache already checked symbols
			foreach ($chars as $i => $char)
			{
				if (array_key_exists($char, $skip)) continue; #speed improve
				$codepoint = self::ord($char);
				if ($codepoint === false) return false; #broken UTF-8
				$is_valid = false;
				$blocks = (array)$blocks;
				foreach ($blocks as $j => $block)
				{
					if (is_string($block))
					{
						if (! array_key_exists($block, self::$unicode_blocks))
						{
							trigger_error('Unknown block "' . $block . '"!', E_USER_WARNING);
							return false;
						}
						list ($min, $max) = self::$unicode_blocks[$block];
					}
					elseif (is_array($block)) list ($min, $max) = $block;
					elseif (is_int($block)) $min = $max = $block;
					else trigger_error('A string/array/int type expected for block[' . $j . ']!', E_USER_ERROR);
					if ($codepoint >= $min && $codepoint <= $max)
					{
						$is_valid = true;
						break;
					}
				}#foreach
				if (! $is_valid) return false;
				$skip[$char] = null;
			}#foreach
			return true;
		}
		if (is_scalar($data) || is_null($data)) return true;  #~ null, integer, float, boolean
		return false; #object or resource
	}