laminas/laminas-hydrator

Hydrate/extract changes value when there are consecutive underscores

Opened this issue · 1 comments

Bug Report

Q A
Version(s) 4.15.0

Summary

Current behavior

Extract/hydrate should be symmetrical. Extracting, then hydrating any value should produce the original value. This fails when there are consecutive underscores.

How to reproduce

$namingStrategy = new UnderscoreNamingStrategy();
$value = 'usb_a__out_usb_a_out';
var_dump($namingStrategy->hydrate($value));
var_dump($namingStrategy->extract($namingStrategy->hydrate($value)));

string(15) "usbA_outUsbAOut"
string(19) "usb_a_out_usb_a_out"

Expected behavior

I would expect the second output to equal the original string.

Here is a simplified version of this class that does not have this issue.

$namingStrategy = new class implements NamingStrategyInterface
{
    /**
     * Converts camel case to snake case.
     *
     * @param string $name The name in camel case.
     * @param null|object $object The original object for context (unused).
     * @return string The name converted to snake case.
     */
    public function extract(string $name, ?object $object = null): string
    {
        return strtolower(preg_replace('/[A-Z]/', '_$0', lcfirst($name)));
    }

    /**
     * Converts snake case to camel case.
     *
     * @param string $name The name in snake case.
     * @param null|mixed[] $data The original data for context (unused).
     * @return string The name converted to camel case.
     */
    public function hydrate(string $name, ?array $data = null): string
    {
        return lcfirst(preg_replace_callback('/(_[a-z])/', static function ($match) {
            return strtoupper($match[1][1]);
        }, $name));
    }
};
$value = 'usb_a__out_usb_a_out';
var_dump($namingStrategy->hydrate($value));
var_dump($namingStrategy->extract($namingStrategy->hydrate($value)));
die(__METHOD__);

string(15) "usbA_OutUsbAOut"
string(19) "usb_a__out_usb_a_out"