I'm rather new in PHP and it turns out that I'm not able to find a solution for the following problem:
I have a simple class like this:
class User{
private $name;
function __construct($name){
$this->name = $name;
}
}
All I want to do is to define a static instance of it, like one of these:
public const UNKNOWN_USER = new User("unknown);
so that I can use this as a dummy everywhere, e.g.:
public static login($name){
if( /* userdoesnotexist */ ){
return UNKNOWN_USER;
}
}
and check for it - of course:
if( login($name) == UNKNOWN_USER){
/* I don't know you! */
}
I've tried the following:
$UNKNOWN_USER = new User("unknown");
/* $UNKNOWN_USER not available in class-methods */
define(UNKNOWN_USER, new User("unknown"));
/* not allowed */
class User{
const UNKNOWN_USER = new User("unknown");
/* "new" leads to a syntax error */
}
For constants only scalar values are allowed (float, int, string, bool, NULL). But you can set your UNKNOWN-insatnce to a static class variable
class User{
public static $unknown_user = NULL;
...
}
User::$unknown_user = new User("unknown");
and then user User::$unknown_user instead of UNKNOWN_USER.
You cannot use const for anything else then scalar types. That excludes any objects. What you are trying to achive here is to make an object immutable. You can do this in many ways though they are nither simple nor straightforward. Take a look at the Builder pattern for instance. Another way would be to make the object lockable. But all of the safe methods to achieve this require some coding on your part.
The simplest lockable pattern for your example, that I can think of:
class User{
private $name;
private $is_locked = false;
function __construct($name){
$this->setName($name);
}
public function lock() {
$this->is_locked = true;
}
public function getName() {
return $this->name;
}
public function setName($name) {
if ( $this->is_locked ) {
throw new Exception("The object is locked");
}
$this->name = $name;
}
}
Now you can do:
$user1 = new User("John");
$user1->setName("Johnny");
but after you lock the object, you can't manipulate it anymore:
$user1->lock();
$user1->setName("Big John"); // Exception thrown
You can do that like this:
<?php
//Normal UnknownUser singleton pattern
class UnknownUser {
private static $instance;
//define properties
public $myproperty = "hiho123";
private function __construct() {
}
public static function getInstance() {
if(empty($instance)) {
self::instance = new UnknownUser();
}
return self::instance;
}
//Make the class constant
function __set($name, $value) {
throw new Exception("Can't set property: " . __CLASS__ . "->$name");
}
}
You can call the UnknownUser, like this:
$unknownUser = UnknownUser::getInstance();
And this make this class global and constant, can not be modified, because the magic method __set is activated to disable editing the properties.
Related
So, in PHP, I know that static classes exist in the Global namespace, and thus cause overhead when having to call them.
But what happens when you assign a local class variable, or just a local variable, to that static class? Is the overhead of the Global reference removed?
In my specific case, I'm using a static singleton.
class Registry {
public static $user;
public static $DB;
public static $config;
public static $user_data;
private static $initialized = FALSE;
public static function init($config) {
if (!registry::$initialized) {
registry::$config = $config;
registry::$DB = new db($config['mysql']);
registry::$user = new user();
registry::$initialized = TRUE;
} else {
throw new Exception('Registry has already been initialized.');
}
}
}
Now, to make the question clearer, would it be beneficial to map the Registry in another class to a class variable/local variable for successive function calls?
class SomethingSomethingDarkSide {
private $registry;
private $db;
private $config;
public function __construct() {
$this->registry = Registry;
$this->db = Registry::$db;
$this->config = Registry::$config;
}
}
Since the static members are now assigned to a class variable after it's been initialized, would calling successive methods that utilize those class variables remove the overhead of the Global namespace that persist with static members?
EDIT: Please remove the idea of Singleton in this case. The question is more about whether we have to look up Registry each time after its been referenced to a variable.
IE:
$registry = Registry;
$registry::doSomething();
$registry::doSomething2();
$registry::doSomething3();
vs.
Registry::doSomething();
Registry::doSomething2();
Registry::doSomething3();
OR:
class Test {
public static function sayHi() {
echo 'Hi';
}
}
$test = Test;
echo $test::sayHi(); // This being done multiple times versus
echo Test::sayHi(); // That being done multiple times
I think your initial problem is that your Singleton implementation is not correct. Look at this code instead:
class Registry {
private static $instance = null;
private $user;
private $DB;
private $config;
private $user_data;
public static function getInstance($config) {
if (self::$instance === null)
{
self::$instance = new self($config);
}
return self::$instance;
}
private function __construct($config) {
$this->$config = $config;
$this->$DB = new db($config['mysql']);
$this->$user = new user();
}
public function doThing() {
}
}
$registry = Registry::getInstance($config);
$registry->doThing();
Since the constructor is private, the only way to get a new instance of Registry class is to use the public static method getInstance.
From then, you have a $registry variable which points to an instance of the Registry class (if it's not what you want then I don't understand why you are using the Singleton pattern...)
I think that's pretty close to want you want:
$registry = Registry;
$registry->doSomething();
$registry->doSomething2();
$registry->doSomething3();
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();
I have a PHP Class which requires a unique value in its constructor. If multiple instances of the same class are passed the same value the results are horrific.
How would I go about detecting other instances of a Class so I can check and prevent this from happening before constructing any new ones?
A simple solution would be to keep a static array of the values inside the class. Then, when a new instance is created, check the static array's contents in the constructor.
Something like..
class Foo {
private static $usedValues = array();
public function __construct($val) {
if(in_array($val, self::$usedValues)) {
throw new Exception('blah');
}
self::$usedValues[] = $val;
}
}
I think the multiton pattern is right for you.
class Foo {
static protected $_instances = array();
static public function getInstance($id) {
if(!self::exists($id)) {
self::$_instances[$id] = new Foo($id);
}
return self::$_instances[$id];
}
static public function exists($id) {
return isset(self::$_instances[$id]);
}
protected function __construct($id) {
}
}
How do I use an object (along with its methods and properties) when I'm inside an object?
Say I have useless classes like these:
class Fruit {
private $name; // Name of the fruit.
private $health = 10; // 0 is eaten, 10 is uneaten.
private $object; // This is a PHP object.
public function __construct($name) {
$this->name = $name;
}
public function set($varname,$value) {
$this->$varname = $value;
}
}
class Eater {
private $name;
public function eat($object) {
$object->set('health',0); // I know I can pass and modify objects like this.
// The object is passed by reference in PHP5 (but not 4), right?
}
}
And I use it as such:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->eat($apple);
?>
But if I modify the Eater class like so:
class Eater {
private $name;
private $objectToEat; // Let's say if I need the object to be over here instead of in a method.
public function set($varname,$value) {
$this->$varname = $value;
}
public function eat() {
$this->objectToEat->set('health',0); // This doesn't work!
}
}
And set the main program like so:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->set('objectToEat',$apple);
$paul->eat();
?>
How can I access the object's properties from inside a method? I know I use $this->objectToEat to tell PHP I'm talking about the class properity, but since that property is an object, how do I access the object's methods?
I've tried $this->objectToEat->set('health',0) but that doesn't work. I hope you guys understand what I'm trying to get at (sorry, I can't figure out how to condense my question without compromising clarity)!
You have to set the property correctly. Since it's private, you can't do this from outside the object, so you have to use encapsulation:
class Eaters {
private $name;
private $objectToEat;
public function eat() {
$this->objectToEat->set('health',0); // Assumed "object" was just a typo
}
public function setObjectToEat($object) {
$this->objectToEat = $object;
}
}
Then use it like so:
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->setObjectToEat($apple);
$paul->eat();
?>
Note: In this brief example, your original method is a better design. In certain cases, you might want to prime the method to be used by setting properties beforehand, but more often you want to call it with parameters directly, since it's more clear and more reusable (compartmentalized).
This answer modifies Renesis' answer
In the class, the object to eat is a private variable hence you can't go
$paul->objectToEat = $apple;
What you can do is to make a setter method inside Eaters
class Eaters {
private $name;
private $objectToEat;
public function eat() {
$this->objectToEat->set('health',0); // Assumed "object" was just a typo
}
public function setFood($object) {
$this->objectToEat = $object;
}
}
Therefore, you can call the setFood() method instead.
OR
Change eat() to
public function eat($object) {
$this->object->set('health',0);
return $object;
}
Saving the modified object back to the original variable.
OR
class Eaters {
private $name;
public function eat(&$object) { // this passes object by reference
$object->set('health', 0);
}
}
Although this code is not tested, that is how you can pass a variable by reference.
NOTE: You only need the & when defining the method not when you're passing an argument. For more info about Passing by Reference go to this link
It's probably because your eat method isn't accepting any parameters, and the Eaters class has no $object property.
Can you make $objectToEat a reference and then use it as such in the eat() function?
you have to set $this->object in class Eaters
function __construct($object){
$this->object = $object;
}
or
<?php
$pear = new Fruit("Pear");
$apple = new Fruit("Apple");
$paul = new Eater("Paul");
$paul->eat($apple);
?>
class Tester {
private $variable;
private $anObj;
public function testFn($val) {
$this->variable = $val;
$this->anObj = new SecondObj();
$this->doSomething();
}
public function doSomething() {
echo("My variable is set to " . $this->variable);
$this->anObj->wow();
}
}
class SecondObj {
public function __construct() {
echo("I'm new!");
}
public function wow() { echo("Wow!"); }
}
$tester = new Tester();
$tester->testFn(42);
Output:
I'm new!My variable is set to 42Wow!
I have a singleton factory and would like it to return a reference to the object instance so that I can use the singleton factory to destroy the instance and not have instances elsewhere in my code to survive.
Example of what I would like to be able to do:
$cat = CatFactory::getInstance();
$cat->talk(); //echos 'meow'
CatFactory::destructInstance();
$cat->talk(); //Error: Instance no longer exists
This could work:
<?php
class FooFactory
{
private static $foo;
private function __construct()
{
}
public static function getInstance()
{
return self::$foo ? self::$foo : (self::$foo = new FooFactory());
}
public static function destroyInstance()
{
self::$foo = null;
}
public function __call($fn, $args)
{
if (!method_exists(self::$foo, $fn) || $fn[0] == "_")
throw new BadMethodCallException("not callable");
call_user_func_array(array(self::$foo, $fn), $args);
}
# function hidden since it starts with an underscore
private function _listen()
{
}
# private function turned public by __call
private function speak($who, $what)
{
echo "$who said, '$what'\n";
}
}
$foo = FooFactory::getInstance();
$foo->speak("cat", "meow");
$foo->_listen(); # won't work, private function
FooFactory::destroyInstance();
$foo->speak("cow", "moo"); # won't work, instance destroyed
?>
Obviously it is a hack.
Based on the documentation for unset, I do not think that is possible. You cannot actually destroy an object, only a handle to it. If other variables are around that still hold a reference, the object will continue to live on.
You can accomplish what you want by having your Cat object enforce a private $destroyed property. PHP 5 passes objects by reference by default, so you don't have to worry about that part.
A work around would be creating a cat class
class cat
{
public $cat;
public function __construct()
{
$this->cat = CatFactory::getInstance();
}
public function __destruct()
{
CatFactory::destructInstance();
}
}
$cat = new cat();
$cat->cat->talk();
$cat->cat->talk();