Appropriate use case of __get in daily life programming - php

I am in learning phase of OOP and PHP. Below is how i implemented __get method. It is working fine but i don't understand why to use it. Since in my example i set the property to protected deliberately so that i can't be accessed via outside class. Then what is the purpose of __get then ?
class Magic {
protected $name = 'John';
public $age = 26;
public $new_name;
public function __get($key){
$this->new_name = $key;
}
public function get_new_name(){
return $this->new_name. " is my friend";
}
}
$person = new Magic();
$person->Alan;
echo $person->get_new_name();

There is no valid reason I would have thought of that you would use __get() with a protected string, protected arrays would be useful, but not strings. The example you provided works, but it isn't going to be the best code for others to understand or for use with IDEs (code editors).
Since you don't seem to understand what I was saying, here is an example of a quick database script.
Let's say you want to insert a row in the database using an ORM-like class. You would do something like:
Class Person {
protected $fields = array();
public function setField($name, $value) {
$this->fields[$name] = $value;
}
public function getField($name) {
return $this->fields[$name];
}
public function save() {
Database::insert($table, $fields); // Generic function to insert the row, don't worry about this.
}
}
Now in this instance you could do:
$person = new Person();
$person->setField('name', 'Bob');
$person->setField('age', '30');
$person->save();
echo $person->getField('name'); // Echoes Bob
Overloading
Now to change this, we could use overloading with __set() instead of setField() and __get() instead of getField():
Class Person {
protected $fields = array();
public function __set($name, $value) {
$this->fields[$name] = $value;
}
public function __get($name) {
return $this->fields[$name];
}
public function save() {
Database::insert($table, $fields);
}
}
Now in this instance you could do:
$person = new Person();
$person->name = 'Bob';
$person->age = '30';
$person->save();
echo $person->name; // Echoes Bob
Hopefully this gives you an easy example of how overloading can work. We don't want to declare the properties $name and $age because we want to use those properties to build the $fields array which is later used in the insert.

For example you can use the __get method for obtain the value of an array. In this case you can have a dynamic number of class variable.
public function __get($key){
return $this->property[$key];
}

Related

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);

How to control json_encode behavior?

Is there any way to control json_encode behavior on objects? Like excluding empty arrays, null fields and so on?
I mean something like when using serialize(), where you can implement magic __sleep() method and specify what properties should be serialized:
class MyClass
{
public $yes = "I should be encoded/serialized!";
public $empty = array(); // // Do not encode me!
public $null = null; // Do not encode me!
public function __sleep() { return array('yes'); }
}
$obj = new MyClass();
var_dump(json_encode($obj));
The most correct solution is extending the interface JsonSerializable;
by using this interface you just need to return with the function jsonSerialize() what you want json_encode to encode instead of your class.
Using your example:
class MyClass implements JsonSerializable{
public $yes = "I should be encoded/serialized!";
public $empty = array(); // // Do not encode me!
public $null = null; // Do not encode me!
function jsonSerialize() {
return Array('yes'=>$this->yes);// Encode this array instead of the current element
}
public function __sleep() { return array('yes'); }//this works with serialize()
}
$obj = new MyClass();
echo json_encode($obj); //This should return {yes:"I should be encoded/serialized!"}
Note: this works in php >= 5.4 http://php.net/manual/en/class.jsonserializable.php
You could make the variables private. Then they won't show up in the JSON encoding.
If that is not an option, you could make a private array, and use the magic methods __get($key) and __set($key,$value) to set and get values in/from this array. In your case the keys would be 'empty' and 'null'. You can then still access them publicly but the JSON encoder will not find them.
class A
{
public $yes = "...";
private $privateVars = array();
public function __get($key)
{
if (array_key_exists($key, $this->privateVars))
return $this->privateVars[$key];
return null;
}
public function __set($key, $value)
{
$this->privateVars[$key] = $value;
}
}
http://www.php.net/manual/en/language.oop5.overloading.php#object.get

Ambiguous syntax of $this->$variable in PHP

Please excuse me if this question has been asked before, but I tried searching for it with no satisfactory results.
I'm learning PHP (coming from a C++ background) and have come across the following ambiguity. The following two bits of code work exactly the same:
class A
{
public $myInteger;
public function __get($name)
{
return $this->$name;
}
public function __set($name, $value)
{
$this->$name = $value;
}
}
and
class A
{
public $myInteger;
public function __get($name)
{
return $this->name;
}
public function __set($name, $value)
{
$this->name = $value;
}
}
that is, in the class methods $this->$name and $this->name have the exact same function. I'm finding this a bit confusing, especially when considering that if you add the following code,
$myA = new A();
$myA->myInteger = 5;
$hereInt = $myA->myInteger;
echo "<p>" . $hereInt . "</p>";
it only works if there is no $ before myInteger. Could someone please explain the rationale behind this?
$this->$name and $this->name do not mean the same thing. The first is using a locally scoped variable $name to access the field of $this whose name is whatever $name contains, while the second accesses the name field directly.
For example, the following will output something:
$foo = new stdClass;
$foo->bar = 'something';
$baz = 'bar';
echo $foo->$baz;
In the case of __get and __set, $name contains the name of the property that was accessed at the call site; in your case, myInteger.
In your example, the __get and __set methods are actually superfluous, since $myA->myInteger is public and can be accessed directly. __get and __set are only needed to catch access attempts to a property that is not declared explicitly in the class.
For example, you might have a backing array that allows arbitrary "properties" to be set dynamically:
class Foo
{
private $_values = array();
public function __get($key)
{
if (isset($this->_values[$key]))
{
return $this->_values[$key]
}
}
public function __set($key, $value)
{
$this->_values[$key] = $value;
}
}
One thing that's somewhat confusing about this aspect of PHP's syntax is that a $ precedes a field declaration in a class, but there is none when accessing that field. This is compounded by the syntax for accessing static fields, which does require a $!

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);

Dynamically picking variable from a class

I'm trying to get data from a class in php5, where the data in the class is private and the calling function is requesting a piece of data from the class. I want to be able to gain that specific piece of data from the private variables without using a case statement.
I want to do something to the effect of:
public function get_data($field)
{
return $this->(variable with name passed in $field, i.e. name);
}
You could just use
class Muffin
{
private $_colour = 'red';
public function get_data($field)
{
return $this->$field;
}
}
Then you could do:
$a = new Muffin();
var_dump($a->get_data('_colour'));
<?php
public function get_data($field)
{
return $this->{$field};
}
?>
You may want to look at the magical __get() function too, e.g.:
<?php
class Foo
{
private $prop = 'bar';
public function __get($key)
{
return $this->{$key};
}
}
$foo = new Foo();
echo $foo->prop;
?>
I would be careful with this kind of code, as it may allow too much of the class's internal data to be exposed.

Categories