There's thousands of examples of php __get and __set out there, unfortunately nobody actually tells you how to use them.
So my question is: how do I actually call the __get and __set method from within the class and when using an object.
Example code:
class User{
public $id, $usename, $password;
public function __construct($id, $username) {
//SET AND GET USERNAME
}
public function __get($property) {
if (property_exists($this, $property)) {
return $this->$property;
}
}
public function __set($property, $value) {
if (property_exists($this, $property)) {
$this->$property = $value;
}
return $this;
}
}
$user = new User(1, 'Bastest');
// echo GET THE VALUE;
How would I set the values in the constructor and how would i get the value in the // echo GET THE VALUE;
This feature is called overloading in PHP. As the documentation states the __get or __set methods will be called if you are trying to access non existent or non accessible properties. The problem in your code is, that the properties your are accessing are existent and accessible. That's why __get/__set will not being called.
Check this example:
class Test {
protected $foo;
public $data;
public function __get($property) {
var_dump(__METHOD__);
if (property_exists($this, $property)) {
return $this->$property;
}
}
public function __set($property, $value) {
var_dump(__METHOD__);
if (property_exists($this, $property)) {
$this->$property = $value;
}
}
}
Test code:
$a = new Test();
// property 'name' does not exists
$a->name = 'test'; // will trigger __set
$n = $a->name; // will trigger __get
// property 'foo' is protected - meaning not accessible
$a->foo = 'bar'; // will trigger __set
$a = $a->foo; // will trigger __get
// property 'data' is public
$a->data = '123'; // will not trigger __set
$d = $a->data; // will not trigger __get
Related
How should one use PHP's magic __get() and __set() methods and limit which properties are supported?
I've typically seen PHP's magic methods used for overloading the below two ways, and neither do so.
I recognize I can hardcode some logic, but doing so doesn't make the classes very extendable.
$obj1=new Entity1(new Foo, new Bar);
$obj1->propAccessible1='propAccessible1'; //Valid
var_dump($obj1->propAccessible1); //Valid
$obj1->privateObject1='privateObject1'; //Should not be allowed
var_dump($obj1->privateObject1); //Should not be allowed
$obj1->unsupportedProperty='unsupportedProperty'; //Correctly is not allowed
var_dump($obj1->unsupportedProperty); //Correctly is not allowed
$obj2=new Entity2(new Foo, new Bar);
$obj2->propAccessible1='propAccessible1'; //Valid
var_dump($obj2->propAccessible1); //Valid
$obj2->privateObject1='privateObject1'; //Should not be allowed
var_dump($obj2->privateObject1); //Should not be allowed (will be if first set using setter)
$obj2->unsupportedProperty='unsupportedProperty'; //Should not be allowed
var_dump($obj2->unsupportedProperty); //Should not be allowed
class Foo{}
class Bar{}
class Entity1
{
private $privateObject1, $privateObject2;
private $propAccessible1, $propAccessible2;
public function __construct($injectedObject1, $injectedObject2) {
$this->privateObject1=$injectedObject1;
$this->privateObject2=$injectedObject2;
}
public function __get($property) {
if (property_exists($this, $property)) return $this->$property;
else throw new \Exception("Property '$property' does not exist");
}
public function __set($property, $value) {
if (!property_exists($this, $property)) throw new \Exception("Property '$property' is not allowed");
$this->$property = $value;
return $this;
}
}
class Entity2
{
private $privateObject1, $privateObject2;
private $data=[];
public function __construct($injectedObject1, $injectedObject2) {
$this->privateObject1=$injectedObject1;
$this->privateObject2=$injectedObject2;
}
public function __set($property, $value) {
$this->data[$property] = $value;
}
public function __get($property) {
if (array_key_exists($property, $this->data)) {
return $this->data[$property];
}
else throw new \Exception("Property '$property' does not exist");
}
}
You could modify the second approach just a little. Define your accessible keys in $data, and the check if those exist in __set() like you're already doing in __get().
class Entity2
{
private $privateObject1, $privateObject2;
private $data = [
'accessible1' => null,
'accessible2' => null
];
public function __construct($injectedObject1, $injectedObject2)
{
$this->privateObject1 = $injectedObject1;
$this->privateObject2 = $injectedObject2;
}
public function __set($property, $value)
{
if (array_key_exists($property, $this->data)) {
$this->data[$property] = $value;
} else throw new \Exception("Property '$property' does not exist");
}
public function __get($property)
{
if (array_key_exists($property, $this->data)) {
return $this->data[$property];
} else throw new \Exception("Property '$property' does not exist");
}
}
I'm not really a believer in strictly avoiding public properties in PHP though. If they're going to be publicly accessible through magic methods anyway, I'd rather just declare them as public to make it more clear how the class works.
I have a class that extends from Yii2's Model and I need to declare a class public property in the constructor, but I'm hitting a problem.
When I call
class Test extends \yii\base\Model {
public function __constructor() {
$test = "test_prop";
$this->{$test} = null; // create $this->test_prop;
}
}
Yii tries to call, from what I understand, the getter method of this property, which of course doesn't exist, so I hit this exception.
Also, when I actually do $this->{$test} = null;, this method gets called.
My question is: Is there a way to declare a class public property in another way? Maybe some Reflexion trick?
You could override getter/setter, e.g. :
class Test extends \yii\base\Model
{
private $_attributes = ['test_prop' => null];
public function __get($name)
{
if (array_key_exists($name, $this->_attributes))
return $this->_attributes[$name];
return parent::__get($name);
}
public function __set($name, $value)
{
if (array_key_exists($name, $this->_attributes))
$this->_attributes[$name] = $value;
else parent::__set($name, $value);
}
}
You could also create a behavior...
Ok, I received help from one of Yii's devs. Here is the answer:
class Test extends Model {
private $dynamicFields;
public function __construct() {
$this->dynamicFields = generate_array_of_dynamic_values();
}
public function __set($name, $value) {
if (in_array($name, $this->dynamicFields)) {
$this->dynamicFields[$name] = $value;
} else {
parent::__set($name, $value);
}
}
public function __get($name) {
if (in_array($name, $this->dynamicFields)) {
return $this->dynamicFields[$name];
} else {
return parent::__get($name);
}
}
}
Note that I'm using in_array instead of array_key_exists because the dynamicFields array is a plain array, not an associative one.
EDIT: This is actually wrong. See my accepted answer.
Try to set variable in init method.
Like this:
public function init() {
$test = "test_prop";
$this->{$test} = null; // create $this->test_prop;
parent::init();
}
how can i access the second property or method in this statement
$this->test->hello();
In my __get() I can only figure out how to figure out what the test property is. I want to be also be able to capture the 'hello' method call. and do some dynamic things with it.
So in short if I type
$this->test->hello()
I want to echo each segment
echo $propert // test
echo $method //hello
The issue is that my test is being used to instantiate a new class object from an outside class. The method hello belongs to the test class object.
I want to capture the method within my __get().
How can i do this?
EDIT:
public function __get($name)
{
if ($name == 'system' || $name == 'sys') {
$_class = 'System_Helper';
} else {
foreach (get_object_vars($this) as $_property => $_value) {
if ($name == $_property)
$_class = $name;
}
}
$classname = '\\System\\' . ucfirst($_class);
$this->$_class = new $classname();
//$rClass = new \ReflectionClass($this->$_class);
$rClass = get_class_methods($this->$_class);
foreach($rClass as $k => $v)
echo $v."\n";
//print_r($rClass);
return $this->$_class;
It seems you are after some kind of proxy class, this might suit your needs.
class ObjectProxy {
public $object;
public function __construct($object) {
$this->object = $object;
}
public function __get($name) {
if (!property_exists($this->object, $name)) {
return "Error: property ($name) does not exist";
}
return $this->object->$name;
}
public function __call($name, $args) {
if (!method_exists($this->object, $name)) {
return "Error: method ($name) does not exist";
}
return call_user_func_array(array($this->object, $name), $args);
}
}
class A {
public $prop = 'Some prop';
public function hello() {
return 'Hello, world!';
}
}
class B {
public function __get($name) {
if (!isset($this->$name)) {
$class_name = ucfirst($name);
$this->$name = new ObjectProxy(new $class_name);
}
return $this->$name;
}
}
$b = new B();
var_dump($b->a->hello());
var_dump($b->a->prop);
var_dump($b->a->foo);
var_dump($b->a->bar());
Output:
string 'Hello, world!' (length=13)
string 'Some prop' (length=9)
string 'Error: property (foo) does not exist' (length=36)
string 'Error: method (bar) does not exist' (length=34)
Example:
http://ideone.com/dMna6
It could be easily extend for other magic methods like __set, __callStatic, __isset, __invoke, etc.
I think you want to use __call instead of __get. Also, don't.
The object you instantiated for $this will use the __get magic method to create the object (as a property) test. The object stored at $this->test needs to implement the __call magic method to use hello() if it's not defined
For example, I have a object like this:
class myObj{
private $a;
private $b;
//getter , setter
}
And I would like to do something like:
$myObj = initWitharray(array('a'=> 'myavalue',
'b'=> 'mybvalue'));
And the myObj will have all the a value and b value. How can I do so ? Thank you.
As NullUserException suggested:
<?php
class myObj {
private $a;
private $b;
public function initWithArray(array $arr) {
foreach ($arr as $k => $v) {
$this->$k = $v;
}
return $this;
}
public function get($name) {
return $this->$name;
}
}
// usage
$myObj = new myObj();
echo $myObj->initWithArray(array(
'a' => 'myavalue',
'b' => 'mybvalue'))
->get('a');
function initWithArray(array $a){
$myObj = new myObj();
foreach($a as $k => $v){
$myObj->$k = $v;
}
return $myObj;
}
class myObj {
private $a;
private $b;
public function __set($name, $value) {
$this->$name = $value;
}
public function __get($name){
if($this->$name != null)
return $this->$name;
return null;
}
}
Or, as said in the comments, it's better if init function would be a member of a class.
Try the following:
class myObj {
private $a;
private $b;
function __construct($passedArray){
$this->a = array_key_exists('a', $passedArray) ? $passedArray['a'] : 'default_value_for_a';
$this->b = array_key_exists('b', $passedArray) ? $passedArray['b'] : 'default_value_for_b';
}
//Rest of the code
}
Then:
newObj = new myObj(array('a'=> 'myavalue', 'b'=> 'mybvalue'))
You could use the class constructor to pass in options when you create a new object. Doing it this way, you should also separate out the setOptions method so you can update the options after init as well.
Use this class like this: (shows both ways to set options)
$object = new myClass(array('a'=>'foo'));
$object->setOptions(array('b'=>'bar'));
Also, try not to confuse object with class. An object is an instance of a class.
class myClass
{
private $a;
private $b;
public function __construct(array $options = null)
{
if (null !== $options) {
$this->setOptions($options);
}
}
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
if (isset($this->$key)) {
$this->$key = $value;
}
}
return $this;
}
}
I usually adopts the approach which gives me total control over the object, like allowing someone to access the property. denying the permission, allowing access to only those which i think is appropriate according to application etc. and that's the purpose of object.
Have a look at the example below.
Example
class MyObj {
private $data = array('one' => null, 'two' => null);
public function __set($property, $value) {
//Only allow to set those properties which is declared in $this->data array
if(array_key_exists($property, $this->data)) {
return $this->data[$property] = $value;
} else {
//if you want to throw some error.
}
}
//you can allow or disallow anyone from accessing the class property directly.
public function __get($property) {
//To deny the access permission, simply throw an error and return false.
$error = 'access denied to class property {' . $property . '}';
return false;
//Or Else Allow permission to access class property
//return $this->data[$property];
}
}
the above example demonstrates on how you can gain more control over the class property, by declaring class property $data as private you are basically disallowing anyone to do any sort of manipulation on the class property directly. whatever operation is to be carried out is done through PHP's getter __get() and setter __set() method. of course you can modify the above code according to your need, you just new a very few line of changes and it will behave the way you want it to.
i was wanting to wrap an object in another - favoring composition over inheritance. but i am not sure i am doing it right tho there are no errors.
i created a class Wrapped thats is wrapped by Wrapper. i made it such that when a method/property is called on $wrapper, if it exists in the class, Wrapper, it will be returned else, it will delegate to the $wrapped object. i wonder apart from the fact i didnt check if the method/property exists, what have i done wrong? can some1 explain __callStatic() too?
class Wrapped {
protected $wrappedProp1 = 'Wrapped: Property 1';
protected $wrappedProp2 = 'Wrapped: Property 2';
function method1($arg1, $arg2) {
echo "Called Wrapped::method1() with the following parameters: $arg1, $arg2";
}
static function sMethod2() {
echo 'Called a static method in wrapped';
}
function __get($name) {
return $this->$name;
}
function __set($name, $val) {
$this->$name = $val;
}
}
class Wrapper {
protected $wrapperProp1 = 'Wrapper: Property 1';
protected $wrapped;
function __construct($wrapped) {
$this->wrapped = $wrapped;
}
function wrapperMethod() {
echo 'In wrapper method';
}
function __get($name) {
if (property_exists($this, $name)) {
return $this->$name;
}
return $this->wrapped->$name;
}
function __set($name, $val) {
if (property_exists($this, $name)) {
$this->$name = $val;
}
$this->wrapped->$name = $val;
}
function __call($name, $args = array()) {
call_user_func_array(array($this->wrapped, $name), $args);
}
static function __callStatic($name, $args = array()) {
call_user_func_array(array('Wrapped', $name), $args);
}
}
$wrapper = new Wrapper(new Wrapped);
// testing normal methods
$wrapper->wrapperMethod();
echo $wrapper->wrapperProp1;
$wrapper->wrapperProp1 = 'New Wrapper Prop 1';
echo $wrapper->wrapperProp1;
// testing delegates
$wrapper->method1('hello', 'world'); //delegated to Wrapped::method1()
$wrapper->sMethod2(); // delegated to static Wrapped::sMethod2() ... what is callStatic for then
echo $wrapper->wrappedProp2;
Wrapper::sMethod2();
As it seems - it's all ok.
About __callStatic() - it allows you to workaround undefined static functions in class.
Example:
<?php
class Foo {
static function __callStatic($name, $args = array()) {
echo "Called static function $name with arguments ". print_r($args, true);
}
}
Foo::Bar('test');
// will output "Called static function Bar with arguments Array ( 0 => test );"