├── Base2n.php ├── LICENSE.txt ├── README.md └── composer.json /Base2n.php: -------------------------------------------------------------------------------- 1 | = ($radix <<= 1) && $bitsPerCharacter < 8) { 92 | $bitsPerCharacter++; 93 | } 94 | 95 | $radix >>= 1; 96 | throw new InvalidArgumentException( 97 | '$bitsPerCharacter can not be more than ' . $bitsPerCharacter 98 | . ' given $chars length of ' . $charLength 99 | . ' (max radix ' . $radix . ')'); 100 | 101 | } elseif ($bitsPerCharacter > 8) { 102 | // $bitsPerCharacter must not be greater than 8 103 | throw new InvalidArgumentException('$bitsPerCharacter can not be greater than 8'); 104 | 105 | } else { 106 | $radix = 1 << $bitsPerCharacter; 107 | } 108 | 109 | $this->_chars = $chars; 110 | $this->_bitsPerCharacter = $bitsPerCharacter; 111 | $this->_radix = $radix; 112 | $this->_rightPadFinalBits = $rightPadFinalBits; 113 | $this->_padFinalGroup = $padFinalGroup; 114 | $this->_padCharacter = $padCharacter[0]; 115 | $this->_caseSensitive = $caseSensitive; 116 | } 117 | 118 | /** 119 | * Encode a string 120 | * 121 | * @param string $rawString Binary data to encode 122 | * @return string 123 | */ 124 | public function encode($rawString) 125 | { 126 | // Unpack string into an array of bytes 127 | $bytes = unpack('C*', $rawString); 128 | $byteCount = count($bytes); 129 | 130 | $encodedString = ''; 131 | $byte = array_shift($bytes); 132 | $bitsRead = 0; 133 | $oldBits = 0; 134 | 135 | $chars = $this->_chars; 136 | $bitsPerCharacter = $this->_bitsPerCharacter; 137 | $rightPadFinalBits = $this->_rightPadFinalBits; 138 | $padFinalGroup = $this->_padFinalGroup; 139 | $padCharacter = $this->_padCharacter; 140 | 141 | $charsPerByte = 8 / $bitsPerCharacter; 142 | $encodedLength = $byteCount * $charsPerByte; 143 | 144 | // Generate encoded output; each loop produces one encoded character 145 | for ($c = 0; $c < $encodedLength; $c++) { 146 | 147 | // Get the bits needed for this encoded character 148 | if ($bitsRead + $bitsPerCharacter > 8) { 149 | // Not enough bits remain in this byte for the current character 150 | // Save the remaining bits before getting the next byte 151 | $oldBitCount = 8 - $bitsRead; 152 | $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount); 153 | $newBitCount = $bitsPerCharacter - $oldBitCount; 154 | 155 | if (!$bytes) { 156 | // Last bits; match final character and exit loop 157 | if ($rightPadFinalBits) $oldBits <<= $newBitCount; 158 | $encodedString .= $chars[$oldBits]; 159 | 160 | if ($padFinalGroup) { 161 | // Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8 162 | $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1); 163 | $bytesPerGroup = $lcmMap[$bitsPerCharacter]; 164 | $pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte); 165 | $encodedString .= str_repeat($padCharacter, $pads); 166 | } 167 | 168 | break; 169 | } 170 | 171 | // Get next byte 172 | $byte = array_shift($bytes); 173 | $bitsRead = 0; 174 | 175 | } else { 176 | $oldBitCount = 0; 177 | $newBitCount = $bitsPerCharacter; 178 | } 179 | 180 | // Read only the needed bits from this byte 181 | $bits = $byte >> 8 - ($bitsRead + ($newBitCount)); 182 | $bits ^= $bits >> $newBitCount << $newBitCount; 183 | $bitsRead += $newBitCount; 184 | 185 | if ($oldBitCount) { 186 | // Bits come from seperate bytes, add $oldBits to $bits 187 | $bits = ($oldBits << $newBitCount) | $bits; 188 | } 189 | 190 | $encodedString .= $chars[$bits]; 191 | } 192 | 193 | return $encodedString; 194 | } 195 | 196 | /** 197 | * Decode a string 198 | * 199 | * @param string $encodedString Data to decode 200 | * @param boolean $strict Returns NULL if $encodedString contains an undecodable character 201 | * @return string 202 | */ 203 | public function decode($encodedString, $strict = FALSE) 204 | { 205 | if (!$encodedString || !is_string($encodedString)) { 206 | // Empty string, nothing to decode 207 | return ''; 208 | } 209 | 210 | $chars = $this->_chars; 211 | $bitsPerCharacter = $this->_bitsPerCharacter; 212 | $radix = $this->_radix; 213 | $rightPadFinalBits = $this->_rightPadFinalBits; 214 | $padFinalGroup = $this->_padFinalGroup; 215 | $padCharacter = $this->_padCharacter; 216 | $caseSensitive = $this->_caseSensitive; 217 | 218 | // Get index of encoded characters 219 | if ($this->_charmap) { 220 | $charmap = $this->_charmap; 221 | 222 | } else { 223 | $charmap = array(); 224 | 225 | for ($i = 0; $i < $radix; $i++) { 226 | $charmap[$chars[$i]] = $i; 227 | } 228 | 229 | $this->_charmap = $charmap; 230 | } 231 | 232 | // The last encoded character is $encodedString[$lastNotatedIndex] 233 | $lastNotatedIndex = strlen($encodedString) - 1; 234 | 235 | // Remove trailing padding characters 236 | if ($padFinalGroup) { 237 | while ($encodedString[$lastNotatedIndex] === $padCharacter) { 238 | $encodedString = substr($encodedString, 0, $lastNotatedIndex); 239 | $lastNotatedIndex--; 240 | } 241 | } 242 | 243 | $rawString = ''; 244 | $byte = 0; 245 | $bitsWritten = 0; 246 | 247 | // Convert each encoded character to a series of unencoded bits 248 | for ($c = 0; $c <= $lastNotatedIndex; $c++) { 249 | 250 | if (!$caseSensitive && !isset($charmap[$encodedString[$c]])) { 251 | // Encoded character was not found; try other case 252 | if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) { 253 | $charmap[$encodedString[$c]] = $charmap[$cUpper]; 254 | 255 | } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) { 256 | $charmap[$encodedString[$c]] = $charmap[$cLower]; 257 | } 258 | } 259 | 260 | if (isset($charmap[$encodedString[$c]])) { 261 | $bitsNeeded = 8 - $bitsWritten; 262 | $unusedBitCount = $bitsPerCharacter - $bitsNeeded; 263 | 264 | // Get the new bits ready 265 | if ($bitsNeeded > $bitsPerCharacter) { 266 | // New bits aren't enough to complete a byte; shift them left into position 267 | $newBits = $charmap[$encodedString[$c]] << $bitsNeeded - $bitsPerCharacter; 268 | $bitsWritten += $bitsPerCharacter; 269 | 270 | } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) { 271 | // Zero or more too many bits to complete a byte; shift right 272 | $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount; 273 | $bitsWritten = 8; //$bitsWritten += $bitsNeeded; 274 | 275 | } else { 276 | // Final bits don't need to be shifted 277 | $newBits = $charmap[$encodedString[$c]]; 278 | $bitsWritten = 8; 279 | } 280 | 281 | $byte |= $newBits; 282 | 283 | if ($bitsWritten === 8 || $c === $lastNotatedIndex) { 284 | // Byte is ready to be written 285 | $rawString .= pack('C', $byte); 286 | 287 | if ($c !== $lastNotatedIndex) { 288 | // Start the next byte 289 | $bitsWritten = $unusedBitCount; 290 | $byte = ($charmap[$encodedString[$c]] ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten; 291 | } 292 | } 293 | 294 | } elseif ($strict) { 295 | // Unable to decode character; abort 296 | return NULL; 297 | } 298 | } 299 | 300 | return $rawString; 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2009-2013 Andre DeMarre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Binary-to-Text Utilities for PHP 2 | ================================= 3 | 4 | For now, the only class in this repository is **Base2n**. 5 | 6 | Base2n is for binary-to-text conversion with arbitrary encoding schemes that represent binary data in a base 2n notation. It can handle non-standard variants of many standard encoding schemes such as [Base64][rfc4648base64] and [Base32][rfc4648base32]. Many binary-to-text encoding schemes use a fixed number of bits of binary data to generate each encoded character. Such schemes generalize to a single algorithm, implemented here. 7 | 8 | [rfc4648base64]: http://tools.ietf.org/html/rfc4648#section-4 "RFC 4648 Base64 Specification" 9 | [rfc4648base32]: http://tools.ietf.org/html/rfc4648#section-6 "RFC 4648 Base32 Specification" 10 | 11 | Binary-to-text encoding is usually used to represent data in a notation that is safe for transport over text-based protocols, and there are several other practical uses. See the examples below. 12 | 13 | 14 | 15 | Basic Base2n Usage 16 | ------------------ 17 | 18 | With Base2n, you define your encoding scheme parametrically. Let's instantiate a [Base32][rfc4648base32] encoder: 19 | 20 | ```php 21 | // RFC 4648 base32 alphabet; case-insensitive 22 | $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE); 23 | $encoded = $base32->encode('encode this'); 24 | // MVXGG33EMUQHI2DJOM====== 25 | ``` 26 | 27 | 28 | ### Constructor Parameters 29 | 30 | - integer $bitsPerCharacter **Required**. The number of bits to use for each encoded character; 1–8. The most practical range is 1–6. The encoding's radix is a power of 2: 2^$bitsPerCharacter. 31 | 1. [base-2, binary][binary] 32 | 2. [base-4, quaternary][quaternary] 33 | 3. [base-8, octal][octal] 34 | 4. [base-16, hexadecimal][hexadecimal] 35 | 5. [base-32][base32] 36 | 6. [base-64][base64] 37 | 7. base-128 38 | 8. base-256 39 | 40 | [binary]: http://en.wikipedia.org/wiki/Binary_numeral_system "Binary Notation" 41 | [quaternary]: http://en.wikipedia.org/wiki/Quaternary_numeral_system "Base-2 Notation" 42 | [octal]: http://en.wikipedia.org/wiki/Octal "Octal Notation" 43 | [hexadecimal]: http://en.wikipedia.org/wiki/Base16 "Hexadecimal Notation" 44 | [base32]: http://en.wikipedia.org/wiki/Base32 "Base32 Encoding" 45 | [base64]: http://en.wikipedia.org/wiki/Base64 "Base64 Encoding" 46 | 47 | - string $chars This string specifies the base alphabet. Must be 2^$bitsPerCharacter long. Default: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ 48 | 49 | - boolean $caseSensitive To decode in a case-sensitive manner. Default: FALSE 50 | 51 | - boolean $rightPadFinalBits How to encode the last character when the bits remaining are fewer than $bitsPerCharacter. When TRUE, the bits to encode are placed in the most significant position of the final group of bits, with the lower bits set to 0. When FALSE, the final bits are placed in the least significant position. For [RFC 4648][rfc4648] encodings, $rightPadFinalBitsshould be TRUE. Default: FALSE 52 | 53 | [rfc4648]: http://tools.ietf.org/html/rfc4648 "RFC 4648: Base16, Base32, Base64" 54 | 55 | - boolean $padFinalGroup It's common to encode characters in groups. For example, Base64 (which is based on 6 bits per character) converts 3 raw bytes into 4 encoded characters. If insufficient bytes remain at the end, the final group will be padded with = to complete a group of 4 characters, and the encoded length is always a multiple of 4. Although the information provided by the padding is redundant, some programs rely on it for decoding; Base2n does not. Default: FALSE 56 | 57 | - string $padCharacter When $padFinalGroup is TRUE, this is the pad character used. Default: = 58 | 59 | 60 | ### encode() Parameters 61 | 62 | - string $rawString **Required**. The data to be encoded. 63 | 64 | 65 | ### decode() Parameters 66 | 67 | - string $encodedString **Required**. The string to be decoded. 68 | - boolean $strict When TRUE, NULL will be returned if $encodedString contains an undecodable character. When FALSE, unknown characters are simply ignored. Default: FALSE 69 | 70 | 71 | 72 | Examples 73 | -------- 74 | 75 | PHP does not provide any Base32 encoding functions. By setting $bitsPerCharacter to 5 and specifying your desired alphabet in $chars, you can handle any variant of Base32: 76 | 77 | ```php 78 | // RFC 4648 base32 alphabet; case-insensitive 79 | $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE); 80 | $encoded = $base32->encode('encode this'); 81 | // MVXGG33EMUQHI2DJOM====== 82 | ``` 83 | 84 | ```php 85 | // RFC 4648 base32hex alphabet 86 | $base32hex = new Base2n(5, '0123456789ABCDEFGHIJKLMNOPQRSTUV', FALSE, TRUE, TRUE); 87 | $encoded = $base32hex->encode('encode this'); 88 | // CLN66RR4CKG78Q39EC====== 89 | ``` 90 | 91 | 92 | Octal notation: 93 | 94 | ```php 95 | $octal = new Base2n(3); 96 | $encoded = $octal->encode('encode this'); 97 | // 312671433366214510072150322711 98 | ``` 99 | 100 | 101 | A convenient way to go back and forth between binary notation and its real binary representation: 102 | 103 | ```php 104 | $binary = new Base2n(1); 105 | $encoded = $binary->encode('encode this'); 106 | // 0110010101101110011000110110111101100100011001010010000001110100011010000110100101110011 107 | $decoded = $binary->decode($encoded); 108 | // encode this 109 | ``` 110 | 111 | 112 | PHP uses a proprietary binary-to-text encoding scheme to generate session identifiers from random hash digests. The most efficient way to store these session IDs in a database is to decode them back to their raw hash digests. PHP's encoding scheme is configured with the [session.hash_bits_per_character][phphashbits] php.ini setting. The decoded size depends on the hash function, set with [session.hash_function][phphash] in php.ini. 113 | 114 | ```php 115 | // session.hash_function = 0 116 | // session.hash_bits_per_character = 5 117 | // 128-bit session ID 118 | $sessionId = 'q3c8n4vqpq11i0vr6ucmafg1h3'; 119 | // Decodes to 16 bytes 120 | $phpBase32 = new Base2n(5, '0123456789abcdefghijklmnopqrstuv'); 121 | $rawSessionId = $phpBase32->decode($sessionId); 122 | ``` 123 | 124 | ```php 125 | // session.hash_function = 1 126 | // session.hash_bits_per_character = 6 127 | // 160-bit session ID 128 | $sessionId = '7Hf91mVc,q-9W1VndNNh3evVN83'; 129 | // Decodes to 20 bytes 130 | $phpBase64 = new Base2n(6, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,'); 131 | $rawSessionId = $phpBase64->decode($sessionId); 132 | ``` 133 | 134 | [phphashbits]: http://php.net/manual/en/session.configuration.php#ini.session.hash-bits-per-character "PHP session.hash_bits_per_character" 135 | [phphash]: http://php.net/manual/en/session.configuration.php#ini.session.hash-function "PHP session.hash_function" 136 | 137 | 138 | Generate random security tokens: 139 | ```php 140 | $tokenEncoder = new Base2n(6, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,'); 141 | $binaryToken = openssl_random_pseudo_bytes(32); // PHP >= 5.3 142 | $token = $tokenEncoder->encode($binaryToken); 143 | // Example: U6M132v9FG-AHhBVaQWOg1gjyUi1IogNxuen0i3u3ep 144 | ``` 145 | 146 | 147 | The rest of these examples are probably more fun than they are practical. 148 | 149 | 150 | We can encode arbitrary data with a 7-bit encoding. (Note that this is not the same as the [7bit MIME content-transfer-encoding][7bit].) 151 | ```php 152 | // This uses all 7-bit ASCII characters 153 | $base128chars = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" 154 | . "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" 155 | . "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F" 156 | . "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F" 157 | . "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F" 158 | . "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F" 159 | . "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F" 160 | . "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x69\x7A\x7B\x7C\x7D\x7E\x7F"; 161 | 162 | $base128 = new Base2n(7, $base128chars); 163 | $encoded = $base128->encode('encode this'); 164 | ``` 165 | [7bit]: http://msdn.microsoft.com/en-us/library/ms526290(v=exchg.10).aspx "7bit MIME Content-Transfer-Encoding" 166 | 167 | 168 | The following encoding guarantees that the most significant bit is set for every byte: 169 | ```php 170 | // "High" base-128 encoding 171 | $high128chars = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" 172 | . "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" 173 | . "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" 174 | . "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" 175 | . "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" 176 | . "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" 177 | . "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" 178 | . "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; 179 | 180 | $high128 = new Base2n(7, $high128chars); 181 | $encoded = $high128->encode('encode this'); 182 | ``` 183 | 184 | 185 | Let's create an encoding using exclusively non-printable control characters! 186 | ```php 187 | // Base-32 non-printable character encoding 188 | $noPrintChars = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" 189 | . "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; 190 | 191 | $nonPrintable32 = new Base2n(5, $noPrintChars); 192 | $encoded = $nonPrintable32->encode('encode this'); 193 | ``` 194 | 195 | 196 | Why not encode data using only whitespace? Here's a base-4 encoding using space, tab, new line, and carriage return: 197 | ```php 198 | // Base-4 whitespace encoding 199 | $whitespaceChars = " \t\n\r"; 200 | 201 | $whitespace = new Base2n(2, $whitespaceChars); 202 | $encoded = $whitespace->encode('encode this'); 203 | // "\t\n\t\t\t\n\r\n\t\n \r\t\n\r\r\t\n\t \t\n\t\t \n \t\r\t \t\n\n \t\n\n\t\t\r \r" 204 | 205 | $decoded = $whitespace->decode( 206 | "\t\n\t\t\t\n\r\n\t\n \r\t\n\r\r\t\n\t \t\n\t\t \n \t\r\t \t\n\n \t\n\n\t\t\r \r" 207 | ); 208 | // encode this 209 | ``` 210 | 211 | 212 | 213 | Counterexamples 214 | ---------------- 215 | 216 | Base2n is not slow, but it will never outperform an encoding function implemented in C. When one exists, use it instead. 217 | 218 | 219 | PHP provides the [base64_encode()][base64_encode] and [base64_decode()][base64_decode] functions, and you should always use them for standard Base64. When you need to use a modified alphabet, you can translate the encoded output with [strtr()][strtr] or [str_replace()][str_replace]. 220 | 221 | [base64_encode]: http://php.net/base64_encode "PHP base64_encode() Function" 222 | [base64_decode]: http://php.net/base64_decode "PHP base64_decode() Function" 223 | [strtr]: http://php.net/strtr "PHP strtr() Function" 224 | [str_replace]: http://php.net/str_replace "PHP str_replace() Function" 225 | 226 | A common variant of Base64 is [modified for URLs and filenames][rfc4648base64url], where + and / are replaced with - and _, and the = padding is omitted. It's better to handle this variant with native PHP functions: 227 | 228 | ```php 229 | // RFC 4648 base64url with Base2n... 230 | $base64url = new Base2n(6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', TRUE, TRUE, FALSE); 231 | $encoded = $base64url->encode("encode this \xBF\xC2\xBF"); 232 | // ZW5jb2RlIHRoaXMgv8K_ 233 | 234 | // RFC 4648 base64url with native functions... 235 | $encoded = str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode("encode this \xBF\xC2\xBF")); 236 | // ZW5jb2RlIHRoaXMgv8K_ 237 | ``` 238 | 239 | [rfc4648base64url]: http://tools.ietf.org/html/rfc4648#page-7 "Modified Base64 for URLs" 240 | 241 | 242 | Native functions get slightly more cumbersome when every position in the alphabet has changed, as seen in this example of [decoding a Bcrypt hash][bmcf]: 243 | ```php 244 | // Decode the salt and digest from a Bcrypt hash 245 | 246 | $hash = '$2y$14$i5btSOiulHhaPHPbgNUGdObga/GC.AVG/y5HHY1ra7L0C9dpCaw8u'; 247 | $encodedSalt = substr($hash, 7, 22); 248 | $encodedDigest = substr($hash, 29, 31); 249 | 250 | // Using Base2n... 251 | $bcrypt64 = new Base2n(6, './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', TRUE, TRUE); 252 | $rawSalt = $bcrypt64->decode($encodedSalt); // 16 bytes 253 | $rawDigest = $bcrypt64->decode($encodedDigest); // 23 bytes 254 | 255 | // Using native functions... 256 | $bcrypt64alphabet = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 257 | $base64alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 258 | $rawSalt = base64_decode(strtr($encodedSalt, $bcrypt64alphabet, $base64alphabet)); // 16 bytes 259 | $rawDigest = base64_decode(strtr($encodedDigest, $bcrypt64alphabet, $base64alphabet)); // 23 bytes 260 | ``` 261 | 262 | [bmcf]: https://github.com/ademarre/binary-mcf "Binary Modular Crypt Format (BMCF)" 263 | 264 | You can encode and decode hexadecimal with [bin2hex()][bin2hex] and [pack()][pack]: 265 | 266 | ```php 267 | // Hexadecimal with Base2n... 268 | $hexadecimal = new Base2n(4); 269 | $encoded = $hexadecimal->encode('encode this'); // 656e636f64652074686973 270 | $decoded = $hexadecimal->decode($encoded); // encode this 271 | 272 | // It's better to use native functions... 273 | $encoded = bin2hex('encode this'); // 656e636f64652074686973 274 | $decoded = pack('H*', $encoded); // encode this 275 | // As of PHP 5.4 you can use hex2bin() instead of pack() 276 | ``` 277 | 278 | [bin2hex]: http://php.net/bin2hex "PHP bin2hex() Function" 279 | [pack]: http://php.net/pack "PHP pack() Function" 280 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ademarre/binary-to-text-php", 3 | "description": "Collection of binary-to-text encoding utilities for PHP. Includes Base32 support and much more.", 4 | "keywords": ["rfc4648", "base32", "octal", "base-8", "base-4", "binary"], 5 | "type": "library", 6 | "homepage": "https://github.com/ademarre/binary-to-text-php", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Andre DeMarre", 11 | "role": "Developer" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=5.2.14" 16 | }, 17 | "autoload": { 18 | "psr-0": { "Base2n": "" } 19 | } 20 | } 21 | --------------------------------------------------------------------------------