diff --git a/.gitattributes b/.gitattributes
index 05ed89a..93aa015 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,14 +1,14 @@
-* text=auto
-
# Always use LF
core.autocrlf=lf
-.editorconfig
+.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
-.php_cs export-ignore
-.scrutinizer.yml export-ignore
-.travis.yml export-ignore
.github export-ignore
-phpspec.yml export-ignore
-/spec export-ignore
+.php-cs-fixer.dist.php export-ignore
+CODE_OF_CONDUCT.md export-ignore
+Makefile export-ignore
+phpunit.xml.dist export-ignore
+phpstan.neon export-ignore
+phpstan-baseline.neon export-ignore
+tests/ export-ignore
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index eeaf12b..0000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,12 +0,0 @@
-| Q | A
-| ---------------- | -----
-| Bug report? | yes/no
-| Feature request? | yes/no
-| BC Break report? | yes/no
-| RFC? | yes/no
-
-
diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.md b/.github/ISSUE_TEMPLATE/1_Bug_report.md
new file mode 100644
index 0000000..1e26723
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1_Bug_report.md
@@ -0,0 +1,20 @@
+---
+name: "\U0001F41B Bug Report"
+about: Report errors and problems
+title: "[bug] "
+labels: Potential Bug
+assignees: ''
+
+---
+
+**Description**
+
+
+**How to reproduce**
+
+
+**Possible Solution**
+
+
+**Additional context**
+
diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.md b/.github/ISSUE_TEMPLATE/2_Feature_request.md
new file mode 100644
index 0000000..c21e708
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/2_Feature_request.md
@@ -0,0 +1,15 @@
+---
+name: "\U0001F680 Feature Request"
+about: "I have a suggestion (and may want to implement it \U0001F642)!"
+title: "[Feature] "
+labels: Feature
+assignees: ''
+
+---
+
+**Description**
+
+
+**Example**
+
diff --git a/.github/ISSUE_TEMPLATE/3_Support_question.md b/.github/ISSUE_TEMPLATE/3_Support_question.md
new file mode 100644
index 0000000..f933bf2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/3_Support_question.md
@@ -0,0 +1,13 @@
+---
+name: 👩🏫 Support Question
+about: Questions about using this library
+labels: Question / Support
+
+---
+
+**Description**
+
diff --git a/.github/ISSUE_TEMPLATE/4_Security_issue.md b/.github/ISSUE_TEMPLATE/4_Security_issue.md
new file mode 100644
index 0000000..ea75484
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/4_Security_issue.md
@@ -0,0 +1,14 @@
+---
+name: ⛔ Security Issue
+about: Report security issues and problems (PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY)
+
+---
+
+⚠ PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW.
+
+If you have found a security issue in this project, please send the details to
+security [at] rollerscapes.net and don't disclose it publicly until we can provide a
+fix for it.
+
+**Note:** Please don't blindly send reports about automated tools, make sure the
+reported issue is in fact exploitable. Thanks.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index d38edd7..da9f029 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -4,12 +4,13 @@
| New feature? | yes/no
| BC breaks? | yes/no
| Deprecations? | yes/no
-| Tests pass? | yes/no
-| Fixed tickets | #...
+| Fixed tickets | Fix #...
| License | MIT
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..cb9a946
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,157 @@
+name: 'CI'
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ cs-fixer:
+ name: 'PHP CS Fixer'
+
+ runs-on: 'ubuntu-latest'
+
+ strategy:
+ matrix:
+ php-version:
+ - '8.2'
+
+ steps:
+ -
+ name: 'Check out'
+ uses: 'actions/checkout@v4'
+
+ -
+ name: 'Set up PHP'
+ uses: 'shivammathur/setup-php@v2'
+ with:
+ php-version: '${{ matrix.php-version }}'
+ coverage: 'none'
+
+ -
+ name: 'Get Composer cache directory'
+ id: 'composer-cache'
+ run: 'echo "cache_dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
+
+ -
+ name: 'Cache dependencies'
+ uses: 'actions/cache@v3'
+ with:
+ path: '${{ steps.composer-cache.outputs.cache_dir }}'
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: 'php-${{ matrix.php-version }}-composer-locked-'
+
+ -
+ name: 'Install dependencies'
+ run: 'composer install --no-progress'
+
+ -
+ name: 'Check the code style'
+ run: 'make cs'
+
+ phpstan:
+ name: 'PhpStan'
+
+ runs-on: 'ubuntu-latest'
+
+ strategy:
+ matrix:
+ php-version:
+ - '8.2'
+
+ steps:
+ -
+ name: 'Check out'
+ uses: 'actions/checkout@v4'
+
+ -
+ name: 'Set up PHP'
+ uses: 'shivammathur/setup-php@v2'
+ with:
+ php-version: '${{ matrix.php-version }}'
+ coverage: 'none'
+
+ -
+ name: 'Get Composer cache directory'
+ id: 'composer-cache'
+ run: 'echo "cache_dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
+
+ -
+ name: 'Cache dependencies'
+ uses: 'actions/cache@v3'
+ with:
+ path: '${{ steps.composer-cache.outputs.cache_dir }}'
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: 'php-${{ matrix.php-version }}-composer-locked-'
+
+ -
+ name: 'Install dependencies'
+ run: 'composer install --no-progress'
+
+ -
+ name: 'Run PhpStan'
+ run: 'vendor/bin/phpstan analyze --no-progress'
+
+ tests:
+ name: 'PHPUnit'
+
+ runs-on: 'ubuntu-latest'
+
+ strategy:
+ matrix:
+ include:
+ -
+ php-version: '8.1'
+ composer-options: '--prefer-stable'
+ symfony-version: '6.3'
+ -
+ php-version: '8.2'
+ composer-options: '--prefer-stable'
+ symfony-version: '^6.4'
+
+ -
+ php-version: '8.2'
+ composer-options: '--prefer-stable'
+ symfony-version: '^7.0'
+
+ steps:
+ -
+ name: 'Check out'
+ uses: 'actions/checkout@v4'
+
+ -
+ name: 'Set up PHP'
+ uses: 'shivammathur/setup-php@v2'
+ with:
+ php-version: '${{ matrix.php-version }}'
+ coverage: 'none'
+
+ -
+ name: 'Get Composer cache directory'
+ id: 'composer-cache'
+ run: 'echo "cache_dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
+
+ -
+ name: 'Cache dependencies'
+ uses: 'actions/cache@v3'
+ with:
+ path: '${{ steps.composer-cache.outputs.cache_dir }}'
+ key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
+ restore-keys: 'php-${{ matrix.php-version }}-composer-locked-'
+
+ -
+ name: 'Install dependencies'
+ env:
+ COMPOSER_OPTIONS: '${{ matrix.composer-options }}'
+ SYMFONY_REQUIRE: '${{ matrix.symfony-version }}'
+ run: |
+ composer global config --no-plugins allow-plugins.symfony/flex true
+ composer global require --no-progress --no-scripts --no-plugins symfony/flex
+ composer update --no-progress $COMPOSER_OPTIONS
+
+ -
+ name: 'Run tests'
+ run: make phpunit
diff --git a/.gitignore b/.gitignore
index 2fc0c2f..e2245b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,9 @@
-/composer.lock
-*.phar
+composer.lock
/vendor/
-.php_cs.cache
+
+phpunit.xml
+.phpunit.result.cache
+.phpunit.cache/
+.phpunit
+
+.php-cs-fixer.cache
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 0000000..f90f35c
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,30 @@
+
+
+This source file is subject to the MIT license that is bundled
+with this source code in the file LICENSE.
+EOF;
+
+/** @var \Symfony\Component\Finder\Finder $finder */
+$finder = PhpCsFixer\Finder::create();
+$finder
+ ->in([
+ __DIR__ . '/src',
+ __DIR__ . '/tests',
+ ]);
+
+$config = new PhpCsFixer\Config();
+$config
+ ->setRiskyAllowed(true)
+ ->setRules(
+ array_merge(
+ require __DIR__ . '/vendor/rollerscapes/standards/php-cs-fixer-rules.php',
+ ['header_comment' => ['header' => $header]])
+ )
+ ->setFinder($finder);
+
+return $config;
diff --git a/.php_cs b/.php_cs
deleted file mode 100644
index 4bc6030..0000000
--- a/.php_cs
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-This source file is subject to the MIT license that is bundled
-with this source code in the file LICENSE.
-EOF;
-
-return PhpCsFixer\Config::create()
- ->setRules([
- '@Symfony' => true,
- '@Symfony:risky' => true,
- '@PHP70Migration' => true,
- '@PHP71Migration' => true,
- 'array_syntax' => array('syntax' => 'short'),
- 'combine_consecutive_unsets' => true,
- 'declare_strict_types' => true,
- 'header_comment' => ['header' => $header],
- 'heredoc_to_nowdoc' => true,
- 'linebreak_after_opening_tag' => true,
- 'no_extra_consecutive_blank_lines' => ['continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'],
- 'no_short_echo_tag' => true,
- 'no_unreachable_default_argument_value' => false,
- 'no_useless_else' => true,
- 'no_useless_return' => true,
- 'ordered_class_elements' => false,
- 'ordered_imports' => true,
- 'phpdoc_add_missing_param_annotation' => false,
- 'phpdoc_annotation_without_dot' => true,
- 'phpdoc_no_empty_return' => false, // PHP 7 compatibility
- 'phpdoc_order' => true,
- // This breaks for variable @var blocks
- 'phpdoc_to_comment' => false,
- 'phpdoc_var_without_name' => false,
- 'semicolon_after_instruction' => true,
- 'single_import_per_statement' => false,
- 'strict_comparison' => false,
- 'strict_param' => true,
- ])
- ->setRiskyAllowed(true)
- ->setFinder(
- PhpCsFixer\Finder::create()
- ->in([__DIR__.'/src', __DIR__.'/spec'])
- )
-;
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index 114ee4c..0000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-filter:
- paths: [ 'src/*' ]
-
-checks:
- php:
- code_rating: true
- duplication: false
- verify_property_names: true
- uppercase_constants: true
- remove_extra_empty_lines: true
- properties_in_camelcaps: true
- parameters_in_camelcaps: true
- overriding_parameter: true
- optional_parameters_at_the_end: true
- function_in_camel_caps: true
- encourage_single_quotes: true
- classes_in_camel_caps: true
- check_method_contracts:
- verify_interface_like_constraints: true
- verify_documented_constraints: true
- verify_parent_constraints: true
- avoid_perl_style_comments: true
- avoid_usage_of_logical_operators: true
- no_exit: false
- no_unnecessary_final_modifier: false
- overriding_private_members: false
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 4edf236..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-language: php
-
-sudo: false
-
-branches:
- only:
- - master
-
-matrix:
- include:
- - php: 7.1
- fast_finish: true
-
-cache:
- directories:
- - $HOME/.composer/cache
-
-before_install:
- - phpenv config-rm xdebug.ini || echo "xdebug not available"
-
-install:
- - composer install -o
-
-script:
- - vendor/bin/phpspec run
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 76f31b0..3bd3991 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1 +1 @@
-This project's code-of-conduct can be found at https://github.com/rollerworks/contributing/blob/master/CODE_OF_CONDUCT.md
+This project's code-of-conduct can be found at https://contributing.rollerscapes.net/latest/code-of-conduct
diff --git a/LICENSE b/LICENSE
index da6f27c..90816ab 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2018 Sebastiaan Stok
+Copyright (c) 2014 Sebastiaan Stok
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2ba1948
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+include vendor/rollerscapes/standards/Makefile
+
+phpunit:
+ ./vendor/bin/phpunit
diff --git a/README.md b/README.md
index 5050608..c59ae3e 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,8 @@
-Rollerworks UriEncoder Component
-================================
+Rollerworks UriEncoder
+======================
-[![Build Status](https://secure.travis-ci.org/rollerworks/rollerworks-uri-encoder.png?branch=master)](http://travis-ci.org/rollerworks/rollerworks-uri-encoder)
-[![SensioLabsInsight](https://insight.sensiolabs.com/projects/0b197295-cc98-4425-afe6-ad2b59283db6/mini.png)](https://insight.sensiolabs.com/projects/0b197295-cc98-4425-afe6-ad2b59283db6)
-[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/rollerworks/rollerworks-uri-encoder/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/rollerworks/rollerworks-uri-encoder/?branch=master)
-
-This package provides the Rollerworks UriEncoder component,
-a simple library, to safely encode a string for usage in a URI.
-
-And some minor extra's, like string compression and conversion caching.
+This package provides the Rollerworks UriEncoder component, a simple library,
+to safely encode a string for usage in a URI. Plus a zlib compression.
**Caution:**
@@ -18,44 +12,38 @@ And some minor extra's, like string compression and conversion caching.
> Use [paragonie/constant_time_encoding](https://github.com/paragonie/constant_time_encoding)
> for time-safe en/decoding. Don't use conversion caching or compression for sensitive information!
-Installation
-------------
+## Installation
-To install this package, add `rollerworks/search-uri-encoder` to your composer.json
+To install this package, add `rollerworks/search-uri-encoder` to your composer.json:
```bash
$ php composer.phar require rollerworks/search-uri-encoder
```
-Now, Composer will automatically download all required files, and install them
-for you.
+Now, [Composer][composer] will automatically download all required files,
+and install them for you.
-Requirements
-------------
+## Requirements
-You need at least PHP 5.3.3 and optionally have support for gzip compression
+You need at least PHP 8.1, and optionally have support for gzip compression
enabled.
This package has no other external dependencies.
-Basic usage
------------
+## Basic usage
The usage of this library is very straightforward, each encoder encodes and decodes
a URL string.
-To encode a string for safe usage in URL call `encodeUri()` on the encoder object.
-
-To decode an encoded string, to the original value call `decodeUri()` on the encoder object.
+To encode a string for safe usage in a URL call `encodeUri()` on the encoder.
+To decode an encoded string, to the original value call `decodeUri()` on the encoder.
-**Note:** Decoders will silently ignore invalid data, and return null instead.
+**Note:** The `decode()` method will silently ignore invalid data,
+and return null instead.
### Base64UriEncoder
```php
-
-require 'vendor/autoload.php';
-
use Rollerworks\Component\UriEncoder\Encoder as UriEncoder;
$stringEncode = 'This string is not safe, for direct usage & must encoded';
@@ -78,19 +66,16 @@ A decorator operates on top of the actual encoder.
* `encodeUri()` modifies the value returned by the decorated encoder.
* `decodeUri()` modifies the passed-in value before passing to the decorated encoder.
-These decorators can not be used as a stand-alone!
+These decorators cannot be used as a stand-alone.
#### GZipCompressionDecorator
The `GZipCompressionDecorator` (de)compresses URI data.
-**Caution:** The GZipCompressionDecorator creates a non-safe binary result,
+**Caution:** The `GZipCompressionDecorator` creates a non-safe binary result,
make sure the original encoder supports this.
```php
-
-require 'vendor/autoload.php';
-
use Rollerworks\Component\UriEncoder\Encoder as UriEncoder;
$stringEncode = 'This string is not safe, for direct usage & must encoded';
@@ -126,8 +111,15 @@ For more information on SemVer, please visit .
License
-------
-The package is provided under the none-restrictive MIT license,
-you are free to use it for any free or proprietary product/application,
-without restrictions.
+This library is released under the [MIT license](LICENSE).
+
+## Contributing
+
+This is an open source project. If you'd like to contribute,
+please read the [Contributing Guidelines][contributing]. If you're submitting
+a pull request, please follow the guidelines in the [Submitting a Patch][patches] section.ß
-[LICENSE](LICENSE)
+[composer]: https://getcomposer.org/doc/00-intro.md
+[flex]: https://symfony.com/doc/current/setup/flex.html
+[contributing]: https://contributing.rollerscapes.net/
+[patches]: https://contributing.rollerscapes.net/latest/patches.html
diff --git a/composer.json b/composer.json
index 54ef979..a8b73be 100644
--- a/composer.json
+++ b/composer.json
@@ -11,20 +11,28 @@
},
{
"name": "Community contributions",
- "homepage": "https://github.com/Rollerworks/uri-encoder/contributors"
+ "homepage": "https://github.com/rollerworks/uri-encoder/contributors"
}
],
"require": {
- "php": "^7.1"
+ "php": "^8.1",
+ "symfony/polyfill-mbstring": "^1.28"
},
"require-dev": {
- "phpspec/phpspec": "^3.4.2"
+ "ext-zlib": "*",
+ "rollerscapes/standards": "^1.0",
+ "phpunit/phpunit": "^10.4"
},
+ "minimum-stability": "dev",
+ "prefer-stable": true,
"autoload": {
"psr-4": {
"Rollerworks\\Component\\UriEncoder\\": "src/"
},
- "exclude-from-classmap": ["spec/"]
+ "exclude-from-classmap": ["tests/"]
+ },
+ "autoload-dev": {
+ "Rollerworks\\Component\\UriEncoder\\Tests\\": "tests/"
},
"extra": {
"branch-alias": {
diff --git a/phpspec.yml b/phpspec.yml
deleted file mode 100644
index 07d8a32..0000000
--- a/phpspec.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-formatter.name: pretty
-
-suites:
- uri_encoder_suite:
- namespace: Rollerworks\Component\UriEncoder
- psr4_prefix: Rollerworks\Component\UriEncoder
- src_path: src/
- spec_prefix: spec\
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..0e56097
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,16 @@
+includes:
+ - vendor/rollerscapes/standards/phpstan.neon
+ #- phpstan-baseline.neon
+
+parameters:
+ #reportUnmatchedIgnoredErrors: false
+
+ paths:
+ - ./src
+ - ./tests
+ excludePaths:
+ - var/
+ - templates/
+ - translations/
+
+ #ignoreErrors:
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..b040071
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,29 @@
+
+
+
+
+ tests
+
+
+
+
+
+
+
+
+
+
diff --git a/spec/Encoder/Base64UriEncoderSpec.php b/spec/Encoder/Base64UriEncoderSpec.php
deleted file mode 100644
index 3e75485..0000000
--- a/spec/Encoder/Base64UriEncoderSpec.php
+++ /dev/null
@@ -1,44 +0,0 @@
-
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace spec\Rollerworks\Component\UriEncoder\Encoder;
-
-use PhpSpec\ObjectBehavior;
-
-final class Base64UriEncoderSpec extends ObjectBehavior
-{
- public function it_is_initializable()
- {
- $this->shouldHaveType('Rollerworks\Component\UriEncoder\Encoder\Base64UriEncoder');
- }
-
- public function its_an_encoder()
- {
- $this->shouldHaveType('Rollerworks\Component\UriEncoder\UriEncoderInterface');
- }
-
- public function it_encodes_a_uri()
- {
- $this->encodeUri('foo-bar-car')->shouldReturn('Zm9vLWJhci1jYXI');
- }
-
- public function it_decodes_an_encoded_uri()
- {
- $this->decodeUri('Zm9vLWJhci1jYXI')->shouldReturn('foo-bar-car');
- }
-
- public function it_returns_null_on_invalid_encoded_data()
- {
- $this->decodeUri('[whoops]')->shouldReturn(null);
- }
-}
diff --git a/spec/Encoder/GZipCompressionDecoratorSpec.php b/spec/Encoder/GZipCompressionDecoratorSpec.php
deleted file mode 100644
index aad94bc..0000000
--- a/spec/Encoder/GZipCompressionDecoratorSpec.php
+++ /dev/null
@@ -1,60 +0,0 @@
-
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
-
-namespace spec\Rollerworks\Component\UriEncoder\Encoder;
-
-use PhpSpec\ObjectBehavior;
-use Prophecy\Argument;
-use Rollerworks\Component\UriEncoder\UriEncoderInterface;
-
-final class GZipCompressionDecoratorSpec extends ObjectBehavior
-{
- public function let(UriEncoderInterface $encoder)
- {
- // Use a callback as we process binary data here
- $encoder->encodeUri(Argument::any())->will(function ($data) {
- return base64_encode($data[0]);
- });
-
- $encoder->decodeUri(Argument::any())->will(function ($data) {
- return base64_decode($data[0], true);
- });
-
- $this->beConstructedWith($encoder);
- }
-
- public function it_is_initializable()
- {
- $this->shouldHaveType('Rollerworks\Component\UriEncoder\Encoder\GZipCompressionDecorator');
- }
-
- public function its_an_encoder()
- {
- $this->shouldHaveType('Rollerworks\Component\UriEncoder\UriEncoderInterface');
- }
-
- public function it_encodes_a_uri()
- {
- $this->encodeUri('foo-bar-car')->shouldReturn('eJxLy8/XTUos0k1OLAIAGFEECg==');
- }
-
- public function it_decodes_an_encoded_uri()
- {
- $this->decodeUri('eJxLy8/XTUos0k1OLAIAGFEECg==')->shouldReturn('foo-bar-car');
- }
-
- public function it_returns_null_on_invalid_encoded_data()
- {
- $this->decodeUri('[whoops]')->shouldReturn(null);
- }
-}
diff --git a/src/Encoder/Base64UriEncoder.php b/src/Encoder/Base64UriEncoder.php
index d3e2b8a..b2b9104 100644
--- a/src/Encoder/Base64UriEncoder.php
+++ b/src/Encoder/Base64UriEncoder.php
@@ -19,27 +19,24 @@
* Base64UriEncoder encodes/decodes URI using URI-safe base64.
*
* Unsafe characters are converted and stripped.
- *
- * @author Sebastiaan Stok
*/
final class Base64UriEncoder implements UriEncoderInterface
{
- /**
- * {@inheritdoc}
- */
public function encodeUri(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
- /**
- * {@inheritdoc}
- */
public function decodeUri(string $data): ?string
{
try {
- return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT), true) ?? null;
- } catch (\Throwable $e) {
+ $decode = base64_decode(
+ str_pad(strtr($data, '-_', '+/'), mb_strlen($data, '8bit') % 4, '='),
+ true
+ );
+
+ return $decode === false ? null : $decode;
+ } catch (\Throwable) {
return null;
}
}
diff --git a/src/Encoder/GZipCompressionDecorator.php b/src/Encoder/GZipCompressionDecorator.php
index 0d12849..5b92363 100644
--- a/src/Encoder/GZipCompressionDecorator.php
+++ b/src/Encoder/GZipCompressionDecorator.php
@@ -18,46 +18,29 @@
/**
* GZipCompressionDecorator (de)compresses URI data and delegates
* back to the original Encoder.
- *
- * @author Sebastiaan Stok
*/
final class GZipCompressionDecorator implements UriEncoderInterface
{
- /**
- * @var UriEncoderInterface
- */
- private $encoder;
+ public function __construct(private UriEncoderInterface $encoder) {}
- /**
- * @param UriEncoderInterface $encoder
- */
- public function __construct(UriEncoderInterface $encoder)
- {
- $this->encoder = $encoder;
- }
-
- /**
- * {@inheritdoc}
- */
public function encodeUri(string $data): string
{
- return $this->encoder->encodeUri(gzcompress($data));
+ return $this->encoder->encodeUri((string) gzcompress($data));
}
- /**
- * {@inheritdoc}
- */
public function decodeUri(string $data): ?string
{
- $data = $this->encoder->decodeUri($data);
+ $decoded = $this->encoder->decodeUri($data);
- if (null === $data) {
+ if ($decoded === null) {
return null;
}
try {
- return gzuncompress($data) ?? null;
- } catch (\Throwable $e) {
+ $compressed = gzuncompress($decoded);
+
+ return $compressed === false ? null : $compressed;
+ } catch (\Throwable) {
return null;
}
}
diff --git a/src/UriEncoderInterface.php b/src/UriEncoderInterface.php
index 60f954c..b28814e 100644
--- a/src/UriEncoderInterface.php
+++ b/src/UriEncoderInterface.php
@@ -15,26 +15,16 @@
/**
* UriEncoderInterface encodes a string for URL usage.
- *
- * @author Sebastiaan Stok
*/
interface UriEncoderInterface
{
/**
* Encodes the URI to a usable format.
- *
- * @param string $data
- *
- * @return string
*/
public function encodeUri(string $data): string;
/**
* Decodes the encoded URI back to the original format.
- *
- * @param string $data
- *
- * @return string|null
*/
public function decodeUri(string $data): ?string;
}
diff --git a/tests/Encoder/Base64UriEncoderTest.php b/tests/Encoder/Base64UriEncoderTest.php
new file mode 100644
index 0000000..cf6fae8
--- /dev/null
+++ b/tests/Encoder/Base64UriEncoderTest.php
@@ -0,0 +1,50 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Rollerworks\Component\UriEncoder\Tests\Encoder;
+
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Rollerworks\Component\UriEncoder\Encoder\Base64UriEncoder;
+
+final class Base64UriEncoderTest extends TestCase
+{
+ #[Test]
+ public function it_encodes_a_uri(): void
+ {
+ $encoder = new Base64UriEncoder();
+
+ self::assertSame('Zm9vLWJhci1jYXI', $encoder->encodeUri('foo-bar-car'));
+ self::assertSame('Zm9vL2Jhci9jYXI', $encoder->encodeUri('foo/bar/car'));
+ self::assertSame('Zm9vL2Jhcj9jYXI', $encoder->encodeUri('foo/bar?car'));
+ }
+
+ #[Test]
+ public function it_decodes_an_encoded_uri(): void
+ {
+ $encoder = new Base64UriEncoder();
+
+ self::assertSame('foo-bar-car', $encoder->decodeUri('Zm9vLWJhci1jYXI'));
+ self::assertSame('foo/bar/car', $encoder->decodeUri('Zm9vL2Jhci9jYXI'));
+ self::assertSame('foo/bar?car', $encoder->decodeUri('Zm9vL2Jhcj9jYXI'));
+ }
+
+ #[Test]
+ public function it_returns_null_on_invalid_encoded_data(): void
+ {
+ $encoder = new Base64UriEncoder();
+
+ self::assertNull($encoder->decodeUri('[whoops]'));
+ self::assertNull($encoder->decodeUri('AZZSDAFSF'));
+ }
+}
diff --git a/tests/Encoder/GZipCompressionDecoratorTest.php b/tests/Encoder/GZipCompressionDecoratorTest.php
new file mode 100644
index 0000000..c0e8b67
--- /dev/null
+++ b/tests/Encoder/GZipCompressionDecoratorTest.php
@@ -0,0 +1,64 @@
+
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Rollerworks\Component\UriEncoder\Tests\Encoder;
+
+use PHPUnit\Framework\Attributes\Test;
+use PHPUnit\Framework\TestCase;
+use Rollerworks\Component\UriEncoder\Encoder\GZipCompressionDecorator;
+use Rollerworks\Component\UriEncoder\UriEncoderInterface;
+
+final class GZipCompressionDecoratorTest extends TestCase
+{
+ #[Test]
+ public function it_encodes_a_uri(): void
+ {
+ $compression = new GZipCompressionDecorator($this->getEncoder());
+
+ self::assertSame('eJxLy8/XTUos0k1OLAIAGFEECg==', $compression->encodeUri('foo-bar-car'));
+ }
+
+ #[Test]
+ public function it_decodes_an_encoded_uri(): void
+ {
+ $compression = new GZipCompressionDecorator($this->getEncoder());
+
+ self::assertSame('foo-bar-car', $compression->decodeUri('eJxLy8/XTUos0k1OLAIAGFEECg=='));
+ }
+
+ #[Test]
+ public function it_returns_null_on_invalid_encoded_data(): void
+ {
+ $compression = new GZipCompressionDecorator($this->getEncoder());
+
+ self::assertNull($compression->decodeUri('[whoops]'));
+ self::assertNull($compression->decodeUri('xknvkjsa'));
+ }
+
+ private function getEncoder(): UriEncoderInterface
+ {
+ return new class() implements UriEncoderInterface {
+ public function encodeUri(string $data): string
+ {
+ return base64_encode($data);
+ }
+
+ public function decodeUri(string $data): ?string
+ {
+ $decode = base64_decode($data, true);
+
+ return $decode === false ? null : $decode;
+ }
+ };
+ }
+}