I think we’ve all gotten sick of keeping track of arguments lists for certain functions and suddenly felt the desire to resort to named params in order to avoid messes such as functions that look like
foo(array $argot, ingot $begot,ingot $cargot, cat $dargot,$ergot,$farthing,$garging,$haring,$baonr,$tarish = 4 ,$smar = "lare",$lar = null,$var = smare, $snare= gare);
The simplicity of
foo($params);
is clear, especially if many or all of our named params have default values and do not need to be passed in explicitly.
I have been playing with the concept of a NamedParam class. It is a class that allows us to clearly specify default values, specify required parameters, required parameters types and constraints on valid parameters values. It also provides functionality for restoring class or construction time default values — this feature may be useful if we want to quickly revert to our specified parameters when for instance our user has provided us with garbage values but we do not wish to halt execution.
/ /For our callers we keep things fairly simple
// Callers Do This:
$ClassInstance->AcceptsNamedParams( array('garfield' => 6, 'apple' => 'fruit'));
// Implementors have a little more work to do. Often we may be able to reduce the size of the second parameter by keeping a common group of default arrays handy at the class scope,
// or by setting many of these universal (to our named params class) values in our namedparams derived classes's $_classConditions member.
//Implementors Do This:
public function AcceptsNamedParams($namedparams)
{
$params = new MyClassesNamedParamedInstance($namedparams, array( 'garfield' /*required field*/ ,
'apple' /*required field*/ ,
'tango' => array( 'required' /* required field */ ,
'type' = array( 'int, /*or*/ , 'float', /*or*/ 'MyComplexNumberClass'),
'lessthan' => 10, /* if the value of tango is not < 10 meetscriteria will fail */
'greaterthan' => 3, /* if the value of tango is not > 3 meetscriteria will fail */
'default' =>5); /* If the user has not set a value for tango it will be set to 5.
if(!$params->MeetsCriteria)
{
echo "Please call this function with all required constraints met! ";
var_dump($this->getUnmetCriteria());
} else {
echo $params->tango . " is the best number ever!";
}
}
/**
* Description of NamedParams
*
* @author kbrings
*/
class NamedParams {
protected $_namedParams = array();
protected $_constructConditions = array();
protected $_constructConditionsCriteria = array();
protected $_constructConditionsDefaults = array();
protected $_classConditions = array();
protected $_classConditionsCriteria = array();
protected $_classConditionsDefaults = array();
protected $_requiredFields = array();
protected $_metCriteria = null;
protected $_failedCriteria = array();
/**
*@example array( firstName => array( 'Required', 'Default' => 7, 'Type' => 'int' );
* @param array $array
*/
public function ProcessConditionArray(array $array = null) {
$_criteria = array();
$_values = array();
foreach($array as $key => $criteria) {
if(is_null($criteria)) {
$_criteria[$key][] = array('required' , null);
}
if(is_numeric($key) && is_string($criteria)) {
$_criteria[$criteria][] = array('required' , null);
}
if(is_array($criteria)) {
foreach ($criteria as $criterion=>$params) {
$criterion = strtolower($criterion);
if($criterion == 'default') {
$_values[$key] = $params;
}
else {
if(is_numeric($criterion) && is_string($params)) {
$criterion = strtolower($params);
$params = null;
}
$_criteria[$key][] = array($criterion, $params);
}
}
}
}
return array($_criteria, $_values);
}
public function MeetsCriteria($refresh = false) {
if($this->_metCritieria !== null && !$refresh) {
return $this->_metCriteria;
}
$meetsCriteria = true;
$this->_failedCriteria = array();
$whiteList = array();
$meetsCriteria &= $this->MeetsCriteriaGroup($this->_constructConditionsCriteria, $whiteList);
$meetsCriteria &= $this->MeetsCriteriaGroup($this->_classConditionsCriteria, $whiteListl);
$this->_metCriteria = $meetsCriteria;
return (bool)$this->_metCriteria;
}
protected function MeetsCriteriaGroup($criteriaGroup) {
$meetsCriteria = true;
foreach ($criteriaGroup as $attribute =>$criteria) {
foreach($criteria as $index => $criterion) {
$func = "cri_" . $criterion[0];
if(method_exists($this, $func)) {
$meetsCriteria &= $pass = $this->$func($attribute,$criterion[1]);
if($pass == false) {
$this->_failedCriteria[] = array('attribute' => $attribute, 'msg'=>"Criteria Not Met", 'criteria'=>$criterion[0], 'conditions'=>$criterion[1], 'value'=>$this->$attribute);
}
}
else {
$this->_failedCriteria[] = array('attribute' => $attribute, 'msg'=>"Criteria Function Not Found", 'criteria'=>$criterion[0], 'conditions'=>$criterion[1], 'value'=>$this->$attribute);
return false;
}
}
}
return $meetsCriteria;
}
public function getUnmetCriteria() {
return $this->_failedCriteria;
}
public function __construct($namedParams, $conditions) {
$this->_namedParams = $namedParams;
$this->_constructConditions = $conditions;
list($this->_constructConditionsCriteria, $this->_constructConditionsDefaults) = $this->ProcessConditionArray($conditions);
list($this->_classConditionsCriteria, $this->_classConditionsDefaults) = $this->ProcessConditionArray($this->_classConditions);
}
public function output() {
return "------- named params -------\n" .
print_r($this->_namedParams,true) .
"------- construct defaults -------\n" .
print_r($this->_constructConditionsDefaults, true) .
"------- class defaults -------\n" .
print_r($this->_classConditionsDefaults, true);
}
public function __get($attribute) {
if(array_key_exists($attribute, $this->_namedParams)) {
return $this->_namedParams[$attribute];
}
else if(array_key_exists($attribute, $this->_constructConditionsDefaults)) {
return $this->_constructConditionsDefaults[$attribute];
}
else if(array_key_exists($attribute, $this->_classConditionsDefaults)) {
return $this->_classConditionsDefaults[$attribute];
}
}
public function __set($attribute, $value) {
$this->_namedParams[$attribute] = $value;
}
public function __isset($attribute) {
return isset($this->_namedParams[$attribute]) ||
isset($this->_constructConditionsDefaults[$attribute]) ||
isset($this->_classConditionsDefaults[$attribute]);
}
public function __unset($attribute) {
if(array_key_exists($attribute, $this->_namedParams)) {
unset($this->_namedParams[$attribute]);
}
else if(array_key_exists($attribute, $this->_constructConditionsDefaults)) {
unset($this->_constructConditionsDefaults[$attribute]);
}
else if(array_key_exists($attribute, $this->_classConditionsDefaults)) {
unset($this->_classConditionsDefaults[$attribute]);
}
}
public function clearToDefault($attribute) {
if(array_key_exists($attribute, $this->_namedParams)) {
unset($this->_namedParams[$attribute]);
}
}
public function clearToClassDefault($attribute) {
if(array_key_exists($attribute, $this->_namedParams)) {
unset($this->_namedParams[$attribute]);
}
if(array_key_exists($attribute, $this->_constructConditionsDefaults)) {
unset($this->_constructConditionsDefaults[$attribute]);
}
}
//==========================================================================
// Criteria Tests
// These functions are called indirectly by the MeetsCriteriaGroup function.
//==========================================================================
protected function cri_equals($attribute,$target) {
return $this->$attribute == $target;
}
protected function cri_tequals($attribute,$target) {
return $this->$attribute === $target;
}
protected function cri_notequals($attribute,$target) {
return $this->$attribute != $target;
}
protected function cri_nottequals($attribute,$target) {
return $this->$attribute !== $target;
}
protected function cri_greaterthan($attribute,$target) {
return $this->$attribute > $target;
}
protected function cri_greaterequalthan($attribute,$target) {
return $this->$attribute >= $target;
}
protected function cri_lessthan($attribute,$target) {
return $this->$attribute < $target;
}
protected function cri_lessequalthan($attribute,$target) {
return $this->$attribute <= $target;
}
protected function cri_required($attribute,$target) {
return isset($this->$attribute);
}
protected function cri_type($attribute,$target) {
if(!is_array($target)) {
$target = array($target);
}
$typeMatched = false;
foreach($target as $entry) {
switch($entry) {
case 'bool':
case 'boolean':
case 'string':
case 'array':
case 'numeric':
case 'float':
case 'double':
case 'array':
case 'object':
case 'null':
case 'int':
$f = "is_$entry";
$typeMatched |= $f($this->$attribute);
break;
default:
$typeMatched |= $this->$attribute instanceof $entry;
break;
}
if($typeMatched) return true;
}
}
}
require_once 'NamedParams.php';
require_once 'PHPUnit/Framework.php';
class TestNamedParams extends NamedParams{
protected $_classConditions = array( 'test' => array('required', 'type'=>'int','default' => 7),
'test2' => array('required', 'type'=>array('string','null'),'default' => "bannaple"),
);
}
class NamedParamsTest extends PHPUnit_Framework_TestCase {
public $functionConditions = array (
'test' => array('default' => 8, 'LessThan' => 3),
'reqParam',
'reqParam2' => array('type' => array('float','double')),
'defParam3' => array('default' => 'foo')
);
public function testSmoke()
{
$params = new TestNamedParams(array(), array());
}
public function testBasicFunctionality()
{
$namedParams = array( 'test'=>7, 'reqParam' => 'apple', 'reqParam2' => 3.14179);
$params = new TestNamedParams($namedParams, $this->functionConditions);
$params->MeetsCriteria(true);
$this->AssertEquals(7,$params->test);
$this->AssertFalse($params->MeetsCriteria(true));
$t = $params->getUnmetCriteria();
$this->AssertEquals($t[0]['attribute'], "test");
$this->AssertEquals($t[0]['criteria'], "lessthan");
$params->test = 2;
$this->AssertTrue($params->MeetsCriteria(true));
$this->AssertEquals(2,$params->test);
$this->AssertEquals('apple',$params->reqParam);
$this->AssertEquals(3.14179,$params->reqParam2);
$this->AssertEquals('bannaple',$params->test2);
$this->AssertEquals('foo',$params->defParam3);
}
/**
*
* @param $namedParams
* @param $attribute
* @param $value
* @param $msg
* @dataProvider attributeRetrievalTestCases
*/
public function testAttributeRetrieval($namedParams, $attribute,$value,$msg)
{
$params = new TestNamedParams($namedParams, $this->functionConditions);
$this->AssertEquals($params->$attribute,$value,$msg);
}
public function attributeRetrievalTestCases()
{
return array(
array(
array("test" => 77),
"test",
77,
"passed class default override value test was not set correctly"
),
array(
array("newp" => 77),
"newp",
77,
"passed value newp was not set correctly"
),
array(
array("reqParam" => 77),
"reqParam",
77,
"passed value reqParam was not set correctly"
),
array(
array("test" => null),
"test",
null,
"overidden value test was not set to null correctly"
),
);
}
}
unit tests output
PHPUnit 3.3.16 by Sebastian Bergmann.
......
Time: 0 seconds
OK (6 tests, 14 assertions)

Categories
Tag Cloud
Blog RSS
Comments RSS
Last 50 Posts
Back
Void « Default
Life
Earth
Wind
Water
Fire
Light 
Yhanks for your great website! ^^