John Smith
John Smith

Reputation: 6207

Logging all operations: the good design pattern?

I have a class:

class Phone
{
    public function addnewnumber ($name, $number)
    {
    }
}

now let's imagine that I want to log all operations. I dont want to put something like Logger::add(); inside addnewnumber() - maybe its not possible to modify that class. Then how to solve it?

Upvotes: 1

Views: 266

Answers (2)

Jon
Jon

Reputation: 437794

In some cases you can use a proxy class that wraps your objects and allows you to forward function calls using __call. It would look something like this:

class Proxy
{
    private $_target;
    public function __construct($target)
    {
        $this->_target = $target;
    }

    public function __call($name, $params)
    {
        $callable = array($this->_target, $name);
        if (!is_callable($callable)) {
            trigger_error('Call to undefined method '.
                           get_class($this->_target).'::'.$name, E_USER_ERROR);
        }

        return $this->dispatch($callable, $params);
    }

    protected function dispatch($callable, $params)
    {
        return call_user_func_array($callable, $params);
    }
}

You can then derive from this class and override dispatch to perform custom processing:

class LoggingProxy extends Proxy
{
    protected function dispatch($callable, $params)
    {
        echo "Before calling ".get_class($callable[0]).'::'.$callable[1]."\n";
        $return = parent::dispatch($callable, $params);
        echo "After calling ".get_class($callable[0]).'::'.$callable[1]."\n";
        return $return;
    }
}

And use it like:

$proxy = new LoggingProxy(new Phone);
$proxy->addnewnumber(1, 2);

See it in action.

However, this approach does have some drawbacks:

  • It doesn't work with code that expects a certain type of object (you are passing around a Proxy instead of a Phone)
  • It doesn't allow you to access non-public members of the wrapped class

Upvotes: 1

Hugo Delsing
Hugo Delsing

Reputation: 14173

You cannot call some logging function automatically on every method of every class. If you want to add logging to the phones addnewnumber method, without changing the class itself, you can extend it in your own class.

class Phone
{
    public function addnewnumber($name, $number)
    {
    }
    public function somethingelse()
    {
    }
}

class MyPhone extends Phone
{
    public function addnewnumber($name, $number)
    {
        Logger::Add();
        parent::addnewnumber($name, $number);
    }
}

now your MyPhone class has all the phone methods, but when calling the addnewnumber method it logs the number and then calls the actual method.

Upvotes: 1

Related Questions