This is one of those "my code works, I don't know why" times.
I have a method in an instantiated class that statically calls a method of what is basically a static class. I say "basically" because while coding it I neglected to declare the methods of the class exclusively static. The method in question belongs to the first class but I blindly copy/pasted the code (which was originally internal) and didn't quite update everything, so the $this-> remained even though the method doesn't belong to the class it's within.
So, basically I had this:
class MyClass{
public function callOtherMethod(){
OtherClass::otherMethod();
}
public function myMethod(){
echo 'Tada!';
}
}
class OtherClass{
public function otherMethod(){
echo get_class($this);
$this->myMethod();
}
}
$thing = new MyClass();
$thing->callOtherMethod();
This somehow worked without issue until I did some cleanup and explicitly declared the OtherClass' methods static as they were meant to be. Everything worked because, for some reason I was unaware of, $this while used within OtherClass instead references the instantiated object that called it (i.e. MyClass).
I didn't think this should be possible, should it? I know it's bad coding standards and I'm making revisions to avoid it, but I found it really, really weird.
Related
How can I store a model that was returned from $bean->box() in RedBean?
For example, the following code doesn't work (it just inserts an empty row):
class Model_Comment extends RedBean_SimpleModel {
public $message;
}
$bean = R::dispense('comment');
$model = $bean->box();
$model->message = "Testing";
R::store($model);
It works if I use $model->unbox()->message = "Testing", but that's probably gonna get annoying real quick...
Obviously the code above is just an example, I could just set the property message on $bean here, but I want to be able to box a bean and pass it to other methods.
Is this how it's supposed to work, or am I missing something here?
This turned out to be caused by a "gotcha" when dealing with PHP's "magic" getter- and setter methods __get() and __set().
Looking at the source code for RedBean_SimpleModel, it actually uses the magic __set() method to update its bean when setting a property.
Here's comes the gotcha, straight from the PHP documentation:
__set() is run when writing data to inaccessible properties.
__get() is utilized for reading data from inaccessible properties.
__isset() is triggered by calling isset() or empty() on inaccessible properties.
__unset() is invoked when unset() is used on inaccessible properties.
So it turns out that __set() is never called for an existing (accessible) class member, i.e. public $message. So I could just remove all the public fields from the class and that would solve the problem, but then I'd lose all the autocomplete functionality and lint checking in my IDE.
So I came up with this solution instead:
class MyBaseModel extends RedBeanPHP\SimpleModel {
public function __construct(){
foreach( get_object_vars($this) as $property => $value ){
if( $property != 'bean' )
unset($this->$property);
}
}
}
class Model_Comment extends MyBaseModel {
public $message;
}
This effectively removes all member variables from the class MyBaseModel when it's instantiated, except $bean, which of course is a vital part of RedBeanPHP_SimpleModel.
Now I can easily subclass MyBaseModel and have all the public fields I need in my subclass models, and the code in the original question will work.
I have a child class that extends a class with only static methods. I would like to make this child class a singleton rather than static because the original developer really wanted a singleton but used static instead (obvious because every method in the static class calls the Init() function (basically a constructor)).
Most of the methods in the parent don't need to be overwritten in the child, but I would like to avoid having to write methods like this:
public function Load($id)
{
return parent::Load($id);
}
when I would prefer not to overwrite the method at all and just use:
$child->Load($id);
Is it possible to call a static method non-statically? Is it possible to extend a static object with an instance object? I know I can try it and it will likely work (PHP is very forgiving), but I don't know if there is anything I should be concerned about.
Can you inherit static methods?
Yes
Can you override static methods?
Yes, but only as of PHP 5.3 do they work as you would expect: http://www.php.net/manual/en/language.oop5.static.php (ie. self binds to the actual class and not the class it's defined in).
Is it possible to call a static method non-statically?
Yes, but will lose $this. You don't get a warning (yet) but there also isn't really a reason to call it the wrong way.
Two part answer.
First, about the titular question: calling a static method non-statically is perfectly fine; #SamDark's comment is correct. It does not produce a warning, nor does it cause any kitten murdering. Try it:
<?php
class test {
public static function staticwarnings(){
echo "YOU ARE (statically) WARNED!\n";
}
}
error_reporting(E_ALL);
$test = new test();
echo "\n\ncalling static non-statically\n";
$test->staticwarnings();
If you had an instance reference, $this, in that static method, then you would get a fatal error. But that is true regardless of how you call it.
Once again, there isn't a warning, nor any kitten killed.
Second part of the answer:
Calling an overridden parent function from an overriding child class requires something called "scope resolution". What the OP is doing in their method is NOT calling a static method. (Or at least, it doesn't have to be; we can't see the parent implementation). The point is, using the parent keyword is not a static call. Using the :: operator on an explicit parent class name is also not a static call, if it is used from an extending class.
Why is that documentation link so strangely named? It's literally Hebrew. If you've ever run into an error related to it, you might have observed the delightfully-named parser error code T_PAAMAYIM_NEKUDOTAYIM.
I always thought I understood how OOP works (and I have been using it for years), but sometimes I realize some concepts are still not so clear to me.
I just came across this question about method visibility in PHP. The accepted answer explains that a private method cannot be overridden by a child class in PHP. Okay, that makes sense. However, the example made me think about the internal inheritance mechanism in PHP, and the way $this behaves on inherited methods.
Consider this code (example from the PHP Manual, also included in the question mentioned above):
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic\n";
}
private function testPrivate() {
echo "Bar::testPrivate\n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic\n";
}
private function testPrivate() {
echo "Foo::testPrivate\n";
}
}
$myFoo = new foo();
$myFoo->test();
/*
Output:
Bar::testPrivate
Foo::testPublic
*/
Now consider this excerpt from the PHP Manual:
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
The explanation states that "$this is a reference to the calling object", which is $myFoo. So I expected that $myFoo->test() would always invoke Foo::testPrivate, and never Bar::testPrivate (unless $myFoo were an instance of Bar). I tested $this with get_class, and it always returns Foo, even from inside Bar::testPrivate and Bar::test. However, $this behaves like an instance of Bar when Bar::test calls $this->testPrivate().
That's really confusing, and I am trying to understand why it works that way!
I thought inherited methods (public or protected) were somehow copied from the base to the child class. Private methods would not be copied at all. But this example indicates that it doesn't work like this. It looks like the instance of Foo keeps an internal instance of Bar, and delegates method calls to it when necessary.
I am trying to learn something here, and I only learn when things make sense to me. This one does not. After writing all this, I think I can summarize it with two questions:
Could someone briefly explain how inheritance works internally in PHP? Or at least point me to an article or documentation about that?
Is the behavior or $this discussed here present on other OO languages as well, or is it particular to PHP?
Inheritance in PHP works the same way it does in most object-oriented languages.
When you have a "virtual" method, the method is not bound directly to the caller. Instead, every class contains a little lookup table which says "this method name is bound to that implementation". So, when you say $this->testPublic(), what actually happens is that PHP:
Gets the virtual table for the current class
Looks up the virtual table entry for testPublic in that table
Invokes the method to which that lookup points
Since Foo overrides testPublic, its virtual table contains an entry for testPublic pointing to Foo::testPublic.
Now, with the private methods, the behavior is different. Since, as you correctly read, private methods cannot be overridden, calling a private method never results in a virtual table lookup. That is to say, private methods cannot be virtual and must always be defined in the class which uses them.
So, the effect is that the name is bound at the time of declaration: all Foo methods will call Foo::testPrivate when they say $this->testPrivate, and all Bar methods will call Bar::testPrivate.
To sum up, saying that "inherited methods are copied to the child" is not correct. What actually happens is that the child begins with its method-name-lookup-table being populated with its parent class' entries, and then adds its own functions and replaces any overridden entries. When you call $this->something, this lookup table is consulted for the current object's class. So if $this is an instance of Foo, and Foo overrides testPublic, you get Foo::testPublic. If $this is an instance of Bar, you will get Bar::testPublic.
Well, private methods and properties are exactly that - private. For all intents and purposes, you can consider them "internal", meaning internal to the class they're defined in. This means that they're never inherited, and can never be overridden.
Thus, when using $this in combination with a private method or property, it will always be the method or property within the same class as the reference to $this. This happens because $this called within a parent class cannot access private methods or properties in another class (because they're private), even from child classes.
Hope this helps.
First of all: A quite similar problem has been posted and somehow solved already, but is still not answering my specific problem. More about this later.
In words: I have a base class which provides some methods to all childs, but doesn't contain any property. My child is inheriting these methods, which should be used to access the child's properties.
If the child's property is protected or public, all works fine, but if the child's property is private, it fails without error (just nothing happens).
In code:
class MyBaseClass {
public function __set($name, $value) {
if(!property_exists($this, $name))
throw new Exception("Property '$name' does not exist!");
$this->$name = $value;
}
}
class ChildClass extends MyBaseClass {
public $publicProperty;
protected $protectedProperty;
private $privateProperty;
}
$myChild = new ChildClass();
$myChild->publicProperty = 'hello world'; //works of course!
$myChild->protectedProperty = 'hello world'; //works as expected
$myChild->privateProperty = 'hello world'; //doesn't work?
The above mentioned similar problem got the solution to use the magic __set() method to access the private properties, but this I am already doing. If I implement __set() within the child, it works of course, but the idea is, that the child inherits the __set() from it's parent, but obviously it can't access the child's private method.
Is that on purpose? Am I doinf something wrong? or is my approach just crap by design?
Background:
My original idea was: The whole dynamic thing about __set() is something I don't like. Usually a private property should never be accessible from outside, so I implemented throwing __set- and __get-methods in my ultimate base class (from which all classes inherit).
Now I want to dynamicially spawn an instance from an XML file and therefore need access to properties. I made the rule, that any XML-instantiatable class needs to implement the magic __set() method and so can be created dynamicially. Instead of implementing it in every Class that might be spawned some day, I decided to make them inherit from a class called like class Spawnable { } which provides the needed __set-method.
This is the difference between private and protected. Private methods and properties cannot be inherited or reached. You will need to change them to protected instead.
See the manual on visibility
Members declared protected can be
accessed only within the class itself
and by inherited and parent classes.
Members declared as private may only
be accessed by the class that defines
the member.
I guess you could fashion something using Reflection. For example, in your Spawnable class:
public function __set($name, $value)
{
$reflector = new ReflectionClass(get_class($this));
$prop = $reflector->getProperty($name);
$prop->setAccessible(true);
$prop->setValue($this, $value);
}
Not the prettiest of code, though.
After reviewing my concept, I think it's a bad idea, to go with that approach. This is a general issue with PHP's lack of differing between properties and fields. Of course private fields should never be accessible from outside, but only properties, which are defined by the programmer. The absence of auto-properties (and I don't mean these magical methods __set() and __get()) or some conventional rules for property access, makes it difficult to guess, which naming convention has been used by the programmer when implementing setters for private fields in his class.
The better concept here might be, to rely on the existence of well-named setters for each spawnable class, although it might break, if someone contributed code, which is not implementing the expected conventional named setters.
However, many thanks for your thoughts and hints!
I'm a Java developer who has turned to the dark side over the last couple of weeks as I've been trying to write some code in PHP for the first time ever.
Most stuff seems familiar, methods become functions, . becomes ->, variables have a $ in front and you never have any idea what type they are, I get it, but I'm totally stumped by the $this keyword.
I expect $this to call a function in the current class as in java this.myCoolMethod(), but often as I poke through the open source project I'm going through, $this->myCoolMethod() calls a function in a totally different class!
What is happening here? What does '$this' actually mean?
Thank you for any help.
John
The reason it sometimes calls a method in a totally different class, is because that class probably extended another class with that method.
Here is a quick example:
class bla {
public function __construct() {}
public function whatwhat() {
echo "what....";
}
}
class lala extends bla {
public function __construct() {}
}
$lalaObj = new lala();
$lala->whatwhat();
As you can see, even though whatwhat() isn't in the class, it inherits it from the class it extends.
Hope that helps.
You should take a look at what Inheritance is.
$this, within a method, refers to the object to which the method belongs. If an object belongs to a class that extends another class, then it may inherit methods from the parent class, which may be called using $this->method_name(), just as with any other method.
$this actually refers to the current instance of the class.
$this->myCoolMethod() calls a function
in a totally different class!
Can you provide an example of this? The only circumstance in which the myCoolMethod() might not be defined in the class itself is if it is defined in a parent or inherited class
$this refers to whatever instance you're in. So if I extended Class_A to become Class B, and called methodName within Class_B, that would invoke Class_B::methodName and not Class_A::methodName.
I'm not sure how to explain clearer without knowing what context you're having difficulties, but try echoing __CLASS__ next to where you're using $this to give you the current class's name you're in.