I'm looking for a way to maintain the $this->property = "value" syntax, but use getter and setter methods.
I found several references to the magic functions __get() and __set(), but I'm looking to provide accessors on more of a case by case basis.
I then found this: https://wiki.php.net/rfc/propertygetsetsyntax-v1.2, which seems like exactly what I was looking for, but alas doesn't seem to have been implemented.
Is there a way to do this without class-wide functions to check every property assignment?
For me, you have two options:
You want to use magic methods to cover all getters and setters, and you'll store your data that would be got/set in a property like $this->_data[] to avoid conflicts with other properties
You want to create specific getters/setters and you manually define them in the classes you want them to be used in (or high up the inheritance chain so they're available to every class that extends it - useful in MVC architecture).
The magic methods approach is a good "cover all bases" approach whereas the individual approach is better for clarity and knowing exactly what is available to implementations/children.
A good example of option 1 (magic methods) is available in the manual, here.
I would add that if you want a "case by case" basis for properties, you could also add a whitelist/blacklist into your magic methods to include/exclude a specific set of properties, example extending what is in the manual:
private $data = array();
private $whitelist = array('PropertyIWant', 'AnotherOneIWant');
// ...
public function __get($name)
{
// ...
if (array_key_exists($name, $this->data) && in_array($name, $this->whitelist)) {
return $this->data[$name];
}
// ...
You could use call:
class Test {
protected $a;
protected $b;
protected $valueForC;
protected $otherData = array('d' => null);
public function __call($method, $args) {
if (preg_match('#^((?:get|set))(.*)$#', $method, $match)) {
$property = lcfirst($match[2]);
$action = $match[1];
if ($action === 'set') {
$this->$property = $args[0];
} else {
return $this->$property;
}
}
}
public function getD() {
return $this->otherData['d'];
}
public function setD($value)
{
$this->otherData['d'] = $value;
}
}
$x = new Test();
$x->setA('a value');
$x->setB('b value');
$x->setValueForC('c value');
$x->setD('special value for D');
echo $x->getA() ."\n";
echo $x->getB() ."\n";
echo $x->getValueForC() ."\n";
echo $x->getD() ."\n";
Related
I currently have the following __get/__set methods in the PHP class example:
class example{
/*Member variables*/
protected $a;
protected $b = array();
public function __get($name){
return $this->$name;
}
public function __set($name, $value){
$this->$name = $value;
}
}
However, in addition to setting standard protected variables, I would also like to be able to set protected arrays within the class. I've looked through some other questions and found general suggestions as to how to simple set variables using the __get/__set methods, but nothing that would allow one to use these magic methods to set BOTH arrays and nonarrays, i.e. in the following manner:
$fun = new $example();
$fun->a = 'yay';
$fun->b['coolio'] = 'yay2';
Simple, define __get() like so:
public function &__get($name)
{
return $this->$name;
}
// ...
$fun = new example();
$fun->a = 'yay';
$fun->b['coolio'] = 'yay2';
var_dump($fun);
Output:
object(example)#1 (2) {
["a":protected]=>
string(3) "yay"
["b":protected]=>
array(1) {
["coolio"]=>
string(4) "yay2"
}
}
Do take necessary care when you're dealing with references, it's easy to mess up and introduce hard to track bugs.
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.
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()
}
Is there a way to tell PHP how to convert your objects to ints? Ideally it would look something like
class ExampleClass
{
...
public function __toString()
{
return $this->getName();
}
public function __toInt()
{
return $this->getId();
}
}
I realize it's not supported in this exact form, but is there an easy (not-so-hacky) workaround?
---------------------- EDIT EDIT EDIT -----------------------------
Thanks everybody! The main reason I'm looking into this is I'd like to make some classes (form generators, menu classes etc) use objects instead of arrays(uniqueId => description). This is easy enough if you decide they should work only with those objects, or only with objects that extend some kind of generic object superclass.
But I'm trying to see if there's a middle road: ideally my framework classes could accept either integer-string pairs, or objects with getId() and getDescription() methods. Because this is something that must have occurred to someone else before I'd like to use the combined knowledge of stackoverflow to find out if there's a standard / best-practice way of doing this that doesn't clash with the php standard library, common frameworks etc.
I'm afraid there is no such thing. I'm not exactly sure what the reason is you need this, but consider the following options:
Adding a toInt() method, casting in the class. You're probably aware of this already.
public function toInt()
{
return (int) $this->__toString();
}
Double casting outside the class, will result in an int.
$int = (int) (string) $class;
Make a special function outside the class:
function intify($class)
{
return (int) (string) $class;
}
$int = intify($class);
Of course the __toString() method can return a string with a number in it: return '123'. Usage outside the class might auto-cast this string to an integer.
Make your objects implement ArrayAccess and Iterator.
class myObj implements ArrayAccess, Iterator
{
}
$thing = new myObj();
$thing[$id] = $name;
Then, in the code that consumes this data, you can use the same "old style" array code that you had before:
// Here, $thing can be either an array or an instance of myObj
function doSomething($thing) {
foreach ($thing as $id => $name) {
// ....
}
}
You can use retyping:
class Num
{
private $num;
public function __construct($num)
{
$this->num = $num;
}
public function __toString()
{
return (string) $this->num;
}
}
$n1 = new Num(5);
$n2 = new Num(10);
$n3 = (int) (string) $n1 + (int) (string) $n2; // 15
__toString() exists as a magic method.
http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
__toInt() does not.
So I have a couple of arrays
$array_1 = Array('one','two','three');
$array_2 = Array('red','blue','green');
Is there a dynamic way to create the Setters and Getters for an array with single value entries?
So the class would be something like:
class xFromArray() {
}
So the above if I passed $array_1 it would generate something like this:
private $one;
setOne($x) {
$one = $x;
}
getOne() {
return $one;
}
if I passed $array_2 it would generate something like this:
private $red;
setRed($x) {
$red = $x;
}
getRed() {
return $red;
}
So I would call it somehow like this? (My best guess but doesn't seem that this would work)
$xFromArray = new xFromArray;
foreach($array_1 as $key=>$data) {
$xFromArray->create_function(set.ucfirst($data)($data));
echo $xFromArray->create_function(get.ucfirst($data));
}
You can use __call() to invoke dynamic methods. So:
class Wrapper {
private $properties;
public function __construct(array $names) {
$this->properties = array_combine(array_keys($names),
array_fill(0, count($names), null));
}
public function __call($name, $args) {
if (preg_match('!(get|set)(\w+)!', $name, $match)) {
$prop = lcfirst($match[2]);
if ($match[1] == 'get') {
if (count($args) != 0) {
throw new Exception("Method '$name' expected 0 arguments, got " . count($args));
}
return $properties[$prop];
} else {
if (count($args) != 1) {
throw new Exception("Method '$name' expected 1 argument, got " . count($args));
}
$properties[$prop] = $args[0];
}
} else {
throw new Exception("Unknown method $name");
}
}
}
Personally I wouldn't go the route of using getters and setters in PHP. Use the special methods __get() and __set() instead and treat these dynamic properties as object properties rather than adding a (most likely unnecessary) method wrapper.
Edit: to clarify, __call() is invoked when you call an method in an object that either doesn't exist or is inaccessible. So:
$wrapper = new Wrapper($array_1);
$wrapper->setOne("foo");
echo $wrapper->getOne(); // foo
$wrapper->getAbc(); // exception, property doesn't exist
__call() is used here to decipher the method name. If it fits the pattern of get or set followed by a property name (from the initial array) then it works as expected, otherwise it throws an exception. You can of course change this behaviour any way you wish.
See Overloading from the PHP manual for a more detailed explanation of these "magic" methods.
You can use __call() (or __set() && __get()), but they have some overhead.