ASCII Table — PHP source
A searchable, filterable reference for all 128 ASCII code points. See the decimal, hex, octal, and binary form of every character, narrow to printable characters only, clamp the code-point range, and click any row to copy. Runs 100% client-side.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* ASCII Table — pure logic (PHP port).
*
* CosmoDev polyglot showcase port of the `ascii-table` tool, ported from
* src/lib/ascii-table.ts. Every output is a pure function of the code point
* 0–127; no I/O, no Composer dependencies — PHP's standard library only.
*
* Display source — part of CosmoDev's polyglot tool pages.
*
* Each of the 128 ASCII code points is described by its decimal / hex / octal /
* binary forms, a display glyph, a human name, whether it is printable, whether
* it is a control character, and (for control chars that have one) a C-style
* escape sequence.
*/
declare(strict_types=1);
namespace CosmoDev\AsciiTable;
/**
* A single ASCII code point and its derived representations.
*/
final class Entry
{
public function __construct(
public readonly int $dec,
public readonly string $hex,
public readonly string $oct,
public readonly string $binary,
public readonly string $char,
public readonly string $name,
public readonly bool $printable,
public readonly bool $control,
public readonly ?string $escape,
) {}
}
/**
* Official ASCII control-character names (0–31 and 127).
* `static` keeps the lookup table local yet initialized only once.
*/
function controlName(int $code): ?string
{
static $names = [
0 => 'Null (NUL)',
1 => 'Start of Heading (SOH)',
2 => 'Start of Text (STX)',
3 => 'End of Text (ETX)',
4 => 'End of Transmission (EOT)',
5 => 'Enquiry (ENQ)',
6 => 'Acknowledge (ACK)',
7 => 'Bell (BEL)',
8 => 'Backspace (BS)',
9 => 'Horizontal Tab (HT)',
10 => 'Line Feed (LF)',
11 => 'Vertical Tab (VT)',
12 => 'Form Feed (FF)',
13 => 'Carriage Return (CR)',
14 => 'Shift Out (SO)',
15 => 'Shift In (SI)',
16 => 'Data Link Escape (DLE)',
17 => 'Device Control 1 (DC1)',
18 => 'Device Control 2 (DC2)',
19 => 'Device Control 3 (DC3)',
20 => 'Device Control 4 (DC4)',
21 => 'Negative Acknowledge (NAK)',
22 => 'Synchronous Idle (SYN)',
23 => 'End of Transmission Block (ETB)',
24 => 'Cancel (CAN)',
25 => 'End of Medium (EM)',
26 => 'Substitute (SUB)',
27 => 'Escape (ESC)',
28 => 'File Separator (FS)',
29 => 'Group Separator (GS)',
30 => 'Record Separator (RS)',
31 => 'Unit Separator (US)',
127 => 'Delete (DEL)',
];
return $names[$code] ?? null;
}
/**
* C-style escape for the control chars that own a standard/common one.
* Returns the literal 2-character source token (backslash + letter), or null.
*/
function controlEscape(int $code): ?string
{
static $escapes = [
0 => '\\0',
7 => '\\a',
8 => '\\b',
9 => '\\t',
10 => '\\n',
11 => '\\v',
12 => '\\f',
13 => '\\r',
27 => '\\e',
];
return $escapes[$code] ?? null;
}
/**
* Names for printable punctuation & symbols; letter and digit names are derived.
*/
function symbolName(int $code): ?string
{
static $names = [
32 => 'Space',
33 => 'Exclamation mark',
34 => 'Quotation mark',
35 => 'Number sign',
36 => 'Dollar sign',
37 => 'Percent sign',
38 => 'Ampersand',
39 => 'Apostrophe',
40 => 'Left parenthesis',
41 => 'Right parenthesis',
42 => 'Asterisk',
43 => 'Plus sign',
44 => 'Comma',
45 => 'Hyphen / Minus',
46 => 'Full stop',
47 => 'Slash',
58 => 'Colon',
59 => 'Semicolon',
60 => 'Less-than sign',
61 => 'Equals sign',
62 => 'Greater-than sign',
63 => 'Question mark',
64 => 'At sign',
91 => 'Left bracket',
92 => 'Backslash',
93 => 'Right bracket',
94 => 'Circumflex / Caret',
95 => 'Underscore',
96 => 'Grave accent',
123 => 'Left brace',
124 => 'Vertical bar',
125 => 'Right brace',
126 => 'Tilde',
];
return $names[$code] ?? null;
}
/**
* Encode a Unicode code point as raw UTF-8 bytes.
*
* PHP's chr() only emits a single byte, so the multi-byte control-picture
* glyphs (U+2400–U+2421) cannot be produced with chr() alone. Rather than pull
* in the optional mbstring/intl extensions, we implement the standard UTF-8
* encoding directly — this keeps the snippet dependency-free.
*/
function utf8(int $codepoint): string
{
if ($codepoint < 0x80) {
return chr($codepoint);
}
if ($codepoint < 0x800) {
return chr(0xC0 | ($codepoint >> 6))
. chr(0x80 | ($codepoint & 0x3F));
}
if ($codepoint < 0x10000) {
return chr(0xE0 | ($codepoint >> 12))
. chr(0x80 | (($codepoint >> 6) & 0x3F))
. chr(0x80 | ($codepoint & 0x3F));
}
return chr(0xF0 | ($codepoint >> 18))
. chr(0x80 | (($codepoint >> 12) & 0x3F))
. chr(0x80 | (($codepoint >> 6) & 0x3F))
. chr(0x80 | ($codepoint & 0x3F));
}
/**
* Derive a name for a printable letter or digit.
*/
function letterName(int $code): string
{
if ($code >= 48 && $code <= 57) {
return 'Digit ' . ($code - 48); // '0'..'9'
}
if ($code >= 65 && $code <= 90) {
return 'Uppercase ' . chr($code); // 'A'..'Z'
}
if ($code >= 97 && $code <= 122) {
return 'Lowercase ' . chr($code); // 'a'..'z'
}
return chr($code);
}
/**
* Build a single entry from its code point (0–127).
*/
function makeEntry(int $code): Entry
{
$control = $code <= 31 || $code === 127;
// Control-picture glyph: U+2400 for 0–31, U+2421 ("symbol for delete") for DEL.
if ($control) {
$cp = $code === 127 ? 0x2421 : 0x2400 + $code;
$char = utf8($cp);
} else {
$char = chr($code);
}
if ($control) {
$name = controlName($code);
} elseif (($sym = symbolName($code)) !== null) {
$name = $sym;
} else {
$name = letterName($code);
}
$escape = $control ? controlEscape($code) : null;
return new Entry(
dec: $code,
hex: '0x' . strtoupper(sprintf('%02x', $code)),
oct: sprintf('%03o', $code),
binary: sprintf('%08b', $code),
char: $char,
name: $name,
printable: !$control,
control: $control,
escape: $escape,
);
}
/**
* The full 128-entry ASCII table, indexed by code point.
*
* Memoized via a static variable so the table is built only once per request.
*/
function entries(): array
{
static $table = null;
if ($table === null) {
$table = array_map(makeEntry(...), range(0, 127));
}
return $table;
}
/**
* Look up a single entry by code point.
*
* Returns null for out-of-range / non-integer input. PHP's is_int already
* excludes floats, strings, and null; booleans are rejected because filterTable
* callers may pass loose input.
*
* @param mixed $code
*/
function getEntry($code): ?Entry
{
if (!is_int($code) || $code < 0 || $code > 127) {
return null;
}
return entries()[$code];
}
/**
* Filter the table.
*
* `$query` is a case-insensitive substring matched against dec / hex / oct /
* binary / name / char / escape. `$printableOnly` hides controls. `$min` /
* `$max` clamp the decimal range (defaults 0–127).
*
* `strtolower` is sufficient here — every cased haystack character is ASCII,
* and the multi-byte control-picture glyphs have no case, so they pass through
* unchanged. This avoids requiring the mbstring extension.
*/
function filterTable(array $opts = []): array
{
$q = strtolower(trim($opts['query'] ?? ''));
$lo = $opts['min'] ?? 0;
$hi = $opts['max'] ?? 127;
$matching = array_filter(entries(), function (Entry $e) use ($q, $lo, $hi, $opts): bool {
if (!empty($opts['printableOnly']) && !$e->printable) {
return false;
}
if ($e->dec < $lo || $e->dec > $hi) {
return false;
}
if ($q === '') {
return true;
}
$haystack = [
(string) $e->dec,
strtolower($e->hex),
$e->oct,
$e->binary,
strtolower($e->name),
strtolower($e->char),
$e->escape ?? '',
];
foreach ($haystack as $h) {
if (str_contains($h, $q)) {
return true;
}
}
return false;
});
// array_values reindexes to a dense list, matching the TS Array.filter result.
return array_values($matching);
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →