Strings7 min readExamples run on PHP 8.5.10

chr()

Learn how chr() turns an integer into a one-byte string, why out-of-range values are deprecated in PHP 8.5, and why it cannot create Unicode characters from code points.

Signature

chr(int $codepoint): string

Purpose

chr() creates a string that contains exactly one byte. You give it a number, and it returns the byte stored at that position in a single-byte encoding table such as ASCII. It is the counterpart of ord(), which goes the other way and reads the first byte of a string as a number.

The key point is that chr() works with bytes, not with text characters in general. It knows nothing about UTF-8, UTF-16 or any other encoding. If you want a character from a Unicode code point, use mb_chr() or IntlChar::chr() instead.

Signature

Parameters and defaults

$codepoint is the only parameter and it has no default value. The valid range is 0 to 255. The argument is read as an unsigned byte value, so 65 gives "A" and 97 gives "a".

If the number lies outside 0..255, PHP does not throw an error. It wraps the value into range with the equivalent of a bitwise AND with 255. Negative numbers are moved up by multiples of 256 until they are non-negative. For example, -159 behaves like 97, and 833 behaves like 65.

Return value and failure cases

The function always returns a string whose length is exactly one byte. The byte may be a printable character, a control character such as chr(10) (line feed), or a null byte from chr(0).

There is no false return for out-of-range input. The failure cases are about types: passing a value that cannot be used as an integer, such as an array or a non-numeric string, raises a TypeError. The manual's changelog also says that PHP 8.5.0 deprecates integers outside 0..255, so it is a good habit to keep values inside the range yourself rather than rely on wrapping.

Example 1: building a string from byte values

This loop converts a list of ASCII codes into text. Each call returns one byte and the concatenation operator joins them.

Example
<?php
$codes = [72, 101, 108, 108, 111];
$text = '';
foreach ($codes as $code) {
    $text .= chr($code);
}
echo json_encode($text, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
"Hello"

Example 2: masking values outside 0..255

PHP reduces values beyond the byte range modulo 256, but as of PHP 8.5 it also emits a deprecation notice. Masking the value yourself with & 0xFF gives the same byte without the notice and makes the intent visible.

Example
<?php
$values = [65, 256 + 66, -159, 833];
$chars = [];
foreach ($values as $value) {
    $chars[] = chr($value & 0xFF);
}
echo json_encode($chars, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["A","B","a","A"]

The mask turns the second value, 322, into 66, which is "B", and the last two values into 97 and 65.

Example 3: assembling a UTF-8 sequence byte by byte

A single call to chr() cannot make a multibyte character, but you can combine several bytes that together form valid UTF-8. The four bytes below encode the elephant emoji.

Example
<?php
$str = chr(240) . chr(159) . chr(144) . chr(152);
echo json_encode([$str, strlen($str), bin2hex($str)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["๐Ÿ˜",4,"f09f9098"]

Notice that strlen() reports 4, because it counts bytes. The hexadecimal view shows the raw bytes clearly.

Edge cases

  • chr(0) returns a string containing a null byte. PHP strings can hold null bytes safely, but they may be awkward when passed to other systems.
  • Codes 128 to 255 do not produce a meaningful standalone character in UTF-8. A lone byte such as chr(200) is usually invalid UTF-8, and functions like json_encode() may fail on it unless it is part of a valid sequence.
  • Codes below 32 are control characters, for example tab (9), line feed (10) and escape (27). They are often invisible when printed.
  • For a single formatted byte, sprintf('%c', 27) is an alternative that fits naturally inside a larger format string.

Common mistakes

  1. Expecting Unicode code points. chr(8364) does not return the euro sign. It wraps to a byte (8364 modulo 256 is 172) and gives a meaningless single byte. Use mb_chr(8364, 'UTF-8') for that.
  2. Relying on wraparound. Out-of-range values are deprecated as of PHP 8.5.0. Validate or mask the value explicitly, for example with $n & 0xFF, so your intent is visible.
  3. Treating the result as a character count. The returned string is one byte long, and a sequence of such strings is measured in bytes, not characters.
  4. Passing text. chr('A') is not a way to get the code of "A"; that is the job of ord(). A non-numeric string raises a TypeError.
  5. Printing raw control bytes. Output containing chr(0) or chr(27) may look empty or confuse a terminal. Convert with bin2hex() or json_encode() when you need to inspect it.
  • ord() returns the byte value of the first byte of a string.
  • sprintf() with %c formats an integer as a byte inside a template.
  • mb_chr() returns a character from a Unicode code point in a chosen encoding.
  • IntlChar::chr() returns a Unicode character from a code point when the intl extension is available.
  • bin2hex() helps you inspect the bytes that chr() produced.

Recap

chr(int $codepoint): string returns one byte for a number in 0..255. Out-of-range integers wrap modulo 256 but are deprecated as of PHP 8.5, so mask them explicitly. It is byte-oriented and encoding-unaware, so use the multibyte functions when you need Unicode characters.

Tested examples

Build a string from ASCII byte values
<?php
$codes = [72, 101, 108, 108, 111];
$text = '';
foreach ($codes as $code) {
    $text .= chr($code);
}
echo json_encode($text, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
"Hello"
Masking values outside 0..255
<?php
$values = [65, 256 + 66, -159, 833];
$chars = [];
foreach ($values as $value) {
    $chars[] = chr($value & 0xFF);
}
echo json_encode($chars, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["A","B","a","A"]
Assemble a UTF-8 sequence byte by byte
<?php
$str = chr(240) . chr(159) . chr(144) . chr(152);
echo json_encode([$str, strlen($str), bin2hex($str)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["๐Ÿ˜",4,"f09f9098"]