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) "" }
Related
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.
I have a parent class that depends on whether child class are instantiated.
class GoogleApp {
protected $auth_token;
public function __construct($scopes) {
$this->auth_token = $scopes;
}
}
class Gmail extends GoogleApp {
public function __construct() {
print_r($this->auth_token);
}
}
$googleApp = new GoogleApp('gmail'); // Change the actual class for all child instances
$gmail = new Gmail();
The idea is that all the children use the same auth_token (which is generated on whether the child classes are used - as of now, I'm just manually adding them to whether I included them in my code). Since I have quite a few child classes (like Calendar or Drive), do I have to inject the parent into each child instance or is there an easier way?
If I understand your request correctly, you're pretty close, you just need to declare your property as static.
class FooParent
{
protected static $scope = null;
public function __construct($scope)
{
self::$scope = $scope;
}
public function getScope()
{
return self::$scope;
}
}
class FooChild extends FooParent
{
public function __construct()
{
if (self::$scope === null) {
throw new Exception('Must set scope first.');
}
}
}
$parent = new FooParent('foo');
$child = new FooChild();
echo $child->getScope(), "\n"; // prints "foo"
So I'd like to be able to call a method of an object from a created object as deep as I'd like.
For example
$test = new sampleObject;
$test2 = $test->createChild();
$test3 = $test2->createChild();
...
catch is, I need to be able to refer to a method from the topmost creator class.
so I have my main class
class sampleObject
{
public $tons, $of, $properties;
public function createChild()
{
$someVar = new childObject();
$this->otherMethod();
return $someVar();
}
public function otherMethod()
{
//Do some stuff
}
}
class childObject
{
public $child, $properties;
function createChild()
{
$someVar = new childObject();
//here is my issue
//I need to call a otherMethod from the creating class here but not static .
return $someVar;
}
}
Is this the wrong approach or is there a way to reference that creating class object.
I'd like to keep the created object's properties secluded from the creator class.
I thought about just passing the object, but I'd like to keep the same structure if possible as the creator class.
The best way I could find to do this was to pass the object that it is a part of to the sub-object.
so $test2 = $test->createChild($test);
and
class childObject
{
public $child, $properties, $parent;
function createChild()
{
$someVar = new childObject($parent);
$this->parent = $parent;
//here is my issue
//I need to call a otherMethod from the creating class here but not static .
return $someVar;
}
}
I have the following class tree:
class A /* Base class */
{
private/protected/public $state
}
class B extends A /* Auto generated class, not to be modified */
{
private $v
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
There is only one class A. There are multiple classes like class B, and all of those classes will have a subclass like C. Class B gets auto-generated and should not be modified.
I am storing objects of type(s) C in the session. What I want to do is to store some state information in every instance, just before PHP gets it serialised, and that will do something with it when it's unserialised. I want all this to be implemented in class A.
Considering, I need to use either __sleep() or Serializable interface. Using __sleep is out of the question, because of what the PHP manual says:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
Meaning that if I sleep an instance of class C, I'll loose the private variables declared in B. So I want to use Serializable, but for some reason, I simply can't get it to do what I want.
In essence, I would like the object to be serialised just as if I didn't implement any serialisation stuff myself, I just want to add information to $state right before it happens. I've tried covering all data with ReflectionObject->getProperties(), but I can't seem to find the right way to fetch and set the private values in class B to be serialised and unserialised.
How do I do this?
You can do this using the Reflection classes. You'll have to get the properties of the class itself and each of it's parent classes. Getting and setting the property values can be done using ReflectionProperty's getValue and setValue methods, combined with setAccessible to get access to private and protected properties. Combining those, I came up with the following code:
<?php
class A implements Serializable /* Base class */
{
protected $state;
public function serialize()
{
$this->state = "something";
return serialize($this->_getState());
}
public function unserialize($data)
{
$this->_setState(unserialize($data));
}
protected function _getState()
{
$reflClass = new ReflectionClass(get_class($this));
$values = array();
while ($reflClass != null)
{
foreach ($reflClass->getProperties() as $property)
{
if ($property->getDeclaringClass() == $reflClass)
{
$property->setAccessible(true);
$values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
}
}
$reflClass = $reflClass->getParentClass();
}
return $values;
}
protected function _setState($values)
{
foreach ($values as $_)
{
list($className, $propertyName, $propertyValue) = $_;
$property = new ReflectionProperty($className, $propertyName);
$property->setAccessible(true);
$property->setValue($this, $propertyValue);
}
}
}
class B extends A /* Auto generated class, not to be modified */
{
private $v;
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);
Which seems to do what you want.
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.