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
Related
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"
could someone explain how to use not static property in static method in php, this is wrong code, but i want to know how to fix this, thank you
<?php
class SomeClass
{
public $_someMember;
public function __construct()
{
$this->_someMember = 1;
}
public static function getSomethingStatic()
{
return $this->_someMember * 5; // here's the catch
}
}
echo SomeClass::getSomethingStatic();
?>
You can't directly. You need to create an object instance. You can make one and pass it to a static method, or make one in static method's body.
Regular (non-static) properties require object instance of given class (type).
Static methods are called by referring to the class itself, not an object.
You can however use static properties or constants for static methods needs without creating object instance at all.
You have to instantiate object
<?php
class SomeClass
{
public $_someMember;
public function __construct()
{
$this->_someMember = 1;
}
public static function getSomethingStatic()
{
$object = new self();
return $object->_someMember * 5; // here's the catch
}
}
echo SomeClass::getSomethingStatic();
You can statically create an instance of the class that the method is being called on via:
$instance = new static();
You can also statically create instances of the class that actually defines the method via:
$instance = new self();
As an example, take these classes First and Second.
class First
{
public static function getStatic()
{
return new static();
}
public static function getSelf()
{
return new self();
}
}
class Second extends First{ }
When we use Second::getStatic(), we will get an instance of Second.
When we use Second::getSelf(), we will get an instance of First.
When we call either method via First, we will get an instance of First.
This means you can change your method to:
public static function getSomethingStatic()
{
$instance = new static(); // or new self() if you always want to use 'SomeClass'
// and never an extending class.
return $instance->_someMember;
}
I have a class that acts as a wrapper to Smarty but want to use it statically across my application.
My setup looks something like this:
class Template extends Smarty {
public function __constructor() {
parent::__constructor();
}
public function setSettings() {
$this-> some smarty settings here
}
public static function loadTpl($tpl) {
self::$tplFile = $tpl;
// other logic
self::setSettings(); // this won't get executed because it uses non static method calls.
}
}
How can I get around this?
Rather than attempt to wrap it to be called all statically, create a singleton instance and call Template::getInstance() to retrieve it rather than new Smarty():
class Template extends Smarty {
public static $instance = NULL;
// Private constructor can't be called
private function __construct() {
parent::__construct();
}
// Instead instantiate or return the existing instance
public static function getInstance () {
return self::$instance ? self::$instance : new self();
}
}
// Instantiate as:
$smarty = Template::getInstance();
I'm using PDT and Aptana on Eclipse Indigo with PHP 5.3 and I want to create a singleton in a class.
By singleton, I mean I want to just have one instance of that object, and for other objects or classes to get that single instance via a function that returns that object (so this would mean I'm trying to create an object within the class that defines that object, ie: creating objA within the class objA)
I understand you can't just go a head and do this:
public $object = new Object();
with in a class definition, you have to define it in the constructor.
How can I go ahead and do this? I'm coming from Java, so it could be I'm confusing some basic stuff. Any help is greatly appreciated. Here's the code:
<?php
class Fetcher{
private static $fetcher = new Fetcher(); //this is where I get the unexpected "new" error
static function getFetcherInstance(){
return $this->$fetcher;
}
}
?>
Solved! Thanks for all the help guys!
try this:
<?php
class myclass{
private static $_instance = null;
public static function getInstance() {
if (self::$_instance === null) {
self::$_instance = new myclass();
}
return self::$_instance;
}
}
?>
and call it with:
<?php
$obj = myclass::getInstace();
?>
You cannot assign a class property in PHP like that. It must be a scalar, or array value, or the property must be set in a method call.
protected static $fetcher;
static function getFetcherInstance(){
if (!self::$fetcher) {
self::$fetcher = new Fetcher();
}
return self::$fetcher;
}
Also, notice that I did not use $this->, as that only works for object instances. To work with static values you need to use self:: when working within the class scope.
You might want to just read common design patterns on the php site. There are pretty good examples with good documentation:
http://www.php.net/manual/en/language.oop5.patterns.php
Else, a singleton is simply a method that returns one single instance of itself:
class MySingletonClass {
private static $mySingleton;
public function getInstance(){
if(MySingletonClass::$mySingleton == NULL){
MySingletonClass::$mySingleton = new MySingletonClass();
}
return MySingletonClass::$mySingleton;
}
}
Building on #periklis answer you might want separate singletons for different application scopes. For example, lets say you want a singleton of a database connection - fine. But what if you have TWO databases you need to connect too?
<?php
class Singleton
{
private static $instances = array();
public static function getInstance($name = 'default')
{
if ( ! isset(static::$instances[$name]))
{
static::$instances[$name] = new static();
}
return static::$instances[$name];
}
}
Class DB extends Singleton {}
$db_one = DB::getInstance('mysql');
$db_two = DB::getInstance('pgsql');
Alse define __clone method
class Fetcher {
protected static $instance;
private function __construct() {
/* something */
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Fetcher();
}
return self::$instance;
}
private function __clone() {
/* if we want real singleton :) */
trigger_error('Cannot clone', E_USER_ERROR);
}
}
Basically implementing a singleton pattern means writing a class with a private constructor and a static method to build itself. Also check PHP site for it: http://www.php.net/manual/en/language.oop5.php and http://it2.php.net/manual/en/book.spl.php
class A {
protected $check;
private function __construct($args) {
}
static public function getSingleton($args) {
static $instance=null;
if (is_null($instance)) {
$instance=new A();
}
return $instance;
}
public function whoami() {
printf("%s\n",spl_object_hash($this));
}
}
$c=A::getSingleton("testarg");
$d=A::getSingleton("testarg");
$c->whoami(); // same object hash
$d->whoami(); // same object hash
$b= new A("otherargs"); // run time error
<?php
class MyObject {
private static $singleInstance;
private function __construct() {
if(!isset(self::$singleInstance)) {
self::$singleInstance = new MyObject;
}
}
public static function getSingleInstance() {
return self::$singleInstance;
}
}
?>
class MyClass {
private static $instance;
public static function getInstance() {
if( !isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
}
Then call get instance using
MyClass::getInstance();
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.