I have an autoloader, initializing alot of objects as a mini-framework is loaded. The objects are saved as static variables, but now I've run into a problem. I have a file which is instantiated in the autoloader, but used later kind of like an exception handler, taking care of special cases when called. The intention is that the methods within this class returns $this, the class object, but when doing so, the returned value is not an instance of the called object, but gets inherited into the class which calls it. Furthermore the called exception_handler is not only an instance of itself, but everything instanziated throughout the entire autoloader, inheriting $this as everything gets loaded. Confusing but I've built a small example:
class a {
public $a_tmp = 'tmp';
}
class b extends a {
public $b_tmp = 'tmp';
public function getOnlyThisClass() {
return $this;
}
}
$b = new b();
$b->getOnlyThisClass();
This returns:
object(b)#1 (2) {
["b_tmp"]=>
string(3) "tmp"
["a_tmp"]=>
string(3) "tmp"
}
And I need it to return ONLY the called class when special methods are called. I know this can be fixed with a factory pattern, but would like to avoid it in this case.
Thanks.
Try to use Reflection to get class object properties without inherited properties:
<?php
class a {
public $a_tmp = 'tmp';
}
class b extends a {
public $b_tmp = 'tmp';
public function getOnlyThisClass() {
$cl = new stdClass();
$refclass = new ReflectionClass($this);
foreach ($refclass->getProperties() as $property)
{
$name = $property->name;
if ($property->class == $refclass->name) {
$cc = $property->name;
$cl->$cc = $this->$name;
}
}
return $cl;
}
}
$b = new b();
var_dump($b->getOnlyThisClass());
Output:
object(stdClass)#2 (1) { ["b_tmp"]=> string(3) "tmp" }
You can create an stdClass and assign found properties there.. Finish this to match your needs.
Related
I encountered something magical about laravel (4.2), that i really want explaind how it could be possible.
When i up a MessageBag Class in Class A
And Pass that variable to Class B, somehow Class B overrides the Class A MessageBag without me declaring it.
class ClassA {
public function test()
{
$msgBag = new \Illuminate\Support\MessageBag;
new ClassB($msgBag);
if($msgBag->any()) {
#This will trigger and spit out "Message from Class B"
dd($msgBag);
}else{
print('nope no messages');
}
}
}
class ClassB {
protected $msgBag;
function __construct(\Illuminate\Support\MessageBag $msgBag)
{
$this->msgBag = $msgBag;
$this->setMessage();
}
public function setMessage()
{
$this->msgBag->add('message', 'Message from Class B');
}
}
I tested the same thing with a normal object but that behaved like i expected it to.
class ClassA {
public function test()
{
$object = (object) ['class'=>'A'];
new ClassB($object);
dd($object->class); # Will return A
}
}
class ClassB {
protected $object;
function __construct($object)
{
$this->object = $object;
$this->setMessage();
}
public function setMessage()
{
$this->object = (object) ['class'=>'B'];
}
}
So obviously Laravel is doing something behind the seances to make this possible, but I haven't found it yet.
Does anyone know how to replicate this ?
There is no Laravel magic here. In PHP, objects behave as though they are passed by reference (although they technically are not, but that's not relevant here).
This means that in your first example, the MessageBag object you created is the same object as the as the one assigned to $this->msgBag in ClassB. Therefore, any modifications made to the object inside ClassB are going to be seen when you inspect the $msgBag object in the test() method in ClassA.
This is not the case in your second example, because in your setMessage() method, you override the first object with an entirely new one.
Basically everything is behaving as you would expect with normal PHP.
I know that protected attributes are available to subclasses when they are defined in a class, but are they available in parent classes? For example:
class My_Class {
// Is $name available here?
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
Code which you write in the parent class can access that property if run in the context of a subclass. Made sense? Example:
class My_Class {
public function test() {
echo $this->name;
}
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
$a = new My_Class;
$b = new My_Subclass;
$a->test(); // doesn't work
$b->test(); // works
Obviously (hopefully), instances of My_Class won't suddenly sprout a name property, so $a->test() won't work. Precisely because of that it's a very bad idea to make a class rely on properties which it doesn't define.
Visibility doesn't only relate to $this BTW, watch:
class My_Class {
public function test($obj) {
echo $obj->name;
}
}
class My_Subclass extends My_Class {
protected $name = 'Henry';
}
$a = new My_Class;
$a->test(new My_Subclass); // Amazeballs, it works!
A parent class has access to the property if and when it tries to access it. That doesn't mean all parent classes suddenly get a copy of that property themselves.
The parent class has no information about its subclasses, so no, $name is not available in My_Class.
Edit: As #deceze points out correctly in a comment code in My_Class can access $name, but that only works if the object was instantiated from a subclass implementing that variable. Accessing the variable in the parent class will give an Undefined Property notice.
Also I would consider that bad style and architecture, but that's my opinion ;)
Sometimes it 'can', but you really shouldn't do it
class A {
function set() {
$this->v = 'a';
}
function get() {
return $this->v;
}
}
class B extends A{
protected $v = 'b';
}
echo $b->get();//b
$b->set();
echo $b->get();//a
var_dump($b); //class B#1 (1) { protected $v => string(1) "a"}
$a = new A();
echo $a->get(); //Undefined property: A::$v
$a->set();
$a->get();//a
var_dump($a); //class A#2 (1) { public $v => string(1) "a"}
No. "protected" access modifier makes any property and method to be visible from the derived class. This is what it is used for. But parent class never knows any information about the derived class.
For more information please see this article.
$model = new static($variable);
All these are within a method inside a class, I am trying to technically understand what this piece of code does. I ran around in the Google world. But can't find anything that leads me to an answer. Is this just another way of saying.
$model = new static $variable;
Also what about this
$model = new static;
Does this mean I'm initializing a variable and settings it's value to null but I am just persisting the variable not to lose the value after running the method?
static in this case means the current object scope. It is used in late static binding.
Normally this is going to be the same as using self. The place it differs is when you have a object heirarchy where the reference to the scope is defined on a parent but is being called on the child. self in that case would reference the parents scope whereas static would reference the child's
class A{
function selfFactory(){
return new self();
}
function staticFactory(){
return new static();
}
}
class B extends A{
}
$b = new B();
$a1 = $b->selfFactory(); // a1 is an instance of A
$a2 = $b->staticFactory(); // a2 is an instance of B
It's easiest to think about self as being the defining scope and static being the current object scope.
self is simply a "shortcut name" for the class it occurs in. static is its newer late static binding cousin, which always refers to the current class. I.e. when extending a class, static can also refer to the child class if called from the child context.
new static just means make new instance of the current class and is simply the more dynamic cousin of new self.
And yeah, static == more dynamic is weird.
You have to put it in the context of a class where static is a reference to the class it is called in. We can optionally pass $variable as a parameter to the __construct function of the instance you are creating.
Like so:
class myClass {
private $variable1;
public function __construct($variable2) {
$this->variable1 = $variable2;
}
public static function instantiator() {
$variable3 = 'some parameter';
$model = new static($variable3); // <-this where it happens.
return $model;
}
}
Here static refers to myClass and we pass the variable 'some parameter' as a parameter to the __construct function.
You can use new static() to instantiate a group of class objects from within the class, and have it work with extensions to the class as well.
class myClass {
$some_value = 'foo';
public function __construct($id) {
if($this->validId($id)) {
$this->load($id);
}
}
protected function validId($id) {
// check if id is valid.
return true; // or false, depending
}
protected function load($id) {
// do a db query and populate the object's properties
}
public static function getBy($property, $value) {
// 1st check to see if $property is a valid property
// if yes, then query the db for all objects that match
$matching_objects = array();
foreach($matching as $id) {
$matching_objects[] = new static($id); // calls the constructor from the class it is called from, which is useful for inheritance.
}
return $matching_objects;
}
}
myChildClass extends myClass {
$some_value = 'bar'; //
}
$child_collection = myChildClass::getBy('color','red'); // gets all red ones
$child_object = $child_collection[0];
print_r($child_object); // you'll see it's an object of myChildClass
The keyword new is used to make an object of already defined class
$model = new static($variable);
so here there is an object of model created which is an instance of class static
Preamble:
What I'm after is; if a method calls the get_typed_ancestor() method, the class name needed to perform the operations required in get_typed_ancestor() is the name of the class in which the calling method is defined.
Passing $this to extract the class name fails because it will resolve to the concrete class. If the calling method is defined in an abstract class higher up the hierarchy than the concrete class of which the instance is, we get the incorrect class name.
Looking for an instanceof static fails for the same reason as described above.
As described below, the purpose for capturing the class name in which the method is defined, is so that get_typed_ancestor() can find an instance of any class derived from the class in which the calling method is defined, not just another instance of the concrete class that initiated the call stack (hence $this and static being unsatisfactory)
So far, passing __CLASS__ to get_typed_ancestor() seems to the be the only solution thus far, as __CLASS__ will properly resolve to the class name in which the calling method is defined, not the class name of the instance invoking the calling method.
Note:
I've included at the end of this question an example showing the working __CLASS__ argument approach, and the failed static approach. If you wanna take a stab, perhaps use that as a start.
Question:
I've seen several "solutions" floating around that leverage debug_backtrace() to capture the calling class of a given method or function; these however are (as my quotation marks may suggest) not exactly solutions as far as I'm concerned, as debug_backtrace() used in this manner is a hack.
Rant aside, if this hack is the only answer, then hack I shall.
Anyways; I'm working on a set of classes that act as nodes in a bottom-to-top traversable tree. Here's a class hierarchy, simplified for brevity:
abstract class AbstractNode{}
abstract class AbstractComplexNode extends AbstractNode{}
class SimpleNode extends AbstractNode{}
class ComplexNodeOne extends AbstractComplexNode{}
class ComplexNodeTwo extends AbstractComplexNode{}
Nodes may have any concrete node (or null) as a parent. Looking at AbstractNode:
abstract class AbstractNode{
protected $_parent;
public function get_typed_ancestor(){
// here's where I'm working
}
public function get_parent(){
return $this->_parent;
}
}
The method get_typed_ancestor() is where I'm at.
From other methods in the extending classes, get_typed_ancestor() is called to find the closest _parent of the class type to which that method belongs. This is better illustrated with an example; given the previous AbstractNode definition:
abstract class AbstractComplexNode extends AbstractNode{
public function get_something(){
if(something_exists()){
return $something;
}
$node = $this->get_typed_ancestor();
if(null !== $node){
return $node->get_something();
}
}
}
The method get_typed_ancestor(), when called from the context of AbstractComplexNode::get_something(), will be looking for an object of type (or extending type) AbstractComplexNode -- in the case of this hierarchy, the possible concrete classes being ComplexNodeOne and ComplexNodeTwo.
Since AbstractComplexNode cannot be instantiated, a concrete instance such as ComplexNodeOne would be invoking get_something().
I need to highlight a point here; the search in this previous case must be for AbstractComplexNode in order to find the first instance of either ComplexNodeOne or ComplexNodeTwo. As will be explained in a moment, searching for and instanceof static will fail, as it may skip instances of sibling classes and/or their children.
The problem is, since there are situations where the calling class is abstract, and the calling method is inherited by (and thus is called from an instance of) a class such as ComplexNodeOne, searching for a parent that is an instanceofstatic doesn't work, as static is late-bound to the concrete ComplexNodeOne.
Now, I have a solution, but I don't like it:
abstract class AbstractNode{
public function get_typed_ancestor($class){
$node = $this;
while(null !== $node->_parent){
if($node->_parent instanceof $class){
return $node->_parent;
}
$node = $node->_parent;
}
return null;
}
}
abstract class AbstractComplexNode extends AbstractNode{
public function get_something(){
if(something_exists()){
return $something;
}
$node = $this->get_typed_ancestor(__CLASS__);
if(null !== $node){
return $node->get_something();
}
}
}
This appears to work, since __CLASS__ resolves to the class name of definition. Unfortunately, I tried using __CLASS__ as a default argument to get_typed_ancestor() with no success (though that was expected)
I'm considering leaving the $class argument as an optional regardless, but if it is at all possible to "implicitly" pass this data along to the method (in absence of the optional argument) that would be great.
Solutions/Failures:
Passing __CLASS__ from the calling method as an argument to get_typed_ancestor().
Works, but is not ideal as I'd like get_typed_ancestor() to resolve the calling class without being explicitly informed of it.
In the search loop, checking if($node->_parent instanceof static).
Doesn't work when the calling class inherits the calling method. It resolves to the concrete class in which the method is called, not the one in which is defined. This failure of course applies also to self and parent.
Use debug_backtrace() to capture $trace[1]['class'] and use that for the check.
Works, but is not ideal as it's a hack.
It's tricky discussing a hierarchical data structure and supporting class hierarchy without feeling like you're confusing your audience.
Example:
abstract class AbstractNode
{
protected $_id;
protected $_parent;
public function __construct($id, self $parent = null)
{
$this->_id = $id;
if(null !== $parent)
{
$this->set_parent($parent);
}
}
protected function get_typed_ancestor_by_class($class)
{
$node = $this;
while(null !== $node->_parent)
{
if($node->_parent instanceof $class)
{
return $node->_parent;
}
$node = $node->_parent;
}
return null;
}
public function get_typed_ancestor_with_static()
{
$node = $this;
while(null !== $node->_parent)
{
if($node->_parent instanceof static)
{
return $node->_parent;
}
$node = $node->_parent;
}
return null;
}
public function set_parent(self $parent)
{
$this->_parent = $parent;
}
}
class SimpleNode extends AbstractNode
{
}
abstract class AbstractComplexNode extends AbstractNode
{
public function test_method_class()
{
var_dump($this->get_typed_ancestor_by_class(__CLASS__));
}
public function test_method_static()
{
var_dump($this->get_typed_ancestor_with_static());
}
}
class ComplexNodeOne extends AbstractComplexNode
{
}
class ComplexNodeTwo extends AbstractComplexNode
{
}
$node_1 = new SimpleNode(1);
$node_2 = new ComplexNodeTwo(2, $node_1);
$node_3 = new SimpleNode(3, $node_2);
$node_4 = new ComplexNodeOne(4, $node_3);
$node_5 = new SimpleNode(5, $node_4);
$node_6 = new ComplexNodeTwo(6, $node_5);
// this call incorrectly finds ComplexNodeTwo ($node_2), skipping
// the instance of ComplexNodeOne ($node_4)
$node_6->test_method_static();
// object(ComplexNodeTwo)#2 (2) {
// ["_id":protected]=>
// int(2)
// ["_parent":protected]=>
// object(SimpleNode)#1 (2) {
// ["_id":protected]=>
// int(1)
// ["_parent":protected]=>
// NULL
// }
// }
// this call correctly finds ComplexNodeOne ($node_4) since it's
// looking for an instance of AbstractComplexNode, resolved from
// the passed __CLASS__
$node_6->test_method_class();
// object(ComplexNodeOne)#4 (2) {
// ["_id":protected]=>
// int(4)
// ["_parent":protected]=>
// object(SimpleNode)#3 (2) {
// ["_id":protected]=>
// int(3)
// ["_parent":protected]=>
// object(ComplexNodeTwo)#2 (2) {
// ["_id":protected]=>
// int(2)
// ["_parent":protected]=>
// object(SimpleNode)#1 (2) {
// ["_id":protected]=>
// int(1)
// ["_parent":protected]=>
// NULL
// }
// }
// }
// }
For solving the problem "to capture the calling class of a given method or function", simply pass the object that creates the instance in the constructor.
<?php
class A {
public function caller() {
$b = new B ($this);
$b->bar();
}
}
class B {
$whoClass = '';
public function __construct($who)
{
$this->whoClass = get_class($who);
}
public function bar($who) {
echo get_class($this->whoClass);
}
}
I have a parent object that I use for general CRUD in my applications - it has basic save & retrieve methods so I can don't have to reinclude them them in all my objects. Most of my child objects extend this base object. This has worked fine, but I'm finding a problem with retrieving a serialized child object. I use a "retrieve" method in the parent object that creates an instance of the child, then populates itself from the properties of the unserialized child - this means is can "self unserialize" the object.
Only problem is - if the child object has a protected or private property, the parent object can't read it, so it doesn't get picked up during retrieval.
So I'm looking either for a better way to "self unserialize" or a way to allow a parent object to "see" the protected properties - but only during the retrieval process.
Example of the code:
BaseObject {
protected $someparentProperty;
public function retrieve() {
$serialized = file_get_contents(SOME_FILENAME);
$temp = unserialize($serialized);
foreach($temp as $propertyName => $propertyValue) {
$this->$propertyName = $propertyValue;
}
}
public function save() {
file_put_contents(SOME_FILENAME, serialize($this));
}
}
class ChildObject extends BaseObject {
private $unretrievableProperty;
public setProp($val) {
$this->unretrivableProperty = $val;
}
}
$tester = new ChildObject();
$tester->setProp("test");
$tester->save();
$cleanTester = new ChildObject();
$cleanTester->retrieve();
// $cleanTester->unretrievableProperty will not be set
EDITED: Should have said "Private" not protected child properties.
try it like this:
abstract class ParentClass
{
protected abstract function GetChildProperty();
public function main()
{
$value = $this->GetChildProperty();
}
}
class ChildClass extends ParentClass
{
private $priv_prop = "somevalue";
protected function GetChildProperty()
{
return $this->priv_prop;
}
}
It doesn't seem that same class visibility policy applies to iherited/parent classes. The php documentation does not address this.
I would suggest that you declared the retrieve method static, and fetched the $cleanTester through a static call rather than your current "self unserialize" approach.
static function retrieve() {
$serialized = file_get_contents(SOME_FILENAME);
return unserialize($serialized);
}
[...]
$cleanTester = BaseObject::retrieve();
Or you could utilize the __get() method to access inaccessible properties... I believe this could be added to the BaseObject class and fetch protected properties from the child class. Since the same class visibility policy should apply to BaseObject you could define the __get() method private or protected.
BaseObject {
private function __get($propertyName) {
if(property_exists($this,$propertyName))
return $this->{$propertyName};
return null;
}
how about a getProperty() function in the child object that returns $this->unretrievableProperty
The best possible answer to fix this is to use reflections.
Example:
$_SESSION[''] = ''; // init
class Base {
public function set_proxy(){
$reflectionClass = new ReflectionClass($this);
$ar = $reflectionClass->getDefaultProperties();
!isset($ar['host']) or $_SESSION['host'] = $ar['host'];
}
}
class Son1 extends Base {
private $host = '2.2.2.2';
}
class Son2 extends Son1 {
}
$son1 = new Son1();
$son1->set_proxy();
var_dump($_SESSION); // array(2) { [""]=> string(0) "" ["host"]=> string(7) "2.2.2.2" }
unset($_SESSION);
$_SESSION[''] = ''; // init
$son2 = new Son2();
$son2->set_proxy();
var_dump($_SESSION); // array(1) { [""]=> string(0) "" }