“Giải mã Encoding trong PHP”: Từ số nhị phân tới emoji 👩🏽💻


Mở đầu: Câu chuyện cái tên có dấu và tấm ảnh base64
Bạn gửi form “Họ tên: Đỗ Ánh”, nhưng lên URL lại ra H%E1%BB%8D+%C4%91%E1%BB%97+%C3%A1nh
. Bạn tải ảnh đại diện mà server trả về… một chuỗi dài ngoằng iVBORw0KGgo…
. Và khi đếm ký tự “👩🏽💻” thì kết quả lúc là 1, lúc là 4, lúc lại là… 15!
Những điều “kỳ quái” này đều liên quan đến encoding—cách biểu diễn dữ liệu (chữ, số, ảnh…) thành byte để máy tính hiểu và truyền đi. Bài này tóm gọn các “món” encoding thường gặp trong PHP theo lối thân thiện, thực dụng, dựa trên bài gốc “Encoding Schemes in PHP” kèm tài liệu chính thống, để bạn mới học có thể tự tin xử lý. (Oliver Lundquist)
1) Hệ đếm (Binary/Octal/Hex) & biểu diễn số
Ý chính: Một số nguyên có thể viết ở nhiều “hệ” khác nhau:
- Binary (nhị phân, base-2): chỉ 0/1, rất hợp với bit/flag.
- Octal (bát phân, base-8): hay gặp ở quyền file (0755…).
- Hex (thập lục phân, base-16): gọn gàng khi mô tả byte, màu CSS (#FFAA00).
Ví dụ thực tế
- Quyền file Linux:
0755
(octal) dễ đọc hơn viết nhị phân dài dòng. - Màu sắc:
#FF0000
= đỏ thuần;FF
trong hex là 255 (max 1 byte).
PHP minh họa
<?php
// 1) Chuyển số thập phân sang nhị phân/hex
echo decbin(5); // "101" // 5 (10) -> 101 (2)
echo dechex(255); // "ff" // 255 (10) -> ff (16)
// 2) Đổi ngược về thập phân
echo bindec('101'); // 5
echo hexdec('ff'); // 255
// 3) Xem byte ở dạng hex (debug dữ liệu nhị phân)
$raw = "\x48\x65\x6C\x6C\x6F"; // "Hello" theo mã byte
echo bin2hex($raw); // "48656c6c6f"
Gợi ý:
bin2hex
/hex2bin
rất hữu dụng khi kiểm tra/ghi log dữ liệu nhị phân. (PHPoC)
2) Base64: “Nén” nhị phân thành chuỗi chữ cái
Để làm gì? Khi cần đưa nhị phân (ảnh, PDF) vào môi trường chỉ chấp nhận text (JSON, HTML, email), ta mã hóa Base64. Cứ 3 byte đầu vào → 4 ký tự Base64, nên dung lượng tăng ~33%. (PHP)
ASCII sơ đồ
[ 3 bytes nhị phân ] --> [ 4 ký tự Base64 ]
24 bits 4 x 6-bit nhóm
Ví dụ thực tế
- HTTP Basic Auth:
Authorization: Basic base64(user:pass)
. - Data URI ảnh nhúng HTML:
src="data:image/png;base64,..."
.
PHP minh họa
<?php
// 1) Basic Auth header
$token = base64_encode('user:pass'); // "dXNlcjpwYXNz"
$header = "Authorization: Basic $token";
// 2) Nhúng ảnh nhỏ vào <img> (demo)
$data = base64_encode(file_get_contents('logo.png'));
echo '<img src="data:image/png;base64,' . $data . '">';
// 3) Giải mã
$raw = base64_decode($data, true); // true => strict mode
Hàm:
base64_encode
,base64_decode
. (PHP)
3) URL Encoding: %20
khác +
khác nhau thế nào?
Bối cảnh: URL chỉ an toàn với một tập ký tự. Ký tự khác (dấu cách, tiếng Việt, “/”, “?”…) cần percent-encode (%HH
).
rawurlencode()
: chuẩn RFC 3986, dùng tốt cho đường dẫn (path).urlencode()
: lịch sử cũ (application/x-www-form-urlencoded), biến dấu cách thành+
, thường dùng trong query string. (PHP)
Ví dụ thực tế
- Mã hóa path có dấu:
/Tài liệu/ghi chú.txt
→/T%C3%A0i%20li%E1%BB%87u/ghi%20ch%C3%BA.txt
(dùngrawurlencode
cho từng đoạn). - Ghép query:
?q=đỗ ánh
→q=%C4%91%E1%BB%97+%C3%A1nh
(vớiurlencode
, dấu cách thành+
).
PHP minh họa
<?php
$segments = ['Tài liệu', 'ghi chú.txt'];
$path = implode('/', array_map('rawurlencode', $segments));
// Kết quả: "T%C3%A0i%20li%E1%BB%87u/ghi%20ch%C3%BA.txt"
$q = urlencode('đỗ ánh'); // " %C4%91%E1%BB%97+%C3%A1nh "
$url = "https://example.com/search?q=$q";
Quy tắc “nhớ nhanh”: path →
rawurlencode
, query →urlencode
. (PHP)
4) Escaping HTML: chặn XSS bằng htmlspecialchars
Vấn đề: Nếu in thẳng input người dùng vào HTML, ký tự như <
, >
sẽ bị trình duyệt hiểu là thẻ → dễ XSS.
Giải pháp: escape các ký tự đặc biệt thành entity (<
, >
, &
, "
, '
). Dùng:
htmlspecialchars()
— an toàn & đủ dùng (5 ký tự chính).htmlentities()
— chuyển đổi nhiều ký tự hơn (ít khi cần cho HTML hiện đại). (PHP)
Ví dụ thực tế
- Hiển thị input an toàn trong HTML.
- Render code snippet có
<div>
thật, không bị “nuốt” mất.
PHP minh họa
<?php
// Escape trước khi echo vào HTML
$safe = htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
// ENT_QUOTES: escape cả ' và "
// ENT_SUBSTITUTE: thay thế byte/chuỗi UTF-8 lỗi bằng ký hiệu thay thế
echo "<p>$safe</p>";
// Khi cần nhiều entity lịch sử:
$more = htmlentities('10 € & <tag>', ENT_QUOTES, 'UTF-8'); // "10 € & <tag>"
Thực tế sản phẩm: ưu tiên
htmlspecialchars
+ UTF-8. (PHP)
5) Chuỗi & Unicode trong PHP: byte ≠ “ký tự”
Quan trọng: Chuỗi PHP là dãy byte. strlen()
đếm byte, không phải “ký tự người dùng thấy”. Với UTF-8, một ký tự có thể dài 1–4 byte. Dùng mbstring để làm việc theo mã Unicode; khi cần đếm “ký tự hiển thị”, dùng grapheme_* (Intl). (Oliver Lundquist, PHP, Wikipedia)
Ví dụ thực tế
Đếm ký tự cho giới hạn 140 ký tự:
strlen("Xin chào 👋")
→ đếm byte.mb_strlen("Xin chào 👋", 'UTF-8')
→ đếm mã Unicode (code points).grapheme_strlen("👩🏽💻")
→ đếm “ký tự hiển thị” (grapheme cluster) = 1. (PHP)
- Cắt chuỗi không vỡ emoji: dùng
grapheme_extract
/grapheme_substr
thay vìsubstr
. (PHP)
PHP minh họa
<?php
$s = "Xin chào 👋";
echo strlen($s); // số byte
echo mb_strlen($s, 'UTF-8'); // số mã Unicode (code points)
// Emoji ZWJ sequence: "👩🏽💻"
$e = "👩🏽💻";
echo mb_strlen($e, 'UTF-8'); // 4 (👩, 🏽, ZWJ, 💻)
echo grapheme_strlen($e); // 1 (một "ký tự" hiển thị)
// Xem mã Unicode & byte:
$elephant = "🐘";
printf("U+%X\n", mb_ord($elephant, 'UTF-8')); // U+1F418
$bytes = unpack('C*', $elephant); // mảng các byte thô
print_r($bytes);
Ghi nhớ:
strlen
=byte,mb_*
=mã Unicode,grapheme_*
=ký tự hiển thị. (PHP)
6) Emoji “ghép” bằng ZWJ: vì sao 1 ký tự mà nhiều code point?
Nhiều emoji là tổ hợp ký tự nối với nhau bởi Zero Width Joiner (ZWJ, U+200D)—một ký tự “vô hình” làm “keo dán” để hiển thị thành một hình duy nhất. Ví dụ 👩🏽💻 = 👩 (woman) + 🏽 (skin tone) + ZWJ + 💻 (laptop). Trên hệ thống không hỗ trợ, chúng có thể tách ra từng emoji riêng. (Unicode, Emojipedia)
PHP minh họa nhanh
<?php
$zwjEmoji = "👩🏽💻"; // woman technologist, medium skin tone
echo strlen($zwjEmoji); // ~15 byte (tùy chuỗi)
echo mb_strlen($zwjEmoji, 'UTF-8'); // 4 code points
echo grapheme_strlen($zwjEmoji); // 1 "ký tự" hiển thị
Khi validate độ dài “tên hiển thị”, hãy dùng grapheme_strlen để không “cắt đôi” emoji. (PHP)
Tóm tắt nhanh (cheat sheet)
- Số:
decbin/hexdec/bin2hex/hex2bin
để đổi qua lại. (PHPoC) - Base64: gói nhị phân thành text; tăng ~33% kích thước. (PHP)
- URL: path→
rawurlencode
, query→urlencode
. (PHP) - HTML: xuất ra HTML →
htmlspecialchars(..., ENT_QUOTES, 'UTF-8')
. (PHP) - Chuỗi:
strlen
(byte) ≠mb_strlen
(Unicode) ≠grapheme_strlen
(ký tự hiển thị). (PHP)
Tương tác nhẹ – thử thách mini
- Viết PHP để mã hóa chuỗi
"Ánh sáng"
thành Base64, rồi giải mã về UTF-8 đúng cách. - Tạo URL an toàn cho file nằm ở thư mục
"/Tài liệu mới/ghi chú.txt"
sao cho khi click vẫn đúng đường dẫn và vẫn thêm được query?download=1
. - Đếm độ dài “👨👩👧👦” theo byte, mb, và grapheme—so sánh kết quả và giải thích vì sao khác nhau (gợi ý: ZWJ). (Emojipedia)
Lời kết
Encoding không phải “lý thuyết suông”—nó chảy qua mọi chỗ: URL, HTML, API, database, và cả tên người dùng có dấu hay emoji. Nắm vững khi nào cần encode/escape, đơn vị đo (byte/code point/grapheme), và hàm PHP phù hợp sẽ giúp bạn tránh lỗi “khó chịu” và viết ứng dụng quốc tế hóa tốt ngay từ đầu. Nếu phân vân: đổi sang UTF-8, escape HTML, encode URL đúng chỗ, và test với dữ liệu có dấu + emoji. (Wikipedia)
Tài liệu tham khảo
- Oliver Lundquist – Encoding Schemes in PHP (2025). (Oliver Lundquist)
- PHP Manual –
base64_encode
,base64_decode
. (PHP) - PHP Manual –
rawurlencode
(RFC 3986),urlencode
. (PHP) - PHP Manual –
htmlspecialchars
,htmlentities
. (PHP) - PHP Manual –
bin2hex
,hex2bin
. (PHPoC) - PHP Manual –
strlen
(đếm byte),mb_strlen
(Unicode), gói mbstring. (PHP) - PHP Manual –
grapheme_strlen
và nhóm hàm grapheme (Intl). (PHP) - Wikipedia – UTF-8. (Wikipedia)
- Unicode Consortium – Emoji ZWJ Sequences. (Unicode)
- Emojipedia – Woman Technologist & Emoji ZWJ Sequence. (Emojipedia)
Tip cuối: Khi debug encoding, hãy in byte ở dạng hex (
bin2hex
) và mã Unicode (mb_ord
)—bạn sẽ “nhìn thấy” vấn đề rõ ngay. (PHPoC, PHP)
Subscribe to my newsletter
Read articles from Binlerdev directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
