So I am working with a settings class that extends a base settings class that would be similar to a "global settings". There are several services and each service has its own settings class that extends the abstract base settings class. The abstract base settings class corresponds to settings that are shared between services. So first I will illustrate with an example below and then I will define the problem:
example:
abstract class BaseSettings {
protected $settingA;
protected $settingB;
}
class MyServiceSettings extends BaseSettings {
private $settingC;
public $settingD;
}
problem:
If I create a ReflectionClass instance like so..
$reflect = new ReflectionClass($this);
..from either the MyServiceSettings class or the BaseSettings class (because obviously you can't have an instance of an abstract class), $reflect->getProperties()
will always return the properties of both MyServiceSettings and the BaseSettings (and I guess that's appropriate because we're really working with a single concrete class)
Now I am sure I could create an empty class that extends the abstract BaseClass to figure out which properties go where (or just get rid of the abstract and create an instance of the BaseClass), but that seems rather messy, so I am wondering if there is, perhaps, a more elegant way to determine which properties belong to the parent class and which belong to the child class?
If you are curious why I am even doing this - I am serializing the settings to a .json file for the purposes of a recovery feature I added so that one can continue from the last atomic operation that last successfully completed. Why I have to do it this way - limitation of the environment.
I would get it like this:
$class = new ReflectionClass('Some_class_name');
$properties = array_filter($class->getProperties(), function($prop) use($class){
return $prop->getDeclaringClass()->getName() == $class->getName();
});
So basically get all properties and iterate through them checking if they were declared in class we reflected.
While the above will work to get only the settings for MyServiceSettings
, when you call this method from the BaseSettings
class, it will still return properties for the class that is extending it (so long as you are dealing with an instance of the BaseSettings
class where the BaseSettings
class has been extended by another class - regardless of the BaseSettings
class being abstract or concrete) at least when you refer to the base class as $this
from within the base class.
Perhaps there is a better work-around, but the one I found was to simply use
$reflectionClass = new ReflectionClass(get_parent_class($this));
just to illustrate with an example of how you could use this, here is an adaptation of the function I am using to deserialize the class and its base class(es):
// "attribute" to be used in doc comments if it should not be
// serialized / deserialized
// ( remember: /** ... */ doc comments must have two asterisks!! )
private $DoNotSerialize = "[@DoNotSerialize]";
protected function dehydrate(ReflectionClass $reflectionClass) {
// if you're using private or protected properties and need to use
// $property->setAccessible()
if(floatval(phpversion()) < 5.3) {
throw new Exception("requires PHP version 5.3 or greater");
}
clearstatcache();
$properties = array_filter(
$reflectionClass->getProperties(ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PUBLIC)
, function($prop)
use($reflectionClass) {
return $prop->getDeclaringClass()->getName() == $reflectionClass->getName();
}
);
$settings = null;
foreach($properties as $property) {
$comment = $property->getDocComment();
if( strpos($comment,$this->DoNotSerialize) !== false ){
continue;
}
if($property->isProtected()){
$property->setAccessible(TRUE);
}
if(isset($settings)) {
// implementation of array_push_associative
// can be found at http://php.net/manual/en/function.array-push.php
array_push_associative(
$settings
, array($property->getName() => $property->getValue($this))
);
}
else {
$settings = array($property->getName() => $property->getValue($this));
}
}
//implementation irrelevant here
writeToFile($reflectionClass->getName(), $settings);
if(get_parent_class($reflectionClass)) {
$this->dehydrate(get_parent_class($reflectionClass));
}
}