Skip to content

Commit a2a1064

Browse files
committed
Add ToKebabCase Filter
1 parent 1d4be71 commit a2a1064

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

docs/filters.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,32 @@ The above example will output the following
443443
Result was valid
444444
```
445445

446+
### ToKebabCase
447+
448+
```php
449+
new Membrane\Filter\String\ToKebabCase();
450+
```
451+
452+
Converts string into KebabCase (i.e. no whitespaces, uppercase for first letter of each word).
453+
454+
```php
455+
$string = "helloThere have_you heard-of OpenAPI?";
456+
$pascalCase = new Membrane\Filter\String\ToKebabCase();
457+
458+
$result = $pascalCase->filter($string);
459+
460+
echo $result->value;
461+
echo $result->isValid() ? 'is valid' : 'is invalid';
462+
```
463+
464+
The above example will output the following
465+
466+
```text
467+
hellothere-have-you-heard-of-openapi?
468+
```
469+
470+
Note that _helloThere_ became _hellothere_. `ToKebabCase` does not split on capitals, otherwise _OpenAPI_ would become _open-a-p-i_ which is a less desirable result.
471+
446472
### ToPascalCase
447473

448474
```php

src/Filter/String/ToKebabCase.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Membrane\Filter\String;
6+
7+
use Membrane\Filter;
8+
use Membrane\Result\Message;
9+
use Membrane\Result\MessageSet;
10+
use Membrane\Result\Result;
11+
12+
class ToKebabCase implements Filter
13+
{
14+
public function filter(mixed $value): Result
15+
{
16+
if (!is_string($value)) {
17+
return Result::invalid($value, new MessageSet(null, new Message(
18+
'Expected string value, received %s',
19+
[gettype($value)],
20+
)));
21+
}
22+
23+
$result = preg_replace(['#[\s\-.,_/]+#', '#[^[:alnum:]-]#'], ['-'], $value);
24+
assert(is_string($result));
25+
26+
$result = mb_strtolower($result);
27+
28+
return Result::noResult($result);
29+
}
30+
31+
public function __toString(): string
32+
{
33+
return 'Convert text to kebab-case';
34+
}
35+
36+
public function __toPHP(): string
37+
{
38+
return sprintf('new %s()', self::class);
39+
}
40+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Filter\String;
6+
7+
use Membrane\Filter\String\ToKebabCase;
8+
use Membrane\Result\{Message, MessageSet, Result};
9+
use PHPUnit\Framework\Attributes\{CoversClass, DataProvider, Test, TestDox, UsesClass};
10+
use PHPUnit\Framework\TestCase;
11+
12+
#[CoversClass(ToKebabCase::class)]
13+
#[UsesClass(Message::class)]
14+
#[UsesClass(MessageSet::class)]
15+
#[UsesClass(Result::class)]
16+
class ToKebabCaseTest extends TestCase
17+
{
18+
#[Test]
19+
#[TestDox('toString() returns a description of its behaviour')]
20+
public function itCaststoString(): void
21+
{
22+
self::assertSame(
23+
'Convert text to kebab-case',
24+
(new ToKebabCase())->__toString()
25+
);
26+
}
27+
28+
#[Test]
29+
#[TestDox('__toPHP() returns a string of evaluable PHP')]
30+
public function itCaststoPHP(): void
31+
{
32+
$sut = new ToKebabCase();
33+
34+
self::assertEquals($sut, eval('return ' . $sut->__toPHP() . ';'));
35+
}
36+
37+
#[Test]
38+
#[TestDox('It will return an invalid Result if it filters values that are not strings')]
39+
public function returnsInvalidResultForNonStringValues(): void
40+
{
41+
$value = 5;
42+
$expected = Result::invalid($value, new MessageSet(null, new Message(
43+
'Expected string value, received %s',
44+
[gettype($value)],
45+
)));
46+
47+
self::assertEquals($expected, (new ToKebabCase())->filter($value));
48+
}
49+
50+
#[Test]
51+
#[TestDox('It filters strings to PascalCase')]
52+
#[DataProvider('provideStringsToFilter')]
53+
public function filtersStringsToKebabCase(string $value, Result $expected): void
54+
{
55+
self::assertEquals($expected, (new ToKebabCase())->filter($value));
56+
}
57+
58+
public static function provideStringsToFilter(): array
59+
{
60+
return [
61+
'"Hello, World!"' => [
62+
'"Hello, World!"',
63+
Result::noResult('hello-world'),
64+
],
65+
'camelCase' => [
66+
'camelCase',
67+
Result::noResult('camelcase'),
68+
],
69+
'kebab-case' => [
70+
'kebab-case',
71+
Result::noResult('kebab-case'),
72+
],
73+
'kebabber------case' => [
74+
'kebabber------case',
75+
Result::noResult('kebabber-case'),
76+
],
77+
'snake_case' => [
78+
'snake_case',
79+
Result::noResult('snake-case'),
80+
],
81+
'snakiest_____case' => [
82+
'snakiest_____case',
83+
Result::noResult('snakiest-case'),
84+
],
85+
'pets/{id}' => [
86+
'pets/{id}',
87+
Result::noResult('pets-id'),
88+
],
89+
'http://petstore.swagger.io/{version}/pets/{id}' => [
90+
'http://petstore.swagger.io/{version}/pets/{id}',
91+
Result::noResult('http-petstore-swagger-io-version-pets-id'),
92+
],
93+
'plain text' => [
94+
'plain text',
95+
Result::noResult('plain-text'),
96+
],
97+
'plain 1text' => [
98+
'plain 1text',
99+
Result::noResult('plain-1text'),
100+
],
101+
'plain 1 text' => [
102+
'plain 1 text',
103+
Result::noResult('plain-1-text'),
104+
],
105+
'sTuPiD-_-cAsE' => [
106+
'sTuPiD-_-cAsE',
107+
Result::noResult('stupid-case'),
108+
],
109+
];
110+
}
111+
112+
113+
}

0 commit comments

Comments
 (0)