Is there a way to get the actual calling object from a method?
For example, I have a scenario where I do something like this:
$user = new User;
$user->setName('name');
$user->save($user);
Instead of passing the $user to the save method, it would be nice to just have it get the calling object information.
I know I can do get_called_class() but that doesn't return the actual object's data. I was also looking at PHP's Reflection classes but didn't see anything exactly right.
Any ideas?
Thanks!
Within your object (and thus from within the save function itself, you can utilize the $this keyword to access the object itself. Alternatively...
public static function getInstance() {
if(!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
Where instance would be a private member variable. Make sure your constructor is private so that your object cannot be instantiated externally. <--- all this assuming php5.
I think you can do self->save($this), if save is a class method, or just save($this) if save is an instance method.
Anyway, the point is $this refers to the current instance. So setName is acts on an instance of user, so $this should equal that user inside of setName.
Related
In my current application I have a number of objects that are required repeatedly
To save overhead of instantiating the same object over and over again, I keep an array of 'known' objects.
I can check the input data against the array, and - if already set - use the existing object, else go ahead to instantiate the new object and add the pointer reference to the relevant known objects array.
In most use cases, I can check prior to instantiating the class:
if(array_key_exists($identifier,$known_ClassObjects)){
$object = $known_ClassObjects[$identifier];
} else {
$object = new Class($params);
}
However, in some cases I can only identify that the object I am instantiating already exists once already inside it.
In that case I would like to be able to do one of 2 things:
return the OTHER (pre-existing) object instead of this one, e.g.
class Test{
public function __construct($params){
//apply processing to $params, resulting in $identifier
if(array_key_exists($identifier, $known_ClassObjects)){ //$known_ClassObjects is global
return $known_ClassObjects[$identifier];
} else {
//continue __construct() logic
return $this;
}
}
}
However, PHP ALWAYS returns the current object, even with code return $other_object;
'Internally Clone' the current object from the found one [of the same class, obv] so that when it returns, it has the correct relevant properties populated.
NOTE: including any parent/child class properties
-> I want to make this object EXACTLY the same as the found one.
So, if there was a PHP function clone_from(), it would work something like:
if(array_key_exists($identifier,$known_ClassObjects)){
$this->clone_from ($known_ClassObjects[$identifier]);
} else {
//continue with __construct()
}
return $this;
Given that 1. doesn't work and 2. doesn't seem to exist, I have only been able to do this in very 'hacky' ways: iterating through all properties of source object and setting all properties of current object.
However, this has obvious issues esp. with extended parent/child classes etc. which then requires things like reflection classes.
This seems like there SHOULD be a really simple solution, but I have been unable to find one
What you actually could do is using either a Singleton Pattern or a Factory pattern - in both cases, the creation of objects is controlled by some piece of code and you can decide, which object to return. Singleton already is a special form of a Factory pattern.
Consider this code
class Singleton {
protected static $instance;
protected function __construct() {
}
public static function instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
The constructor is protected which will prevent a object construction from "outside" via new. However, there is also a static function instance with which somebody can request an object instance from the factory method.
$obj = Singleton::instance();
So, the (internal) object is created only once, and then delivered afterwards until the script ends.
However, PHP ALWAYS returns the current object, even with code return $other_object;
Constructor is not a regular function. You do not return from it. If you are in __construct() you are already constructing the new object. Before proceed, I strongly recommend you read at least: https://www.php.net/manual/en/language.oop5.decon.php
You most likely need a helper method or factory that would deal with that instead. Putting the logic you tried to place into your constructor is wrong.
Also you missed to clarify what the $identifier really is. But if that's object's property, then you can expose it (i.e. via getter) and have it read for your comparison/whatever by other code.
I just started learning OOPS in php. I wrote a simple program for implementing inheritance. I am getting a fatal error of $this when not in object context. Can anyone explain me this error, what does it mean?
here is my code:
<?php
class human{
public $gender;
public function _construct($gender)
{
$this->gender=$gender;
echo $this->get_gender();
}
public function get_gender()
{
return $this->gender;
}
}
class person extends human{
public $name;
public $surname;
public static function set_name($name)
{
$this->name=$name;
}
public static function set_surname($surname)
{
$this->surname=$surname;
}
public static function get_name()
{
return $this->name;
}
public static function get_surname()
{
return $this->surname;
}
}
$john = new person('male');
$john->set_name('John');
$john->set_surname('Williams');
echo $john->get_name().' '.$john->get_surname().'is a '.$john->get_gender();
?>
There are two problems here:
You have defined your methods as static. You should not do that as they are not, they depend on being called on an object as you want to use the objects non-static properties.
You have a typo in your constructor function. The correct name for the constructor is __construct, notice the two _ at the beginning.
In a static function, $this does not exist. You can refer to the class itself with self::. So, instead of $this->surname, use self::surname. As noted below, this will change your error to a new error as it requires the variables to be declared static as well.
Of course, you really need to figure out WHY you made those functions static. Should they be static?
You're trying to access the $this property from a static method. You cannot do this, as anything static can be accessed without instantiating the class, therefore the $this variable would not make sense, as you are not in an actual class instance.
The this keyword refers to the current object you are working with, e.g. if you were to make 5 instances of person, each $this would refer to that specific instance of person, and you could change each objects properties freely.
When you use static you are not referring to any instance, you are simply accessing static variables and calling static methods of that class. All these variables and methods are 'class wide'. That means if you change the static property in one instance of the class, the static property will change in all the classes.
Another way to imagine this is, take when calling the new keyword, you create a new instance of the object. Every instance will have its own copy of all the properties and methods you describe in the class, EXCEPT for the static ones. Imagine that when you access a static method or a static property you are accessing one object always.
That's why it would not make sense to access $this from a static context. You are not referring to any specific instance, only the class itself.
I hope I've explained this well enough, it's a very important concept to grasp in OOP, and it does give some people a lot of trouble to understand the concept.
How can I ensure that an object will be instantiated only via another particular object?
For example, say I have a Registry object to store my Mappers. When client code calls the get() method on the Registry, it lazy loads and returns the requested Mapper. That's fine, except there is nothing to stop client code from creating a duplicate instance of the Mapper using the new operator.
The only option I can think of is that my Mappers require a Registry object as a parameter. Are there other options?
What do you do? Should I even bother about preventing this kind of duplication?
Perhaps you should not try to prevent people from creating instances themselves? If you don't trust yourself or your colleagues not to instantiate objects in places where they should not instantiate them, you have a problem.
If the mappers do not need a registry to function, you should not object it via the constructor. Passing it to some static method seems rather odd, and makes your code less flexible since you're using static. And how are you going to unit test the mappers, without writing some hacks to properly instantiate them via the registry you should not have need for in these tests? Good post on that here: http://kore-nordmann.de/blog/0103_static_considered_harmful.html
You can't protect from the new operator. What you could do though is that you have a get() method in your class to make your class/object singleton (or using a Registry as you do).
class clTest {
private static $oInstance;
public static function get() {
if( !self::$oInstance ) {
self::$oInstance = new clText;
}
return self::$oInstance;
}
}
if you wish to prevent outside instantiation you only need to declare __construct as private and then use a call to a static method to get an instance of the Mapper class. You can then pass in an instance of the registry class and only return a new instance if the parameter is an instance of the registry class.
class Mapper{
private __construct(){}
public static function getInstance($registry){
if($registry instanceof Registry){
return new Mapper();
}
}
}
$registry = new Registry();
$mapper = Mapper::getInstance($registry);
If I create a registry object to store other objects and settings in like this...
$registry = Registry::getInstance();
And then create 2 other objects and pass the Registry object into them like this below...
$core = new core($registry);
$database = Database::getInstance($registry);
and then later in the script I add some objects to the Registry object...
// Store some Objects into our Registry object using a Registry Pattern
$registry->storeObject("database", $database);
$registry->storeObject("session", $session);
$registry->storeObject("cache", $cache);
$registry->storeObject("user", $user);
Will methods in the core and database objects that were created at the top, still have access to all the objects that I store into the registry even though the other objets were stored into the registry AFTER the core and database Objects were created?
yes they will, objects are passed by reference (in php >= 5) so, each variable will refer to the same underlying object.
in old php, you would need to pass by ref:
$obj->registry =& $registry;
function f(& $registry) { // $registry is a reference }
the key in php <5 is the ampersand syntax when assigning and in function declarations.
If its the same instance of the object, than yes - otherwise they'll have to use shared memory to pass the objects around or no, you won't have access to them.
However, I'd imagine your registry is a singleton right? So yes.
edit: maybe Im not understanding your question ....
Yes, they'll have access to these object, but IMO Registry isn't the best solution here - Context would be much better because it allows you to model each property. It also gives you 100% sure that when you call $registry->get('user') some User object will be returned.
Context is pretty similar to Registry:
class Context {
protected $database;
protected $user;
protected $logger;
public function setDatabse(PDO $database) {
$this->database = $database;
}
public function getDatabase() {
return $this->database;
}
public function setLogger(Logger $logger) {
if ($logger instanceof MySpecificLogger) {
// do sth
}
$this->logger = $logger;
}
// rest of setters/getters
}
And when you use it later:
$db = $contextObject->getDatabase();
When you use Registry you don't have 100% sure that $db is then an object of PDO (in this example) what might causes some problems in some situations.
No, not by default. Parameters are passed by value in PHP in almost every case. What you want is called "passing by reference", which you can read more about in the manual.
Is it ok to use a singleton method inside of a __constructor to initiate the class if it wasn't already?
No -- A singleton method is an alternative to using a constructor.
Instead of saying $session = new Session();
you should say
$session = Session::getStaticSession();
Then define Session::getStaticSession() as a function tht returns an internal static var, and calls "new Session()" if the internal static var is null.
If you create it in each constructor that uses it, then it isn't a singleton.
If you follow this tutorial then it should help you understand how to use the singleton design pattern in php.
http://www.developertutorials.com/tutorials/php/php-singleton-design-pattern-050729/page1.html
You can't use a singleton method inside a constructor, as the object has already been created, and you can't return anything. Your singleton method, on the other hand, needs to either return the current object or create a new one.
You can use a single method to do that, however, such as the following:
<?php
class X {
// ...
function instance() {
static $instance;
if(!is_a($instance, 'X')) {
$instance = new X();
}
}
?>