IdnaEncoder::punycode_encode( string $input ): string

In this article

RFC3492-compliant encoder

Parameters

$inputstringrequired
UTF-8 encoded string to encode

Return

string Punycode-encoded string

Source

public static function punycode_encode($input) {
	$output = '';
	// let n = initial_n
	$n = self::BOOTSTRAP_INITIAL_N;
	// let delta = 0
	$delta = 0;
	// let bias = initial_bias
	$bias = self::BOOTSTRAP_INITIAL_BIAS;
	// let h = b = the number of basic code points in the input
	$h = 0;
	$b = 0; // see loop
	// copy them to the output in order
	$codepoints = self::utf8_to_codepoints($input);
	$extended   = [];

	foreach ($codepoints as $char) {
		if ($char < 128) {
			// Character is valid ASCII
			// TODO: this should also check if it's valid for a URL
			$output .= chr($char);
			$h++;

			// Check if the character is non-ASCII, but below initial n
			// This never occurs for Punycode, so ignore in coverage
			// @codeCoverageIgnoreStart
		} elseif ($char < $n) {
			throw new Exception('Invalid character', 'idna.character_outside_domain', $char);
			// @codeCoverageIgnoreEnd
		} else {
			$extended[$char] = true;
		}
	}

	$extended = array_keys($extended);
	sort($extended);
	$b = $h;
	// [copy them] followed by a delimiter if b > 0
	if (strlen($output) > 0) {
		$output .= '-';
	}

	// {if the input contains a non-basic code point < n then fail}
	// while h < length(input) do begin
	$codepointcount = count($codepoints);
	while ($h < $codepointcount) {
		// let m = the minimum code point >= n in the input
		$m = array_shift($extended);
		//printf('next code point to insert is %s' . PHP_EOL, dechex($m));
		// let delta = delta + (m - n) * (h + 1), fail on overflow
		$delta += ($m - $n) * ($h + 1);
		// let n = m
		$n = $m;
		// for each code point c in the input (in order) do begin
		for ($num = 0; $num < $codepointcount; $num++) {
			$c = $codepoints[$num];
			// if c < n then increment delta, fail on overflow
			if ($c < $n) {
				$delta++;
			} elseif ($c === $n) { // if c == n then begin
				// let q = delta
				$q = $delta;
				// for k = base to infinity in steps of base do begin
				for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) {
					// let t = tmin if k <= bias {+ tmin}, or
					//     tmax if k >= bias + tmax, or k - bias otherwise
					if ($k <= ($bias + self::BOOTSTRAP_TMIN)) {
						$t = self::BOOTSTRAP_TMIN;
					} elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) {
						$t = self::BOOTSTRAP_TMAX;
					} else {
						$t = $k - $bias;
					}

					// if q < t then break
					if ($q < $t) {
						break;
					}

					// output the code point for digit t + ((q - t) mod (base - t))
					$digit   = (int) ($t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)));
					$output .= self::digit_to_char($digit);
					// let q = (q - t) div (base - t)
					$q = (int) floor(($q - $t) / (self::BOOTSTRAP_BASE - $t));
				} // end
				// output the code point for digit q
				$output .= self::digit_to_char($q);
				// let bias = adapt(delta, h + 1, test h equals b?)
				$bias = self::adapt($delta, $h + 1, $h === $b);
				// let delta = 0
				$delta = 0;
				// increment h
				$h++;
			} // end
		} // end
		// increment delta and n
		$delta++;
		$n++;
	} // end

	return $output;
}

User Contributed Notes

You must log in before being able to contribute a note or feedback.