Does an overloaded __get need to manage all member variables? - php

I am creating a __get() function for a class to control access to my private member variables. Do I need to design the function to handle all possible member value reads or can I not write it for members that are public? Also, I am assuming that classes that inherit this class will use my __get() function to access private members.
class ClassA{
private $collection = array();
public $value;
function __get($item){
return $collection[$item];
}

No, you don't.
class A {
public $foo = 'bar';
private $private = array();
public function __get($key) {
echo 'Called __get() at line #' ,__LINE__, ' with key {', $key ,'}',"\n";
return $this->private[$key];
}
public function __set($key, $val) {
$this->private[$key] = $val;
}
}
$a = new A();
var_dump($a->foo);
$a->bar = 'baz';
var_dump($a->bar);
And yes, it will:
class B extends A { private $private = array(); }
$b = new B();
var_dump($b->bar);

Well, your code would fail on private items not set in your array. But then again, you can use this as a way to deal with what's in and out of your array, as such ;
function __get($item){
if ( isset ( $collection[$item] ) )
return $collection[$item];
else {
try {
return $this->$item ; // Dynamically try public values
} catch (Exception $e) {
$collection[$item] = 0 ; // Make it exist
}
}
}
Classes that inherit your calls will use this __get(), but can be overridden, so use parent::__construct() for explicity. Also note that these cannot be static. Further reading.

First of all PHP searches for property name in class definition and tries to return its value. If there's no property - PHP tries to call __get($var) and here you can return anything you want. This is a little confusing behavior for those, who know Java-like getters/setters where you have to define them for every class member you want to access.
When it's comfortable to use Java-like getters/setters - you may write something like this:
public function __set($var, $value)
{
if (method_exists($this, $method = "_set_" . $var))
{
call_user_func(array($this, $method), $value);
}
}
public function __get($var)
{
if (method_exists($this, $method = "_get_" . $var))
{
return call_user_func(array($this, $method), $value);
}
}
and then use this code by defining custom getters/setters
protected function _get_myValue()
{
return $this->_myValue;
}
protected function _set_myValue($value)
{
$this->_myValue = $value;
}
and access to defined methods this way:
$obj->myValue = 'Hello world!';

Related

Add regular not antonymous function to class

I wanted to add functions to class, those functions are in separate file(s), those functions contain ($this).
class myClass {
private $myFunctions=array(); // will contain two keys (title, function_object)
private $var;
public function __construct() {
$this->var = 'Hello world';
}
public function add_function($title, $func) {
$this->myFunctions[$title] = $func;
}
}
function add($x, $y) {
echo $this->var;
return $x + $y;
}
$class = new myClass;
$class->add_function('add', 'add()');
echo $class->add(1,2);
my goal is to add regular functions (not anonymous functions) to the class or to assign those function(s) to a var which can be passed to the class!
Can this be achieved ?
You must have to understand the oops concepts here.
$this refers to your current class / object. So all functions inside a class will be able to access $this ( Current Object ).
But the function declared out side of class can not use $this of that object. (Are you clear up to here.)
But anyways nothing is impossible.
You can achieve your goal in at-least 2 ways.
2) You can use trait to implement multiple inheritance.
it will be like this
trait commonFunctions
{
function add($a, $b)
{
$this->result = $a + $b;
return $this;
}
}
class myClass
{
use commonFunctions;
public function myClassFunction()
{
//
}
}
2 ) Or you can follow below code. Source php.net
<?php
class A {
function __construct($val) {
$this->val = $val;
}
function getClosure() {
//returns closure bound to this object and scope
return function() { return $this->val; };
}
}
$ob1 = new A(1);
$ob2 = new A(2);
$cl = $ob1->getClosure();
echo $cl(), "\n";
$cl = $cl->bindTo($ob2);
echo $cl(), "\n";
Here __call will be called when undeclared method is being called. So you can dynamically append methods to class by this way.
Of course you can polyfill your object in javascript like way. But keep in mind you need to use Reflection to bind this to closure.
<?php
class A {
private $dynamic_functions=[];
public function __call($method, $args=[])
{
if(isset($this->dynamic_functions[$method]) && $this->dynamic_functions[$method] instanceof \Closure)
{
$cl = $this->dynamic_functions[$method]->bindTo($this);
return call_user_func_array($cl, $args);
}
else
{
die("Wrong method, Not yet polyfilled.");
}
}
public function __set($property, $value)
{
$this->dynamic_functions[$property] = $value;
}
}
$a = new A;
$a->sum = function($a,$b){
// var_dump($this); will give you current instance of A
return $a+$b;
};
echo $a->sum(5, 3);
It is possible, but their are limits. When calling undefined methods on an object, PHP checks if there is a magic method named __call:
public mixed __call ( string $name , array $arguments )
This function then receives the function name, and the list of arguments. For your construction you could use
public function __call($name, $args) {
// invokes the function with all arguments:
// e.g: function_name($arg[0], $arg[1], ...)
return call_user_func_array($this->myFunctions[$name], $args);
}
There is just one problem, namely you cannot use $this within your anonymous functions. In python this problem got solved by passing the instance as the first argument (which is usually called self). We can build something similar by adding the instance to the argument list:
public function __call($name, $args) {
// we add the instance at the beginning of the argument list
array_unshift($args, $this);
return call_user_func_array($this->myFunctions[$name], $args);
}
Now you could do something like
function bork($instance, $a, $b) {
return $a*$b;
}
$class = new myClass;
$class->add_function('derp', 'bork');
$class->add_function('add', function($instance, $x, $y) {
echo $instance->var;
return $x + $y;
});
echo $class->derp(1,2);
echo $class->add(35,34534);
If you have already some function which does not have $instance as a first argument, you could wrap it
function my_cool_existing_function($bla) {
echo $bla.$bla;
}
$class->add_function('super_bork', function($instance, $bla) {
return my_cool_existing_function($bla);
});
No, you can't add a function to a class like this. You can extend your class with other class having this function add(). This way inherited class will have parent's function add().

Is there a way to catch undefined globals in PHP, and provide a value, like autoload, but for variables?

We have a lot of existing code that, rather than creating an instance of a class, or using a static function on that class, will call the method on a global singleton of that class.
For example (stringclass.php):
class String {
function endsWith($str, $search) {
return substr($str, -strlen($search)) == $search;
}
}
$STRING_OBJECT = new String();
then it will use this in the following way:
include_once("stringclass.php");
if ($STRING_OBJECT->endsWith("Something", "thing")) {
echo "It's there\n";
}
I realise that this is not a very sensible way of calling the function, but I was wondering if we could fix all the places where people have forgotten to include the right classes using an auto-loader, without changing all the code that uses these singletons. It would detect the use of an undeclared global, and include the correct class file based on the name of the global that was being referenced.
You can use the ArrayAccess interface
http://php.net/manual/en/class.arrayaccess.php
class Ztring implements arrayaccess
{
private $container = array ();
public function offsetSet ($offset, $value)
{
$this->container[$offset] = $value;
}
public function offsetGet ($offset)
{
// exception
if ($offset == 'something')
{
return 'works!';
}
return $this->container[$offset];
}
public function offsetExists ($offset)
{
return isset($this->container[$offset]);
}
public function offsetUnset ($offset)
{
unset ($this->container[$offset]);
}
}
$x = new Ztring ();
$x['zzz'] = 'whatever';
echo $x['zzz']."\n";
echo $x['something']."\n";

What is the order of __set __get, accessing a public field and __call?

Assume we have this code:
class SomeClass{
private $somePrivateField;
public function __get($name){
$function = "get".ucfirst($name);
return $function;
}
public function __set($name,$value){
$function = "set".ucfirst($name);
return $function($value);
}
public function __call($name, $arguments) {
//if function being called is getSomething
//getThat private/protected field if exists and return
//if not raise exception
// similar for setSomething...
}
}
This is a passage from some tutorial:
The __get() method accepts an argument that represents the name of the property being set. In the case of $obj->property, the argument will be property.
Our __get() method then converts this to getProperty, which matches the pattern we defined in the __call() method. What this means is that $obj->property will first try to set a public property with the same name, then go to __get(), then try to call the public method setProperty(), then go to __call(), and finally set the protected $_property.
So when I say somewhere in my code
$obj->property
I can understand it tried to access the public field first..
Why does it go to __get() first? Why not __set() ?
Why does it go to __set() then?
Can someone please explain? Thanks...
__get() will only ever return a string comprising the name of a function that probably doesn't exist.
__set() actually calls the function whose name it constructs, but I'm having trouble determining why because...
__call() seemingly has to determine if the function it's calling is actually a "setter" or "getter" function, which is the entire point of __get() and __set() in the first place.
$obj->property is a non-sensical fragment of code that does not actually do anything on it's own.
// assuming $obj->property is declared as private, or does not exist in the class.
$var = $obj->property; // invokes __get('property')
$obj->property = $var; // invokes __set('property', $var)
$obj->someFunction($var1, $var2, ...);
// invokes __call('someFunction', array($var1, $var2, ...)), but only if
// the specified function is private, or otherwise does not exist.
To re-write the example code so that it makes some semblance of sense:
class SomeClass{
private $somePrivateField;
public function __get($name){
if( isset($this->$name) ) {
return $this->$name;
} else {
Throw new Exception("Object property $name does not exist.");
}
}
public function __set($name,$value){
if( isset($this->$name) ) {
$this->$name = $value;
} else {
Throw new Exception("Object property $name does not exist.");
}
}
}
$obj = new SomeClass();
$obj->somePrivateField = $var; // uses __set()
$var = $obj->somePrivateField; // uses __get()
Using __call() all is rarely necessary, certainly not for the given example.
Or if you would like to be able to set/get private/public properties without having to explicitly declare them first:
class SomeClass{
private $properties = array();
public function __get($name){
if( isset($this->properties['name']) ) {
return $this->properties['name'];
} else {
Throw new Exception("Object property $name does not exist.");
}
}
public function __set($name,$value){
$this->properties[$name] = $value;
}
// it's also a good idea to define __isset() and __unset() in this case as well
public function __isset($name) {
return isset($this->properties['name']);
}
public function __unset($name) {
return unset($this->properties['name']);
}
}

__get and __set magic methods not accessible

I have a class 'base' and a class 'loader', which looks like this.
class base {
protected $attributes = Array();
public $load = null;
function __construct() {
$this->load = loader::getInstance();
echo $this->load->welcome(); //prints Welcome foo
echo $this->load->name; //prints Foo
echo $this->name; //doesnt print anything and i want it to print Foo
}
public function __get($key) {
return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : null;
}
public function __set($key, $value) {
$this->attributes[$key] = $value;
}
}
class loader {
private static $m_pInstance;
private function __construct() {
$this->name = "Foo";
}
public static function getInstance() {
if (!self::$m_pInstance) {
self::$m_pInstance = new loader();
}
return self::$m_pInstance;
}
function welcome() {
return "welcome Foo";
}
}
$b = new base();
Now what I want is a way to store variables from loader class and access them from base class using $this->variablename.
How can I achieve this? I don't want to use extends. Any idea ?
I don't feel like you've fully understood what coding the OOP way means. And usually Singletons are code smells so I'll just warn you:
There's probably a better way of accomplish you goal. If you provide more informations we will help you out. In its current form the answer is the following; just remember that I higly discourage its implementation in your code.
Assuming that you want to access only public (and non static) loader's variables as this->varname in the base class you should just insert this line in the beginning of the base class constructor:
$this->attributes = get_object_vars(loader::getInstance());
This will basically initialize the attributes array with all the loader public vars so that via your __get() method you can access its value.
On a side note, take a look at Dependency Injection design pattern in order to avoid using Singletons.
Your __get/__set methods access $this->attributes but not $this->load.
You could e.g. do something like (pseudocode)
function __get($key) {
- if $attribute has an element $key->$value return $attribute[$key] else
- if $load is an object having a property $key return $load->$key else
- return null;
}
see also: http://docs.php.net/property_exists
You can make static variable and then you can access this variable from anywhere
public statis $var = NULL;
and you can access it like this
classname::$var;

Automated Getter/Setter Functions

I'm looking at Doctrine 2 and Symfony documentation for creating a model class.There are several code snippets where a getProperty and setProperty is used within the class, and these are somehow used automatically when a value is assigned directly to the property. This is different then the typical get/set magic methods, and the sample code I have come across does not implement any custom magic methods, so I believe this is handled by Doctrine somewhere.
From what I've read, Doctrine implements accessors and mutators. Maybe I missed a package when downloading Pear, or maybe I'm not including something in my script.
For example:
class User {
public $name;
public function getName()
{
// Do stuff
}
}
$user = new User();
$foo = $user->name; // getName is called
Note: I'm looking for a Doctrine specific solution. I know this can be done in someway with PHP, but I want to use Doctrine's native functions.
Edit: Updated to clarify how this differs from typical get/set magic methods, and note.
class User {
private $name;
public function __get($property) {
$methodName = "get".ucfirst($property);
if (method_exists($this, $methodName)) {
return call_user_func(array($this, $methodName));
} elseif (isset($this->{$property})) {
return $this->{$property};
}
return null;
}
public function __set($property, $value) {
$methodName = "set".ucfirst($property);
if (method_exists($this, $methodName)) {
call_user_func_array(array($this,$methodName), array($value));
} else {
$this->{$property} = $value;
}
}
public function getName() {
return "My name is ".$this->name;
}
}
$user = new User();
$user->name = "Foo";
$bar = $user->name;
echo $bar; // "My name is Foo"
If there is a method getSomething or setSomething it will be called when accessing the properties directly.
As I read in this documentation page, it is exactly what the code above does what Doctrine does. But it calls the method _set('fieldName', 'value').
If $name is declared public then this line of code:
$foo = $user->name;
is actually accessing the $name field, and is not in fact calling the getName function.
You can use PHP's magic __get and __set methods to automagically provide accessors and mutators, like this:
class User {
private $name;
public function __get($property) {
//check first to make sure that $property exists
return $this->$property;
}
public function __set($property, $value) {
//check first to make sure that $property exists
$this->$property = $value;
}
}
$user = new User();
$user->name = "Foo";
$bar = $user->name;
echo $bar; //"Foo"
You can find more info about PHP's magic methods here.
UPDATE: This is what I think Doctrine is doing:
class User {
private $name;
public function __get($property) {
$propertyTitleCase = mb_convert_case($property, MB_CASE_TITLE);
$method = "get{$propertyTitleCase}";
if(method_exists($this, $method))
{
return $this->$method();
}
else {
return $this->$property;
}
}
public function getName() {
//Do Stuff
}
}

Categories