Avoiding if-statements with object oriented design, PHP - php

I'm basically creating a display-module for an ad-system I've created.
I'm trying to avoid the following construction, with repeated if-statements.
My gut feeling tells me there's a smarter way to do this, maybe with polymorphism?
<?php
class Ad {
public $adState = 'active';
}
class AdWriter {
public function displayAd(Ad $ad, $viewmode = 'visitor') {
if ($viewmode =='visitor') {
if ($adState == 'active') {}
else if ($adState == 'paused') {}
else if ($adState == 'inactive') {}
}
else if ($viewmode = 'owner') {
if ($adState == 'active') {}
else if ($adState == 'paused') {}
else if ($adState == 'inactive') {}
}
else if ($viewmode == 'administrator') {
if ($adState == 'active') {}
else if ($adState == 'paused') {}
else if ($adState == 'inactive') {}
}
}
}
?>

Apply the Refactoring Replace Conditional with Polymorphism and have a look at the State Pattern.

You could create an Factory (Pattern), using an switch on viewmode and create an specific Ad implementing an interface having an simple function 'display' for example.
Pseudo example:
class AdFactory {
public static function getAd($sType) {
switch($sType) {
case "AdOne":
return new AdOne();
case "AdTwo":
return new AdTwo();
}
}
throw new Exception("Unknown ad!");
}
class AdOne implement AdInterface {
public function display() {
// All that AdOne does when displaying.
}
}
interface AdInterface {
public function display() { }
}
$oAd1 = AdFactory::getAd('typeOne');
$oAd1->display();
$oAd2 = AdFactory::getAd('typeTwo');
$oAd2->display();

Instead of passing $viewmode, pass an object that would encapsulate the logic for this viewmore and call its method that would do the work. This way you'll avoid the need for if-statements.

I'm sneaking onto StackOverflow at work, so don't have time to write a detailed reply of all your possibilities.
But to 'tidy up' those ifs, you can do this:
switch $viewmode {
case 'visitor':
your_code_here;
break;
case 'owner':
your_code_here;
break;
default:
will_run_if_nothing_above_matches;
break;
}

switch($viewmode){
case "visitor":
switch($adstate){
case "active":
//statement
break;
case "paused":
break;
case "inactive":
break;
}
break;
case "owner":
break;
case "administrator":
break;
}

In 8 chapter of this book you can find very detailed answer to your question.
In short: use Composition or Factories. (see answer of Wesley van Opdorp).
Also, avoid using string arguments as enumerable:
$viewmode = 'visitor'
with this argument, you will have to keep in memory all possible values of this argument. Or look into code of function to remember them. And these values are strings - good place for typos. Also, it will be very difficult to change values in feature, because all calls of this method will contain hardcoded strings.
Use class-constants:
class AdWriter {
const view_mode_visitor = 1;
...
Also, $adState - wrong code, should be $ad->state. But using public fields it's bad practice too :)

Related

Toplevel equivalent in PHP of "return" keyword outside function block?

Toplevel equivalent in PHP of "return" keyword outside function blocks ?
A nice, useful property of the return keyword is that when it is invoked,
it exits the main function block in which it is, no matter how many other
nested blocks may surround it.
I am not aware of any equivalent for the toplevel scope. And I would like
to have a systematic way of "expanding" a function
call into an equivalent list of statements, and for that I need to know
a systematic way to deal with all the return ’s in the code.
Consider for example
function seekAUnicorn()
{
for($i=1;some_test($i);$i++) {
for($j=1;some_test($j);$j++) {
for($k=1;some_test($k);$k++) {
if(unicorn_test_for_three_parameters($i,$j,$k)) return(array($i,$j,$k));
}
if(unicorn_test_for_two_parameters($i,$j)) return(array($i,$j));
}
if(unicorn_test_for_one_parameter($i)) return(array($i));
}
}
The expansion of $searchResult=seekAUnicorn(); might look something like this :
for($i=1;some_test($i);$i++) {
for($j=1;some_test($j);$j++) {
for($k=1;some_test($k);$k++) {
if(unicorn_test_for_three_parameters($i,$j,$k)) {
$searchResult = array($i,$j,$k);
break 3;
}
}
if(unicorn_test_for_two_parameters($i,$j)) {
$searchResult = array($i,$j);
break 2;
}
}
if(unicorn_test_for_one_parameter($i)){
$searchResult = array($i);
break;
}
}
But to do it this way, you need to count the number of nested blocks
around each nested return, which becomes painstaking and error-prone for
longer and more complicated code. Is there a simpler way ?
In your case, you can either use goto (since PHP 5.3) or by adding another simple check.
$loopcheck = true;
for($i=1;some_test($i) && $loopcheck;$i++) {
for($j=1;some_test($j) && $loopcheck;$j++) {
for($k=1;some_test($k) && $loopcheck;$k++) {
if(unicorn_test_for_three_parameters($i,$j,$k)) {
$searchResult = array($i,$j,$k);
$loopcheck = false;
break;
}
}
if(unicorn_test_for_two_parameters($i,$j)) {
$searchResult = array($i,$j);
$loopcheck = false;
break;
}
}
if(unicorn_test_for_one_parameter($i)){
$searchResult = array($i);
$loopcheck = false;
}
}

PHP create object inside it's own constructor

EDIT: Link to my first question. Might clear some things up.
PHP Get corresponding data, with default and error handling
I have a function that checks if a GET statement exists. If so, it passes the value to a other function that then selects a class based on the value of the GET statement.
explaining:
The url = Page=Contact
The GetFormVariable approves it, and the class Contact is selected and it will give back a string. This string is used as an object 'Content' that, as it says, creats the content of the page.
public function getFormVariable($value){
switch (strtoupper($_SERVER['REQUEST_METHOD'])) {
case 'GET':
if (isset($_GET[$value]) && $_GET[$value] != NULL) {
return $_GET[$value];
}
else{
return false;
}
break;
case 'POST':
if (isset($POST[$value]) && $POST[$value] != NULL) {
return $POST[$value];
}
else{
return false;
}
break;
default:
return false;
}
}
Now the question.
When there is no GET statement in the url. The GetFormVariable returns false. And this means there is nothing shown.
How do i give this constructor.
public function SetProperty ($prob, $val){
$this->$prob = $val;
}
The information to create the ContentHome.
SetProperty('Content', 'ContentHome');
Sorry for poor explanation, if anything is unclear please tell me so.
I'm suggesting we close this question as unclear what you're asking, but decided to throw some help on the provided code sample anyway...
You can strip this down dramatically, and since there's no context, the function can be static too.
static public function getFormVariable($value)
{
if($_SERVER['REQUEST_METHOD'] == 'GET' &&
isset($_GET[$value]) &&
!empty($_GET[$value]))
return $_GET[$value];
elseif($_SERVER['REQUEST_METHOD'] == 'POST' &&
isset($POST[$value]) &&
!empty($POST[$value]))
return $POST[$value];
return false;
}
Your original isset and != NULL checks were doing the same check. Maybe you want the empty() check as a third check, but look it up to be certain.
The question is unclear.
How you call getFormVariable? How you use SetProperty with the information getFormVariable provides?
As far as I understood, you mean this...
$var = getFormVariable(???);
if (false === $var)
{
SetProperty('Content', 'ContentHome');
} else {
SetProperty('var', $var);
}

Pass by null reference in PHP

In PHP I need to pass some arguments to a function by reference.
I don't want to write 2 different methods for similar behaviour.
So i need to select behaviour by argument.
But I can't pass null by reference.
So I created a dummy array.
So i run it either by
$temp[0]=-1;
$this->doSomething($bigIds, $temp);
or
$temp[0]=-1;
$this->doSomething($temp, $smallIds);
public function doSomething(&$bigIds, &$smallIds) {
if ($bigIds[0] != -1) {
// make some thing
}
if ($smallIds[0] != -1) {
// make some thing
}
}
Is there a better/ elegant way to do this?
I would suggest an enum but this is PHP. So this should do it for you:
class YourClass
{
const DoSomethingSmall = 0;
const DoSomethingBig = 1;
public function doSomething(&$ids, $actionType) {
// can also use a switch here
if($actionType == self::DoSomethingSmall) {
// do small
}
else if($actionType == self::DoSomethingBig) {
// do big
}
}
}
Then you can do:
$this->doSomething($bigIds, self::DoSomethingBig);
$this->doSomething($smallIds, self::DoSomethingSmall);
From outside the class you can use YourClass::DoSomethingBig and YourClass::DoSomethingSmall
There could be loads of things you might rather do, for instance what #ad7six says in a comment, and you could also just give it some sort of setting and just one array..
public function doSomething(&$bIds, $mode) {
switch($mode){
case 1: do smallthing;break;
case 2: do bigthing;break;
case 3: do both;break;
default: do nothing;break;
}
It all depends on what you need really

Breaking out of multiple functions (short circuiting) in PHP

I want to return multiple nested functions in PHP. It's possible to break out of multiple loops by adding a number after "break". Eg.
while(1)
while(1)
while(1)
break 3;
Can I do a circuit break while calling a sequence of functions?
Not that I know of, it's also not very healthy of a design, as the parent and grandparent functions in question will never know of the break. You should throw an exception and catch it on the parent, which in turn will throw an exception and catch it on the grandparent etc.
To "break" out of functions, you can use the return.
function somefunction()
{
return;
echo 'This will never get displayed';
}
Another solution would be to add a condition to each while.
while(1 && $isTrue)
while(1 && $isTrue)
while(1 && $isTrue)
$isTrue = false;
break;
Although I don't think this is a very clean approach.
As the manual states break is for loop only.
What I do in such cases is that have an exception return value(or object) and do value check on return value at every function return point to make sure that the situation is propagated or handled appropriately, be careful while doing recursions though, you might completely fold up the tree by mistake....btw if it is a simple exit on error kind of situation you can also use exceptions.
It's possible to return a special result from child functions that indicates a specific condition has been met. WordPress uses WP_Error and is_wp_error() for this sort of operation. Any number of nested functions can check to see if a called function returned an error state, and opt to pass that error up the chain rather than continue with processing.
Example:
function outer() {
$result = inner();
// pass failure back to parent
if( is_wp_error($result) ) {
return $result;
}
// other processing
return $final_result;
}
function inner() {
if( some_condition() ) {
// generate an error
return new WP_Error( 'code', 'message' );
}
return $other_result;
}
$result = outer();
// did we get an error?
if( is_wp_error($result) ) {
echo 'Something went wrong.';
} else {
echo $result;
}
Yes, you can very simply construct a "body-less" while() or if() block. Typically, you will see PSR-12 compliant PHP code using {} to bookend the body of the loop/condition block, but the body is not required. Writing a semicolon at the end of the line will be sufficient and your IDE will not complain about bad syntax.
Returning a truthy value from each function will be an adequate indicator that the following function is authorised for execution.
This will provide the "short circuit" functionality that is desired without creating nested control structures or passing variables into different scopes.
I'll demonstrate with a battery of generic functions:
function echo1T() {
echo "1";
return true;
}
function echo2T() {
echo "2";
return true;
}
function echo3T() {
echo "3";
return true;
}
function echo1F() {
echo "1";
return false;
}
function echo2F() {
echo "2";
return false;
}
function echo3F() {
echo "3";
return false;
}
Code: (Demo with more scenarios)
while (echo1T() && echo2F() && echo3T()); // outputs: 12
if (echo1T() && echo2F() && echo3T()); // outputs: 12
$return = echo1T() && echo2F() && echo3T(); // outputs: 12
var_export($return); // outputs false

Should I use methods or constant flags?

If I have a database table containing a flag that can have multiple states, should I do this
if ($Object->isStateOne()) {
// do something
}
else if ($Object->isStateTwo()) {
// do something else
}
else if ($Object->isStateThree()) {
// do yet something else
}
or this
switch ($Object->getSomeFlag()) {
case ObjectMapper::STATE_ONE:
// do something
break;
case ObjectMapper::STATE_TWO:
// do something else
break;
case ObjectMapper::STATE_THREE:
// do yet something else
break;
}
?
Whichever makes sense, of course.
The switch looks much cleaner. But, what is this 'state' you are checking? If you're translating a string, use an array, for example.
The if has different behavior from switch. The method calls MAY have side effects. The if is better if multiple states may be active on the object at once, though.
From an OO perspective, both are discouraged. If you have different state, you may want to create a virtual methods, and override it in inherited class. Then based on polymorphism, you can avoid the if, and switch statement.
The second option just seems to be the better solution. IMHO, the unsightly duplication of the comparison code that would accompany the methods of the first solution would be the show-stopper for me, for example:
public function isStateOne() {
if(strcmp(ObjectMapper::STATE_ONE, '1') == 0) {
return true;
}
}
public function isStateTwo() {
if(strcmp(ObjectMapper::STATE_TWO, '2') == 0) {
return true;
}
}
public function isStateThree() {
if(strcmp(ObjectMapper::STATE_THREE, '3') == 0) {
return true;
}
}
Of course, others might disagree. I just don't like having classes cluttered up with 'almost the same' methods.

Categories