Skip to content

Commit ea495cd

Browse files
author
Iurii Bogdanov
committed
Init project
0 parents  commit ea495cd

16 files changed

+539
-0
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
.idea
3+
vendor/
4+
composer.lock
5+
phpunit.xml
6+
.phpunit.result.cache
7+
/.phpunit
8+
.php-cs-fixer.cache

.php-cs-fixer.dist.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
return (new PhpCsFixer\Config())
6+
->setRules([
7+
'@PHP82Migration' => true,
8+
'@Symfony' => true,
9+
'@Symfony:risky' => true,
10+
'@PhpCsFixer' => true,
11+
'@PhpCsFixer:risky' => true,
12+
'@PHPUnit100Migration:risky' => true,
13+
'array_syntax' => ['syntax' => 'short'],
14+
'strict_param' => true,
15+
])
16+
->setFinder(PhpCsFixer\Finder::create()->in(__DIR__))
17+
->setCacheFile('.php-cs-fixer.cache')
18+
;

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Iurii Bogdanov
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# PHP Base64 (encode/decode) Library
2+
3+
[![Latest Stable Version](http://poser.pugx.org/yurijbogdanov/base64/v)](https://packagist.org/packages/yurijbogdanov/base64)
4+
[![Total Downloads](http://poser.pugx.org/yurijbogdanov/base64/downloads)](https://packagist.org/packages/yurijbogdanov/base64)
5+
[![License](http://poser.pugx.org/yurijbogdanov/base64/license)](https://packagist.org/packages/yurijbogdanov/base64)
6+
[![PHP Version Require](http://poser.pugx.org/yurijbogdanov/base64/require/php)](https://packagist.org/packages/yurijbogdanov/base64)
7+
8+
9+
## Installation
10+
```terminal
11+
composer require yurijbogdanov/base64
12+
```
13+
14+
15+
## Usage
16+
Encode:
17+
```terminal
18+
$content = 'hello WoRld 123 ~~~';
19+
$encodedContent = Base64::encode($content); // aGVsbG8gV29SbGQgMTIzIH5+fg==
20+
```
21+
22+
Encode with variant:
23+
```terminal
24+
$content = 'hello WoRld 123 ~~~';
25+
$encodedContent = Base64::encode($content, Base64::BASE64_VARIANT_ORIGINAL); // aGVsbG8gV29SbGQgMTIzIH5+fg==
26+
$encodedContent = Base64::encode($content, Base64::BASE64_VARIANT_ORIGINAL_NO_PADDING); // aGVsbG8gV29SbGQgMTIzIH5+fg
27+
$encodedContent = Base64::encode($content, Base64::BASE64_VARIANT_URLSAFE); // aGVsbG8gV29SbGQgMTIzIH5-fg==
28+
$encodedContent = Base64::encode($content, Base64::BASE64_VARIANT_URLSAFE_NO_PADDING); // aGVsbG8gV29SbGQgMTIzIH5-fg
29+
```
30+
31+
Encode urlsafe (Syntactic sugar for "Base64::encode($content, Base64::BASE64_VARIANT_URLSAFE_NO_PADDING)"):
32+
```terminal
33+
$content = 'hello WoRld 123 ~~~';
34+
$encodedContent = Base64::encodeUrlsafe($content); // aGVsbG8gV29SbGQgMTIzIH5-fg
35+
```
36+
37+
Decode:
38+
```terminal
39+
$content = 'aGVsbG8gV29SbGQgMTIzIH5+fg==';
40+
$decodedContent = Base64::decode($content); // hello WoRld 123 ~~~
41+
```
42+
43+
Decode with variant:
44+
```terminal
45+
$decodedContent = Base64::decode("aGVsbG8gV29SbGQgMTIzIH5+fg==", Base64::BASE64_VARIANT_ORIGINAL); // hello WoRld 123 ~~~
46+
$decodedContent = Base64::decode("aGVsbG8gV29SbGQgMTIzIH5+fg", Base64::BASE64_VARIANT_ORIGINAL_NO_PADDING); // hello WoRld 123 ~~~
47+
$decodedContent = Base64::decode("aGVsbG8gV29SbGQgMTIzIH5-fg==", Base64::BASE64_VARIANT_URLSAFE); // hello WoRld 123 ~~~
48+
$decodedContent = Base64::decode("aGVsbG8gV29SbGQgMTIzIH5-fg", Base64::BASE64_VARIANT_URLSAFE_NO_PADDING); // hello WoRld 123 ~~~
49+
```
50+
51+
Decode urlsafe (Syntactic sugar for "Base64::decode($content, Base64::BASE64_VARIANT_URLSAFE_NO_PADDING)"):
52+
```terminal
53+
$content = 'aGVsbG8gV29SbGQgMTIzIH5-fg';
54+
$decodedContent = Base64::decode($content); // hello WoRld 123 ~~~
55+
```
56+
57+
58+
## Usage via Terminal
59+
List of commands:
60+
```terminal
61+
bin/base64
62+
```
63+
64+
Encode:
65+
```terminal
66+
bin/base64 encode [CONTENT]
67+
bin/base64 encode "hello WoRld 123 ~~~"
68+
# Output: aGVsbG8gV29SbGQgMTIzIH5+fg==
69+
```
70+
71+
Encode with variant:
72+
```terminal
73+
bin/base64 encode_with_variant [CONTENT] [VARIANT]
74+
bin/base64 encode_with_variant "hello WoRld 123 ~~~" 1
75+
# Output: aGVsbG8gV29SbGQgMTIzIH5+fg==
76+
bin/base64 encode_with_variant "hello WoRld 123 ~~~" 2
77+
# Output: aGVsbG8gV29SbGQgMTIzIH5+fg
78+
bin/base64 encode_with_variant "hello WoRld 123 ~~~" 4
79+
# Output: aGVsbG8gV29SbGQgMTIzIH5-fg==
80+
bin/base64 encode_with_variant "hello WoRld 123 ~~~" 8
81+
# Output: aGVsbG8gV29SbGQgMTIzIH5-fg
82+
```
83+
84+
Encode urlsafe (Syntactic sugar for "bin/base64 encode_with_variant [CONTENT] 8"):
85+
```terminal
86+
bin/base64 encode_urlsafe [CONTENT]
87+
bin/base64 encode_urlsafe "hello WoRld 123 ~~~"
88+
# Output: aGVsbG8gV29SbGQgMTIzIH5-fg
89+
```
90+
91+
Decode:
92+
```terminal
93+
bin/base64 decode [CONTENT]
94+
bin/base64 decode aGVsbG8gV29SbGQgMTIzIH5+fg==
95+
# Output: hello WoRld 123 ~~~
96+
```
97+
98+
Decode with variant:
99+
```terminal
100+
bin/base64 decode_with_variant [CONTENT] [VARIANT]
101+
bin/base64 decode_with_variant aGVsbG8gV29SbGQgMTIzIH5+fg== 1
102+
# Output: hello WoRld 123 ~~~
103+
bin/base64 decode_with_variant aGVsbG8gV29SbGQgMTIzIH5+fg 2
104+
# Output: hello WoRld 123 ~~~
105+
bin/base64 decode_with_variant aGVsbG8gV29SbGQgMTIzIH5-fg== 4
106+
# Output: hello WoRld 123 ~~~
107+
bin/base64 decode_with_variant aGVsbG8gV29SbGQgMTIzIH5-fg 8
108+
# Output: hello WoRld 123 ~~~
109+
```
110+
111+
Decode urlsafe (Syntactic sugar for "bin/base64 decode_with_variant [CONTENT] 8"):
112+
```terminal
113+
bin/base64 decode_urlsafe [CONTENT]
114+
bin/base64 decode_urlsafe aGVsbG8gV29SbGQgMTIzIH5-fg
115+
# Output: hello WoRld 123 ~~~
116+
```

bin/base64

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
declare(strict_types=1);
5+
6+
if ('cli' !== \PHP_SAPI) {
7+
exit(1);
8+
}
9+
10+
require dirname(__DIR__).'/vendor/autoload.php';
11+
12+
use YB\Base64\Base64;
13+
14+
$command = (string) ($argv[1] ?? '');
15+
$content = $argv[2] ?? null;
16+
$variant = $argv[3] ?? null;
17+
18+
if (null !== $variant) {
19+
$variant = (int) $variant;
20+
}
21+
22+
try {
23+
echo match ($command) {
24+
'' => <<<'TEXT'
25+
Available commands:
26+
encode [CONTENT]
27+
decode [CONTENT]
28+
encode_with_variant [CONTENT] [VARIANT]
29+
decode_with_variant [CONTENT] [VARIANT]
30+
encode_urlsafe [CONTENT]
31+
decode_urlsafe [CONTENT]
32+
TEXT,
33+
'encode' => Base64::encode($content),
34+
'decode' => Base64::decode($content),
35+
'encode_with_variant' => Base64::encode($content, $variant),
36+
'decode_with_variant' => Base64::decode($content, $variant),
37+
'encode_urlsafe' => Base64::encodeUrlsafe($content),
38+
'decode_urlsafe' => Base64::decodeUrlsafe($content),
39+
default => sprintf('Command "%s" not found', $command),
40+
};
41+
} catch (\Throwable $e) {
42+
echo sprintf('Error occurred: %s', $e->getMessage()), "\n";
43+
44+
exit(1);
45+
}
46+
47+
echo "\n";

composer.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "yurijbogdanov/base64",
3+
"type": "library",
4+
"description": "PHP Base64 (encode/decode) Library",
5+
"keywords": ["php", "base64", "encode", "decode", "url-safe", "urlsafe-base64", "base64-encode", "base64-decode"],
6+
"homepage": "https://github.com/yurijbogdanov/base64",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "Iurii Bogdanov",
11+
"email": "bogdanovyurij@gmail.com"
12+
}
13+
],
14+
"require": {
15+
"php": ">=8.0"
16+
},
17+
"require-dev": {
18+
"roave/security-advisories": "dev-latest",
19+
"phpunit/phpunit": "^10",
20+
"phpstan/phpstan": "^1",
21+
"friendsofphp/php-cs-fixer": "^3"
22+
},
23+
"minimum-stability": "stable",
24+
"prefer-stable": true,
25+
"autoload": {
26+
"psr-4": {
27+
"YB\\Base64\\": "src/"
28+
},
29+
"exclude-from-classmap": [
30+
"/Tests/"
31+
]
32+
},
33+
"autoload-dev": {
34+
"psr-4": {
35+
"YB\\Base64\\Tests\\": "tests/"
36+
}
37+
},
38+
"scripts": {
39+
"test": [
40+
"php vendor/bin/php-cs-fixer fix --dry-run --allow-risky=yes --using-cache=no --diff -vv",
41+
"php vendor/bin/phpstan analyse -vv",
42+
"php vendor/bin/phpunit --no-coverage"
43+
]
44+
}
45+
}

phpstan.neon.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
parameters:
2+
level: max
3+
paths: [ src, tests ]
4+
excludePaths: [ tests/bootstrap.php ]
5+
checkMissingIterableValueType: false
6+
checkGenericClassInNonGenericObjectType: false

phpunit.xml.dist

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" failOnRisky="true" failOnWarning="true">
3+
<php>
4+
<ini name="error_reporting" value="-1"/>
5+
</php>
6+
<testsuites>
7+
<testsuite name="Crypto Library Test Suite">
8+
<directory>./Tests/</directory>
9+
</testsuite>
10+
</testsuites>
11+
<coverage/>
12+
<source>
13+
<include>
14+
<directory>./</directory>
15+
</include>
16+
<exclude>
17+
<directory>./Tests</directory>
18+
<directory>./vendor</directory>
19+
</exclude>
20+
</source>
21+
</phpunit>

src/Base64.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace YB\Base64;
6+
7+
final class Base64 implements Base64Interface
8+
{
9+
/**
10+
* BASE64_VARIANT_ORIGINAL for standard (A-Za-z0-9/\+) Base64 encoding.
11+
* BASE64_VARIANT_ORIGINAL_NO_PADDING for standard (A-Za-z0-9/\+) Base64 encoding, without = padding characters.
12+
* BASE64_VARIANT_URLSAFE for URL-safe (A-Za-z0-9\-_) Base64 encoding.
13+
* BASE64_VARIANT_URLSAFE_NO_PADDING for URL-safe (A-Za-z0-9\-_) Base64 encoding, without = padding characters.
14+
*/
15+
public const BASE64_VARIANT_ORIGINAL = 1;
16+
public const BASE64_VARIANT_ORIGINAL_NO_PADDING = 2;
17+
public const BASE64_VARIANT_URLSAFE = 4;
18+
public const BASE64_VARIANT_URLSAFE_NO_PADDING = 8;
19+
public const BASE64_VARIANTS = [
20+
self::BASE64_VARIANT_ORIGINAL,
21+
self::BASE64_VARIANT_ORIGINAL_NO_PADDING,
22+
self::BASE64_VARIANT_URLSAFE,
23+
self::BASE64_VARIANT_URLSAFE_NO_PADDING,
24+
];
25+
26+
public static function encode(string $content, int $variant = null): string
27+
{
28+
if (null === $variant) {
29+
$variant = self::BASE64_VARIANT_ORIGINAL;
30+
}
31+
32+
if (!\in_array($variant, self::BASE64_VARIANTS, true)) {
33+
throw new Exception\UnsupportedVariantException(sprintf('Unsupported variant: %d', $variant));
34+
}
35+
36+
$content = base64_encode($content);
37+
38+
if ($variant & (self::BASE64_VARIANT_URLSAFE | self::BASE64_VARIANT_URLSAFE_NO_PADDING)) {
39+
$content = strtr($content, '+/', '-_');
40+
}
41+
42+
if ($variant & (self::BASE64_VARIANT_ORIGINAL_NO_PADDING | self::BASE64_VARIANT_URLSAFE_NO_PADDING)) {
43+
$content = str_replace('=', '', $content);
44+
}
45+
46+
return $content;
47+
}
48+
49+
public static function decode(string $content, int $variant = null): string
50+
{
51+
if (null === $variant) {
52+
$variant = self::BASE64_VARIANT_ORIGINAL;
53+
}
54+
55+
if (!\in_array($variant, self::BASE64_VARIANTS, true)) {
56+
throw new Exception\UnsupportedVariantException(sprintf('Unsupported variant: %d', $variant));
57+
}
58+
59+
if ($variant & (self::BASE64_VARIANT_URLSAFE | self::BASE64_VARIANT_URLSAFE_NO_PADDING)) {
60+
$content = strtr($content, '-_', '+/');
61+
}
62+
63+
if ($variant & (self::BASE64_VARIANT_ORIGINAL_NO_PADDING | self::BASE64_VARIANT_URLSAFE_NO_PADDING)) {
64+
$remainder = \strlen($content) % 4;
65+
if ($remainder) {
66+
$content .= str_repeat('=', 4 - $remainder);
67+
}
68+
}
69+
70+
$content = base64_decode($content, true);
71+
if (false === $content) {
72+
throw new Exception\DecodeFailedException('Cannot decode content');
73+
}
74+
75+
return $content;
76+
}
77+
78+
public static function encodeUrlsafe(string $content): string
79+
{
80+
return self::encode($content, self::BASE64_VARIANT_URLSAFE_NO_PADDING);
81+
}
82+
83+
public static function decodeUrlsafe(string $content): string
84+
{
85+
return self::decode($content, self::BASE64_VARIANT_URLSAFE_NO_PADDING);
86+
}
87+
}

0 commit comments

Comments
 (0)