0

I need a function that can set its deepest value on an instance, whether it is an object or an array.

For example, I have a function like this that I wrote and played with, but the setValue function does not work, my silly placing of links here and there did not help me((

functions:


function setValue($name, &$initial, $value) {
   // Set function not work
    $names = explode('.', $name);
    $lastIndex = (count($names) - 1);

    $getter = (function ($name) {
        if (property_exists($this, $name) === false) {
            return null;
        }

        $value = &$this->{$name};

        return $value;
    })(...);

    $setter = (function ($name, $value) {
        if (property_exists($this, $name) === true) {
            $this->{$name} = $value;
        }
    })(...);

    $reference = &$initial;
    foreach ($names as $index => $keyOrProperty) {
        if (is_array($reference) === true) {
            if ($lastIndex === $index) {
                $reference[$keyOrProperty] = $value;

                break;
            }

            $nextReference = &$reference[$keyOrProperty];
            if ($nextReference === null) {
                break;
            }

            $reference = &$nextReference;

            continue;
        }

        if (is_object($reference) === true) {
            if ($lastIndex === $index) {
                $setter->call($reference, $keyOrProperty, $value);

                break;
            }

            $nextReference = $getter->call($reference, $keyOrProperty);
            if ($nextReference === null) {
                break;
            }

            $reference = &$nextReference;

            continue;
        }

        break;
    }
}

function getValue($name, $initial) {
    $names = explode('.', $name);

    $getter = (function ($name) {
        if (property_exists($this, $name) === false) {
            return $this;
        }

        return $this->{$name};
    })(...);

    $accumulator = static function (mixed $curr, string $name) use ($getter): mixed {
        if ($curr === null) {
            return null;
        }

        if (is_array($curr) === true) {
            return (array_key_exists($name, $curr) === true) ? $curr[$name] : null;
        }

        if (is_object($curr) === true) {
            return $getter->call($curr, $name);
        }

        return $curr;
    };

    return array_reduce($names, $accumulator, $initial);
}

test:

class MyFoo
{
    private MyBar $bar;

    private array $array;

    public function setBar(MyBar $bar): static
    {
        $this->bar = $bar;

        return $this;
    }

    public function setArray(array $array): static
    {
        $this->array = $array;

        return $this;
    }
}

class MyBar
{
    private MyFoo $foo;

    private array $array;

    public function setFoo(MyFoo $foo): static
    {
        $this->foo = $foo;

        return $this;
    }

    public function setArray(array $array): static
    {
        $this->array = $array;

        return $this;
    }
}


$instance = (new MyFoo())
    ->setBar(
        (new MyBar())
            ->setFoo(
                (new MyFoo())
                    ->setArray(
                        [
                            'myBar' => (new MyBar())
                                ->setArray(
                                    [
                                        'deep' => [
                                            'myFoo' => (new MyFoo())
                                                ->setBar(
                                                    (new MyBar())
                                                        ->setArray(['test' => 'is Test'])
                                                )
                                        ]
                                    ]
                                )
                        ]
                    )
            )
    );

echo getValue('bar.foo.array.myBar.array.deep.myFoo.bar.array.test', $instance);
setValue('bar.foo.array.myBar.array.deep.myFoo.bar.array.test', $instance, 'Set Test');
echo getValue('bar.foo.array.myBar.array.deep.myFoo.bar.array.test', $instance);

Is it even possible to write such a function, or does anyone have any ideas?

UKRman
  • 404
  • 3
  • 16
  • What's its deepest value on an instance? – shingo Aug 20 '23 at 14:06
  • @shingo I didn't quite understand you, but the function itself is just an example, a piece of logic that will be a mapper of complex JSON from databases to objects. The function itself looks exactly so that I can understand how it can be implemented, so that it is possible to map the key from Jason to the object and conditionally save it back to the database. – UKRman Aug 20 '23 at 14:27
  • This is what you said at the beginning: _"I need a function that can set **its deepest value on an instance**"_, I didn't know what this means, so I asked. – shingo Aug 20 '23 at 15:07
  • @shingo Oh, sorry, my English is not good, I meant that the chain of properties or keys of the array is not limited, in the code demo I created an object that demonstrates this, from which I can get the value, but not write it, that is, not necessarily what it will be perm the final value in the property. Something like that `setValue('bar.foo', new Foo())` – UKRman Aug 20 '23 at 15:22

1 Answers1

1

The main problem is that when you want to get an array from a property, the return must also be a reference. But you cannot use Closure::call, because its declaration return type is not a reference. You need to bind the closure first, then directly call the closure.

# $nextReference = $getter->call($reference, $keyOrProperty);

$cl = $getter->bindTo($reference, $reference);
$nextReference =& $cl($keyOrProperty);

Besides, anonymous functions are already closures, the first class callable syntax is not necessary here. Note the anonymous function has an ampersand to return a reference instead of a value.

# $getter = (function ($name) {
#     blabla
# })(...);

$getter = function &($name) {
    if (property_exists($this, $name) === false) {
        return null;
    }
    return $this->{$name};
};
shingo
  • 18,436
  • 5
  • 23
  • 42