Skip to content

Commit 52943f5

Browse files
authored
feat: add back compatibility for >= PHP 7.1 (#405)
1 parent 92aa12d commit 52943f5

File tree

5 files changed

+66
-49
lines changed

5 files changed

+66
-49
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
php: [ "8.0", "8.1"]
13+
php: [ "7.1", "7.2", "7.3", "7.4", "8.0", "8.1"]
1414
name: PHP ${{matrix.php }} Unit Test
1515
steps:
1616
- uses: actions/checkout@v2

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
],
2121
"license": "BSD-3-Clause",
2222
"require": {
23-
"php": "^8.0"
23+
"php": "^7.1||^8.0"
2424
},
2525
"suggest": {
2626
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
@@ -31,6 +31,6 @@
3131
}
3232
},
3333
"require-dev": {
34-
"phpunit/phpunit": "^9.5"
34+
"phpunit/phpunit": "^7.5||9.5"
3535
}
3636
}

src/JWK.php

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,24 +128,18 @@ private static function createPemFromModulusAndExponent(
128128
string $n,
129129
string $e
130130
): string {
131-
if (false === ($modulus = JWT::urlsafeB64Decode($n))) {
132-
throw new UnexpectedValueException('Invalid JWK encoding');
133-
}
134-
if (false === ($publicExponent = JWT::urlsafeB64Decode($e))) {
135-
throw new UnexpectedValueException('Invalid header encoding');
136-
}
131+
$mod = JWT::urlsafeB64Decode($n);
132+
$exp = JWT::urlsafeB64Decode($e);
137133

138-
$components = [
139-
'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
140-
'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
141-
];
134+
$modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
135+
$publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
142136

143137
$rsaPublicKey = \pack(
144138
'Ca*a*a*',
145139
48,
146-
self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
147-
$components['modulus'],
148-
$components['publicExponent']
140+
self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
141+
$modulus,
142+
$publicExponent
149143
);
150144

151145
// sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
@@ -176,7 +170,7 @@ private static function createPemFromModulusAndExponent(
176170
* @param int $length
177171
* @return string
178172
*/
179-
private static function encodeLength($length)
173+
private static function encodeLength(int $length): string
180174
{
181175
if ($length <= 0x7F) {
182176
return \chr($length);

src/JWT.php

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ class JWT
3636
* When checking nbf, iat or expiration times,
3737
* we want to provide some extra leeway time to
3838
* account for clock skew.
39+
*
40+
* @var int
3941
*/
40-
public static int $leeway = 0;
42+
public static $leeway = 0;
4143

4244
/**
4345
* @var array<string, string[]>
4446
*/
45-
public static array $supported_algs = [
47+
public static $supported_algs = [
4648
'ES384' => ['openssl', 'SHA384'],
4749
'ES256' => ['openssl', 'SHA256'],
4850
'HS256' => ['hash_hmac', 'SHA256'],
@@ -77,8 +79,10 @@ class JWT
7779
* @uses jsonDecode
7880
* @uses urlsafeB64Decode
7981
*/
80-
public static function decode(string $jwt, Key|array|ArrayAccess $keyOrKeyArray): stdClass
81-
{
82+
public static function decode(
83+
string $jwt,
84+
$keyOrKeyArray
85+
): stdClass {
8286
// Validate JWT
8387
$timestamp = \time();
8488

@@ -90,24 +94,18 @@ public static function decode(string $jwt, Key|array|ArrayAccess $keyOrKeyArray)
9094
throw new UnexpectedValueException('Wrong number of segments');
9195
}
9296
list($headb64, $bodyb64, $cryptob64) = $tks;
93-
if (false === ($headerRaw = static::urlsafeB64Decode($headb64))) {
94-
throw new UnexpectedValueException('Invalid header encoding');
95-
}
97+
$headerRaw = static::urlsafeB64Decode($headb64);
9698
if (null === ($header = static::jsonDecode($headerRaw))) {
9799
throw new UnexpectedValueException('Invalid header encoding');
98100
}
99-
if (false === ($payloadRaw = static::urlsafeB64Decode($bodyb64))) {
100-
throw new UnexpectedValueException('Invalid claims encoding');
101-
}
101+
$payloadRaw = static::urlsafeB64Decode($bodyb64);
102102
if (null === ($payload = static::jsonDecode($payloadRaw))) {
103103
throw new UnexpectedValueException('Invalid claims encoding');
104104
}
105105
if (!$payload instanceof stdClass) {
106106
throw new UnexpectedValueException('Payload must be a JSON object');
107107
}
108-
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
109-
throw new UnexpectedValueException('Invalid signature encoding');
110-
}
108+
$sig = static::urlsafeB64Decode($cryptob64);
111109
if (empty($header->alg)) {
112110
throw new UnexpectedValueException('Empty algorithm');
113111
}
@@ -159,7 +157,7 @@ public static function decode(string $jwt, Key|array|ArrayAccess $keyOrKeyArray)
159157
* Converts and signs a PHP object or array into a JWT string.
160158
*
161159
* @param array<mixed> $payload PHP array
162-
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate|array<mixed> $key The secret key.
160+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
163161
* @param string $keyId
164162
* @param array<string, string> $head An array with header elements to attach
165163
*
@@ -170,7 +168,7 @@ public static function decode(string $jwt, Key|array|ArrayAccess $keyOrKeyArray)
170168
*/
171169
public static function encode(
172170
array $payload,
173-
string|OpenSSLAsymmetricKey|OpenSSLCertificate|array $key,
171+
$key,
174172
string $alg,
175173
string $keyId = null,
176174
array $head = null
@@ -197,7 +195,7 @@ public static function encode(
197195
* Sign a string with a given key and algorithm.
198196
*
199197
* @param string $msg The message to sign
200-
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate|array<mixed> $key The secret key.
198+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
201199
* @param string $alg Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
202200
* 'HS512', 'RS256', 'RS384', and 'RS512'
203201
*
@@ -207,7 +205,7 @@ public static function encode(
207205
*/
208206
public static function sign(
209207
string $msg,
210-
string|OpenSSLAsymmetricKey|OpenSSLCertificate|array $key,
208+
$key,
211209
string $alg
212210
): string {
213211
if (empty(static::$supported_algs[$alg])) {
@@ -222,7 +220,7 @@ public static function sign(
222220
return \hash_hmac($algorithm, $msg, $key, true);
223221
case 'openssl':
224222
$signature = '';
225-
$success = \openssl_sign($msg, $signature, $key, $algorithm);
223+
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
226224
if (!$success) {
227225
throw new DomainException("OpenSSL unable to sign data");
228226
}
@@ -258,7 +256,7 @@ public static function sign(
258256
*
259257
* @param string $msg The original message (header and body)
260258
* @param string $signature The original signature
261-
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate|array<mixed> $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
259+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
262260
* @param string $alg The algorithm
263261
*
264262
* @return bool
@@ -268,7 +266,7 @@ public static function sign(
268266
private static function verify(
269267
string $msg,
270268
string $signature,
271-
string|OpenSSLAsymmetricKey|OpenSSLCertificate|array $keyMaterial,
269+
$keyMaterial,
272270
string $alg
273271
): bool {
274272
if (empty(static::$supported_algs[$alg])) {
@@ -278,7 +276,7 @@ private static function verify(
278276
list($function, $algorithm) = static::$supported_algs[$alg];
279277
switch ($function) {
280278
case 'openssl':
281-
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm);
279+
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
282280
if ($success === 1) {
283281
return true;
284282
} elseif ($success === 0) {
@@ -322,7 +320,7 @@ private static function verify(
322320
*
323321
* @throws DomainException Provided string was invalid JSON
324322
*/
325-
public static function jsonDecode(string $input): mixed
323+
public static function jsonDecode(string $input)
326324
{
327325
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
328326

@@ -339,11 +337,11 @@ public static function jsonDecode(string $input): mixed
339337
*
340338
* @param array<mixed> $input A PHP array
341339
*
342-
* @return string|false JSON representation of the PHP array
340+
* @return string JSON representation of the PHP array
343341
*
344342
* @throws DomainException Provided object could not be encoded to valid JSON
345343
*/
346-
public static function jsonEncode(array $input): string|false
344+
public static function jsonEncode(array $input): string
347345
{
348346
if (PHP_VERSION_ID >= 50400) {
349347
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
@@ -356,6 +354,9 @@ public static function jsonEncode(array $input): string|false
356354
} elseif ($json === 'null' && $input !== null) {
357355
throw new DomainException('Null result with non-null input');
358356
}
357+
if ($json === false) {
358+
throw new DomainException('Provided object could not be encoded to valid JSON');
359+
}
359360
return $json;
360361
}
361362

@@ -365,8 +366,10 @@ public static function jsonEncode(array $input): string|false
365366
* @param string $input A Base64 encoded string
366367
*
367368
* @return string A decoded string
369+
*
370+
* @throws InvalidArgumentException invalid base64 characters
368371
*/
369-
public static function urlsafeB64Decode(string $input): string|false
372+
public static function urlsafeB64Decode(string $input): string
370373
{
371374
$remainder = \strlen($input) % 4;
372375
if ($remainder) {
@@ -399,8 +402,10 @@ public static function urlsafeB64Encode(string $input): string
399402
*
400403
* @return Key
401404
*/
402-
private static function getKey(Key|array|ArrayAccess $keyOrKeyArray, ?string $kid): Key
403-
{
405+
private static function getKey(
406+
$keyOrKeyArray,
407+
?string $kid
408+
): Key {
404409
if ($keyOrKeyArray instanceof Key) {
405410
return $keyOrKeyArray;
406411
}

src/Key.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,39 @@
99

1010
class Key
1111
{
12+
/** @var string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate */
13+
private $keyMaterial;
14+
/** @var string */
15+
private $algorithm;
16+
1217
/**
13-
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate|array<mixed> $keyMaterial
18+
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
1419
* @param string $algorithm
1520
*/
1621
public function __construct(
17-
private string|OpenSSLAsymmetricKey|OpenSSLCertificate|array $keyMaterial,
18-
private string $algorithm
22+
$keyMaterial,
23+
string $algorithm
1924
) {
25+
if (
26+
!is_string($keyMaterial)
27+
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
28+
&& !$keyMaterial instanceof OpenSSLCertificate
29+
&& !is_resource($keyMaterial)
30+
) {
31+
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
32+
}
33+
2034
if (empty($keyMaterial)) {
2135
throw new InvalidArgumentException('Key material must not be empty');
2236
}
2337

2438
if (empty($algorithm)) {
2539
throw new InvalidArgumentException('Algorithm must not be empty');
2640
}
41+
42+
// TODO: Remove in PHP 8.0 in favor of class constructor property promotion
43+
$this->keyMaterial = $keyMaterial;
44+
$this->algorithm = $algorithm;
2745
}
2846

2947
/**
@@ -37,9 +55,9 @@ public function getAlgorithm(): string
3755
}
3856

3957
/**
40-
* @return string|OpenSSLAsymmetricKey|OpenSSLCertificate|array<mixed>
58+
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
4159
*/
42-
public function getKeyMaterial(): string|OpenSSLAsymmetricKey|OpenSSLCertificate|array
60+
public function getKeyMaterial()
4361
{
4462
return $this->keyMaterial;
4563
}

0 commit comments

Comments
 (0)