|
10 | 10 |
|
11 | 11 | namespace chillerlan\HTTP\Utils;
|
12 | 12 |
|
| 13 | +use InvalidArgumentException; |
13 | 14 | use Psr\Http\Message\StreamInterface;
|
| 15 | +use function in_array; |
| 16 | +use function preg_match; |
| 17 | +use function str_contains; |
| 18 | +use function substr; |
14 | 19 |
|
15 | 20 | /**
|
16 | 21 | *
|
17 | 22 | */
|
18 |
| -class StreamUtil{ |
| 23 | +final class StreamUtil{ |
19 | 24 |
|
20 |
| - public const MODES_READ_WRITE = ['a+', 'c+', 'c+b', 'c+t', 'r+' , 'r+b', 'r+t', 'w+' , 'w+b', 'w+t', 'x+' , 'x+b', 'x+t']; |
21 |
| - public const MODES_READ = [...self::MODES_READ_WRITE, 'r', 'rb', 'rt']; |
22 |
| - public const MODES_WRITE = [...self::MODES_READ_WRITE, 'a', 'rw', 'w', 'wb']; |
| 25 | + /** |
| 26 | + * Checks whether the given mode allows reading and writing |
| 27 | + */ |
| 28 | + public static function modeAllowsReadWrite(string $mode):bool{ |
| 29 | + return str_contains(self::validateMode($mode), '+'); |
| 30 | + } |
| 31 | + |
| 32 | + /** |
| 33 | + * Checks whether the given mode allows only reading |
| 34 | + */ |
| 35 | + public static function modeAllowsReadOnly(string $mode):bool{ |
| 36 | + $mode = self::validateMode($mode); |
| 37 | + |
| 38 | + return $mode[0] === 'r' && !str_contains($mode, '+'); |
| 39 | + } |
| 40 | + |
| 41 | + /** |
| 42 | + * Checks whether the given mode allows only writing |
| 43 | + */ |
| 44 | + public static function modeAllowsWriteOnly(string $mode):bool{ |
| 45 | + $mode = self::validateMode($mode); |
| 46 | + |
| 47 | + return in_array($mode[0], ['a', 'c', 'w', 'x']) && !str_contains($mode, '+'); |
| 48 | + } |
| 49 | + |
| 50 | + /** |
| 51 | + * Checks whether the given mode allows reading |
| 52 | + */ |
| 53 | + public static function modeAllowsRead(string $mode):bool{ |
| 54 | + $mode = self::validateMode($mode); |
| 55 | + |
| 56 | + return $mode[0] === 'r' || (in_array($mode[0], ['a', 'c', 'w', 'x']) && str_contains($mode, '+')); |
| 57 | + } |
| 58 | + |
| 59 | + /** |
| 60 | + * Checks whether the given mode allows writing |
| 61 | + */ |
| 62 | + public static function modeAllowsWrite(string $mode):bool{ |
| 63 | + $mode = self::validateMode($mode); |
| 64 | + |
| 65 | + return in_array($mode[0], ['a', 'c', 'w', 'x']) || ($mode[0] === 'r' && str_contains($mode, '+')); |
| 66 | + } |
| 67 | + |
| 68 | + /** |
| 69 | + * Checks if the given mode is valid for fopen(). |
| 70 | + * Returns the first 15 characters, throws if that string doesn't match the pattern. |
| 71 | + * |
| 72 | + * Note: we don't care where the modifier flags are in the string, what matters is that the first character |
| 73 | + * is one of "acrwx" and the rest may contain one of "bet+" from 2nd position onwards, so "aaaaaaaaaaaaaa+b" is valid. |
| 74 | + * |
| 75 | + * The documentation of fopen() says that the text-mode translation flag (b/t) should be added as last character, |
| 76 | + * however, it doesn't matter as PHP internally only reads the mode from the first character and 15 characters total. |
| 77 | + * and does a strchr() on it for the flags, so technically "rb+" is equivalent to "r+b" and "rrrbbbb++". |
| 78 | + * Also, some libraries allow a mode "rw" which is wrong and just falls back to "r" - see above. (looking at you, Guzzle) |
| 79 | + * |
| 80 | + * gzopen() adds a bunch of other flags that are hardly documented, so we'll ignore these until we get a full list. |
| 81 | + * |
| 82 | + * @see https://www.php.net/manual/en/function.fopen |
| 83 | + * @see https://www.php.net/manual/en/function.gzopen.php |
| 84 | + * @see https://stackoverflow.com/a/44483367/3185624 |
| 85 | + * @see https://github.com/php/php-src/blob/6602ddead5c81fb67ebf2b21c32b58aa1de67699/main/streams/plain_wrapper.c#L71-L121 |
| 86 | + * @see https://github.com/guzzle/psr7/blob/815698d9f11c908bc59471d11f642264b533346a/src/Stream.php#L19 |
| 87 | + * |
| 88 | + * @throws \InvalidArgumentException |
| 89 | + */ |
| 90 | + public static function validateMode(string $mode):string{ |
| 91 | + $mode = substr($mode, 0, 15); |
| 92 | + |
| 93 | + if(!preg_match('/^[acrwx]+[befht+\d]*$/', $mode)){ // [bet+]* |
| 94 | + throw new InvalidArgumentException('invalid fopen mode: '.$mode); |
| 95 | + } |
| 96 | + |
| 97 | + return $mode; |
| 98 | + } |
23 | 99 |
|
24 | 100 | /**
|
25 | 101 | * Reads the content from a stream and make sure we rewind
|
|
0 commit comments