I\'m designing a class that defines a highly complex object with a ton (50+) of mostly optional parameters, many of which would have defaults (eg: $type = \'foo\'; $width
Another approach is to instantiate the class with a FooOptions
object, acting solely as an options container:
<?php
class Foo
{
/*
* @var FooOptions
*/
private $_options;
public function __construct(FooOptions $options)
{
$this->_options = $options;
}
}
class FooOptions
{
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
public function setType($type);
public function getType();
public function setWidth($width);
public function getWidth();
// ...
}
Your options are well documented and you have an easy way to set/retrieve them. This even facilitates your testing, as you can create and set different options objects.
I don't remember the exact name of this pattern, but I think it's Builder or Option pattern.
I use this on a few of my classes. Makes it easy to copy and paste for rapid development.
private $CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType;
function __construct($CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType){
$varsValues = array($CCNumber, $ExpMonth, $ExpYear, $CV3, $CardType);
$varNames = array('CCNumber', 'ExpMonth', 'ExpYear', 'CV3', 'CardType');
$varCombined = array_combine($varNames, $varsValues);
foreach ($varCombined as $varName => $varValue) {$this->$varName = $varValue;}
}
Steps to use:
I can think of two ways of doing that. If you want to keep your instance variables you can just iterate through the array passed to the constructor and set the instance variable dynamically:
<?php
class Foo {
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
function __construct($args){
foreach($args as $key => $val) {
$name = '_' . $key;
if(isset($this->{$name})) {
$this->{$name} = $val;
}
}
}
}
?>
When using the array approach you don't really have to abandon documentation. Just use the @property annotations in the class body:
<?php
/**
* @property string $type
* @property integer $width
* @property boolean $interactive
*/
class Foo {
private $_instance_params = array(
'type' => 'default_type',
'width' => 100,
'interactive' => true
);
function __construct($args){
$this->_instance_params = array_merge_recursive($this->_instance_params, $args);
}
public function __get($name)
{
return $this->_instance_params[$name];
}
public function __set($name, $value)
{
$this->_instance_params[$name] = $value;
}
}
?>
That said, a class with 50 member variables is either only used for configuration (which can be split up) or it is just doing too much and you might want to think about refactoring it.
Just to follow up with how I implemented this, based on one of Daff's solutions:
function __construct($args = array()){
// build all args into their corresponding class properties
foreach($args as $key => $val) {
// only accept keys that have explicitly been defined as class member variables
if(property_exists($this, $key)) {
$this->{$key} = $val;
}
}
}
Improvement suggestions welcomed!
Just a little improvement on Daff's first solution to support object properties that may have a null default value and would return FALSE to the isset() condition:
<?php
class Foo {
private $_type = 'default_type';
private $_width = 100;
private $_interactive = true;
private $_nullable_par = null;
function __construct($args){
foreach($args as $key => $val) {
$name = '_' . $key;
if(property_exists(get_called_class(),$name))
$this->{$name} = $val;
}
}
}
}
?>
You also could make a parent class.
In that class you only define the variables.
protected function _SetVarName( $arg ){
$this->varName=$arg;
}
Then extend that class into a new file and in that file you create all your processes.
So you get
classname.vars.php
classname.php
classname extends classnameVars {
}
Because most will be on default you only have to Set/Reset the ones you need.
$cn=new classname();
$cn->setVar($arg);
//do your functions..