क्या आपको Base64 फ़ॉर्मेट के साथ काम करना होता है? तो यह साइट आपके लिए एकदम सही है! अपने डेटा को एन्कोड या डिकोड करने के लिए, हमारे बेहद आसान ऑनलाइन टूल का इस्तेमाल करें।

PHP में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड

आपके पास ऐसा डेटा है जिसे किसी चैनल से होकर जाना है जो उसे पसंद नहीं करता। कोई बाइनरी ब्लॉब जो JSON फ़ील्ड में बैठना चाहता है। कोई छवि जो HTML टैग के अंदर रहनी चाहिए। कोई प्रमाणपत्र जो कॉन्फ़िग फ़ाइल में आता है। कोई टोकन जो URL, हेडर्स, और कूकी से होकर गुज़रेगा। यह ही Base64 एन्कोडिंग की रोज़मर्रा की ज़िंदगी है: यह कच्चे डेटा के हर तीन बाइट को 64-अक्षरों की वर्णमाला से चार चरों में पुनर्लिखती है, पूँछ को सुलझाने के लिए एक दो = निशानों के साथ, ताकि रिज़ल्ट सादा टेक्स्ट हो जो कुछ भी उठा ले सके। इस साइट का होम पेज फ़ॉर्मैट को पूरा तौर पर समझाता है, इसलिए यह लेख इस पर केंद्रित है कि PHP आपको क्या देता है, वह चुपचाप आपके लिए क्या-क्या फैसला करता है, और फ़ंदे कहाँ बसे हैं।

PHP पक्ष पर, कहानी अच्छी ख़बर से शुरू होती है: base64_encode() PHP 4 से ही कॉर में है, यह एक ही आर्गुमेंट लेता है, हमेशा स्ट्रिंग लौटाता है, और यह फेल नहीं हो सकता। न कोई सख़्त मोड है, न कोई एरर पथ, न कोई कॉन्फ़िगरेशन। एन्कोडिंग डेटर्मिनिस्टिक है: वही बाइट्स हमेशा वही अक्षर पैदा करते हैं। डेवलपर के तौर पर आपका काम इस फ़ंक्शन को चलाए रखना नहीं है, बल्कि आसपास की दुनिया को सुलझाए रखना है: पता की जगह के लिए सही वर्णमाला चुनें, सही लाइन ब्रेक जोड़ें, सही कैरेक्टर सेट बदलें, और 33 प्रतिशत का साइज़ बिल खुली आँखों से उठाएँ। (Base64 आमतौर पर डेटा को करीब एक-तिहाई बढ़ाता है, हर तीन इनपुट बाइट पर चार चर; इसे मन के कोने में रखें, क्योंकि यह बार-बार सामने आता है।)

इस लेख के अंत तक आपको पता होगा कि PHP डेवलपर को वाक़ई में मिलने वाले हर फ़्लेवर की Base64 कैसे बनाएं: सिंगल-लाइन आउटपुट, MIME-रैप ईमेल, PEM-रैप कुंजियाँ, URL-सुरक्षित टोकन, और डेटा URI, साथ में स्ट्रीमिंग की चालें, जब डेटा मेमोरी में समा रहा नहीं।

एक फ़ंक्शन, शून्य विकल्प

पूरा API, ठीक वैसा ही जैसा आधुनिक PHP रिपोर्ट करता है:

base64_encode(string $string): string

फिर से पढ़ें। एक पैरामीटर, एक रिटर्न मान, कोई फ़्लैग नहीं। मैनुअल इसे MIME base64 कहता है, "जिसका डिज़ाइन बाइनरी डेटा को 8-बिट क्लीन न होने वाले ट्रांसपोर्ट लेयर्स, जैसे मेल बॉडीज़, से होकर बचाने के लिए है"। ध्यान दें कि उस शब्दबद्धता में क्या नहीं कहा गया: कोई लाइन ब्रेक नहीं, कोई रैपिंग नहीं, आउटपुट कहाँ बसेगा इस पर कोई राय नहीं। फ़ंक्शन एक लंबी लाइन निकालता है, और पता की जगह जैसी भी रैपिंग चाहे वह दूसरी कॉल में आपका काम है। PHP 8.0 से साइग्नेचर के साथ नेटिव टाइप चलते हैं; PHP 8.1 से null पास करने पर एक डीप्रेकेशन नोटिस निकलती है, इसलिए पहले किसी भी न्युलेबल मान को '' पर कोयलेस कर लें।

आउटपुट का साइज़ एक फ़िक्स्ड पैटर्न का पालन करता है जिसे कॉल करने से पहले ही अनुमान लगाया जा सकता है:

इनपुट बाइट आउटपुट चर पैडिंग
0 0 कोई नहीं
1 4 दो =
2 4 एक =
3 4 कोई नहीं
3,000,000 4,000,000 कोई नहीं
100,000 133,336 दो =

पैटर्न है: हर पूरे तीन-बाइट ग्रुप पर चार चर, साथ में आख़िरी अधूरा ग्रुप जो एक दो = निशानों से पैड होता है। एक परिणाम जानने लायक है: एक बाइट और तीन बाइट दोनों चार चर बनाते हैं, इसलिए एन्कोडेड लंबाई इनपुट की सही साइज़ छुपाती है। आप अनुमान लगा सकते हैं (चार से भागें, तीन से गुणा करें, पैड घटाएं), लेकिन बिल्कुल सही पढ़कर नहीं निकाल सकते।

लाइन ब्रेक कहाँ जाएँ

क्योंकि base64_encode() कभी ख़ुद रैप नहीं करता, रैपिंग का फैसला पता की जगह का सवाल है। अमल में तीन जवाब हैं।

कोई लाइन ब्रेक नहीं। फ़ंक्शन का कच्चा आउटपुट, बिल्कुल एक लाइन। URLs, JSON पेलोड्स, हेडर्स, डेटाबेस वैल्यूज़, और बाक़ी हर जगह यही चाहिए जहाँ लाइन ब्रेक बग बन जाता। यही सबसे ज़्यादा लोग "बस मुझे Base64 दे दो" से भी मतलब रखते हैं।

MIME रैपिंग: 76 चर प्लस CRLF। RFC 2045, सेक्शन 6.8 की ईमेल रस्म: एन्कोडेड लाइनें 76 चरों से ज़्यादा लंबी नहीं हो सकतीं, और डिकोडर लाइन ब्रेक को नज़रअंदाज़ करना ज़रूरी है। क्लासिक साथी कॉल chunk_split() है, जिसे मैनुअल इसी वजह से "See Also" लिस्ट में base64_encode() के साथ जोड़े हुए दिखाता है:

$binary = file_get_contents('/var/www/uploads/report.pdf');
$wrapped = chunk_split(base64_encode($binary), 76, "\r\n");

PEM रैपिंग: 64 चर प्लस LF। कुंजियाँ और प्रमाणपत्र पुरानी Privacy-Enhanced Mail रस्म (RFC 1421) इस्तेमाल करते हैं: छोटी 64-चर लाइनें। वही औज़ार, अलग नंबर:

$der = file_get_contents('/etc/ssl/raw-key.der');
$armor = "-----BEGIN PRIVATE KEY-----\n"
  . chunk_split(base64_encode($der), 64, "\n")
  . "-----END PRIVATE KEY-----\n";

दोनों रैप किए गए फ़्लेवर पर chunk_split() का एक गॉच लागू होता है: जब इनपुट की लंबाई लाइन लंबाई की बिल्कुल गुणज हो तब भी फ़ंक्शन आउटपुट के अंत में सेपरेटर जोड़ देता है। अगर नीचे कोई कन्ज़्युमर आख़िरी ख़ाली लाइन पर ठेठ जाए, तो यही वजह है; सेपरेटर पर rtrim() कॉल इसे ठीक कर देती है। साथ ही उस असममितता को नोट करें जो कभी आपकी जान बचाएगी: डिकोडर लाइन ब्रेक को बिल्कुल नज़रअंदाज़ करते हैं, इसलिए MIME-रैप पेलोड और बिना रैप पेलोड दोनों एक जैसी बाइट्स में डिकोड होते हैं। रैपिंग लाइन-आधारित टूलों और इंसानों के लिए एक सुविधा है, कोई अर्थ-अंतर नहीं।

आउटपुट को URL-सुरक्षित बनाना

स्टैंडर्ड वर्णमाला में + और / शामिल हैं, और टेक्स्ट फ़ाइल के बाहर दोनों मुसीबत हैं। फ़ॉर्म-एन्कोडेड क्वेरी स्ट्रिंग में + आपके ऐप को दिखने से पहले ही स्पेस बन जाता है, और URL में / पाथ सेपरेटर है। फ़ाइल-नामों और टोकन्स के अपने-अप शिकायत हैं। RFC 4648, सेक्शन 5, इसे URL- और फ़ाइल-नाम-सेफ़ वर्णमाला से हल करता है: + बन जाता है -, / बन जाता है _, और आख़िरी = पैडिंग आमतौर पर छोड़ दी जाती है। RFC ज़ोर देता है कि इसे "base64 एन्कोडिंग से समान नहीं माना जाना चाहिए", इसलिए इसे एक अलग फ़ॉर्मैट के तौर पर उठाएं, जिसे आम तौर पर base64url कहते हैं।

इसे बनाना दो स्ट्रिंग ऑपरेशन हैं:

function base64url_encode(string $data): string
{
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
var_dump(base64url_encode("hi>?there\x00bin")); // string(18) "aGk-P3RoZXJlAGJpbg"

कब इस्तेमाल करें: JSON Web Token के हिस्से, OAuth के स्टेट और nonce पैरामीटर, URL पाथ्स में रखे जाने वाले API ID, और वह हर चीज़ जो एड्रेस बार या फ़ाइल-नाम में कॉपी होगी। कब न करें: ईमेल बॉडीज़, PEM कवच, और वह हर जगह जहाँ सामने की तरफ़ स्टैंडर्ड-वर्णमाला वाला कन्ज़्युमर बैठा है, क्योंकि - और _ उनके शब्दकोश में नहीं हैं। और दोनों वर्णमालाओं को चुपचाप मत मिलाएँ: URL-सुरक्षित एन्कोड किया गया टोकन हर जगह, हमेशा, URL-सुरक्षित डिकोड होना चाहिए। यही base64url का पूरा इंटरओपरेबिलिटी नियम है।

Unicode और वे बाइट्स जो आपका मतलब थे

PHP स्ट्रिंग्स बाइट्स की कतारें हैं, और base64_encode() जो बाइट्स दिए जाएँ वही एन्कोड कर देगा, यह पूछे बिना कि उनका मतलब क्या है। तब तक यह ख़ासियत है जब तक कि आपका "टेक्स्ट" वाक़ई में वह एन्कोडिंग न हो जो आप समझते हैं। क्लासिक फेल्योर: ऐसी स्ट्रिंग जो आपके एडिटर में UTF-8 जैसी दिखती है, मगर लीगेसी सोर्स से Windows-1252 के रूप में आई है। उन बाइट्स को जैसा-है वैसा एन्कोड करें, तो पाने वाला, जो डिकोड करके UTF-8 मानेगा, आपके एक्सेंट वाले अक्षरों के बजाय मोज़िबेक पाएगा।

इलाज़ है एन्कोड करने से पहले नॉर्मलाइज़ करना, mbstring एक्सटेंशन के साथ (यह PHP सोर्स के साथ शिप होता है, मगर डिफ़ॉल्ट में चालू नहीं होता):

$fromLegacy = "caf\xE9 au lait"; // Windows-1252 बाइट्स: é है 0xE9
$utf8 = mb_convert_encoding($fromLegacy, 'UTF-8', 'Windows-1252');
$encoded = base64_encode($utf8);
var_dump($utf8); // string(13) "café au lait": é अब दो UTF-8 बाइट्स है

अगर सोर्स पहले से UTF-8 है, तो आप कन्वर्ज़न छोड़ सकते हैं, और एक सस्ती सेनिटी जाँच है mb_check_encoding($utf8, 'UTF-8')। एक वाक्य की सलाह: पहले से एन्कोडेड Base64 स्ट्रिंग को "ठीक करने" के लिए उसे टेक्स्ट के रूप में फिर एन्कोड करने की कोशिश कभी न करें। यह नीचे फ़ंदे सेक्शन वाला डबल-एन्कोडिंग फ़ंदा है, और PHP कोडबेस में Base64 बगों का सबसे आम एकल बग यही है।

फ़ाइलें, ब्लॉब्स और .b64 रस्म

सबसे सीधा एन्कोडिंग-काम: फ़ाइल टेक्स्ट बन जाती है। PHP स्ट्रिंग्स बाइट्स होती हैं, इसलिए कोई "बाइनरी मोड" जैसा सवाल ही नहीं है; file_get_contents() आपको बिल्कुल सही बाइट्स सौंपता है और base64_encode() बिल्कुल सही टेक्स्ट सौंपता है:

$binary = file_get_contents('/var/www/uploads/photo.png');
$encoded = base64_encode($binary);
file_put_contents('/var/www/uploads/photo.png.b64', $encoded);
var_dump(strlen($binary), strlen($encoded)); // हर बार 33% का बिल

दो आदतें इसे सुरक्षित रखती हैं। पहली: जान लें कि आप क्या एन्कोड कर रहे हैं। finfo क्लास (fileinfo एक्सटेंशन, जो स्टैंडर्ड PHP बिल्ड्स के साथ बंडल होता है) बाइट्स से, फ़ाइल-नाम से नहीं, असली टाइप बताती है:

$mime = (new finfo(FILEINFO_MIME_TYPE))->file('/var/www/uploads/photo.png');
var_dump($mime); // string(9) "image/png"

दूसरी: स्टोरेज की योजना बनाते समय साइज़ बिल का ख़्याल रखें: 500 KB की छवि 670 KB की टेक्स्ट फ़ाइल बन जाती है, और 1 GB का वीडियो 1.33 GB की टेक्स्ट फ़ाइल। यही वजह है कि नीचे बड़े डेटा वाला सेक्शन मौजूद है।

डेटा URI: छवि को पेज में रोपना

डेटा URI पेलोड को सीधे URL में रोपता है, इसलिए उसे लाने के लिए दूसरी रिक्वेस्ट की ज़रूरत नहीं। RFC 2397 आकार की परिभाषा देता है: data:, एक वैकल्पिक मीडिया टाइप, एक वैकल्पिक ;base64 फ़्लैग, एक कॉमा, और डेटा। छवियों जैसे बाइनरी मीडिया के लिए फ़्लैग मौजूद होता है, इसलिए पेलोड बिल्कुल वही है जो base64_encode() ने बनाया:

function data_uri_for(string $path): string
{
  $mime = (new finfo(FILEINFO_MIME_TYPE))->file($path);
  $payload = base64_encode(file_get_contents($path));
  return 'data:' . $mime . ';base64,' . $payload;
}
$uri = data_uri_for('/var/www/uploads/avatar.png');
echo '<img src="' . $uri . '" alt="avatar" />' . "\n";

यहाँ Base64 का काम ही क्या है? क्योंकि URI में राव बाइट्स या कॉमा सुरक्षित रूप से नहीं रखे जा सकते, और Base64 वर्णमाला को बिल्कुल एस्केपिंग की ज़रूरत नहीं होती। ट्रेड-ऑफ़्स असली हैं। एन्कोडेड पेलोड फ़ाइल से करीब 33 प्रतिशत बड़ा होता है, जिससे HTML डॉक्यूमेंट खुद बड़ा जाता है। ब्राउज़र डेटा URI को फ़ाइल URL की तरह कैश नहीं करते, इसलिए हर पेज व्यू पर बाइट्स दोबारा डाउनलोड होते हैं। और RFC खुद कहता है कि डेटा URI सिर्फ़ छोटे मानों के काम आते हैं; पुराने HTML पार्सर में एट्रिब्यूट लंबाई की सख़्त सीमाएँ थीं, और आधुनिक ब्राउज़र, ज़्यादा सहिष्णु होने के बावजूद, टैग के अंदर मेगाबाइट से खुश नहीं होते। एवेटार, आइकॉन्स, और छोटे इनलाइन ग्राफ़िक्स के लिए इन्हें इस्तेमाल करें; बाक़ी सब के लिए असली फ़ाइलें इस्तेमाल करें।

JWTs और API टोकन्स

JSON Web Tokens आधुनिक APIs में Base64 के फ़्लैगशिप कन्ज़्युमर हैं, और वे उपर के सेक्शन वाला URL-सुरक्षित, बिना पैडिंग वाला डायलैक्ट इस्तेमाल करते हैं। RFC 7519 के अनुसार, कम्पैक्ट JWT तीन डॉट से अलग किए गए base64url हिस्सों से बनता है: हेडर, पेलोड, सिग्नेचर। हेडर और पेलोड सादा JSON हैं; सिग्नेचर राव बाइट्स हैं। हाथ से एक बनाना हर मूविंग पार्ट को देखने का सुखद तरीका है:

function base64url_encode(string $data): string
{
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
$header = base64url_encode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$payload = base64url_encode(json_encode([
  'sub' => '1234567890',
  'name' => 'John Doe',
  'iat' => 1516239022,
]));
$signingInput = $header . '.' . $payload;
$signature = base64url_encode(hash_hmac('sha256', $signingInput, $secret, true));
$token = $signingInput . '.' . $signature;

दो चीज़ें नोट करें। सिग्नेचर राव HMAC बाइट्स का base64url एन्कोडिंग है, इसलिए hash_hmac() राव आउटपुट के लिए true के साथ कॉल होता है। और हेडर और पेलोड किसी भी इंसान के लिए पढ़े जा सकते हैं, जो डिज़ाइन से है: JWT एक साइन किया हुआ टिकट है, कोई गुप्त नहीं। प्रोडक्शन के लिए, आप साइनिंग या वेरिफ़िकेशन हाथ से नहीं लिखते। कॉम्युनिटी पैकेज है firebase/php-jwt (v7, PHP 8.0 या नया ज़रूरी), जो Composer से इंस्टॉल होता है:

composer require firebase/php-jwt
use Firebase\JWT\JWT;
$secret = 'correct-horse-battery-staple-long-enough-secret';
$token = JWT::encode([
  'sub' => '1234567890',
  'name' => 'John Doe',
  'exp' => time() + 3600,
], $secret, 'HS256');
var_dump(substr_count($token, '.')); // int(2): header, payload, सिग्नेचर

लाइब्रेरी की v7 लाइन के लिए वर्ज़न नोट: HMAC एल्गोरिदम न्यूनतम कुंजी लंबाई ज़रूरत रखते हैं, इसलिए 32 बाइट से छोटा HS256 सीक्रेट किसी भी एन्कोडिंग से पहले ही अस्वीकार हो जाता है। लंबे सीक्रेट्स बस क़ायदा ही हैं; यह बस लाइब्रेरी को इसके बारे में लापरवाह होने से रोकता है।

लाइब्रेरी आपके लिए base64url कन्वर्ज़न, साइनिंग, और एक्सपायरी जाँच सब संभालती है, और आधा भरोसा जताता डेटा लौटाने के बजाय टाइप्ड एक्सेप्शन थ्रो करती है। इससे टोकन लिखते समय आप base64_encode() से बिल्कुल नहीं छूते, जो बिल्कुल सही ही है।

HTTP: Basic Auth और WebSocket हैंडशेक

दो हेडर-बनाने वाले काम जहाँ PHP Base64 करता है और प्रोटोकॉल बाक़ी सब।

HTTP Basic auth (RFC 7617): क्लाइंट Authorization: Basic भेजता है, साथ में username:password का Base64। इसे बनाना एक स्ट्रिंग जोड़ने का काम है:

function basic_authorization_header(string $username, string $password): string
{
  return 'Basic ' . base64_encode($username . ':' . $password);
}
$headers[] = 'Authorization: ' . basic_authorization_header('alice', 'secret123');

एक बार जोर से बोल लें, क्योंकि RFC आपको बोलने पर मजबूर करता है: यह एन्कोडिंग है, प्रोटेक्शन नहीं। पैकेट कैप्चर वाले किसी को दोनों ही हिस्से एक ही कीस्ट्रोक में हाथ लग जाते हैं, इसलिए Basic auth सिर्फ़ HTTPS कनेक्शन्स पर ही आता है।

WebSocket हैंडशेक (RFC 6455): सर्वर यह साबित करता है कि उसने क्लाइंट सुना, एक बदला हुआ कुंजी इको करके। वह क्लाइंट की Sec-WebSocket-Key को एक फ़िक्स्ड मैजिक GUID से जोड़ता है, परिणाम का SHA-1 लेता है, और डायजेस्ट को base64 एन्कोड करता है। यह स्टैंडर्ड Base64 है, पैडिंग समेत, क्योंकि यह हेडर में रहता है, URL में नहीं:

function websocket_accept_key(string $clientKey): string
{
  return base64_encode(hash('sha1', $clientKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}
$accept = websocket_accept_key('dGhlIHNhbXBsZSBub25jZQ==');
var_dump($accept); // string(28) "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

उदाहरण वही है जो RFC का अपना है, इसलिए यह एक बढ़िया सेल्फ-टेस्ट भी है: अगर आपकी इम्प्लीमेंटेशन वही 28 चर निकाले, तो WebSocket लेयर सही बोल रही है।

ईमेल: मूल उपयोग-केस

इस लेख की बाक़ी हर चीज़ एक तथ्य के वंशज है: SMTP को 7-बिट ASCII उठाने के लिए डिज़ाइन किया गया था, और लोग बाइनरी भेजना चाहते थे। MIME मानक का जवाब, RFC 2045 सेक्शन 6.8 में, था Base64 एक Content-Transfer-Encoding के तौर पर, आपके पहले से मिले दो घर के नियमों के साथ: ज़्यादा से ज़्यादा 76 चरों की लाइनें, और ऐसे डिकोडर जो वर्णमाला से बाहर के हर चर को नज़रअंदाज़ करते हैं। तो एक PDF अटैचमेंट ऐसे यात्रा करता है:

$pdf = file_get_contents('/var/www/uploads/report.pdf');
$attachment = chunk_split(base64_encode($pdf), 76, "\r\n");
// मेल लाइब्रेरी अब $attachment को MIME part में डालती है,
// Content-Transfer-Encoding: base64 के साथ

अमली अंक: एन्कोडिंग खुद का ख़र्च 33 प्रतिशत है, और हर 76 चरों पर CRLF थोड़ा और ले जाता है, इसलिए 100 KB का अटैचमेंट करीब 137 KB टेक्स्ट बनकर निकलता है। जब आप PHP से ईमेल लिखते हैं, तो लाइब्रेरियाँ (PHPMailer और उसके स्थिर रिश्तेदार) रैपिंग आपके लिए कर देती हैं, और आप उन्हें राव बाइनरी सौंपते हैं। अगर कभी किसी राव .eml फ़ाइल में आपको 76-चर चौड़ी अक्षरों की दीवार दिखे, तो अब आपको वही सटीक एल्गोरिदम पता है जिसने उसे पैदा किया।

कुंजियों और प्रमाणपत्रों के लिए PEM कवच

कुंजियों और प्रमाणपत्रों को अक्षरों की दीवार से ज़्यादा चाहिए: उन्हें लेबल चाहिए। PEM कवच है एक BEGIN लाइन, 64-चर रैप किया हुआ Base64 ब्लॉक, और एक END लाइन, Privacy-Enhanced Mail (RFC 1421) से आई रस्म, जिसे OpenSSL जीवित रखता है। PHP का openssl एक्सटेंशन इसी आकार को सीधे बनाता और इस्तेमाल करता है:

$res = openssl_pkey_new([
  'private_key_bits' => 2048,
  'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
openssl_pkey_export($res, $pem);
// $pem पहले से कवच वाला है: BEGIN लेबल, 64-चर लाइनें, END लेबल

मज़ेदार मामला तब है जब कवच हाथ से फिर से बनाना पड़े, उदाहरण के तौर पर जब आप किसी API से राव DER बाइट्स पाएँ और किसी टूल के लिए PEM फ़ाइल चाहिए हो जो सिर्फ़ PEM पढ़ता हो। रस्म है 64 चर प्रति लाइन, LF लाइन ब्रेक, और एक ऐसा लेबल जो सामग्री का नाम लेता है:

$rearmored = "-----BEGIN CERTIFICATE-----\n"
  . chunk_split(base64_encode($der), 64, "\n")
  . "-----END CERTIFICATE-----\n";
var_dump(openssl_x509_parse($rearmored) !== false); // bool(true): कवच वैध है

लेबल ग़लत करें तो फ़ाइल कचरा है, Base64 कितनी भी सही क्यों न हो। और लाइन लंबाई ग़लत करें तो ज़्यादातर टूल फिर भी पढ़ लेंगे, क्योंकि डिकोडर लाइन ब्रेक को नज़रअंदाज़ करते हैं, मगर डिफ़ टूल और इंसान पीड़ित होंगे। 64 ही वह नंबर है।

कॉन्फ़िग फ़ाइलें, एनवायरनमेंट वेरिएबल्स और डेटाबेस

Base64 एक टेक्स्ट कंटेनर है, और इसीलिए यह उन मानों का स्मगलिंग औज़ार बनता है जो वरना अपने कंटेनर को तोड़ देते। सेमीकोलॉन्स और कोट्स से भरा डेटाबेस DSN, .env फ़ाइल में JWT, TEXT कॉलम में बाइनरी ब्लॉब: सब एक लंबी सुरक्षित स्ट्रिंग बन जाते हैं।

एनवायरनमेंट वेरिएबल वाला फ़्लेवर दो-कदमी रस्म है। एक बार, उस मशीन पर जो कॉन्फ़िग बनाती है, आप एन्कोड करते हैं:

echo 'DB_DSN_B64=' . base64_encode('pg:host=db;password=qu"ote') . PHP_EOL;

फिर, application के हर बूट पर, आप स्टार्टअप पर डिकोड और वैलिडेट करते हैं, ताकि आधा-पेस्ट कॉन्फ़िग क्रिप्टिक के बजाय जोर से फेल हो:

$dsn = base64_decode(getenv('DB_DSN_B64') ?: '', true);
if ($dsn === false) {
  exit('DB_DSN_B64 is not valid Base64.');
}

डेटाबेस के लिए, वही सोच बाइनरी को टेक्स्ट कॉलम्स में स्टोर करती है। साइज़ बिल लागू होता है: स्टोर किया गया मान ब्लॉब से करीब 33 प्रतिशत बड़ा होता है, इसलिए 1 MB की फ़ाइल कॉलम में करीब 1.33 MB जगह लेती है, और आपको कॉलम टाइप उसी सोच के साथ चुनना चाहिए। और बाक़ी हर जगह वाली वही चेतावनी: यह फ़ॉर्मेट सेफ़्टी है, सीक्रेसी नहीं। जो भी कॉन्फ़िग पढ़ सकता है या कॉलम क्वेरी कर सकता है, वह एक ही कॉल में उसे उलट सकता है। अगर मान संवेदनशील है, तो उसे एन्क्रिप्ट करें; Base64 बस उसे पोर्टेबल बनाता है।

बड़ा डेटा और स्थिर मेमोरी

एन्कोडिंग वह दिशा है जो आपको ख़र्च करती है: आउटपुट इनपुट से एक-तिहाई बड़ा होता है, इसलिए 2 GB बाइनरी को मेमोरी में 2.66 GB एन्कोडेड स्ट्रिंग चाहिए। लंबे समय तक चलने वाले वेब प्रोसेस या मेमोरी-सीमित होस्ट पर, यह वही वजह है कि आप स्लर्प के बजाय स्ट्रीम करें, और PHP आपको दो तरीके देता है।

पहला तरीका है convert.base64-encode स्ट्रीम फ़िल्टर, इस फ़ंक्शन का स्ट्रीमिंग साथी। यह पैरामीटर को एसोसिएटिव आरे के तौर पर सहन करता है: रैप चौड़ाई के लिए line-length और सेपरेटर के लिए line-break-chars, जो पूरा स्ट्रिंग अपने पास रखे बिना chunk_split() का असर दोहराता है:

$in = fopen('/var/www/uploads/big.bin', 'rb');
$out = fopen('/var/www/uploads/big.b64', 'wb');
stream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, [
  'line-length' => 64,
  'line-break-chars' => "\n",
]);
stream_copy_to_stream($in, $out);
fclose($in);
fclose($out);

दूसरा तरीका क्लासिक 57-बाइट ट्रिक है, और यह PHP लोर का छोटा सा टुकड़ा है। 76-चर MIME लाइन में मूल डेटा के बिल्कुल 57 बाइट बसते हैं, इसलिए अगर आप इनपुट फ़ाइल को 57 बाइट की गुणज टुकड़ों में पढ़ें, तो हर चंक स्वतंत्र रूप से एन्कोड होता है, चंक-दर-चंक किसी अंदर-बाहर जाने वाले बिट्स के बिना। 8151-बाइट टुकड़ों में पढ़ने (57 बार 143: आउटपुट की 143 पूरी 76-चर लाइनें, PHP की पारंपरिक 8192-बाइट I/O बफ़र के करीब) से मेमोरी सपाट रहती है, जबकि फ़ाइल MIME-परफ़ेक्ट बहती हुई बाहर निकलती है:

$in = fopen('/var/www/uploads/big.bin', 'rb');
$out = fopen('/var/www/uploads/big.b64', 'wb');
while (!feof($in)) {
  $plain = fread($in, 57 * 143);
  $encoded = chunk_split(base64_encode($plain), 76, "\r\n");
  fwrite($out, $encoded);
}
fclose($in);
fclose($out);

कौन सा चुनें? फ़िल्टर तब, जब आप चाहते हों कि PHP पूरा प्लंबिंग संभाले और आपको ठीक-से-ठीक चंक सीमाओं की परवाह नहीं; 57-बाइट लूप तब, जब आपको डेटर्मिनिस्टिक MIME आउटपुट, प्रोग्रेस हुक्स, या बफ़र साइज़ की सख़्त सीमा चाहिए। दोनों में, मेमोरी फ़ुटप्रिंट एक चंक पर रहती है, एक फ़ाइल पर नहीं।

PHP-लहजा वाली गड़वाहटें

वे फ़ंदे जो असली PHP कोडबेस में दिखते हैं, एक जगह इकट्ठे:

  • डबल एन्कोडिंग। क्लासिक: ऐसा मान जो पहले से Base64 है (किसी एनव्-वेरिएबल, डेटाबेस, पिछले स्क्रिप्ट से) बिना किसी की जाँच के फिर से base64_encode() से गुज़र जाता है। रिज़ल्ट एक बार डिकोड होता है और हाथ आता है... और Base64। इलाज बाउंडरी पर एक राउंड-ट्रिप जाँच है, या कोडबेस की सारी एन्कोडिंग का मालिक बनने वाला एक ही मशहूर फ़ंक्शन।
  • URL में +। स्टैंडर्ड आउटपुट में + और / होते हैं। फ़ॉर्म-एन्कोडेड क्वेरी स्ट्रिंग में प्लस आपके कोड को दिखने से पहले स्पेस बन जाता है; पाथ में वह सेपरेटर है। किसी भी URL-जुड़ी चीज़ के लिए base64url निकालें या पूरे मान को rawurlencode() से परसेंट-एन्कोड करें।
  • आख़िरी सेपरेटर। chunk_split() लाइन लंबाई की बिल्कुल गुणज पर भी रिज़ल्ट को सेपरेटर पर ख़त्म करता है। आख़िरी ख़ाली लाइन आमतौर पर बेज़ारह रहती है (डिकोडर उसे नज़रअंदाज़ करते हैं), मगर यह सरसरी लाइन-गिनती और डिफ़ टूलों को उलझाती है। अगर कन्ज़्युमर चुटीला है तो सेपरेटर को rtrim() कर दें।
  • रैप का मेल न होना। 76-चर MIME लाइनों में लिखना और कन्ज़्युमर का 64-चर PEM लाइनों की उम्मीद रखना (या उल्टा) डिकोडिंग की समस्या नहीं है, क्योंकि डिकोडर ब्रेक को नज़रअंदाज़ करते हैं, मगर यह लाइन-टूलिंग और इंसानी पढ़ाई की समस्या है। उस रस्म को चुनें जो आपकी पता की जगह अपेक्षा करती है, और उसी पर टिके रहें।
  • आख़िरी न्यूलाइन डेटा है। base64_encode() हर बाइट एन्कोड करता है, टेक्स्ट फ़ाइल के अंत का न्यूलाइन समेत। जब दो सिस्टम्स एक जैसी दिखने वाली टेक्स्ट के लिए "अलग" Base64 पैदा करते हैं, तो आख़िरी \n ही आम शकनीय है।
  • Base64 एन्क्रिप्शन नहीं है। डेटाबेस में पड़ने से पहले पासवर्ड को एन्कोड करना उसे प्रोटेक्ट नहीं करता; उसे फ़ॉर्मेट करता है। "एन्क्रिप्टेड" कॉलम क्वेरी एक्सेस वाले किसी के लिए प्लेन टेक्स्ट से एक फ़ंक्शन कॉल दूर है। असली सीक्रेट्स को एन्क्रिप्ट या हैश करें; Base64 ट्रांसपोर्ट का कपड़ा है।
  • मेमोरी एक-तिहाई बड़ी। 32-बिट PHP बिल्ड या कसके मेमोरी सीमा वाले होस्ट पर, बड़े बाइनरी को एन्कोड करना बिल्कुल फेल हो सकता है। memory_limit ट्यून करने से पहले, उपर दिखाए तौर पर उसे स्ट्रीम करें।
  • लाइन ब्रेक कभी नहीं जोड़े जाते। फ़ंक्शन विवरण में "MIME base64" से मतलब "MIME-रैप आउटपुट" नहीं है। अगर आपके आउटपुट को 76-चर लाइनों की ज़रूरत है, तो आप chunk_split() या फ़िल्टर से उन्हें जोड़ते हैं।

base64_encode का छोटा-सा इतिहास

PHP की कहानी का एन्कोडिंग पक्ष सबसे अच्छे अर्थ में ताज़गी देने वाली बोरिंग है। base64_encode() PHP 4 में कॉर फ़ंक्शन के रूप में आया, एक पैरामीटर के साथ और बिना किसी ऑप्शन, और उसने अब तक एक भी नहीं जोड़ा है। सख़्त मोड की कभी ज़रूरत ही नहीं पड़ी (जब आप खुद डेटा पैदा कर रहे हों, तो सख़्त होने के लिए कुछ नहीं है), पैडिंग ऑप्शन कभी नहीं जोड़ा गया, और रैपिंग का काम पहले दिन से chunk_split() को सौंप दिया गया, यही वजह है कि दो फ़ंक्शन आज भी मैनुअल की "See Also" लिस्टों में साथ बैठे हैं।

33 प्रतिशत का आंकड़ा मैनुअल में किसी को याद हो तब से चल रहा है: "Base64-एन्कोडेड डेटा मूल डेटा से करीब 33% ज़्यादा स्पेस लेता है"। वह वाक्य आज भी वही है, और यही वजह है कि आंकड़ा इस लेख में बस है। स्ट्रीम फ़िल्टर convert.base64-encode बाद में जुड़ा, और उसके पास अपना बग था जिससे बड़ना था: 2015 में, PHP ने एक ख़राबी (बग #68532) ठीक की जिसमें फ़िल्टर, मेमोरी स्ट्रीम्स पर रीड मोड में, आख़िरी पैडिंग चर छोड़ सकता था, जो वही ख़ामोश ख़राबी है जिसे फ़ंक्शन-आधारित पथ कभी नहीं सहता। PHP 8.0 ने नेटिव string पैरामीटर और रिटर्न टाइप्स जोड़े, और चेंजलॉग वहीं ख़त्म होता है। एक फ़ंक्शन, एक पैरामीटर, बीस साल, शून्य ऑप्शन: सतह को पहले बार सही पकड़ने का स्मारक।

मज़ेदार PHP तथ्य

क्योंकि रीफ़रेंस तब तक अधूरा है जब तक उसके अजीब-गरीब नहीं:

  • ख़ालीपन की पहचान। base64_encode('') '' है। न पैडिंग, न आउटपुट, न सरप्राइज़: ख़ालीपन अंदर, ख़ालीपन बाहर।
  • एक अजीब पता। PHP मैनुअल base64_encode() को "अन्य बेसिक एक्सटेंशन" किताब में "URLs" के तहत दर्ज करता है। "एन्कोडिंग" का कोई अध्याय नहीं है; "URLs" वही जगह है जहाँ आप इसे पाएँगे, parse_url() के साथ।
  • वर्णमाला कभी नहीं हिली। PHP 4 से लेकर आज तक PHP के एन्कोडर से वही 64 चर निकल रहे हैं। 2001 में Windows बॉक्स पर PHP 4 स्क्रिप्ट से बना Base64 स्ट्रिंग आज Linux पर PHP 8.4 पर बिल्कुल वैसे ही डिकोड होती है। यह 25 साल का ट्रैक रिकॉर्ड वाला इंटरओपरेबिलिटी है।
  • एक बाइट और तीन बाइट लंबाई में एक जैसी दिखती हैं। दोनों चार चर बनाते हैं; फ़र्क़ सिर्फ़ पैडिंग बताती है। यही वजह है कि इस लेख में वह साइज़ टेबल मौजूद है।
  • 57 एक जादुई नंबर है। 76-चर MIME लाइन में मूल डेटा के बिल्कुल 57 बाइट बसते हैं, और यही संयोग बड़े डेटा वाले सेक्शन के स्ट्रीमिंग चंक लूप को पढ़ने के बीच स्टेट संभाले बिना संभव बनाता है।
  • इसका एक डायल-अप भाई है। base64_encode() की "See Also" में convert_uuencode() भी सूचीबद्ध है, uuencode का PHP रैपर, वह फ़ॉर्मैट जिसने MIME के Base64 को मानक बनाने से पहले ईमेल के लिए बाइनरी एन्कोड किया। यह स्ट्रिंग फ़ंक्शन्स अध्याय में रहता है, मगर यह इस फ़ंक्शन के मूल उद्देश्य का जीवाश्म-रिकॉर्ड है।
  • पुराने ब्राउज़र पैडिंग के लिए चुटीले थे। 2004 का एक php.net नोट रिपोर्ट करता है कि Internet Explorer = वाला कूकी नाम मना कर देता था, यही वजह है कि वैटेरन कोड कभी-कभी कूकी में स्टोर किए Base64 से आख़िरी पैड हटा देता है। आधुनिक सेटअप्स को उस ट्रिक की ज़रूरत नहीं, मगर यह उन अजीब rtrim($x, '=') कॉलों की व्याख्या करता है जो आप विरासत में पा सकते हैं।

उसकी दूसरी तरफ़

यह थी एन्कोडिंग की तरफ़, और यह दोनों में आसान है: फ़ंक्शन फेल नहीं हो सकता, आउटपुट डेटर्मिनिस्टिक है, और फ़ॉर्मैट वह है जो आप सिरे से सिरे तक संभालते हैं। मुश्किल दिशा वही है जहाँ आप दूसरों का Base64 पाते हैं: उनकी पैडिंग की पसंद, उनके लाइन ब्रेक, उनके URL-सुरक्षित डायलैक्ट, उनके ख़राब पेस्ट। वही जगह है जहाँ डिकोडर को सख़्त मोड, एक वैलिडेशन पाइपलाइन, और सब कुछ के लिए स्वस्थ संदेह चाहिए। PHP में Base64 डिकोडिंग, जो इस पेज से जुड़ा है, डिकोडिंग पक्ष को उसी गहराई में कवर करता है।

अंतिम अपडेट: 2026-09-08

संबंधित लेख: PHP में Base64 डिकोडिंग: एक सम्पूर्ण गाइड