Completed
Push — master ( 8ced3e...c66cb3 )
by smiley
02:28
created

Base32::checkCharacterSet()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
/**
3
 * Class Base32
4
 *
5
 * @filesource   Base32.php
6
 * @author       Shannon Wynter {@link http://fremnet.net/contact}
7
 * @author       Smiley <[email protected]>
8
 * @copyright    Copyright (c) 2006 Shannon Wynter
9
 */
10
11
namespace chillerlan\Base32;
12
13
/**
14
 * Class to provide base32 encoding/decoding of strings
15
 *
16
 * ChangeLog
17
 * -----------
18
 * version 1.1.0, 2016-02-29, smiley {@link https://github.com/codemasher}
19
 *  - cleanup
20
 *  - separated character set class
21
 * version 1.0.0, 2015-12-06, smiley {@link https://github.com/codemasher}
22
 *  - static all the things!
23
 * version 0.2, 2008-08-07, Shannon Wynter {@link http://fremnet.net/contact}
24
 *  - Fixed transposition of Y and Z in csRFC3548
25
 * version 0.1, 2006-06-22, Shannon Wynter {@link http://fremnet.net/contact}
26
 *  - Initial release
27
 *
28
 * Notes
29
 * -----------
30
 * For dealing with humans it's probably best to use csSafe rather then csRFC3548
31
 */
32
class Base32{
33
34
	/**
35
	 * charset
36
	 *
37
	 * Internal holder of the current character set.
38
	 *
39
	 * @var string
40
	 */
41
	public static $charset = Base32Characters::RFC3548;
42
43
	/**
44
	 * setCharset
45
	 *
46
	 * Used to set the internal _charset variable
47
	 * I've left it so that people can arbirtrarily set their
48
	 * own charset
49
	 *
50
	 * @param string $charset The character set you want to use
51
	 *
52
	 * @throws \chillerlan\Base32\Base32Exception
53
	 */
54
	public static function setCharset($charset = Base32Characters::RFC3548){
55
		if(strlen($charset) === 32){
56
			self::$charset = strtoupper($charset);
57
		}
58
		else{
59
			throw new Base32Exception('Length must be exactly 32');
60
		}
61
	}
62
63
	/**
64
	 * str2bin
65
	 *
66
	 * Converts any ascii string to a binary string
67
	 *
68
	 * @param string $str The string you want to convert
69
	 *
70
	 * @return string String of 0's and 1's
71
	 */
72
	public static function str2bin($str){
73
		$chrs = unpack('C*', $str);
74
75
		return vsprintf(str_repeat('%08b', count($chrs)), $chrs);
76
	}
77
78
	/**
79
	 * bin2str
80
	 *
81
	 * Converts a binary string to an ascii string
82
	 *
83
	 * @param string $str The string of 0's and 1's you want to convert
84
	 *
85
	 * @return string The ascii output
86
	 * @throws \chillerlan\Base32\Base32Exception
87
	 */
88
	public static function bin2str($str){
89
		self::checkLength($str);
90
		self::checkBin($str);
91
92
		preg_match_all('/.{8}/', $str, $chrs);
93
		$chrs = array_map('bindec', $chrs[0]);
94
95
		// I'm just being slack here
96
		array_unshift($chrs, 'C*');
97
98
		return call_user_func_array('pack', $chrs);
99
	}
100
101
	/**
102
	 * fromBin
103
	 *
104
	 * Converts a correct binary string to base32
105
	 *
106
	 * @param string $str The string of 0's and 1's you want to convert
107
	 *
108
	 * @return string String encoded as base32
109
	 * @throws \chillerlan\Base32\Base32Exception
110
	 */
111
	public static function fromBin($str){
112
		self::checkLength($str);
113
		self::checkBin($str);
114
115
		// Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out
116
		$str = preg_replace('/(.{5})/', '000$1', $str);
117
118
		// We need a string divisible by 5
119
		$length = strlen($str);
120
		$rbits = $length&7;
121
122
		if($rbits > 0){
123
			// Excessive bits need to be padded
124
			$ebits = substr($str, $length-$rbits);
125
			$str = substr($str, 0, $length-$rbits).'000'.$ebits.str_repeat('0', 5-strlen($ebits));
126
		}
127
128
		preg_match_all('/.{8}/', $str, $chrs);
129
130
		$chrs = array_map(function ($str){
131
			return self::$charset[bindec($str)];
132
		}, $chrs[0]);
133
134
		return implode('', $chrs);
135
	}
136
137
	/**
138
	 * toBin
139
	 *
140
	 * Accepts a base32 string and returns an ascii binary string
141
	 *
142
	 * @param string $str The base32 string to convert
143
	 *
144
	 * @return string Ascii binary string
145
	 * @throws \chillerlan\Base32\Base32Exception
146
	 */
147
	public static function toBin($str){
148
		self::checkCharacterSet($str);
149
150
		// Convert the base32 string back to a binary string
151
		$str = array_map(function ($chr){
152
			return sprintf('%08b', strpos(self::$charset, $chr));
153
		}, str_split($str));
154
155
		// Remove the extra 0's we added
156
		$str = preg_replace('/000(.{5})/', '$1', implode('', $str));
157
158
		// Unpad if nessicary
159
		$length = strlen($str);
160
		$rbits = $length&7;
161
162
		if($rbits > 0){
163
			$str = substr($str, 0, $length-$rbits);
164
		}
165
166
		return $str;
167
	}
168
169
	/**
170
	 * fromString
171
	 *
172
	 * Convert any string to a base32 string
173
	 * This should be binary safe...
174
	 *
175
	 * @param string $str The string to convert
176
	 *
177
	 * @return string The converted base32 string
178
	 */
179
	public static function fromString($str){
180
		return self::fromBin(self::str2bin($str));
181
	}
182
183
	/**
184
	 * toString
185
	 *
186
	 * Convert any base32 string to a normal sctring
187
	 * This should be binary safe...
188
	 *
189
	 * @param string $str The base32 string to convert
190
	 *
191
	 * @return string The normal string
192
	 */
193
	public static function toString($str){
194
		$str = strtoupper($str);
195
196
		// csSave actually has to be able to consider extra characters
197
		if(self::$charset === Base32Characters::CROCKFORD){
198
			$str = str_replace('O', '0', $str);
199
			$str = str_replace(['I', 'L'], '1', $str);
200
		}
201
202
		return self::bin2str(self::toBin($str));
203
	}
204
205
	/**
206
	 * @param string $str
207
	 *
208
	 * @throws \chillerlan\Base32\Base32Exception
209
	 */
210
	protected static function checkLength($str){
211
		if(strlen($str)%8 > 0){
212
			throw new Base32Exception('Length must be divisible by 8');
213
		}
214
	}
215
216
	/**
217
	 * @param string $str
218
	 *
219
	 * @throws \chillerlan\Base32\Base32Exception
220
	 */
221
	protected static function checkBin($str){
222
		if(!preg_match('/^[01]+$/', $str)){
223
			throw new Base32Exception('Only 0 and 1 are permitted');
224
		}
225
	}
226
227
	/**
228
	 * @param string $str
229
	 *
230
	 * @throws \chillerlan\Base32\Base32Exception
231
	 */
232
	protected static function checkCharacterSet($str){
233
		if(!preg_match('/^['.self::$charset.']+$/', $str)){
234
			throw new Base32Exception('Must match character set');
235
		}
236
	}
237
238
}
239