Hello, fellow developers!🧑🏼💻
I want to talk about design patterns in php in the few next articles. And I really like how the language is progressing that's why I'll make examples with the last innovations of php 8.
Singleton
Sometimes we need only one instance of some a class in different code places. For example, when we make a web application, usually it needs to connect to the database and it will be a really good idea to create just one copy of the connection and use it in every file, class, function. The pattern that will help us with this - Singleton, is one of the most popular and easiest design patterns.
Let's see how we can implement classes a database connection and logging with one instance in all objects:
class Singleton
{
protected static self|null $instance = null;
final private function __construct(){}
final protected function __clone(){}
final protected function __wakeup(){}
public static function getInstance(): static
{
if (static::$instance === null) {
static::$instance = new static;
}
return static::$instance;
}
}
class Database extends Singleton
{
public function connect()
{
// ...
}
}
class Logger extends Singleton
{
private $connection;
public function settings($connection = null)
{
$this->connection = $connection ?? '/var/logs/filename.log';
}
public function error(string $message)
{
// ...
}
public function warn(string $message)
{
// ...
}
}
Now we'll use a logger with writing logs in a database table. For that, we need a connection to the db and set it by the settings method:
$db = Database::getInstance();
$db->connect();
$logger = Logger::getInstance();
$logger->settings($db);
$logger->error('Something wrong');
Multiton
But what if we need more than just one instance of a logger because some messages must be written in a file, some need to be send by email? Or we can have other reasons. For that, we'll use the Multiton pattern. Multiton looks really resembling the previous pattern but with an array of instances of a class.
class Multiton
{
protected static array|null $instance = null;
final private function __construct(){}
final protected function __clone(){}
final protected function __wakeup(){}
public static function getInstance(int|string $key): self
{
if (!array_key_exists($key, self::$instance)) {
self::$instance[$key] = new self;
}
return self::$instance[$key];
}
}
class Logger extends Multiton
{
private array $settings;
public function setSettings(array $settings)
{
// ...
}
public function error(string $message)
{
// ...
}
public function warn(string $message)
{
// ...
}
}
Let's make two loggers with different settings for saving logs into files and database. I won't describe the setter of settings and writer to file/database in details cause it isn't important for the pattern.
$fileLogger = Logger::getInstance('file');
$fileLogger->setSettings([
//...
]);
$fileLogger->error('Error text');
$dbLogger = Logger::getInstance('database');
$dbLogger->setSettings([
//...
]);
$dbLogger->error('Error will write in Database');
Top comments (10)
I'm trying to avoid using singletons, because they tend to make the code mot tightly coupled. I'm using an alternative implementation of the singleton pattern in php using functions.
In the Singleton example you are getting an instance of Logger, but you´re calling a static method to log. In this case, singleton is not needed.
Thanks for your comment, you're right, I fixed this in article.
Good.
Thank you for the breakdown!
Thanks you very much
I found recommend not to use "extend" for such patterns.
PS: you maybe do not need this at all because you can move this decision into your container e.g.: container.thephpleague.com/4.x/def...
Thanx for detailed comment
You need to replace all those
selfs withstatic, or your other classes will only beSingleton, and notDatabaseorLogger.