Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

So, I'm playing around with PHP, trying to write a small ORM. Having worked with Magento quite a bit lately, I've fallen in love with the automagic getters/setters that Magento, I think, inherited from Zend.

For those, who've never seen it in action, this is what I'm talking about:

$my_object->setSomeProperty('foo');

just works, as does

$my_object->getSomeProperty();

which obviously yields "foo".

Since I'm trying to implement an ORM, my goal is to provide the user with the ability to use generic functions on one hand, where

$orm_object->setSomeProperty('foo')

would map to

$orm_object->set('some_property', 'foo') // set() would obviously be a generic part of the ORM

by convention. On the other hand, if the user wanted to handle some situations differently, he could do so by defining a more specifically named function.

class foo extends ORM {
    function setSome($arg_1, $arg_2)
    {
        …
    }
}

The above example call

$orm_object->setSomeProperty('foo')

would now be mapped to

$orm_object->setSome('property', 'foo')

instead of the default set().

Alright, let's get to the point: Do you have any feedback on my implementation?

function __call($name, $arguments) {
    if(preg_match('/([a-z]+)/', $name, $matches))
    {
        $action = $matches[0];
        if(preg_match_all('/([A-Z]+)([^A-Z]+)/', $name, $matches))
        {
            $matches = $matches[0];
            array_unshift(&$matches, $action);
            for ($i = count($matches); $i > 0; $i--)
            {
                $potential_func_name = implode('', array_slice($matches, 0, $i));
                if (method_exists($this, $potential_func_name))
                {
                    $arguments_rest = array_slice($matches, $i);
                    break; // found matching function! 
                }
            }

            // handle number of arguments now!
            $reflector = new ReflectionClass($this);
            $num_expected_params = count($reflector->getMethod($potential_func_name)->getParameters());

            $num_expected_params = $num_expected_params - count($arguments);

            $params = array();
            for ($i = $num_expected_params; $i > 1; $i--)
            {
                $params[] = strtolower(array_shift($arguments_rest));
            }

            $last_var = '';
            $i = 0;
            foreach($arguments_rest as $part)
            {
                $last_var .= strtolower($part);
                $i++;
                if ($i != count($arguments_rest)) $last_var .= '_';
            }

            $params[] = $last_var;

            if (count($arguments) == 1) {
                $arguments = array_shift(&$arguments);
            }

            $params[] = $arguments;

            return(call_user_func_array(array($this, $potential_func_name), $params));
        }
    }
}
share|improve this question
    
There are probably a lot of reasons I'm unaware of as a frontend guy to use this code, but I'm wondering why you would use this instead of existing PHP magic overloading? See php.net/language.oop5.overloading.php –  Rene Geuze Oct 24 '12 at 10:39

1 Answer 1

This is how I would probably implement task similar to yours:

class A {
    public function set($property, $value)
    {
        echo sprintf('setting %s = %s', 
            $property, $value)."\n";
    }

    public function setFirstName($value)
    {
        $this->set('first_name', $value);
    }

    public function __call($name, $arguments)
    {
        if (!preg_match('/^(get|set)(.+?)$/i', $name, $matches)) {
            throw new BadMethodCallException(sprintf('method `%s` doesn\'t exits', $name));
        }

        list(, $method, $property) = $matches;

        $property = preg_replace('/([a-z\d])([A-Z])/', '\\1_\\2', $property);
        $method   = strtolower($method);

        if ('set' == $method) {
            array_unshift($arguments, strtolower($property));
        }

        return call_user_func_array(array($this, $method), $arguments);
    }
}

$a = new A();
$a->setFirstName('John');
$a->setLastName('McLane');

Output:

setting first_name = John

setting last_name = McLane

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.