Is there a way to Override recursion limits for __get and __set on the same property. I want to be able to handle the second reentry differently than the first entry.
This code example isn't practical but the simplest to make the point.
class Foo {
public function __set($name,$value){
print "$name entered\n";
this->$name = $value; // want it to recurse here
}
}
$a = new Foo();
$a->baz = "derp";
print $a->baz;
// should get (cannot test at the moment)
// baz entered
// derp <- you get derp because the current php implementation creates an instance variable from the second call to __set
My Internet is down so I'm typing on my phone so typos are likely.
There's no way to do this with that syntax. Just call __set directly instead, e.g.:
class Foo {
public function __set($name, $value) {
print "$name entered\n";
$this->__set($name, $value);
}
}
I know this is a old question, but I think this is what you actually were looking for.
<?php
class Foo {
private $_data = array();
public function __set($name,$value){
print "$name entered\n";
$this->_data[$name] = $value;
}
public function __get($name){
if(array_key_exists($name, $this->_data)){
return $this->_data[$name];
} else {
return false;
}
}
}
$a = new Foo();
$a->baz = "derp";
print $a->baz;
?>
http://phpfiddle.org/main/code/4h0-an7
Related
I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?
Use Trait introduced in PHP 5.4
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
which prints Hello World!
http://php.net/manual/en/language.oop5.traits.php
This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.
It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.
Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.
To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.
Example:
class Foo
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
class Fizz
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.
Mixins for PHP (PHP does not implement Mixins natively, but this library will help)
First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html
First google result for "php mixin": http://www.advogato.org/article/470.html
Short answer: yes, but not natively (yet, evidently, as #mchl notes). Check those out.
Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."
I based mixins functionality on the blog entry found at jansch.nl.
class Node
{
protected $__decorator_lookup = array();
public function __construct($classes = array())
{
foreach($classes as $class)
if (class_exists($class))
{
$decorator = new $class($this);
$methods = get_class_methods($decorator);
if (is_array($methods))
foreach($methods as $method)
$this->__decorator_lookup[strtolower($method)] = $decorator;
}
else
trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
}
public function __get($name)
{
switch($name)
{
default:
if ($this->__decorator_lookup[strtolower($name)])
return $this->__call($name);
}
}
public function __call($method, $args = array())
{
if(isset($this->__decorator_lookup[strtolower($method)]))
return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
else
trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
}
public function __clone()
{
$temp = $this->decorators;
$this->decorators = array();
foreach($temp as $decorator)
{
$new = clone($decorator);
$new->__self = $this;
$this->decorators[] = $new;
}
}
}
class Decorator
{
public $__self;
public function __construct($__self)
{
$this->__self = $__self;
}
public function &__get($key)
{
return $this->__self->$key;
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->__self, $method), $arguments);
}
public function __set($key, $value)
{
$this->__self->$key = $value;
}
}
class Pretty extends Decorator
{
public function A()
{
echo "a";
}
public function B()
{
$this->b = "b";
}
}
$a = new Node(array("Pretty"));
$a->A(); // outputs "a"
$a->B();
echo($a->b); // outputs "b"
EDIT:
As PHP clone is shallow, added __clone support.
Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.
Having the following code
class test {
private $name;
public function __get($name){
return $name;
}
public function __set($name,$value){
$this->name = $value;
}
}
$obj = new test();
$obj->a = 2;
if (!empty($obj->a)) {
echo 'not empty';
}
This is calling __isset. But this is not being defined so it always return empty. What is the best way to check for a non empty property?
Update :changing the class is not a solution because it's a 3th party component and it has to remain intact.
If you can't change the class, I think the only possible workaround is using a temporary variable.
$obj->a = 2;
$test = $obj->a;
if (!empty($test)) {
echo 'not empty';
}
I know I am very late to the party here, however I am posting this for the edificationof any who may stumble across this question.
Firstly, I believe that the test class is wrong and if that is really what the 3rd party component does, I would chuck it out because it's rubbish. Do you really want all property names to map internally to the single property 'name', and thereby overwrite each other? Do you really want all property names to be returned as the property value? The code should look like this:
class test {
public function __get($name){
return $this->$name;
}
public function __set($name,$value){
$this->$name = $value;
}
}
Secondly, you can change the class, even if it has to remain intact. That's the point of inheritance. This is the open-closed principle. If the functions are incorrect, simply extend test like this to correct them:
class test {
private $name;
public function __get($name){
return $name;
}
public function __set($name,$value){
$this->name = $value;
}
}
class my_test extends test
{
public function __get($name)
{
return $this->$name;
}
public function __set($name,$value){
$this->$name = $value;
}
}
You shouldn't need to define __isset() as the corrected code will do what it is meant to do, but if you did you could do that here too.
Now the following will do what it is supposed to do (note the change of class name):
$obj = new my_test();
$obj->a = 2;
if (!empty($obj->a)) {
echo 'not empty';
}
change
public function __set($name,$value){
$this->name = $value;
}
To
public function __set($name,$value){
$this->$name = $value;
}
And then try
It does not make sense when used with anything other than the variable; ie empty (addslashes ($ name)) does not make sense, since it will be checked by anything other than a variable as a variable with a value of FALSE.
In your case, you should use the type conversion:
if ((bool)$obj->a) {
echo 'not empty';
}
I need to have a variable that only one function can write (let's call that function a) and that only one other function can read (let's call that function b). Is that possible?
You could use a static variable:
function foo($val=null) {
static $var = null;
if (!is_null($var)) $var = $val;
return $val;
}
Here $var is only visible inside the function foo and is maintained throughout multiple calls:
foo(123);
echo foo(); // 123
foo(456);
echo foo(); // 456
Or use a class with a private member and access/modify it with public methods:
class A {
private $var;
public function setVar($val) {
$this->var = $val;
}
public function getVar() {
return $this->var;
}
}
With this the private member var is only visible to a particular instance of this class:
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 123
echo $obj2->getVar(); // 456
If you make the member static, then there is just one for the class instead of for each instance:
class A {
private static $var;
public function setVar($val) {
self::$var = $val;
}
public function getVar() {
return self::$var;
}
}
$obj1 = new A();
$obj1->setVar(123);
$obj2 = new A();
$obj2->setVar(456);
echo $obj1->getVar(); // 456
echo $obj2->getVar(); // 456
You can use a static abstract class.
abstract class Settings
{
private static var $_settings = array();
public static function get($key,$default = false)
{
return isset(self::$_settings[$key]) ? self::$_settings[$key] : $default;
}
public static function set($key,$value)
{
self::$_settings[$key] = $value;
}
}
Example Usage:
Settings::set('SiteName',`SomeResult`);
echo Settings::get('SiteName');
Since 5.3.0, you can use anonymous functions as closures. The advantage here, is that you can hold on to b... which is returned by a... and fire it off when you're ready:
<?php
function a()
{
// Only a() can write to $myVar
$myVar = 42;
$b = function() use ($myVar)
{
// $b can read $myVar
// no one else can
return $myVar;
};
return $b;
}
// get $b
$test = a();
// use $b
echo $test();
?>
Another solution before 5.3.0, but here a has to fire b which may not be that practical:
You can simply create an internal variable and pass it as an argument. You can do this inside a class, or just inside simple functions:
function a()
{
// ...
// Write the variable that
// only this function can write to
$thisVar = 1;
b($thisVar);
//...
}
function b($myVar)
{
// ...
// Do stuff w $myVar, a copy of $thisVar
// Changing $myVar has no effect on $thisVar
//
}
Do you mean friend functions? Because I'd love to be able to do that. So far I haven't found an easy way though (although you could try using Reflection, but that seems like way to much effort).
For me, it usually hasn't been an issue of maintaining data integrity / encapsulation, but of keeping the list of public methods (which is kinda like a class's API) free of clutter. A perfect framework should be easy to use, have obvious function names etc etc etc. Methods intended for use by a single other method really mess things up. The "solution" I've taken to is prefixing those function names by one or two underscores and writing "intended for internal use only" or something to that extent in the comments.
Does anyone know how to reset the instance variables via a class method. Something like this:
class someClass
{
var $var1 = '';
var $var2 = TRUE;
function someMethod()
{
[...]
// this method will alter the class variables
}
function reset()
{
// is it possible to reset all class variables from here?
}
}
$test = new someClass();
$test->someMethod();
echo $test->var1;
$test->reset();
$test->someMethod();
I know I could simply do $test2 = new SomeClass() BUT I am particularly looking for a way to reset the instance (and its variables) via a method.
Is that possible at all???
You can use reflection to achieve this, for instance using get_class_vars:
foreach (get_class_vars(get_class($this)) as $name => $default)
$this -> $name = $default;
This is not entirely robust, it breaks on non-public variables (which get_class_vars does not read) and it will not touch base class variables.
Yes, you could write reset() like:
function reset()
{
$this->var1 = array();
$this->var2 = TRUE;
}
You want to be careful because calling new someClass() will get you an entirely new instance of the class completely unrelated to the original.
this could be easy done;
public function reset()
{
unset($this);
}
Sure, the method itself could assign explicit values to the properties.
public function reset()
{
$this->someString = "original";
$this->someInteger = 0;
}
$this->SetInitialState() from Constructor
Just as another idea, you could have a method that sets the default values itself, and is called from within the constructor. You could then call it at any point later as well.
<?php
class MyClass {
private $var;
function __construct() { $this->setInitialState(); }
function setInitialState() { $this->var = "Hello World"; }
function changeVar($val) { $this->var = $val; }
function showVar() { print $this->var; }
}
$myObj = new MyClass();
$myObj->showVar(); // Show default value
$myObj->changeVar("New Value"); // Changes value
$myObj->showVar(); // Shows new value
$myObj->setInitialState(); // Restores default value
$myObj->showVar(); // Shows restored value
?>
Just an idea:
example (in PHP):
to set name:
$object->name('name');
to get name:
$object->name();
If no argument: the method is used as getter, else as setter. For simple getters/setter. Stupid, whatever, maybe?
edit: to follow up on the answers: I don't really like get and set because I prefer to have the interface as explicit as possible. When there are only a few properties it's also overkill IMHO. So I'd like to narrow it down to classes/objects with a couple of explicit getters/setters.
The problem is it would be hard to follow. I'd much rather use PHP5's __get and __set so it is more natural to get and set variables, and everyone would know exactly what I am doing. IE:
class myClass
{
function __get($name)
{
return $this->array[$name];
}
function __set($name, $value)
{
$this->array[$name] = $value;
}
function print()
{
echo $this->array['test'];
}
}
$obj = new myClass;
$obj->test = "Hi";
echo $obj->test; //echos Hi.
$obj->print(); //echos Hi.
It can be done using the __call() magic method.
class Test {
public function __call($name, array $args) {
$variable =& $this->$name;
if(!empty($args)) {
$variable = $args[0];
}
return $variable;
}
}
Sure, you could do that if it makes sense in your application, otherwise I would just use the standard getters/setters which have already been set up for you. Your function could look something like this:
public function name($val = null)
{
if (is_null($val))
{
return $this->name;
}
else
{
$this->name = $val;
}
}