Skip to content

Commit

Permalink
Merge pull request #6 from veewee/normalize-object-names
Browse files Browse the repository at this point in the history
Normalize property names to supported php names in the object encoder
  • Loading branch information
veewee authored Jun 11, 2024
2 parents df563b0 + 925a842 commit ad67e98
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 3 deletions.
12 changes: 9 additions & 3 deletions src/Encoder/ObjectEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Soap\Encoding\Encoder;

use Closure;
use Soap\Encoding\Normalizer\PhpPropertyNameNormalizer;
use Soap\Encoding\TypeInference\ComplexTypeBuilder;
use Soap\Encoding\TypeInference\XsiTypeDetector;
use Soap\Encoding\Xml\Reader\DocumentToLookupArrayReader;
Expand All @@ -16,6 +17,7 @@
use VeeWee\Reflecta\Iso\Iso;
use VeeWee\Reflecta\Lens\Lens;
use function Psl\Dict\map;
use function Psl\Dict\pull;
use function Psl\Dict\reindex;
use function Psl\Iter\any;
use function Psl\Vec\sort_by;
Expand Down Expand Up @@ -97,7 +99,10 @@ private function to(Context $context, array $properties, object|array $data): st
$properties,
function (Property $property) use ($context, $data, $defaultAction) : Closure {
$type = $property->getType();
$lens = $this->decorateLensForType(property($property->getName()), $type);
$lens = $this->decorateLensForType(
property(PhpPropertyNameNormalizer::normalize($property->getName())),
$type
);
/**
* @psalm-var mixed $value
* @psalm-suppress PossiblyInvalidArgument - Psalm gets lost in the lens.
Expand Down Expand Up @@ -136,7 +141,7 @@ private function from(Context $context, array $properties, string $data): object
$nodes = (new DocumentToLookupArrayReader())($data);

return object_data($this->className)->from(
map(
pull(
$properties,
function (Property $property) use ($context, $nodes): mixed {
$type = $property->getType();
Expand All @@ -158,7 +163,8 @@ function (Property $property) use ($context, $nodes): mixed {
onValue: fn (): mixed => $value !== null ? $this->grabIsoForProperty($context, $property)->from($value) : $defaultValue,
onElements: fn (): mixed => $value !== null ? $this->grabIsoForProperty($context, $property)->from($value) : $defaultValue,
);
}
},
static fn (Property $property) => PhpPropertyNameNormalizer::normalize($property->getName()),
)
);
}
Expand Down
29 changes: 29 additions & 0 deletions src/Normalizer/PhpPropertyNameNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Normalizer;

use function Psl\Type\non_empty_string;

final class PhpPropertyNameNormalizer
{
public static function normalize(string $name): string
{
return self::camelCase($name, '{[^a-z0-9_]+}i');
}

/**
* @param literal-string $regexp
*/
private static function camelCase(string $word, string $regexp):string
{
$parts = array_filter(preg_split($regexp, $word));
$keepUnchanged = array_shift($parts);
$parts = array_map('ucfirst', $parts);
array_unshift($parts, $keepUnchanged);

return non_empty_string()->assert(
implode('', $parts)
);
}
}
41 changes: 41 additions & 0 deletions tests/Unit/Encoder/ObjectEncoderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,47 @@ public static function provideIsomorphicCases(): iterable
'xml' => '<tns:user xmlns:tns="https://test"><tns:active xmlns:tns="https://test">true</tns:active><tns:hat xmlns:tns="https://test"><tns:color xmlns:tns="https://test">green</tns:color></tns:hat></tns:user>',
'data' => new User(active: true, hat: new Hat('green')),
];

yield 'unsupported-property-chars' => [
...$baseConfig,
'context' => self::createContext(
$xsdType,
new TypeCollection(
new Type(
XsdType::create('user')
->withXmlTypeName('user')
->withXmlNamespace("https://test")
->withXmlNamespaceName('test')
->withXmlTargetNodeName('user')
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsQualified(true)
->withIsElement(true)
),
new PropertyCollection(
new Property(
'bon-jour',
XsdType::create('bon-jour')
->withXmlTypeName('boolean')
->withXmlTargetNodeName('bon-jour')
->withXmlNamespace(Xmlns::xsd()->value())
->withXmlNamespaceName('xsd')
->withMeta(
static fn (TypeMeta $meta): TypeMeta => $meta
->withIsSimple(true)
->withIsElement(true)
->withIsQualified(true)
)
),
)
)
)
),
'xml' => '<user><bon-jour>true</bon-jour></user>',
'data' => (object)[
'bonJour' => true,
],
];
}


Expand Down
33 changes: 33 additions & 0 deletions tests/Unit/Normalizer/PhpPropertyNameNormalizerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);

namespace Soap\Encoding\Test\Unit\Normalizer;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Soap\Encoding\Normalizer\PhpPropertyNameNormalizer;

#[CoversClass(PhpPropertyNameNormalizer::class)]
final class PhpPropertyNameNormalizerTest extends TestCase
{

/**
*
* @dataProvider provideCases
*/
public function test_it_can_normalize(string $in, string $expected): void
{
static::assertEquals($expected, PhpPropertyNameNormalizer::normalize($in));
}

public static function provideCases()
{
yield ['prop1', 'prop1'];
yield ['final', 'final'];
yield ['Final', 'Final'];
yield ['UpperCased', 'UpperCased'];
yield ['my-./*prop_123', 'myProp_123'];
yield ['My-./*prop_123', 'MyProp_123'];
yield ['My-./final*prop_123', 'MyFinalProp_123'];
}
}

0 comments on commit ad67e98

Please sign in to comment.