1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Sop\CryptoEncoding; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Container for multiple PEM objects. |
7
|
|
|
* |
8
|
|
|
* The order of PEMs shall be retained, eg. when read from a file. |
9
|
|
|
*/ |
10
|
|
|
class PEMBundle implements \Countable, \IteratorAggregate |
11
|
|
|
{ |
12
|
|
|
/** |
13
|
|
|
* Array of PEM objects. |
14
|
|
|
* |
15
|
|
|
* @var PEM[] $_pems |
16
|
|
|
*/ |
17
|
|
|
protected $_pems; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Constructor. |
21
|
|
|
* |
22
|
|
|
* @param PEM ...$pems |
23
|
|
|
*/ |
24
|
2 |
|
public function __construct(PEM ...$pems) |
25
|
|
|
{ |
26
|
2 |
|
$this->_pems = $pems; |
27
|
2 |
|
} |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Initialize from a string. |
31
|
|
|
* |
32
|
|
|
* @param string $str |
33
|
|
|
* @throws \UnexpectedValueException |
34
|
|
|
* @return self |
35
|
|
|
*/ |
36
|
3 |
|
public static function fromString($str) |
37
|
|
|
{ |
38
|
3 |
|
if (!preg_match_all(PEM::PEM_REGEX, $str, $matches, PREG_SET_ORDER)) { |
39
|
1 |
|
throw new \UnexpectedValueException("No PEM blocks."); |
40
|
|
|
} |
41
|
2 |
|
$pems = array_map( |
42
|
|
|
function ($match) { |
43
|
2 |
|
$payload = preg_replace('/\s+/', "", $match[2]); |
44
|
2 |
|
$data = base64_decode($payload, true); |
45
|
2 |
|
if (false === $data) { |
46
|
1 |
|
throw new \UnexpectedValueException( |
47
|
1 |
|
"Failed to decode PEM data."); |
48
|
|
|
} |
49
|
1 |
|
return new PEM($match[1], $data); |
50
|
2 |
|
}, $matches); |
51
|
1 |
|
return new self(...$pems); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Initialize from a file. |
56
|
|
|
* |
57
|
|
|
* @param string $filename |
58
|
|
|
* @throws \RuntimeException If file reading fails |
59
|
|
|
* @return self |
60
|
|
|
*/ |
61
|
2 |
View Code Duplication |
public static function fromFile($filename) |
|
|
|
|
62
|
|
|
{ |
63
|
2 |
|
if (!is_readable($filename) || |
64
|
2 |
|
false === ($str = file_get_contents($filename))) { |
65
|
1 |
|
throw new \RuntimeException("Failed to read $filename."); |
66
|
|
|
} |
67
|
1 |
|
return self::fromString($str); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Get self with PEM objects appended. |
72
|
|
|
* |
73
|
|
|
* @param PEM ...$pems |
74
|
|
|
* @return self |
75
|
|
|
*/ |
76
|
1 |
|
public function withPEMs(PEM ...$pems) |
77
|
|
|
{ |
78
|
1 |
|
$obj = clone $this; |
79
|
1 |
|
$obj->_pems = array_merge($obj->_pems, $pems); |
80
|
1 |
|
return $obj; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Get all PEMs in a bundle. |
85
|
|
|
* |
86
|
|
|
* @return PEM[] |
87
|
|
|
*/ |
88
|
1 |
|
public function all() |
89
|
|
|
{ |
90
|
1 |
|
return $this->_pems; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Get the first PEM in a bundle. |
95
|
|
|
* |
96
|
|
|
* @throws \LogicException If bundle contains no PEM objects |
97
|
|
|
* @return PEM |
98
|
|
|
*/ |
99
|
2 |
|
public function first() |
100
|
|
|
{ |
101
|
2 |
|
if (!count($this->_pems)) { |
102
|
1 |
|
throw new \LogicException("No PEMs."); |
103
|
|
|
} |
104
|
1 |
|
return $this->_pems[0]; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* |
109
|
|
|
* @see \Countable::count() |
110
|
|
|
* @return int |
111
|
|
|
*/ |
112
|
2 |
|
public function count() |
113
|
|
|
{ |
114
|
2 |
|
return count($this->_pems); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Get iterator for PEMs. |
119
|
|
|
* |
120
|
|
|
* @see \IteratorAggregate::getIterator() |
121
|
|
|
* @return \ArrayIterator |
122
|
|
|
*/ |
123
|
1 |
|
public function getIterator() |
124
|
|
|
{ |
125
|
1 |
|
return new \ArrayIterator($this->_pems); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Encode bundle to a string of contiguous PEM blocks. |
130
|
|
|
* |
131
|
|
|
* @return string |
132
|
|
|
*/ |
133
|
2 |
|
public function string() |
134
|
|
|
{ |
135
|
2 |
|
return implode("\n", |
136
|
2 |
|
array_map( |
137
|
2 |
|
function (PEM $pem) { |
138
|
2 |
|
return $pem->string(); |
139
|
2 |
|
}, $this->_pems)); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* |
144
|
|
|
* @return string |
145
|
|
|
*/ |
146
|
1 |
|
public function __toString() |
147
|
|
|
{ |
148
|
1 |
|
return $this->string(); |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.