I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"
Related
So in PHP 5.4 and up you can call a method on instantiation like so.
$class = new Foo()->methodName();
I would like to check when the class is instantiated if it was done so with a method at the same time.
Is it possible to check if the class was instantiated without a method at the same time and default to a method if not?
you can use instanceOf on the object for which you wanted to check the class.
Constructor of the class cannot return a value in php, so you can't use singleton incapsulated in constructor and have it looks nice. Of cource, you can make something like this
// NOT FOR USE
class Foo {
private static $instance;
public function __construct($skipIncapsulate = false) {
if (!$skipIncapsulation && !self::$instance)
{
self::instance = new self(true);
}
}
public function bar() {
// Do what you want with self::$instance
}
}
N.B. Do not use the example above, it is ugly solution, which also incapsulates unnecessary logic in controller, and while working with the class
Better use a usual singleton like this
class Foo {
private static $instance;
private function __construct()
{}
public static getInstance()
{
return self::$instance ?? self::$instance = new self();
}
}
Nevertheless you can create a new class FooManager, which will delegate the if the Foo class is defined
class FooManager {
private static $foo;
public function __construct()
{
self::$foo = self::$foo ?? self::$foo = new Foo();
}
public function getFoo()
{
return self::$foo;
}
}
// usage in your code
(new FooManager)->getFoo()->bar();
// You can add __call method to use (new FooManager)->foo->bar()
This will simplify working with your code. Incapsulating static self-instance in constructor is not the good practice
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...
I want to add some functionality to a type provided by a framework, but this type is a singleton.
Thus, it's constructor is private. My class is supposed to be singleton too, but I have no idea how to get it initialized. I cannot alter code of original class.
Now I do not extend, just keep a private property that gets initialized in my getInstance, and use __call(), but it's insufficient and not intuitive to use. I can't pass my objects where originall class is expected.
You will need to keep the constructor in your extended class private. However, you could either do the following:
class originalClass
{
private function __construct()
{
/* Do something */
}
public static function init()
{
self::__construct();
}
}
class extendedClass extends originalClass
{
private function __construct()
{
parent::init();
}
public static function init()
{
self::__construct();
}
}
$var = extendedClass::init();
Alternatively, you could opt to have your class as a standalone, and use a magic method to access the 'parent' object:
class pdoExtender
{
private $someProperty;
public function __construct()
{
$this->someProperty = new PDO(...);
}
public function __call($name, $args)
{
if(method_exists(array($this, $someProperty), $name))
{
return call_user_func_array(array($this, $someProperty), $args);
}
}
}
// Example function calls:
$db = new pdoExtender;
// As (obj)pdoExtender doesn't have the method 'prepare()', the __call() method is invoked to check the 'parent' object, PDO
$db->prepare();
I am trying to make a script in which different classes (e.g. Database, Utilities, Config) are all used to form one central Main class. I have tried extending a chain of them:
Main -> Utilities -> Database -> Configuration
But how can I set the different parts so that they can be called like this:
<?php
$this->db->select("WAFFLES");
echo($this->config->app_path);
?>
You could create a global class that does you basic initializing
class Base {
$var1, var2;
public function __construct() {
$this->var1 = new DB();
$this->var2 = new Config();
....
}
}
Then your classes can extend the base class and have access to the data
class Foo extends Base {
public function bar() {
$this->var1->someOpertaion();
}
}
You need to declare each new object as variable in your Main Class like:
class Main{
private $db = NULL;
private $config = NULL;
$this->db = new Database;
$this->config = new Config;
}
etc.
While i'm not a professional coder i'll considering a better approach than this. This kind of object-handling can cause a bloated main class and in the worst case you may face some performance issues.
1) use __autoload or spl_autoload_register to load classes
2) use magic methods, to call function when getting unknown property. Following examples demonstrates how to use __get and dynamicaly initialize object only when you use them.
//use __autoload to load db and config class when they are called.
class db{
function lol(){
echo 'Hello from db->lol() <br />';
}
}
class config{
function lol(){
echo 'Hello from config->lol() <br />';
}
}
//Manager class to use with classes where you want to access other object trough $this
class Manager{
private $_instances=array();
function __get($name){
//if instance does not exists, create one
if (!isset($this->_instances[$name])){
$this->_instances[$name]=new $name();
}
//return instance
return $this->_instances[$name];
}
}
class Some extends Manager{
function f1(){
$this->db->lol();
$this->config->lol();
}
}
$some=new Some();
$some->f1(); //echoes 'Hello from db->lol()' and 'Hello from config->lol()'
But for accessing global class instances I prefer using following method:
Use singleton pattern to access global class trough GloballClass::i() and if global class is not defined use autoload to load that class.
class db extends mysqli{
private static $_i;
//Access to singleton instance
public static function i() {
return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
}
//class functions
function q($q){
echo 'Hello from db->q()';
}
}
class config{
private static $_i;
//Access to singleton instance
public static function i() {
return (self::$_i instanceof self)?self::$_i:self::$_i = new self();
}
//class functions
function somefunction(){
echo 'Hello from config->somefunction()';
}
}
db::i()->q('SELECT * FROM users');
config::i()->somefunction();
Following is solution inspired by Gordons comment:
It uses GlobalClassFactory class to define only one instance of global classes.
class db{
function lol(){
echo 'Hello from db->lol() <br />';
}
}
class config{
function lol(){
echo 'Hello from config->lol() <br />';
}
}
class GlobalClassFactory{
private static $_classes=array();
public static function getInstance($name){
if (!isset(self::$_classes[$name])){
self::$_classes[$name]=new $name();
}
return self::$_classes[$name];
}
}
class Base{
function __get($name){
return GlobalClassFactory::getInstance($name);
}
}
class Some extends Base{
function f1(){
$this->db->lol();
$this->config->lol();
}
}
$some=new Some();
$some->f1();
Here is the sample prototype:
include 'db.php'; // include db class
include 'config.php'; // include config class
class main{
public $db = NULL;
public $config = NULL;
function __construct() {
$this->db = new db;
$this->config = new config;
}
}
Creating a composite object with instances of everything that might be needed during code execution up front is a complete waste of resources. You want to create instances only when needed. One way to achieve this would be to add a magic __get method to the class:
public function __get($name) {
// if self::$instances (or main) contains instance of $name, return instance
// else if class_exists $name, create, store and return instance
// else throw exception
}
But even then, chances are you are creating a God Object and magic methods are somewhat slower than regular accessors. If you need to create instances this way, have a look at the Symfony Dependency Injection Container or implement a Registry.
When creating a Singleton in PHP, I ensure that it cannot be instantiated by doing the following:
class Singleton {
private function __construct() {}
private function __clone() {}
public static function getInstance() {}
}
However, I realised that defining a class as 'abstract' means that it cannot be instantiated. So is there anything wrong with doing the following instead:
abstract class Singleton {
public static function getInstance() {}
}
The second scenario allows me to write fewer lines of code which would be nice. (Not that it actually makes much of a difference.)
When creating a singleton in PHP, declaring the __construct and __clone as private ensures that the class cannot be instanciated from the outside : it can still be instanciated from inside its declaration.
When declaring a class as abstract, it can not be instanciated at all ; not even from inside its declaration.
This means your solution would not work : in the second case, your getInstance() method will not be able to instanciate the class -- while it can do so in the first case.
No because then then you can't instantiate the class at all (not even in the static getInstance method). The private constructor in the singleton example just assures, that only the static getInstance method from the same class can access the constructor.
No, you cannot use an abstract class instead of a private __construct() when creating a singleton. But if your intention is to create an Abstract Singleton from which to extend from, you can do so like this:
abstract class Singleton
{
private static $_instances;
public static function getInstance()
{
$className = get_called_class(); // As of PHP 5.3
if(! isset(self::$_instances[$className] )) {
self::$_instances[$className] = new $className();
}
return self::$_instances[$className];
}
protected function __construct( ) {}
final private function __clone( ) {}
final private function __wakeup( ) {}
}
You can then extend from Singleton like this:
class Foo extends Singleton {
protected $_foo = 1;
public function setFoo($i) { $this->_foo = $i; }
public function getFoo() { return $this->_foo; }
}
and
class Bar extends Singleton {
protected $_foo = 1;
public function setFoo($i) { $this->_foo = $i; }
public function getFoo() { return $this->_foo; }
}
and manipulating:
$foo1 = Foo::getInstance();
$foo1->setFoo(5);
$foo2 = Foo::getInstance();
var_dump($foo2);
$bar1 = Bar::getInstance();
var_dump($bar1);
echo new ReflectionObject($foo2);
echo new ReflectionObject($bar1);
However, keep in mind that Singletons are very hard to unit-test and should be avoided if possible. See my answer here for some background:
How to remove multiple instances and just have one instance while multiple function calls in php?
Is there a use-case for singletons with database access in PHP?
It could work if your Singleton::getInstance() is supposed to return an instance of a different class.
abstract class Singleton {
public static function getInstance() {
static $instance = null;
if ( is_null($instance) ) {
$instance = new StdClass; // a different class than 'abstract class Singleton'
$instance->x = time();
}
return $instance;
}
}
$obj = Singleton::getInstance();
But I'd find that confusing. A bit like misusing abstract to combine the complexity of an abstract factory with the restraints of a singleton.