I've read that you can make all your access through a single section of code using accessor function. The book shows me the code, I got it.
But I don't know how to use it. Can someone give me an example or a syntax to use this function please?
The code from my book:
class classname
{
public $attribute;
function __get($name)
{
return $this->$name;
}
function __set($name, $value)
{
$this->$name = $value;
}
}
Accessors provide a way to access private class variables.
An example(let's just say that $attribute is private):
<?php
$classNameObject = new classname();
// Setting the value
$classNameObject->attribute = "A value";
// Getting the value
echo $classNameObject->attribute;
?>
But in php the __set() and __get() functions work in a way that they can create dynamic properties.
Related
I'm trying to dynamically create the base for a DB entity generalization for a project I'm working on. I basically want to dynamically create a set of standard methods and tools for the properties in any class that extends this. Much like the tools you get for free with Python/Django.
I got the idea from this guy: http://www.stubbles.org/archives/65-Extending-objects-with-new-methods-at-runtime.html
So I've implemented the __call function as described in the post above,
public function __call($method, $args) {
echo "<br>Calling ".$method;
if (isset($this->$method) === true) {
$func = $this->$method;
$func();
}
}
I have a function which gives me the objects public/protected properties through get_object_vars,
public function getJsonData() {
$var = get_object_vars($this);
foreach($var as &$value) {
if (is_object($value) && method_exists($value, 'getJsonData')) {
$value = $value->getJsonData;
}
}
return $var;
}
and now I want to create some methods for them:
public function __construct() {
foreach($this->getJsonData() as $name => $value) {
// Create standard getter
$methodName = "get".$name;
$me = $this;
$this->$methodName = function() use ($me, $methodName, $name) {
echo "<br>".$methodName." is called";
return $me->$name;
};
}
}
Thanks to Louis H. which pointed out the "use" keyword for this down below.
This basically creates an anonymous function on the fly. The function is callable, but it is no longer within the context of it's object. It produces a "Fatal error: Cannot access protected property"
Unfortunately I'm bound to PHP version 5.3, which rules out Closure::bind. The suggested solution in Lazy loading class methods in PHP will therefore not work here.
I'm rather stumped here... Any other suggestions?
Update
Edited for brevity.
Try it like this (you have to make the variables you'll need available to the method)
$this->$methodName = function() use ($this, $methodName, $name){
echo "<br>".$methodName." is called";
return $this->$$name;
};
You should have access to the object context through $this.
Instead of updating the original question above, I include the complete solution here for anybody struggling with the same issues:
First of all, since the closure cannot have real object access, I needed to include the actual value with the "use" declaration when creating the closure function (see original __construct function above):
$value =& $this->$name;
$this->$methodName = function() use ($me, $methodName, &$value) {
return $value;
};
Secondly the __call magic method did not just need to call the closure function, it needed also to return any output from it. So instead of just calling $func(), I return $func();
This did the trick! :-)
I am trying to build a base class that all of my classes extend from that allows for property access through simple $obj->property syntax, but if get_property() or set_property($value) is defined, then any attempt to get or set that property is routed through those getters and setters.
The tricky part, though, is that I would like updates to object properties to be reflected in an array (which is a property of the object, call it $changed_array) which can output an array of the properties that were changed, for some purpose, say, insertion into a db update call.
The problem lies in this sample:
class Sample {
private $changed_array;
public __get($var_ame){
if(method_exists($this, $method = 'get_' . $var_name)){
return $this->$method();
} else {
return $this->$var_name;
}
}
public __set($var_name, $value){
if(method_exists($this, $method = 'set_' . $var_name))}
return $this->$method($value);
} else {
// pseudo code
if($this->$var_name isset and isn't $value) { // add to $changed_array }
return $this->$var_name = $value;
}
}
}
Which works great, until there is a setter method defined like so:
public set_var_name($value){
// pretend we're mapping a db column to another name
$this->other_var_name = $value;
}
With this, the setter is called, but property it is setting is accessible, so the new value doesn't use the __set or __get function, and the changed array isn't updated with other_var_name as a changed property.
Is there some kind of hack, other that using something like $this->set('variable', 'value') to achieve the result? I would just write getter's and setters, but they vary based on the db schema, and it would be lovely if there was an elegantly simple solution.
Try this,
$objects = array("model", "make", "version");
foreach ($objects as $object) {
$getter = "get".ucfirst($object);
if (is_object($iProduct->$getter())) {
echo "getvalue"+$iProduct->$getter()
}
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 $!
So for example I have this code:
class Object{
public $tedi;
public $bear;
...some other code ...
}
Now as you can see there are public variables inside this class. What I would like to do is to make these variables in a dynamic way, with a function something like:
private function create_object_vars(){
// The Array what contains the variables
$vars = array("tedi", "bear");
foreach($vars as $var){
// Push the variables to the Object as Public
public $this->$var;
}
}
So how should I create public variables in a dynamic way?
$vars = (object)array("tedi"=>"bear");
or
$vars = new StdClass();
$vars->tedi = "bear";
Yes, you can do this.
You're pretty much correct - this should do it:
private function create_object_vars(){
// The Array of names of variables we want to create
$vars = array("tedi", "bear");
foreach($vars as $var){
// Push the variables to the Object as Public
$this->$var = "value to store";
}
}
Note that this makes use of variable variable naming, which can do some crazy and dangerous things!
As per the comments, members created like this will be public - I'm sure there's a way of creating protected/private variables, but it's probably not simple (eg you could do it via the C Zend API in an extension).
As alternative, you can also derive your object from ArrayObject. So it inherits array-behaviour and a few methods which make injecting attributes easier.
class YourObject extends ArrayObject {
function __construct() {
parent::__construct(array(), ArrayObject::PROPS_AS_ARRAY);
}
function create_object_vars() {
foreach ($vars as $var) {
$this[$var] = "some value";
}
}
Attributes will then be available as $this->var and $this["var"] likewise, which may or not may suit the use case. The alternative method for setting attributes would be $this->offsetSet("VAR", "some value");.
Btw, there is nothing evil about variable variables. They're a proper language construct, as would be reusing ArrayObject.
Im' wondering if this is possible:
I've successfully used __set() magic method to set values to properties of a class:
class View
{
private $data;
public function __set( $key, $value )
{
$this->data[$key] = $value;
}
}
So I'm able to:
$view = new View();
$view->whatever = 1234;
The problem comes when I want to concatenate a string for example. It seems like __set() is not being called (it's not being called in fact).
$view = new View();
$view->a_string = 'hello everybody'; //Value is set correctly
$view->a_string.= '<br>Bye bye!'; //Nothing happens...
echo $view->a_string;
This outputs "hello everybody". I'm not able to execute __set() in the second assignment.
Reading php.net it says that:
__set() is run when writing data to inaccessible properties.
So as a_string already exists, __set is not called.
My question finally is... how could I achieve that concatenation operation??
Note:
Ok... Murphy came and gave me the answer as soon as I posted this...
The answer (As I understood), is that PHP is not able to decide if a_string is available as I didn't defined a __get() method.
Defining __get() allows php to find the current value of a_string, then uses __set() to concatenate the value.
You should add a __get() method that allows you to access inaccessable properties just as you did with __set(). You could then do the following:
$view->myvar = $view->myvar.'Added text.';
The __get() method would be:
public function __get($var) {
return (isset($this->data[$var])) ? $this->data[$var]: '';
}
FYI, for simple assignment, magic methods aren't even necessary.
class A {
}
$a = new A();
$a->str = '1';
$a->str .= '2';
echo $a->str; // outputs 12
This will work just fine as PHP will create the new properties through assignment. __set/get usually is used when additional checks/code need to be run on the values.
It's because __set() is a method and not a string. If you want it to "act" like a string you can change it to this.
class View
{
private $data;
public function __set( $key, $value )
{
if (empty($this->data[$key])) {
$this->data[$key] = $value;
} else {
$this->data[$key] .= $value;
}
}
}