Let's say we have an interface for logging:
interface Logger {
function log($message);
}
Now say we have two concrete implementations of the Logger
interface: the FileLogger
and the ConsoleLogger
.
class FileLogger implements Logger {
public function log($message) {
// Append log message to some file
}
}
class ConsoleLogger implements Logger {
public function log($message) {
// Log message to the console
}
}
Now if you define some other class Foo
which you also want to be able to perform logging tasks, you could do something like this:
class Foo implements Logger {
private $logger;
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function log($message) {
if ($this->logger) {
$this->logger->log($message);
}
}
}
Foo
is now also a Logger
, but its functionality depends on the Logger
implementation passed to it via setLogger()
. If we now want class Bar
to also have this logging mechanism, we would have to duplicate this piece of logic in the Bar
class.
Instead of duplicating the code, a trait can be defined:
trait LoggableTrait {
protected $logger;
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
public function log($message) {
if ($this->logger) {
$this->logger->log($message);
}
}
}
Now that we have defined the logic in a trait, we can use the trait to add the logic to the Foo
and Bar
classes:
class Foo {
use LoggableTrait;
}
class Bar {
use LoggableTrait;
}
And, for example, we can use the Foo
class like this:
$foo = new Foo();
$foo->setLogger( new FileLogger() );
//note how we use the trait as a 'proxy' to call the Logger's log method on the Foo instance
$foo->log('my beautiful message');