I am trying to isolate the attributes of an originating class in an included trait. IE The trait should make an array of the names of all the attributes of the class but not the attributes of the trait for use within the trait.
I have tried doing this by extending a class. I have tried using static methods as per PHP: Is it possible to get the name of the class using the trait from within a trait static method? and I am getting nowhere.
I am about to use known attributes in the trait and simply remove them from the attribute array (as I know their names). This is a rather ugly solution but it will work.
Anyone see a better way to do this?
trait FooTrait
{
public $classVariables;
public function classAttributes()
{
$callingClass = get_class($this);
$rawAttributes= $this->$classVariables = get_class_vars($callingClass);
var_dump($rawAttributes);
var_dump($callingClass);
return $rawAttributes;
}
public function info()
{
var_dump($this->classVariables);
}
// manipulate $this -> classVaribales to do generic database operations
}
class Mine
{
use FooTrait;
protected $attrib1;
protected $attrib2;
protected $attrib3;
}
$needed = new Mine;
$needed->classAttributes();
$needed->info();
OUTPUT is attribute 1,2,3 and bar. How do I get just attribute 1, 2, 3?
EDIT: I edited a couple of attributes to try and make it more comprehensible.
UPDATE: This does not work if trait attributes are protected or private. As traits should not be directly referenced ... bit of a deal breaker.
The only way I could find to get the attributes of the trait WITHOUT those of the calling class was to name it as a literal. BUT that limits the scope and so cannot see the private and protected attributes.
I am giving up at this point and will use an array of the names of attributes used in the trait. Not a big problem just massively inelegant.
class ThisClass {
use ThisTrait;
public $classAttribute1 = 3;
public $classAttribute2 = 3;
public $classAttribute3 = 3;
}
trait ThisTrait {
public $traitTrait1 = 3;
public $traitTrait2 = 3;
public $traitTrait3 = 3;
public function classAttributes (){
$traitAttributes = get_class_vars("ThisTrait"); //NB String not variable
$traitAttributes = array_keys ($traitAttributes);
$className = get_class($this); //NB Var = gets class where this called
$classAttributes = get_class_vars($className);
$classAttributes = array_keys($classAttributes);
$classOnly = array_diff($classAttributes, $traitAttributes);
return $classOnly;
}
}
$thisClass = new ThisClass ();
$result = $thisClass -> classAttributes();
var_dump($result);
=========================================
array (size=3)
0 => string 'classAttribute1' (length=15)
1 => string 'classAttribute2' (length=15)
2 => string 'classAttribute3' (length=15)
Related
I'm trying to make some very rudimental database mapping conversion where I'm fetching data from the database and then trying to convert that array to an instance of an arbitrary class. This should be dynamic so that I can use the same function no matter the output object class/properties.
Let's say I have CASE1:
$student = [
'name' => 'Jhon',
'surname' => 'Wick',
'age' => 40
];
class Student{
private string $name;
private string $surname;
private int $age;
... getter and setters
}
CASE2:
$car = [
'manufacturer' => 'Toyota',
'model' => 'one of their model',
'commercialName' => 'the name'
];
class Car{
private $manufacturer;
private $model;
private $commercialName;
// Getter and Setter
}
And I want something that transforms the $student array var to a Student instance or the $car to Car. How can I do that?
I know I can do that using something like:
$funcName = 'get' . ucfirst($camelCaseName);
if (method_exists($this, $funcName)) {
$funcValue = $this->$funcName();
}
But I'm searching for something a little more modern like using Reflection.
What is the best approach to this? What could be an efficient solution?
To give further info, this is needed for an extension of the WordPress $wpdb object. If possible I wouldn't like to use public class properties because I may need to actually call the class setter in some case to change some other class value. Let's say something like giving birth date should calculate age
As I stated out in the comments already, all that you need is the process of hydration. The boys and girls from Laminas got a good maintained package called laminas/laminas-hydrator which can do the job for you.
An easy example:
<?php
declare(strict_types=1);
namespace Marcel;
use Laminas\Hydrator\ClassMethodsHydrator;
use Marcel\Model\MyModel;
$hydrator = new ClassMethodsHydrator();
// hydrate arry data into an instance of your entity / data object
// using setter methods
$object = $hydrator->hydrate([ ... ], new MyModel());
// vice versa using the getter methods
$array = $hydrator->extract($object);
Your approach is not so wrong. It is at least going in the right direction. In my eyes you should not use private properties. What kind of advantage do you expect from using private properties? They only bring disadvantages. Think of extensions of your model. Protected properties do the same job for just accessing the properties via getters and setters. Protected properties are much more easy to handle.
<?php
declare(strict_types=1);
namespace Marcel;
class MyDataObject
{
public ?string $name = null;
public ?int $age = null;
public function getName(): ?string
{
return $name;
}
public function setName(?string $name): void
{
$this->name = $name;
}
public function getAge(): ?int
{
return $this->age;
}
public function setAge(?int $age): void
{
$this->age = $age;
}
}
class MyOwnHydrator
{
public function hydrate(array $data, object $object): object
{
foreach ($data as $property => $value) {
// beware of naming your properties in the data array the right way
$setterName = 'set' . ucfirst($property);
if (is_callable([$object, $setterName])) {
$object->{$setterName}($value);
}
}
return $object;
}
}
// example
$data = [
'age' => 43,
'name' => 'Marcel',
'some_other_key' => 'blah!', // won 't by hydrated
];
$hydrator = new MyOwnHydrator();
$object = new MyDataObject();
$object = $hydrator->hydrate($data, $object);
This is the simplest hydrator you can get. It iterates through the data array, creates setter method names and checks if they are callable. If so the value will be hydrated into the object by calling the setter method. At the end you 'll get a hydrated object.
But beware. There are some stumbling blocks with this solution that need to be considered. The keys of your data array must be named exactly like the properties of your data object or entity. You have to use some naming strategies, when you want to use underscore separated keys in your array but your object properties are camel cased, e.g. Another problem could be type hints. What if the example had a birthday and only accepts DateTime instances and your data array only contains the birhtday as string "1979-12-19"? What if your data array contains sub arrays, that should be hydrated in n:m relations?
All that is done already in the menetiond laminas package. If you don 't need all that fancy stuff, follow your first thought and build your own hydrator. ;)
I would say drop setters/getters and use readonly properties:
class Car{
public function __construct(
public readonly string $manufacturer,
public readonly string $model,
public readonly string $commercialName,
);
}
...
new Car(...$car);
I have a object having some protected property that I want to get and set. The object looks like
Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] =>
[_description:protected] =>
[_disableLoadDefaultDecorators:protected] =>
[_errorMessages:protected] => Array
(
)
[_errors:protected] => Array
(
)
[_isErrorForced:protected] =>
[_label:protected] => Current City
[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)
I want to get value property of the object. When I try $obj->_value or $obj->value it generates error. I searched and found the solution to use PHP Reflection Class. It worked on my local but on server PHP version is 5.2.17 So I cannot use this function there. So any solution how to get such property?
Here's the really simple example (with no error checking) of how to use ReflectionClass:
function accessProtected($obj, $prop) {
$reflection = new ReflectionClass($obj);
$property = $reflection->getProperty($prop);
$property->setAccessible(true);
return $property->getValue($obj);
}
I know you said you were limited to 5.2, but that was 2 years ago, 5.5 is the oldest supported version and I'm hoping to help people with modern versions.
Object can be typecasted into (associative) array and the protected members have keys prefixed with chr(0).'*'.chr(0) (see #fardelian's comment here). Using this undocummented feature you can write an "exposer":
function getProtectedValue($obj, $name) {
$array = (array)$obj;
$prefix = chr(0).'*'.chr(0);
return $array[$prefix.$name];
}
Alternatively, you can parse the value from serialized string, where (it seems) protected members have the same prefix.
This works in PHP 5.2 without the overhead of ReflectionClass. However, there are reasons why some property is protected and hidden from client code. The reading or writing can make the data inconsistent or the author provides some other way to expose it in effort to make the interface as lean as possible. When there are reasons to read the protected property directly, the then-correct approach was to implement __get() magic method, so always check if there is any and see what it does. This counter intuitive lookup was finally solved in PHP 8.1 with readonly properties.
Since PHP 8.0, there also attributes metadata accessible by ReflectionClass, make sure to check them also before performing attempts to break into protected members. Attributes superseded "Annotations"1, so check them, too.
1: annotations are a very nasty surprise to client coders: they parse comments to add crazy fancy black-box useless confusing functionality, should not be used anymore, but they still exist
That's what "protected" is meant for, as the Visibility chapter explains:
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
If you need to access the property from outside, pick one:
Don't declare it as protected, make it public instead
Write a couple of functions to get and set the value (getters and setters)
If you don't want to modify the original class (because it's a third-party library you don't want to mess) create a custom class that extends the original one:
class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}
... and add your getter/setter there.
If you want to tinker with a class without adding getters and setters....
PHP 7 adds a call($obj) method (faster than old bindTo) on closures allowing you to call a function so the $this variable will act just as it would within a class -with full permissions.
//test class with restricted properties
class test{
protected $bar="protected bar";
private $foo="private foo";
public function printProperties(){
echo $this->bar."::".$this->foo;
}
}
$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
$this->bar="I changed bar";
$this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0
For PHP 7.4+, we can use an Arrow Function and the Closure::call to access private and protected members using just one small line:
PHP 7.4+
Retrieving protected/private members:
class Test {
protected $data = 'Protected variable!';
}
// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);
Altering protected/private members:
class Test {
protected $data = 'Testing';
}
$test = new Test;
(fn() => $this->data = "New Data!")->call($test);
// Will output "New Data!"
echo (fn() => $this->data)->call($test);
Of course, we can use a normal Closure function if we want to alter/use multiple members:
class Test {
protected $data = 'Data!';
}
$test = new Test;
(function() {
$this->new_data = "New {$this->data}";
})->call($test);
// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);
If you cannot modify the original class and extending it is not an option either, you can use the ReflectionProperty interface.
The phptoolcase library has a handy method for this:
$value = PtcHandyMan::getProperty($your_object , 'propertyName');
Static property from a singleton class:
$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');
You can find the tool here: http://phptoolcase.com/guides/ptc-hm-guide.html
$a=json_encode((array)$obj);
$b=(array)json_decode(str_replace('\u0000*\u0000','',$a));
echo($b['value']);
What I like to do is declare every property that would be writable from outside as public. The properties that you want to be visible for the outside world but not writable you should declare as protected and write __get() magic method so you can read them. Example:
/**
* Class Test
*
* #property int $protected
*
*/
class Test
{
private const READABLE = ['protected'];
protected $protected = 1;
public $public = 2;
public function __get($property)
{
//if you want to read every protected or private
return $this->$property ?? null;
//if you want only some protected and private values to be readable
if (in_array($property, self::READABLE)) {
return $this->$property;
}
}
}
$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2
$test->protected = 3; //outputs error - protected property
The best would be to have property declaration like:
public readonly $protected = 1; //only readable from the outside
public $public = 2; //readable and writable from the outside
but no such syntax exists yet(or... at least I don't know about it). P.S. you should declare the protected/private properties that will be readable in the Class DockBlock as shown, so you can autocomplete them, otherwise you will be able to access them, but your IDE won't recognize them on autocomplete when you are writing code.
I am new to laravel. I want to change table name to given string in the constructor in model. The code below is what I tried, but it seems not working.
Any suggestion or advice would be appreciated.
Thank you
Model
class Custom extends Model
{
protected $guarded = ['id', 'ct'];
const UPDATED_AT = null;
const CREATED_AT = 'ct';
public function __construct(array $attributes = [], string $tableName = null) {
parent::__construct($attributes);
$this->setTable($tableName);
}
}
Controller
$tableName = 'some string';
$custom = new Custom([], $tableName);
$result = $custom->create($data);
You are only passing one parameter on the constructor function, but it expects 2 params. So, either pass two param, or make the constructor like this-
public function __construct($table = null, $attr = [])
{
$this->setTable($table);
parent::__construct($attributes);
}
But I don't understand why would you do that? The standard practice is create one model per table. You should do that either.
There is no Model::create method. This is magical shenanigans via Model::__call. Calling $custom->create(...) will forward the call to Builder::create which calls Builder::newModelInstance, which in turns calls Model::newInstance. This code does not know about the original model at all, it only knows about the $data attributes. (It is usually called in a static context, like Model::create(...), without an origin instance.)
The easiest workaround for you would be to create a new class deriving from Custom, and have it declare the $table property. This would require one model per table.
class Custom2 extends Custom {
public $table = 'some string';
}
I have a object having some protected property that I want to get and set. The object looks like
Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] =>
[_description:protected] =>
[_disableLoadDefaultDecorators:protected] =>
[_errorMessages:protected] => Array
(
)
[_errors:protected] => Array
(
)
[_isErrorForced:protected] =>
[_label:protected] => Current City
[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)
I want to get value property of the object. When I try $obj->_value or $obj->value it generates error. I searched and found the solution to use PHP Reflection Class. It worked on my local but on server PHP version is 5.2.17 So I cannot use this function there. So any solution how to get such property?
Here's the really simple example (with no error checking) of how to use ReflectionClass:
function accessProtected($obj, $prop) {
$reflection = new ReflectionClass($obj);
$property = $reflection->getProperty($prop);
$property->setAccessible(true);
return $property->getValue($obj);
}
I know you said you were limited to 5.2, but that was 2 years ago, 5.5 is the oldest supported version and I'm hoping to help people with modern versions.
Object can be typecasted into (associative) array and the protected members have keys prefixed with chr(0).'*'.chr(0) (see #fardelian's comment here). Using this undocummented feature you can write an "exposer":
function getProtectedValue($obj, $name) {
$array = (array)$obj;
$prefix = chr(0).'*'.chr(0);
return $array[$prefix.$name];
}
Alternatively, you can parse the value from serialized string, where (it seems) protected members have the same prefix.
This works in PHP 5.2 without the overhead of ReflectionClass. However, there are reasons why some property is protected and hidden from client code. The reading or writing can make the data inconsistent or the author provides some other way to expose it in effort to make the interface as lean as possible. When there are reasons to read the protected property directly, the then-correct approach was to implement __get() magic method, so always check if there is any and see what it does. This counter intuitive lookup was finally solved in PHP 8.1 with readonly properties.
Since PHP 8.0, there also attributes metadata accessible by ReflectionClass, make sure to check them also before performing attempts to break into protected members. Attributes superseded "Annotations"1, so check them, too.
1: annotations are a very nasty surprise to client coders: they parse comments to add crazy fancy black-box useless confusing functionality, should not be used anymore, but they still exist
That's what "protected" is meant for, as the Visibility chapter explains:
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
If you need to access the property from outside, pick one:
Don't declare it as protected, make it public instead
Write a couple of functions to get and set the value (getters and setters)
If you don't want to modify the original class (because it's a third-party library you don't want to mess) create a custom class that extends the original one:
class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}
... and add your getter/setter there.
If you want to tinker with a class without adding getters and setters....
PHP 7 adds a call($obj) method (faster than old bindTo) on closures allowing you to call a function so the $this variable will act just as it would within a class -with full permissions.
//test class with restricted properties
class test{
protected $bar="protected bar";
private $foo="private foo";
public function printProperties(){
echo $this->bar."::".$this->foo;
}
}
$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
$this->bar="I changed bar";
$this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0
For PHP 7.4+, we can use an Arrow Function and the Closure::call to access private and protected members using just one small line:
PHP 7.4+
Retrieving protected/private members:
class Test {
protected $data = 'Protected variable!';
}
// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);
Altering protected/private members:
class Test {
protected $data = 'Testing';
}
$test = new Test;
(fn() => $this->data = "New Data!")->call($test);
// Will output "New Data!"
echo (fn() => $this->data)->call($test);
Of course, we can use a normal Closure function if we want to alter/use multiple members:
class Test {
protected $data = 'Data!';
}
$test = new Test;
(function() {
$this->new_data = "New {$this->data}";
})->call($test);
// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);
If you cannot modify the original class and extending it is not an option either, you can use the ReflectionProperty interface.
The phptoolcase library has a handy method for this:
$value = PtcHandyMan::getProperty($your_object , 'propertyName');
Static property from a singleton class:
$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');
You can find the tool here: http://phptoolcase.com/guides/ptc-hm-guide.html
$a=json_encode((array)$obj);
$b=(array)json_decode(str_replace('\u0000*\u0000','',$a));
echo($b['value']);
What I like to do is declare every property that would be writable from outside as public. The properties that you want to be visible for the outside world but not writable you should declare as protected and write __get() magic method so you can read them. Example:
/**
* Class Test
*
* #property int $protected
*
*/
class Test
{
private const READABLE = ['protected'];
protected $protected = 1;
public $public = 2;
public function __get($property)
{
//if you want to read every protected or private
return $this->$property ?? null;
//if you want only some protected and private values to be readable
if (in_array($property, self::READABLE)) {
return $this->$property;
}
}
}
$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2
$test->protected = 3; //outputs error - protected property
The best would be to have property declaration like:
public readonly $protected = 1; //only readable from the outside
public $public = 2; //readable and writable from the outside
but no such syntax exists yet(or... at least I don't know about it). P.S. you should declare the protected/private properties that will be readable in the Class DockBlock as shown, so you can autocomplete them, otherwise you will be able to access them, but your IDE won't recognize them on autocomplete when you are writing code.
I have a object having some protected property that I want to get and set. The object looks like
Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] =>
[_description:protected] =>
[_disableLoadDefaultDecorators:protected] =>
[_errorMessages:protected] => Array
(
)
[_errors:protected] => Array
(
)
[_isErrorForced:protected] =>
[_label:protected] => Current City
[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)
I want to get value property of the object. When I try $obj->_value or $obj->value it generates error. I searched and found the solution to use PHP Reflection Class. It worked on my local but on server PHP version is 5.2.17 So I cannot use this function there. So any solution how to get such property?
Here's the really simple example (with no error checking) of how to use ReflectionClass:
function accessProtected($obj, $prop) {
$reflection = new ReflectionClass($obj);
$property = $reflection->getProperty($prop);
$property->setAccessible(true);
return $property->getValue($obj);
}
I know you said you were limited to 5.2, but that was 2 years ago, 5.5 is the oldest supported version and I'm hoping to help people with modern versions.
Object can be typecasted into (associative) array and the protected members have keys prefixed with chr(0).'*'.chr(0) (see #fardelian's comment here). Using this undocummented feature you can write an "exposer":
function getProtectedValue($obj, $name) {
$array = (array)$obj;
$prefix = chr(0).'*'.chr(0);
return $array[$prefix.$name];
}
Alternatively, you can parse the value from serialized string, where (it seems) protected members have the same prefix.
This works in PHP 5.2 without the overhead of ReflectionClass. However, there are reasons why some property is protected and hidden from client code. The reading or writing can make the data inconsistent or the author provides some other way to expose it in effort to make the interface as lean as possible. When there are reasons to read the protected property directly, the then-correct approach was to implement __get() magic method, so always check if there is any and see what it does. This counter intuitive lookup was finally solved in PHP 8.1 with readonly properties.
Since PHP 8.0, there also attributes metadata accessible by ReflectionClass, make sure to check them also before performing attempts to break into protected members. Attributes superseded "Annotations"1, so check them, too.
1: annotations are a very nasty surprise to client coders: they parse comments to add crazy fancy black-box useless confusing functionality, should not be used anymore, but they still exist
That's what "protected" is meant for, as the Visibility chapter explains:
Members declared protected can be accessed only within the class itself and by inherited and parent classes.
If you need to access the property from outside, pick one:
Don't declare it as protected, make it public instead
Write a couple of functions to get and set the value (getters and setters)
If you don't want to modify the original class (because it's a third-party library you don't want to mess) create a custom class that extends the original one:
class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}
... and add your getter/setter there.
If you want to tinker with a class without adding getters and setters....
PHP 7 adds a call($obj) method (faster than old bindTo) on closures allowing you to call a function so the $this variable will act just as it would within a class -with full permissions.
//test class with restricted properties
class test{
protected $bar="protected bar";
private $foo="private foo";
public function printProperties(){
echo $this->bar."::".$this->foo;
}
}
$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
$this->bar="I changed bar";
$this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0
For PHP 7.4+, we can use an Arrow Function and the Closure::call to access private and protected members using just one small line:
PHP 7.4+
Retrieving protected/private members:
class Test {
protected $data = 'Protected variable!';
}
// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);
Altering protected/private members:
class Test {
protected $data = 'Testing';
}
$test = new Test;
(fn() => $this->data = "New Data!")->call($test);
// Will output "New Data!"
echo (fn() => $this->data)->call($test);
Of course, we can use a normal Closure function if we want to alter/use multiple members:
class Test {
protected $data = 'Data!';
}
$test = new Test;
(function() {
$this->new_data = "New {$this->data}";
})->call($test);
// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);
If you cannot modify the original class and extending it is not an option either, you can use the ReflectionProperty interface.
The phptoolcase library has a handy method for this:
$value = PtcHandyMan::getProperty($your_object , 'propertyName');
Static property from a singleton class:
$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');
You can find the tool here: http://phptoolcase.com/guides/ptc-hm-guide.html
$a=json_encode((array)$obj);
$b=(array)json_decode(str_replace('\u0000*\u0000','',$a));
echo($b['value']);
What I like to do is declare every property that would be writable from outside as public. The properties that you want to be visible for the outside world but not writable you should declare as protected and write __get() magic method so you can read them. Example:
/**
* Class Test
*
* #property int $protected
*
*/
class Test
{
private const READABLE = ['protected'];
protected $protected = 1;
public $public = 2;
public function __get($property)
{
//if you want to read every protected or private
return $this->$property ?? null;
//if you want only some protected and private values to be readable
if (in_array($property, self::READABLE)) {
return $this->$property;
}
}
}
$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2
$test->protected = 3; //outputs error - protected property
The best would be to have property declaration like:
public readonly $protected = 1; //only readable from the outside
public $public = 2; //readable and writable from the outside
but no such syntax exists yet(or... at least I don't know about it). P.S. you should declare the protected/private properties that will be readable in the Class DockBlock as shown, so you can autocomplete them, otherwise you will be able to access them, but your IDE won't recognize them on autocomplete when you are writing code.