So if I have an interface iConnection
interface iConnection
{
}
Which is implemented by:
class OutlookConnection implements iConnection
{
}
And
class GoogleConnection implements iConnection
{
}
And another interface iComparison
interface iComparison
{
}
Which is implemented by:
class OutlookComparison implements iComparison
{
private $connection;
public function __construct($user, iConnection $iConnection) {
$this->connection = $iConnection;
}
}
And
class GoogleComparison implements iComparison
{
private $connection;
public function __construct($user, iConnection $iConnection) {
$this->connection = $iConnection;
}
}
In the main program I want to be able to switch between GoogleComparison and OutlookComparison based on the iConnection type without using an if or switch statement:
public function __construct(iConnection $connection)
{
$this->connect = $connection;
if($this->connection instanceof GoogleConnection){
$this->comparison = new GoogleComparison();
}
elseif($this->connection instanceof OutlookConnection){
$this->comparison = new OutlookComparison();
}
}
Is this possible to achieve within this constructor without switch or if statements?
This is an architectural issue. I suggest passing responsibility for providing comparison to the connection. One of the possible solutions could look like this below.
First - interface gets getter stub:
interface iConnection
{
public function getComparison();
}
Then - all the different connections implement it as you want, for example:
class OutlookConnection implements iConnection
{
public function getComparison()
{
return new OutlookComparison();
}
}
And finally, your construction similar to:
public function __construct(iConnection $connection)
{
$this->connect = $connection;
$this->comparison = $connection->getComparison();
}
From now on, you can either operate on comparison property or simply $this->connect->getComparison()->fooBarBaz(). All depends on your needs.
Related
Let's say I have a class that takes in an array:
class Database {
function __construct($options = ['pdo' => '' ]) {
$this->pdo = $options['pdo'];
}
}
Is there a way to specify class for the array key? Maybe something like this?
class Database {
function __construct($options = [\PDO 'pdo']) {
$this->pdo = $options['pdo'];
}
}
Actually it is not possible to type hint array keys with PHP. There are some possible solutions to this, but none of them will work with type hints on arrays.
Validating the options in the constructor
Since it is not possible to check the array keys for a specific type hint you can check with an if condition, if a specific option isset and if it is an instance of PDO.
class Database
{
protected PDO $pdo;
function __construct(array $options)
{
if (!isset($options['pdo']) || !$options['pdo'] instanceof PDO) {
// error handling here
}
$this->pdo = $options['pdo'];
}
}
Using an interface
The use of an interface is obvious here. Others, like e.g. Doctrine ORM or Laminas, use an aware interface to point out, that there is an instance of a specific class available.
interface PdoAwareInterface
{
public function getPdo(): PDO;
public function setPdo(PDO $pdo): void;
}
class Database implements PdoAwareInterface
{
protected PDO $pdo;
public function __construct(array $options = [])
{
...
}
public function getPdo(): PDO
{
return $this->pdo;
}
public function setPdo(PDO $pdo): void
{
$this->pdo = $pdo;
}
}
class DatabaseFactory
{
public function __invoke()
{
$pdo = new PDO(...);
$database = new Database([]);
$database->setPdo($pdo);
return $database;
}
}
As you can see the PDOAwareInterface is implemented. It points out, that a PDO instance is aware. You can use it e.g. in a factory, to set the pdo instance via the interface implemented methods.
Options Interface
Using in interface for the options itself is a bit messy, because here you cannot be sure whether the PDO instance was actually set before intializing the Database class. Anyway ... it could be a possible way.
interface DatabaseOptionsInterface
{
public function getPdo(): PDO;
}
class DatabaseOptions implements DatabaseOptionsInterface
{
protected PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function getPdo(): PDO
{
return $this->pdo;
}
}
class Database
{
protected PDO $pdo;
public function __construct(DatabaseOptionsInterface $options)
{
$this->pdo = $options->getPdo();
}
}
class DatabaseFactory
{
public function __invoke()
{
$pdo = new PDO(...);
$options = new DatabaseOptions($pdo);
$database = new Database($options);
return $database;
}
}
Basically like initialisation with an AwareInterface, but via an additional DatabaseOptions instance.
Conclusion
Personally, I tend towards awareness, because here I can always check whether the object holds a certain instance ready. This would only be possible in a roundabout way via an options class, for example.
PHP does not have strongly typed arrays.
What you can do instead is use a class or interface to represent the $options parameter
interface Options {
public function pdo(): \PDO;
}
class Database {
private \PDO $pdo;
function __construct(Options $options) {
$this->pdo = $options->pdo();
}
}
Unfortunately I'm stuck here.
Consider the following rudimentary examples:
interface ChargeInterface
{
public function charge($amount);
}
class BraintreeCharge implements ChargeInterface
{
public function charge($amount)
{
// braintree logic here
}
}
class StripeCharge implements ChargeInterface
{
public function charge($amount)
{
// stripe logic here
}
}
So there's an interface for charging a payment method, and there are, in this example, two concrete classes which implement the interface.
I'd like to be able to decide on runtime which implementation should be used. So I thought I'd achieve this with custom factory classes:
class PaymentFactory
{
public static $implementation;
public static function charge()
{
return $implementation::charge();
}
}
class StripeFactory
{
public static function charge()
{
return new StripeCharge();
}
}
class BraintreeFactory
{
public static function charge()
{
return new BraintreeCharge();
}
}
Than I could just use the factories:
PaymentFactory::$implemention = StripeFactory::class;
$payments = PaymentFactory::charge();
$payments->charge(100);
Another idea was to use a singleton based logic:
class PaymentFactory extends Singleton
{
protected $implementation;
// Singleton logic missing in this example
public function useImplementation($class)
{
$this->implementation = $class;
}
public function getImplementation()
{
return $this->implementation;
}
public static function charge()
{
$instance = self::getInstance();
return new $instance->getImplementation();
}
}
Later ...
PaymentFactory::getInstance()->useImplementation(StripeCharge::class);
$payments = PaymentFactory::charge();
$payments->charge(100);
Do you've any suggestions regarding best practices here?
I think I'd favour the first one, since the real implementation consists of more than just one class per package, as outlined in the example.
Also it seems to me, this would be the more cleaner way.
Here is sample code:
namespace myApp
class app
{
private $pdo = null;
public function __construct() {
$this->user=new User();
$this->user->getPDO();
}
}
class User
{
public function getPDO() {
//get PDO from app class
}
}
How from User class get $pdo from app class??
Can't use extends because of construct in app class.
I don't want to declare independent classes (app and User) and than use global to communicate with each other.
EDIT:
is this solution ok?
namespace myApp
class app
{
public $pdo = null;
public function __construct() {
$this->user=new User($this);
$this->user->getPDO();
}
}
class User
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getPDO() {
return $this->app->pdo;
}
}
This sounds like a good candidate for dependency injection.
You can declare the constructor for your Userclass for example like
__constuct($pdo) {
$this->pdo = $pdo;
}
And then create User like
$this->user = new User($this->pdo)
An added advantage of this is that when you want to test your code you can easily mock pdo and test user without doing actual database actions.
You have to declare 2 functions in your app class like this:
Then you have acces through this functions to the pdo variable
public function setPDO($pdo) {
$this->pdo = $pdo;
}
public function getPDO() {
return $this->pdo;
}
Can I reuse decorators?
I have a ClientDecorator to decorate an entity that has a reference of a client, this decorator gets the client on database on call getClient (before it gets decorated, this method returns the clientId, after being decorated, it returns an instance of Client).
Okay, but, I've some other entities that can be decorated with the same decorator, for example, I have another table named questions, this table has a reference pointing to a client that has asked a question, and I have another table named schedules, that has a reference of a client.
By the way, I can decorate question and schedule with ClientDecorator.
But, I have an QuestionDecorator too; this guy decorates an Answer, etc.
How I can do this abstraction, so I can reuse decorators whenever I want?
I've tried to create ClientDecorable, QuestionDecorable interfaces, but have made no progress.
You can always instance the decorator class passing parameters to the constructor that will tell it how it should behave or what class it should impersonate. You don't really have to declare your decorator as an extension of another class.
PHP classes support magic methods that make it possible to forward calls to the class your object is impersonating, just as if it was extending it with extends.
For instance:
class Client
{
public function getId() { return 123; }
}
class Decorator
{
private $instance = null;
public function __construct($class)
{
$this->instance = new $class();
}
public function __call($method, $params) // magic method
{
return call_user_func_array(array($this->instance, $method), $params);
}
}
$object = Decorator('Client');
echo $object->getId(); // 123
The magic method __call() will be invoked when you try to access a method that doesn't belong to the class Decorator. The same can be done with properties by using the magic methods __get() and __set().
That's a really tricky problem. I could find a solution, but it is kind of McGiver style... Works for PHP 5.4+ (yes, traits).
<?php
interface Decorable
{
public function getTarget();
}
interface ClientDecorable extends Decorable
{
public function getClient();
}
interface LogDecorable extends Decorable
{
public function getLog();
}
abstract class AbstractDecorator implements Decorable
{
private $target;
public function __construct(ClientDecorable $target)
{
$this->target = $target;
}
public function getTarget()
{
// I'll be able to access the leaf node of my decorator single way 'tree'
return $this->target->getTarget();
}
public function __call($method, $args) {
$reflected = new ReflectionClass($this->target);
if ($reflected->hasMethod($method)) {
return call_user_func_array([$this->target, $method], $args);
}
}
}
class ClientDecorator extends AbstractDecorator implements ClientDecorable
{
public function __construct(Decorable $target) {
if (! $target->getTarget() instanceof ClientDecorable) {
throw new Exception('Must be an instance de ClientDecorable');
}
parent::__construct($target);
}
public function getClient()
{
return new Client($this->getTarget()->getClient());
}
}
class LogDecorator extends AbstractDecorator implements LogDecorable
{
public function __construct(Decorable $target) {
if (! $target->getTarget() instanceof LogDecorable) {
throw new Exception('Must be an instance de LogDecorable');
}
parent::__construct($target);
}
public function getLog()
{
return new Log($this->getTarget()->getLog());
}
}
abstract class AbstractTarget implements Decorable
{
// this does the trick
public function getTarget() { return $this; }
}
trait ClientDecorableTrait {
public function getClient()
{
return $this->client;
}
}
trait LogDecorableTrait {
public function getLog()
{
return $this->log;
}
}
class Payment extends AbstractTarget implements ClientDecorable, LogDecorable
{
use ClientDecorableTrait;
use LogDecorableTrait;
private $client = 1;
private $log = 101;
}
class Sale extends AbstractTarget implements ClientDecorable
{
use ClientDecorableTrait;
private $client = 2;
}
class Client
{
// ...
}
class Log
{
// ...
}
$sale = new Sale();
var_dump($sale->getClient());
$saleDec = new ClientDecorator($sale);
var_dump($saleDec->getClient());
$payment = new Payment();
var_dump($payment->getClient());
$paymentDec = new ClientDecorator($payment);
var_dump($paymentDec->getClient());
var_dump($paymentDec->getLog());
$paymentDecTwice = new LogDecorator($paymentDec);
var_dump($paymentDecTwice->getLog());
$saleDecTwice = new LogDecorator($saleDec); // will throw an exception
This is just a skeleton, a real world implementation must be tricky. I think you'd better keep your decorators separated...
Is there any way in php to make sure that a class can be extended by one and only one class?
I have some code that illustrates what I'm trying to do, basically I have a DB manager class and a DB query class that is extended by the manager class. What I'd like to do is make sure that the DB query class can only be used by the DB manager class.
The code below works, but it seems very rough. In the code I delcare the query class abstract with a single abstract function that checks the classname, or I could simply declare all of the Manager functions as abstract in the query class (which seems hacky). If there is a simpler way to do this than my code below that would be very useful...
abstract class DB_Query {
private static $HOST = 'localhost';
private static $USERNAME = 'guest';
private static $PASSWORD = 'password';
private static $DATABASE = 'APP';
//////////
/* USING ABSTRACT FUNCTION HERE TO ENFORCE CHILD TYPE */
abstract function isDB();
/* OR USING ALTERNATE ABSTRACT TO ENFORE CHILD TYPE */
abstract function connect();
abstract function findConnection();
abstract function getParamArray();
//////////
private function __construct() { return $this->Connect(); }
public function Read($sql) { //implementation here }
public function Query($sql) { //implementation here }
public function Fetch($res, $type='row', $single='true') { //implementation here }
}
class DB extends DB_Query {
public $connections = array();
public static $instance;
public function isDB() {
if (get_parent_class() === 'Database' && get_class($this)!=='DB') {
throw new \Exception('This class can\'t extend the Database class');
}
}
public function connect($host=null,$user=null,$pass=null,$db=null) { //implementation here }
function findConnection($user, $password=null) { //implementation here }
public function getParamArray($param) {}
public function threadList() {}
public function getThread($threadId=null) {}
public static function Singleton() { //implementation here }
private function __construct() { //implementation here }
}
I would go after marking the constructor of DB_Query as final and implementing it the way that it checks the instance and fires some exception. Something like this
class Base {
final function __construct() {
if (!$this instanceof Base && !$this instanceof TheChosenOne) {
throw new RuntimeException("Only TheChosenOne can inherit Base");
}
/**
* use this function as constructor
*/
$this->__internal_base_construct();
}
protected function __internal_base_construct() {
// constructor code
}
}
But your problem is rather strange and kind of breaking the idea of OOP in several ways. Just combine it into a single class and use final class directive.
http://php.net/manual/en/language.oop5.final.php
class Database_Query extends Database {
public static $instance;
public function Query($sql) {}
public function Fetch($res, $type='row', $single='true') {}
public static function Singleton() {}
private function __construct() {
$this->link = $this->connect()->find('guest')->getLink();
}
}