Following "problem"
PHP Class with a lot of propertys. A lot of Getters / Setter.
Is there any nice solution to convert all propertys to an array?
protected $name;
protected $date;
public function getName();
public function getDate();
public function asArray(); // call all getters?
Is your API already defined and are you stuck with getX and setX methods? I much prefer properties. Less typing, better distinction between properties and methods, and resulting code looks more like PHP and less like Java. But exposing properties doesn't mean you lose encapsulation and make all your internals public. With __get and __set magic methods you can have pretty fine-grained control over what you present. Plus, it would be rather trivial to dump the properties as an array:
class Foo
{
protected $properties;
public function __construct() {
$this->properties = array();
}
public function __set($prop, $value) {
$this->properties[$prop] = $value;
}
public function __get($prop) {
return $this->properties[$prop];
}
public function toArray() {
return $this->properties;
}
}
Alas, if you're stuck with setters/getters because of a cranky boss or some misunderstanding of what OOP must be, why not just cast the object to an array?
class Bar
{
public $x;
public $y;
public $z;
protected $a;
protected $b;
protected $c;
private $q;
private $r;
private $s;
public function __construct() {
}
public function setA($value) {
$this->a = $value;
}
public function getA() {
return $this->a;
}
public function setB($value) {
$this->b = $value;
}
public function getB() {
return $this->b;
}
public function setC($value) {
$this->c = $value;
}
public function getC() {
return $this->c;
}
public function toArray() {
return (array)$this;
}
}
Notice how public, protected, and private properties are cast:
$bar = new Bar();
print_r($bar->toArray());
array(9) {
["x"]=>
NULL
["y"]=>
NULL
["z"]=>
NULL
[" * a"]=>
NULL
[" * b"]=>
NULL
[" * c"]=>
NULL
[" Foo q"]=>
NULL
[" Foo r"]=>
NULL
[" Foo s"]=>
NULL
}
Note that the array keys for protected/private don't start with a space, it's a null. You can re-key them, or even filter out protected/private properties if you like:
public function toArray() {
$props = array();
foreach ((array)$this as $key => $value) {
if ($key[0] != "\0") {
$props[$key] = $value;
}
}
return $props;
}
You're working with a dynamic language; take advantage of that and enjoy it!
How about using ReflectionClass and ReflectionMethod, something like this:
class PropertyHolder
{
private $name;
private $date;
private $anotherProperty;
public function __construct($name, $date)
{
$this->name = $name;
$this->date = $date;
}
public function getName()
{
return $this->name;
}
public function getDate()
{
return $this->date;
}
public function asArray()
{
$result = array();
$clazz = new ReflectionClass(__CLASS__);
foreach ($clazz->getMethods() as $method) {
if (substr($method->name, 0, 3) == 'get') {
$propName = strtolower(substr($method->name, 3, 1)) . substr($method->name, 4);
$result[$propName] = $method->invoke($this);
}
}
return $result;
}
You could use PHP's reflection capabilities. Here's an example:
http://www.weberdev.com/get_example-4672.html
Try looking into get_object_vars(), get_class_vars and others in the same category. The examples shown there look like pretty much what you need. Check the comments there (for example http://www.php.net/manual/en/function.get-class-vars.php#87772) they already provide ways that suit your needs.
A simple (array) cast on $this will suffice:
(array) $this;
If you have additional properties (private ones for example, that shall not be toArray()ed) you can afterwards unset these:
public function toArray() {
$array = (array) $this;
unset($array['private'], $array['privateagain']);
return $array;
}
One option would be to create an array in your constructor.
You will have one getter and one setter..
When you want to set or get something, do something like:
$foo->get( 'UID' ); //(to get user id)
or
$foo->set( 'UID', 5 ); // to set something)
Related
I like PHP, but I miss some of the constructs from other languages that although don't do anything for performance, make the code look cleaner and possibly more maintainable. I'm thinking of Visual Basic days and the "with" statement.
So ideally in PHP we could do this:
with($myWellDescribedInstance) {
->property1="string";
->property2=1;
->property3=2;
->myMethod();
}
Instead of
$myWellDescribedInstance->property1="string";
$myWellDescribedInstance->property2=1;
$myWellDescribedInstance->property3=2;
$myWellDescribedInstance->myMethod();
Is there anything like this in PHP?
You can implement a fluent interface on any class just by having a function return $this.
This is mostly used for setters, but of course it works for any method for which you would normally not have a return value.
For example:
class Person
{
protected $name = '';
protected $surname = '';
protected $email = '';
public function getName()
{
return $this->name;
}
public function getSurname()
{
return $this->surname;
}
public function getEmail()
{
return $this->email;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setSurname($surname)
{
$this->surname = $surname;
return $this;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
}
Usage:
$person = new Person;
$person->setName('John')
->setSurname('Doe')
->setEmail('johndoe#email.com');
Of course, calling the method (for example) setName or withName would be entirely up to you.
Another idea might be to have both a setName method (which doesn't return anything) and a withName method (which returns $this), but that might be a bit of an overkill.
If you use "setters" instead of direct property access you can chain methods.
class A {
private $a;
private $b;
public function setA($a)
{
$this->a = $a;
return $this;
}
public function setB($b)
{
$this->b = $b;
return $this;
}
public function doSomething()
{}
}
$a = new A();
$a->setA('a')
->setB('b')
->doSomething();
so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.
I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.
Does anyone know of an efficient technique in PHP to auto assign class parameters with identically named __construct() method arguments?
For instance, I've always thought it was highly inefficient to do something like the following:
<?php
class Foo
{
protected $bar;
protected $baz;
public function __construct($bar, $baz)
{
$this->bar = $bar;
$this->baz = $baz;
}
}
I'm wondering if there's a better/more efficient way/magic method to auto-assign class properties with identically named method parameters.
Thanks,
Steve
PHP 8
Constructor Promotion
function __construct(public $bar, public $baz) {}
PHP 5
function _promote(&$o) {
$m = debug_backtrace(0, 2)[1];
$ref = new ReflectionMethod($m['class'], $m['function']);
foreach($ref->getParameters() as $i=>$p) {
$o->{$p->name} = $m['args'][$i] ?? $p->getDefaultValue();
}
}
class Foo {
function __construct($bar, $baz) {
_promote($this);
}
}
I think this way is a pretty generally accepted way to do it, although you could make getters and setters. Or, if you're looking for magic methods in php: http://php.net/manual/en/language.oop5.magic.php
Not in a constructor. You can always wrap your properties into an array, instead, and only have a single property that needs to be set:
<?php
class SomeClass
{
protected $data = [];
public function __construct(array $data = [])
{
$this->data = $data;
}
public function getData()
{
return $this->data;
}
}
$params = ['bar' => 'bar', 'baz' => 'baz'];
$someClass = new SomeClass($params);
echo $someClass->getData()['bar'] . PHP_EOL;
There is the magic method __set, but it is only called when attempting to write data to inaccessible properties:
<?php
class SomeClass
{
protected $data = [];
public function __construct(array $data = [])
{
$this->data = $data;
}
public function __set($name, $value)
{
$this->data[$name] = $value;
}
public function __get($name)
{
if(isset($this->data[$name])) {
return $this->data[$name];
}
return null;
}
}
$class = new SomeClass;
$class->bar = 'bar';
$class->baz = 'baz';
echo $class->bar . PHP_EOL . $class->baz . PHP_EOL;
If your class is starting to have a lot of parameters being passed in to the constructor, it may be a sign that your class is getting too big and trying to do too much. A refactoring may be in order.
How do I know what to load in a constructor and what to set using the set methods later on?
For example, I have a question class which most of the time will call the following vars:
protected $question;
protected $content;
protected $creator;
protected $date_added;
protected $id;
protected $category;
At the moment I have it so only the bare essentials $id, $question, and $content are set in the constructor so I don't start building up a huge list of constructor arguments. This however, means that when I make a new question object elsewhere, I have to set the other properties of that object straight after meaning 'setter code' getting duplicated all over the place.
Should I just pass them all into the constructor right away, do it the way I'm doing it already, or is there a better solution that I'm missing? Thanks.
Depending on the language you can have multiple constructors for any one class.
You could use an array as the parameter to the constructor or setter method.
Just example:
public function __construct($attributes = array()) {
$this->setAttributes($attributes);
}
public function setAttributes($attributes = array()) {
foreach ($attributes as $key => $value) {
$this->{$key} = $value;
}
}
PHP doesn't support traditional constructor overloading (as other OO languages do). An option is to pass an array of arguments into the constructor:
public function __construct($params)
{
}
// calling it
$arr = array(
'question' => 'some question',
'content' => ' some content'
...
);
$q = new Question($arr);
Using that, you're free to pass a variable number of arguments and there is no dependency on the order of arguments. Also within the constructor, you can set defaults so if a variable is not present, use the default instead.
I would pass an array to the constructor with the values I want to set.
public function __construct(array $values = null)
{
if (is_array($values)) {
$this->setValues($values);
}
}
Then you need a method setValues to dynamicly set the values.
public function setValues(array $values)
{
$methods = get_class_methods($this);
foreach ($values as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
For this to work you need setter methods for your properties like setQuestion($value) etc.
A fluent interface is another solution.
class Foo {
protected $question;
protected $content;
protected $creator;
...
public function setQuestion($value) {
$this->question = $value;
return $this;
}
public function setContent($value) {
$this->content = $value;
return $this;
}
public function setCreator($value) {
$this->creator = $value;
return $this;
}
...
}
$bar = new Foo();
$bar
->setQuestion('something')
->setContent('something else')
->setCreator('someone');
Or use inheritance...
class Foo {
protected $stuff;
public function __construct($stuff) {
$this->stuff = $stuff;
}
...
}
class bar extends Foo {
protected $moreStuff;
public function __construct($stuff, $moreStuff) {
parent::__construct($stuff);
$this->moreStuff = $moreStuff;
}
...
}
Or use optional parameters...
class Foo {
protected $stuff;
protected $moreStuff;
public function __construct($stuff, $moreStuff = null) {
$this->stuff = $stuff;
$this->moreStuff = $moreStuff;
}
...
}
In any case, there are many good solutions. Please dont use a single array as params or func_get_args or _get/_set/__call magic, unless you have a really good reason to do so, and have exhausted all other options.
If I have the following class example:
<?php
class Person
{
private $prefix;
private $givenName;
private $familyName;
private $suffix;
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
public function getPrefix()
{
return $this->prefix;
}
public function setGivenName($gn)
{
$this->givenName = $gn;
}
public function getGivenName()
{
return $this->givenName;
}
public function setFamilyName($fn)
{
$this->familyName = $fn;
}
public function getFamilyName()
{
return $this->familyName;
}
public function setSuffix($suffix)
{
$this->suffix = $suffix;
}
public function getSuffix()
{
return $suffix;
}
}
$person = new Person();
$person->setPrefix("Mr.");
$person->setGivenName("John");
echo($person->getPrefix());
echo($person->getGivenName());
?>
I there a way in PHP (5.4 preferably), to combine these return values into one function, this way it models a little bit more like the revealing module pattern in JavaScript?
UPDATE:
OK, I am now beginning to learn that within PHP, it is normative to return a single value from a function, but you "can" return an array of multiple values. This is the ultimate answer to my question and what I will dive into some practices with this understanding.
small example -
function fruit () {
return [
'a' => 'apple',
'b' => 'banana'
];
}
echo fruit()['b'];
Also an article I ran across on stackoverflow on the topic...
PHP: Is it possible to return multiple values from a function?
Good luck!
You sound like you want the __get() magic method.
class Thing {
private $property;
public function __get($name) {
if( isset( $this->$name ) {
return $this->$name;
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
In the case that you want all of the values returned at once, as in Marc B's example, I'd simplify the class design for it thusly:
class Thing {
private $properties = array();
public function getAll() {
return $properties;
}
public function __get($name) {
if( isset( $this->properties[$name] ) {
return $this->properties[$name];
} else {
throw new Exception('Cannot __get() class property: ' . $name);
}
}
} // -- end class Thing --
$athing = new Thing();
$prop = $athing->property;
$props = $athing-> getAll();
Perhaps
public function getAll() {
return(array('prefix' => $this->prefix, 'givenName' => $this->giveName, etc...));
}