| @@ -2,14 +2,19 @@ | ||
| 2 | 2 | ================================= |
| 3 | 3 | |
| 4 | 4 | For now, the only class in this repository is **Base2n**. |
| 5 | 5 | |
| 6 | -Base2n is for binary-to-text conversion with arbitrary encoding schemes that represent binary data in a base 2<sup>n</sup> 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. | |
| 6 | +Base2n is for binary-to-text conversion with arbitrary encoding schemes that represent binary data in a base 2<sup> | |
| 7 | +n</sup> notation. It can handle non-standard variants of many standard encoding schemes such as [Base64][rfc4648base64] | |
| 8 | +and [Base32][rfc4648base32]. Many binary-to-text encoding schemes use a fixed number of bits of binary data to generate | |
| 9 | +each encoded character. Such schemes generalize to a single algorithm, implemented here. | |
| 7 | 10 | |
| 8 | 11 | [rfc4648base64]: http://tools.ietf.org/html/rfc4648#section-4 "RFC 4648 Base64 Specification" |
| 12 | + | |
| 9 | 13 | [rfc4648base32]: http://tools.ietf.org/html/rfc4648#section-6 "RFC 4648 Base32 Specification" |
| 10 | 14 | |
| 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. | |
| 15 | +Binary-to-text encoding is usually used to represent data in a notation that is safe for transport over text-based | |
| 16 | +protocols, and there are several other practical uses. See the examples below. | |
| 12 | 17 | |
| 13 | 18 | |
| 14 | 19 | |
| 15 | 20 | Basic Base2n Usage |
| @@ -23,12 +28,12 @@ | ||
| 23 | 28 | $encoded = $base32->encode('encode this'); |
| 24 | 29 | // MVXGG33EMUQHI2DJOM====== |
| 25 | 30 | ``` |
| 26 | 31 | |
| 27 | - | |
| 28 | 32 | ### Constructor Parameters |
| 29 | 33 | |
| 30 | -- <code>integer $bitsPerCharacter</code> **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: <code>2^$bitsPerCharacter</code>. | |
| 34 | +- <code>integer $bitsPerCharacter</code> **Required**. The number of bits to use for each encoded character; 1–8. The | |
| 35 | + most practical range is 1–6. The encoding's radix is a power of 2: <code>2^$bitsPerCharacter</code>. | |
| 31 | 36 | 1. [base-2, binary][binary] |
| 32 | 37 | 2. [base-4, quaternary][quaternary] |
| 33 | 38 | 3. [base-8, octal][octal] |
| 34 | 39 | 4. [base-16, hexadecimal][hexadecimal] |
| @@ -37,43 +42,57 @@ | ||
| 37 | 42 | 7. base-128 |
| 38 | 43 | 8. base-256 |
| 39 | 44 | |
| 40 | 45 | [binary]: http://en.wikipedia.org/wiki/Binary_numeral_system "Binary Notation" |
| 46 | + | |
| 41 | 47 | [quaternary]: http://en.wikipedia.org/wiki/Quaternary_numeral_system "Base-2 Notation" |
| 48 | + | |
| 42 | 49 | [octal]: http://en.wikipedia.org/wiki/Octal "Octal Notation" |
| 50 | + | |
| 43 | 51 | [hexadecimal]: http://en.wikipedia.org/wiki/Base16 "Hexadecimal Notation" |
| 52 | + | |
| 44 | 53 | [base32]: http://en.wikipedia.org/wiki/Base32 "Base32 Encoding" |
| 54 | + | |
| 45 | 55 | [base64]: http://en.wikipedia.org/wiki/Base64 "Base64 Encoding" |
| 46 | 56 | |
| 47 | -- <code>string $chars</code> This string specifies the base alphabet. Must be <code>2^$bitsPerCharacter</code> long. Default: <code>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_</code> | |
| 57 | +- <code>string $chars</code> This string specifies the base alphabet. Must be <code>2^$bitsPerCharacter</code> long. | |
| 58 | + Default: <code>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_</code> | |
| 48 | 59 | |
| 49 | 60 | - <code>boolean $caseSensitive</code> To decode in a case-sensitive manner. Default: <code>FALSE</code> |
| 50 | 61 | |
| 51 | -- <code>boolean $rightPadFinalBits</code> How to encode the last character when the bits remaining are fewer than <code>$bitsPerCharacter</code>. When <code>TRUE</code>, the bits to encode are placed in the most significant position of the final group of bits, with the lower bits set to <code>0</code>. When <code>FALSE</code>, the final bits are placed in the least significant position. For [RFC 4648][rfc4648] encodings, <code>$rightPadFinalBits</code>should be <code>TRUE</code>. Default: <code>FALSE</code> | |
| 62 | +- <code>boolean $rightPadFinalBits</code> How to encode the last character when the bits remaining are fewer than <code> | |
| 63 | + $bitsPerCharacter</code>. When <code>TRUE</code>, the bits to encode are placed in the most significant position of | |
| 64 | + the final group of bits, with the lower bits set to <code>0</code>. When <code>FALSE</code>, the final bits are placed | |
| 65 | + in the least significant position. For [RFC 4648][rfc4648] encodings, <code>$rightPadFinalBits</code>should be <code> | |
| 66 | + TRUE</code>. Default: <code>FALSE</code> | |
| 52 | 67 | |
| 53 | 68 | [rfc4648]: http://tools.ietf.org/html/rfc4648 "RFC 4648: Base16, Base32, Base64" |
| 54 | 69 | |
| 55 | -- <code>boolean $padFinalGroup</code> 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 <code>=</code> 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: <code>FALSE</code> | |
| 70 | +- <code>boolean $padFinalGroup</code> It's common to encode characters in groups. For example, Base64 (which is based on | |
| 71 | + 6 bits per character) converts 3 raw bytes into 4 encoded characters. If insufficient bytes remain at the end, the | |
| 72 | + final group will be padded with <code>=</code> to complete a group of 4 characters, and the encoded length is always a | |
| 73 | + multiple of 4. Although the information provided by the padding is redundant, some programs rely on it for decoding; | |
| 74 | + Base2n does not. Default: <code>FALSE</code> | |
| 56 | 75 | |
| 57 | -- <code>string $padCharacter</code> When <code>$padFinalGroup</code> is <code>TRUE</code>, this is the pad character used. Default: <code>=</code> | |
| 76 | +- <code>string $padCharacter</code> When <code>$padFinalGroup</code> is <code>TRUE</code>, this is the pad character | |
| 77 | + used. Default: <code>=</code> | |
| 58 | 78 | |
| 59 | - | |
| 60 | 79 | ### <code>encode()</code> Parameters |
| 61 | 80 | |
| 62 | 81 | - <code>string $rawString</code> **Required**. The data to be encoded. |
| 63 | 82 | |
| 64 | - | |
| 65 | 83 | ### <code>decode()</code> Parameters |
| 66 | 84 | |
| 67 | 85 | - <code>string $encodedString</code> **Required**. The string to be decoded. |
| 68 | -- <code>boolean $strict</code> When <code>TRUE</code>, <code>NULL</code> will be returned if <code>$encodedString</code> contains an undecodable character. When <code>FALSE</code>, unknown characters are simply ignored. Default: <code>FALSE</code> | |
| 86 | +- <code>boolean $strict</code> When <code>TRUE</code>, <code>NULL</code> will be returned if <code>$encodedString</code> | |
| 87 | + contains an undecodable character. When <code>FALSE</code>, unknown characters are simply ignored. Default: <code> | |
| 88 | + FALSE</code> | |
| 69 | 89 | |
| 70 | - | |
| 71 | - | |
| 72 | 90 | Examples |
| 73 | 91 | -------- |
| 74 | 92 | |
| 75 | -PHP does not provide any Base32 encoding functions. By setting <code>$bitsPerCharacter</code> to 5 and specifying your desired alphabet in <code>$chars</code>, you can handle any variant of Base32: | |
| 93 | +PHP does not provide any Base32 encoding functions. By setting <code>$bitsPerCharacter</code> to 5 and specifying your | |
| 94 | +desired alphabet in <code>$chars</code>, you can handle any variant of Base32: | |
| 76 | 95 | |
| 77 | 96 | ```php |
| 78 | 97 | // RFC 4648 base32 alphabet; case-insensitive |
| 79 | 98 | $base32 = new Base2n(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', FALSE, TRUE, TRUE); |
| @@ -87,9 +106,8 @@ | ||
| 87 | 106 | $encoded = $base32hex->encode('encode this'); |
| 88 | 107 | // CLN66RR4CKG78Q39EC====== |
| 89 | 108 | ``` |
| 90 | 109 | |
| 91 | - | |
| 92 | 110 | Octal notation: |
| 93 | 111 | |
| 94 | 112 | ```php |
| 95 | 113 | $octal = new Base2n(3); |
| @@ -96,9 +114,8 @@ | ||
| 96 | 114 | $encoded = $octal->encode('encode this'); |
| 97 | 115 | // 312671433366214510072150322711 |
| 98 | 116 | ``` |
| 99 | 117 | |
| 100 | - | |
| 101 | 118 | A convenient way to go back and forth between binary notation and its real binary representation: |
| 102 | 119 | |
| 103 | 120 | ```php |
| 104 | 121 | $binary = new Base2n(1); |
| @@ -107,11 +124,13 @@ | ||
| 107 | 124 | $decoded = $binary->decode($encoded); |
| 108 | 125 | // encode this |
| 109 | 126 | ``` |
| 110 | 127 | |
| 128 | +PHP uses a proprietary binary-to-text encoding scheme to generate session identifiers from random hash digests. The most | |
| 129 | +efficient way to store these session IDs in a database is to decode them back to their raw hash digests. PHP's encoding | |
| 130 | +scheme is configured with the <code>[session.hash_bits_per_character][phphashbits]</code> php.ini setting. The decoded | |
| 131 | +size depends on the hash function, set with <code>[session.hash_function][phphash]</code> in php.ini. | |
| 111 | 132 | |
| 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 <code>[session.hash_bits_per_character][phphashbits]</code> php.ini setting. The decoded size depends on the hash function, set with <code>[session.hash_function][phphash]</code> in php.ini. | |
| 113 | - | |
| 114 | 133 | ```php |
| 115 | 134 | // session.hash_function = 0 |
| 116 | 135 | // session.hash_bits_per_character = 5 |
| 117 | 136 | // 128-bit session ID |
| @@ -131,12 +150,14 @@ | ||
| 131 | 150 | $rawSessionId = $phpBase64->decode($sessionId); |
| 132 | 151 | ``` |
| 133 | 152 | |
| 134 | 153 | [phphashbits]: http://php.net/manual/en/session.configuration.php#ini.session.hash-bits-per-character "PHP session.hash_bits_per_character" |
| 154 | + | |
| 135 | 155 | [phphash]: http://php.net/manual/en/session.configuration.php#ini.session.hash-function "PHP session.hash_function" |
| 136 | 156 | |
| 137 | 157 | |
| 138 | 158 | Generate random security tokens: |
| 159 | + | |
| 139 | 160 | ```php |
| 140 | 161 | $tokenEncoder = new Base2n(6, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,'); |
| 141 | 162 | $binaryToken = openssl_random_pseudo_bytes(32); // PHP >= 5.3 |
| 142 | 163 | $token = $tokenEncoder->encode($binaryToken); |
| @@ -142,13 +163,13 @@ | ||
| 142 | 163 | $token = $tokenEncoder->encode($binaryToken); |
| 143 | 164 | // Example: U6M132v9FG-AHhBVaQWOg1gjyUi1IogNxuen0i3u3ep |
| 144 | 165 | ``` |
| 145 | 166 | |
| 146 | - | |
| 147 | 167 | The rest of these examples are probably more fun than they are practical. |
| 148 | 168 | |
| 169 | +We can encode arbitrary data with a 7-bit encoding. (Note that this is not the same as | |
| 170 | +the [7bit MIME content-transfer-encoding][7bit].) | |
| 149 | 171 | |
| 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 | 172 | ```php |
| 152 | 173 | // This uses all 7-bit ASCII characters |
| 153 | 174 | $base128chars = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" |
| 154 | 175 | . "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" |
| @@ -161,12 +182,14 @@ | ||
| 161 | 182 | |
| 162 | 183 | $base128 = new Base2n(7, $base128chars); |
| 163 | 184 | $encoded = $base128->encode('encode this'); |
| 164 | 185 | ``` |
| 186 | + | |
| 165 | 187 | [7bit]: http://msdn.microsoft.com/en-us/library/ms526290(v=exchg.10).aspx "7bit MIME Content-Transfer-Encoding" |
| 166 | 188 | |
| 167 | 189 | |
| 168 | 190 | The following encoding guarantees that the most significant bit is set for every byte: |
| 191 | + | |
| 169 | 192 | ```php |
| 170 | 193 | // "High" base-128 encoding |
| 171 | 194 | $high128chars = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" |
| 172 | 195 | . "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" |
| @@ -180,10 +203,10 @@ | ||
| 180 | 203 | $high128 = new Base2n(7, $high128chars); |
| 181 | 204 | $encoded = $high128->encode('encode this'); |
| 182 | 205 | ``` |
| 183 | 206 | |
| 207 | +Let's create an encoding using exclusively non-printable control characters! | |
| 184 | 208 | |
| 185 | -Let's create an encoding using exclusively non-printable control characters! | |
| 186 | 209 | ```php |
| 187 | 210 | // Base-32 non-printable character encoding |
| 188 | 211 | $noPrintChars = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" |
| 189 | 212 | . "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; |
| @@ -191,10 +214,10 @@ | ||
| 191 | 214 | $nonPrintable32 = new Base2n(5, $noPrintChars); |
| 192 | 215 | $encoded = $nonPrintable32->encode('encode this'); |
| 193 | 216 | ``` |
| 194 | 217 | |
| 218 | +Why not encode data using only whitespace? Here's a base-4 encoding using space, tab, new line, and carriage return: | |
| 195 | 219 | |
| 196 | -Why not encode data using only whitespace? Here's a base-4 encoding using space, tab, new line, and carriage return: | |
| 197 | 220 | ```php |
| 198 | 221 | // Base-4 whitespace encoding |
| 199 | 222 | $whitespaceChars = " \t\n\r"; |
| 200 | 223 | |
| @@ -207,24 +230,28 @@ | ||
| 207 | 230 | ); |
| 208 | 231 | // encode this |
| 209 | 232 | ``` |
| 210 | 233 | |
| 211 | - | |
| 212 | - | |
| 213 | 234 | Counterexamples |
| 214 | 235 | ---------------- |
| 215 | 236 | |
| 216 | 237 | Base2n is not slow, but it will never outperform an encoding function implemented in C. When one exists, use it instead. |
| 217 | 238 | |
| 239 | +PHP provides the <code>[base64_encode()][base64_encode]</code> and <code>[base64_decode()][base64_decode]</code> | |
| 240 | +functions, and you should always use them for standard Base64. When you need to use a modified alphabet, you can | |
| 241 | +translate the encoded output with <code>[strtr()][strtr]</code> or <code>[str_replace()][str_replace]</code>. | |
| 218 | 242 | |
| 219 | -PHP provides the <code>[base64_encode()][base64_encode]</code> and <code>[base64_decode()][base64_decode]</code> 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 <code>[strtr()][strtr]</code> or <code>[str_replace()][str_replace]</code>. | |
| 243 | +[base64_encode]: http://php.net/base64_encode "PHP base64_encode() Function" | |
| 220 | 244 | |
| 221 | -[base64_encode]: http://php.net/base64_encode "PHP base64_encode() Function" | |
| 222 | 245 | [base64_decode]: http://php.net/base64_decode "PHP base64_decode() Function" |
| 246 | + | |
| 223 | 247 | [strtr]: http://php.net/strtr "PHP strtr() Function" |
| 248 | + | |
| 224 | 249 | [str_replace]: http://php.net/str_replace "PHP str_replace() Function" |
| 225 | 250 | |
| 226 | -A common variant of Base64 is [modified for URLs and filenames][rfc4648base64url], where <code>+</code> and <code>/</code> are replaced with <code>-</code> and <code>_</code>, and the <code>=</code> padding is omitted. It's better to handle this variant with native PHP functions: | |
| 251 | +A common variant of Base64 is [modified for URLs and filenames][rfc4648base64url], where <code>+</code> | |
| 252 | +and <code>/</code> are replaced with <code>-</code> and <code>_</code>, and the <code>=</code> padding is omitted. It's | |
| 253 | +better to handle this variant with native PHP functions: | |
| 227 | 254 | |
| 228 | 255 | ```php |
| 229 | 256 | // RFC 4648 base64url with Base2n... |
| 230 | 257 | $base64url = new Base2n(6, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', TRUE, TRUE, FALSE); |
| @@ -238,9 +265,11 @@ | ||
| 238 | 265 | |
| 239 | 266 | [rfc4648base64url]: http://tools.ietf.org/html/rfc4648#page-7 "Modified Base64 for URLs" |
| 240 | 267 | |
| 241 | 268 | |
| 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]: | |
| 269 | +Native functions get slightly more cumbersome when every position in the alphabet has changed, as seen in this example | |
| 270 | +of [decoding a Bcrypt hash][bmcf]: | |
| 271 | + | |
| 243 | 272 | ```php |
| 244 | 273 | // Decode the salt and digest from a Bcrypt hash |
| 245 | 274 | |
| 246 | 275 | $hash = '$2y$14$i5btSOiulHhaPHPbgNUGdObga/GC.AVG/y5HHY1ra7L0C9dpCaw8u'; |
| @@ -275,5 +304,6 @@ | ||
| 275 | 304 | // As of PHP 5.4 you can use hex2bin() instead of pack() |
| 276 | 305 | ``` |
| 277 | 306 | |
| 278 | 307 | [bin2hex]: http://php.net/bin2hex "PHP bin2hex() Function" |
| 308 | + | |
| 279 | 309 | [pack]: http://php.net/pack "PHP pack() Function" |