Get class prop name in php - php

I'm not good at php OOP.
class Example{
public $name;
public $age;
}
$example = new Example();
I would like to get the property name as string, like-
echo get_property_name($example->name); //should echo 'name'
//OR,
echo $example->name->toString(); //should echo 'name'
Please note that, I don't want to write the property name in a string or variable like-
$property = $class->getProperty('name');
I don't want to get the value of property, I want to get the name of the property as a string.
Is it possible in php?

You can build a helper function with get_object_vars(). Because you already know the var-name, the function only checks, if this exists in the object and returns the var as string:
function get_property_name($oObject, $sString) {
$aObjectVars = get_object_vars($oObject);
if( isset($aObjectVars[$sString]) ) {
return $sString;
}
return false; // object var not exists
}

In PHP you can introspect a class, function, or... with ReflectionClass:
<?php
class Example
{
public $name;
public $age;
}
$example = new Example();
$ref = new ReflectionClass($example);
$props = $ref->getProperties();
foreach($props as $prop) {
var_dump($prop->name);
}
The output:
string(4) "name"
string(3) "age"

one other option would be a trait with following methods.
public function toArray(): array
{
return (array) $this;
}
public function properties(): array
{
return array_keys($this->toArray());
}
We do not know your use case. If you write a DTO, Reflection might be your desired way. If you write a model with some extra sugar, you could store all attributes in an array to load and edit them. Probably two arrays so you could compare edited and loaded values. with __isset, __get, __set you can always preload attributes.

Related

Query array or object property?

I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private

Is it possible to make a class' variable read-only in PHP? [duplicate]

Is there a way to make a read-only property of an object in PHP? I have an object with a couple arrays in it. I want to access them as I normally would an array
echo $objObject->arrArray[0];
But I don't want to be able to write to those arrays after they're constructed. It feels like a PITA to construct a local variable:
$arrArray = $objObject->getArray1();
echo $arrArray[0];
And anyways, while it keeps the array in the object pristine, it doesn't prevent me from re-writing the local array variable.
Well, the question is where do you want to prevent writing from?
The first step is making the array protected or private to prevent writing from outside of the object scope:
protected $arrArray = array();
If from "outside" of the array, a GETTER will do you fine. Either:
public function getArray() { return $this->arrArray; }
And accessing it like
$array = $obj->getArray();
or
public function __get($name) {
return isset($this->$name) ? $this->$name : null;
}
And accessing it like:
$array = $obj->arrArray;
Notice that they don't return references. So you cannot change the original array from outside the scope of the object. You can change the array itself...
If you really need a fully immutable array, you could use a Object using ArrayAccess...
Or, you could simply extend ArrayObject and overwrite all of the writing methods:
class ImmutableArrayObject extends ArrayObject {
public function append($value) {
throw new LogicException('Attempting to write to an immutable array');
}
public function exchangeArray($input) {
throw new LogicException('Attempting to write to an immutable array');
}
public function offsetSet($index, $newval) {
throw new LogicException('Attempting to write to an immutable array');
}
public function offsetUnset($index) {
throw new LogicException('Attempting to write to an immutable array');
}
}
Then, simply make $this->arrArray an instance of the object:
public function __construct(array $input) {
$this->arrArray = new ImmutableArrayObject($input);
}
It still supports most array like usages:
count($this->arrArray);
echo $this->arrArray[0];
foreach ($this->arrArray as $key => $value) {}
But if you try to write to it, you'll get a LogicException...
Oh, but realize that if you need to write to it, all you need to do (within the object) is do:
$newArray = $this->arrArray->getArrayCopy();
//Edit array here
$this->arrArray = new ImmutableArrayObject($newArray);
If you're using PHP 5+ you can do it with __set() and __get() methods.
You have to define how they work but should do just this.
Edit an example would be like this.
class Example {
private $var;
public function __get($v) {
if (is_array($v)) {
foreach () {
// handle it here
}
} else {
return $this->$v;
}
}
}
This might not be the "best" way of doing it but it'll work depending on what you need
If defined, the magic functions __get() and __set() will be called whenever a non-existing or private property is accessed. This can be used to create "get" and "set" methods for private properties, and for instance make them read-only or manipulate the data when stored or retrieved in it.
For instance:
class Foo
{
private $bar = 0;
public $baz = 4; // Public properties will not be affected by __get() or __set()
public function __get($name)
{
if($name == 'bar')
return $this->bar;
else
return null;
}
public function __set($name, $value)
{
// ignore, since Foo::bar is read-only
}
}
$myobj = new Foo();
echo $foo->bar; // Output is "0"
$foo->bar = 5;
echo $foo->bar; // Output is still "0", since the variable is read-only
See also the manual page for overloading in PHP.
For PHP 8.1+, you can use readonly properties:
class Test
{
public readonly array $arrArray;
public function __construct()
{
$this->arrArray = [1, 2, 3];
}
}
$test = new Test();
var_dump($test->arrArray); // OK
$test->arrArray = [4, 5, 6]; // Error
in the class, do this:
private $array;
function set_array($value) {
$this->array = $value;
}
then you just set like this:
$obj->set_array($new_array);

Can I freely set members in php?

Can I freely assign something to non-existent or not-known-existent members in php? Is there any difference between member names and associative array index?
I there any difference beteween
$a = array();
$a['foo'] = 'something';
and
$a->foo = 'something';
If there is a difference, then how can I create "empty" object and add members dynamically to it?
You are mixing Arrays (which are bags/containers for data) and Objects (which are wrappings for data with semantic meaning and functionality).
Array Access
The first is correct since you are using an Array which acts like a HashTable or Dictionary in other languages.
$a = array(); // create an empty "box"
$a['foo'] = 'something'; // add something to this array
Object Access
The second is an Object access. You would use something like this:
class Foo {
public $foo;
}
$a = new Foo();
$a->foo = 'something';
Although the better usage in that case is to use a setter/getter approach like this.
class Foo {
private $foo;
public function setFoo($value) {
$this->foo = $value;
}
public function getFoo() {
return $this->foo;
}
}
$a = new Foo();
$a->setFoo('something');
var_dump($a->getFoo());
PHP Magic
However there is still an option to use PHPs Magic Methods to create a behavior like you describe it. Nevertheless this should be considers not the usual way of storing data to a object since this lead to errors and gives you a much harder time with (unit) testing.
class Foo {
private $data = array();
public function __set($key, $value) {
$this->data[$key] = $value;
}
public function __get($key) {
return $this->data[$key];
}
}
$a = new Foo();
$a->foo = 'something'; // this will call the magic __set() method
var_dump($a->foo) // this will call the magic __get() method
This hopefully did help you solving your problem.
If you want to assign arbitrary members to an object as you do on an associative array, you'll probably want to look into PHP's magic property overloading.
Here's an example class that will let you just assign and retrieve variables (mostly taken from the PHP documentation):
<?php
class PropertyTest
{
/** Location for overloaded data. */
private $data = array();
public function __set($key, $value) {
$this->data[$key] = $value;
}
public function __get($key) {
return $this->data[$key];
}
}
// sample use:
$a = new PropertyTest();
$a->foo = "bar";
echo $a->foo; // will print "bar"
?>
You can create an empty class object and afterwards add properties to it, e.g.:
<?php
$myObject = new StdClass();
$myObject->id = 1;
$myObject->name = "Franky";
$myObject->url = "http://www.google.com";
var_dump($myObject);
...this should produce
object(stdClass)#1 (3) { ["id"]=> int(1) ["name"]=> string(6) "Franky" ["url"]=> string(21) "http://www.google.com" }
Personally, I prefer using object classes instead of arrays.

get the name of the object calling one of its methods

is it possible to get the name of the object which calls one of its methods.
scenario:
I have class A. I instantiate 2 objects of that class. If one object calls a method, is it possible to retrieve the name of the object which called it?
EDIT:
class Property() {
public function __call($name, $atts) {
if ($name === 'foo') {
//I want to differ Between Color and Position
}
}
}
$Color = new Property();
$Position = new Property();
$Color->foo();
$Position->foo();
Add a name element to your object:
class ObJA {
$this->name;
function __construct($name){
$this->name = $name;
}
function getName(){ return $this->name; }
}
On object create:
$a = new ObJA('a');
$b = new ObJA('is b');
echo $a->getName(); //`a`
echo $b->getName(); //`is b`
You can always use get_class(), but the name is not going to change simply by creating two or more instances of the object. Neal's solution will work but it doesn't actually change the name of the class, and begs the question: Why do you need it?

read-only properties in PHP?

Is there a way to make a read-only property of an object in PHP? I have an object with a couple arrays in it. I want to access them as I normally would an array
echo $objObject->arrArray[0];
But I don't want to be able to write to those arrays after they're constructed. It feels like a PITA to construct a local variable:
$arrArray = $objObject->getArray1();
echo $arrArray[0];
And anyways, while it keeps the array in the object pristine, it doesn't prevent me from re-writing the local array variable.
Well, the question is where do you want to prevent writing from?
The first step is making the array protected or private to prevent writing from outside of the object scope:
protected $arrArray = array();
If from "outside" of the array, a GETTER will do you fine. Either:
public function getArray() { return $this->arrArray; }
And accessing it like
$array = $obj->getArray();
or
public function __get($name) {
return isset($this->$name) ? $this->$name : null;
}
And accessing it like:
$array = $obj->arrArray;
Notice that they don't return references. So you cannot change the original array from outside the scope of the object. You can change the array itself...
If you really need a fully immutable array, you could use a Object using ArrayAccess...
Or, you could simply extend ArrayObject and overwrite all of the writing methods:
class ImmutableArrayObject extends ArrayObject {
public function append($value) {
throw new LogicException('Attempting to write to an immutable array');
}
public function exchangeArray($input) {
throw new LogicException('Attempting to write to an immutable array');
}
public function offsetSet($index, $newval) {
throw new LogicException('Attempting to write to an immutable array');
}
public function offsetUnset($index) {
throw new LogicException('Attempting to write to an immutable array');
}
}
Then, simply make $this->arrArray an instance of the object:
public function __construct(array $input) {
$this->arrArray = new ImmutableArrayObject($input);
}
It still supports most array like usages:
count($this->arrArray);
echo $this->arrArray[0];
foreach ($this->arrArray as $key => $value) {}
But if you try to write to it, you'll get a LogicException...
Oh, but realize that if you need to write to it, all you need to do (within the object) is do:
$newArray = $this->arrArray->getArrayCopy();
//Edit array here
$this->arrArray = new ImmutableArrayObject($newArray);
If you're using PHP 5+ you can do it with __set() and __get() methods.
You have to define how they work but should do just this.
Edit an example would be like this.
class Example {
private $var;
public function __get($v) {
if (is_array($v)) {
foreach () {
// handle it here
}
} else {
return $this->$v;
}
}
}
This might not be the "best" way of doing it but it'll work depending on what you need
If defined, the magic functions __get() and __set() will be called whenever a non-existing or private property is accessed. This can be used to create "get" and "set" methods for private properties, and for instance make them read-only or manipulate the data when stored or retrieved in it.
For instance:
class Foo
{
private $bar = 0;
public $baz = 4; // Public properties will not be affected by __get() or __set()
public function __get($name)
{
if($name == 'bar')
return $this->bar;
else
return null;
}
public function __set($name, $value)
{
// ignore, since Foo::bar is read-only
}
}
$myobj = new Foo();
echo $foo->bar; // Output is "0"
$foo->bar = 5;
echo $foo->bar; // Output is still "0", since the variable is read-only
See also the manual page for overloading in PHP.
For PHP 8.1+, you can use readonly properties:
class Test
{
public readonly array $arrArray;
public function __construct()
{
$this->arrArray = [1, 2, 3];
}
}
$test = new Test();
var_dump($test->arrArray); // OK
$test->arrArray = [4, 5, 6]; // Error
in the class, do this:
private $array;
function set_array($value) {
$this->array = $value;
}
then you just set like this:
$obj->set_array($new_array);

Categories