refactoring this block - php

I'm refactoring some code that wasn't written by me. This block sets the value of $val but I want to clean it up a bit. Obviously I can't use the tertiary operator here. What other ways I can make this code cleaner?
if (isset($vars[$input])) {
$val = $vars[$input];
} elseif (isset($this->getI['io'])) {
$val = $this->getI['io'];
} elseif (isset($vars[5])) {
$val = $vars[5];
} else {
$val = 10;
}

$val = 10;
if (isset($vars[$input])) {
$val = $vars[$input];
} elseif (isset($this->getI['io'])) {
$val = $this->getI['io'];
} elseif (isset($vars[5])) {
$val = $vars[5];
}
This is about as simple as it gets without obfuscating the code. I'd rather try to simplify the logic, it's kinda hard to comprehend why the value is being looked for in so many different places.

I'm afraid I don't know php. I'm assuming that if you were to pass (say) $vars[$input] to a function, by the time it was a parameter to the function, the parameter's set-ness would be true (if that's not the case, I'd try writing a function that tested isset() on its parameter and set $val if so). I find elseif's to add complexity; I try to avoid them. In this case, I would write a function that returned the value; then all my elseif's can become plain if's.
f() {
if (isset($vars[$input])) {
return $vars[$input];
}
if (isset($this->getI['io'])) {
return $this->getI['io'];
}
if (isset($vars[5])) {
return $vars[5];
}
return 10;
}
And, of course, in your calling function, assign $val to the result of this function.

In my opinion, your example is as clean as it gets. Sure, you could write it as a huge one-liner using the ternary operator:
$val = isset($vars[$input]) ? $vars[$input] : isset($this->getI['io'] ? $this->getI['io'] : isset($vars[5]) ? $vars[5] : 10;
But this is obviously much harder to read and to maintain, so the original example is definitely cleaner (although it might be missing some comments).

I don't know...it seems to be pretty concise, as is.
If you know what it does, it does it well and it is clean enough that you can figure it out again in the future, I say don't touch it.

While you're at it figure out what it's doing and add some comments.
e.g. why assign it to the magic number 10? maybe the context of the rest of it may shed some light.
As far as code goes, you're not going to get it any simpler than this.

Related

PHP: Correct use of return in function - use variable or use a number directly?

I am looking to return an integer with a value of 50 or value of 25. Is this the correct way to do this or should I create a variable $temp1=50 and $temp2=25 and return the variable instead of just returning 50 and 25.
function somefunction($length)
{
if ($length > 50)
{
return 50;
} else {
return 25;
}
}
Sorry if duplicate, I looked.
It is perfectly fine the way you're doing it. Assigning a value to a variable to only return it makes no sense really.
As a better version to your alternative, for some more complicated cases, where you'd eventually need to return a variable, you could use only one variable instead of two as you suggested. Something more like
function somefunction($length)
{
$myVar = 0;
if ($length > 50) {
$myVar = 50;
}
else {
$myVar = 25;
}
return $myVar;
}
It is not necessary to assign a variable before. Just write
return ($length > 50 ? 50 : 25);
Assigning to a variable before returning is pointless. You're returning a value. 50 is a perfectly good value by itself, it does not need to be assigned to a variable before being returned.
In other words: you're doing it right already.
It depends on what you want to do :
if you just want to return a constant or if you want to parameter this constant.
in a case of a constant, for readability, you can name them :
define('RETURN_CONSTANT_SUP',50);
define('RETURN_CONSTANT_INF',25);
function somefunction($length)
{
if ($length > RETURN_CONSTANT_SUP)
{
return RETURN_CONSTANT_SUP;
} else {
return RETURN_CONSTANT_INF;
}
}
So you do it the right way, you can just use constant if, one day, you want to reuse those values.
I'd say the problem with your function is less the return but the input.
Anyway, your question is a bit theoretic. Because both returning a constant integer value as well as a variable would work. It wouldn't even make much of a difference. See the following example:
function somefunction($length)
{
return 25 + 25 * ($length > 50);
}
The problem with the code is that you have written it only for these specific values. So not using a variable can be a sign that the code is limited. But less because of the return and more because of the flow.
function integer_top_cut_drop($value, $top, $default)
{
$value = (int) $value;
$top = (int) $top;
$default = (int) $default;
if ($value > $top)
{
return $top;
}
return $default;
}
As this function shows, there are no numbers in there, but only variables. It pretty much does what your existing function does, but everything is variable. The numbers with their constant values have been removed and are now being passed as parameters:
function somefunction($length)
{
return integer_top_cut_drop($length, 50, 25);
}
So you normally never are concerned whether your return a variable, a constant, some other kind of expression or another functions return value.
More important is, that your code does what it has to do and you do it in a useful way.
That could mean in your code that your function already is totally fine.
But consider you enable yourself that you can re-use common parts, e.g. like shown here with a new function. That might be a bit over the top for your example, but just to show the picture.
A function can preserve the logic, so you don't need to write that part more than once. It also reduces the size of the other parts of the code using that function.
There is no correct way to do this.
This question is about style and preferences and you won't be able to get a definitive answer.
Just use what you like best (unless you are working in a team of course, in which case you should adapt the team's coding styleguide).

PHP - Could this if statement EVER return true?

I'm trying to edit my co-worker's code (he's on vacation and out of reach) and there is this if statement:
if($this->carrier() == 1 and $this->carrier() == 2) {
return 'V';
}
My hope is that he accidentally put "and" instead of "or" which could explain a bug I'm getting, but he knows much more about PHP than I do and I want to be sure that this is wrong instead of just counter intuitive.
Yes, since it's a function with potential side effects, it might be true.
For example:
function carrier() {
return $someValue++;
}
Yes. The carrier() method could increment whatever value it returns each time you call it.
There's a small chance it could, yes.
He's calling a function twice, and you've not included the text of that function. So that could be doing something we can't see, like counting the number of times it's been called by this process.
On the other hand, it's much more likely that it is indeed a typo.
It is possible. Here is a working example you can run.
class program
{
private $i = 1;
function carrier()
{
$this->i=$this->i+1;
return $this->i-1;
}
function run()
{
if ($this->carrier()==1 && $this->carrier()==2)
{
echo "works";
}
else
{
echo "doesnt work" . $i;
}
}
}
$prog = new Program();
$prog->run();

a simple php logic

I just saw this source code on a website, but I don't know what it means, can anyone tell me what it is? thank you so much.
private function buildCache()
{
!empty($this->cache_list) && $this->cache->loadCache($this->cache_list);
}
It is the example of bad code which is hard to support.
The !empty($this->cache_list) && $this->cache->loadCache($this->cache_list); statement is equivalent to $dummy = !empty($this->cache_list) && $this->cache->loadCache($this->cache_list);.
There is such thing as lazy evaluation, so that in A && B, B will be evaluated only is A is true (otherwise A && B is knowingly false and there is no need to evaluate B). Basically, $x = a() && b() is the same as
$x = true;
if(!a()) {
$x = false;
} else {
$x = b();
}
Thus, we can expand the original statement as
$dummy = true;
if(empty($this->cache_list)) {
$dummy = false;
} else {
$dummy = $this->cache->loadCache($this->cache_list);
}
which, remembering that we don't need the $dummy variable, is the same as
if(!empty($this->cache_list)) {
$this->cache->loadCache($this->cache_list);
}
Despite this code is 2 lines longer than the original one, it is much easier to understand and to mantain. You should write the code which is like this final version and avoid writing anything like the original one-liner.
You can see it by yourself: while it was hard for you to tell what is going on in the original one-liner (so hard that you had to ask the question on SO), it is quite easy to see what is going on in the final version: if the cache_list is not empty, we're calling loadCache passing cache_list to it as the argument (otherwise, if the cache_list would be empty, it would probably be pointless to call loadCache passing empty value to it as the argument).
It means if $this->cache_list is not empty and $this->cache->loadCache() function returns true
I guess that's a shortcut for:
private function buildCache()
{
if( ! empty($this->cache_list)){
$this->cache->loadCache($this->cache_list);
}
}
If there is a 'cache_list', it loads it.
You have to check the class or framework documentation for more info on these actions.

More elegant way to write these conditions?

out of curiosity I'm wondering if there's a more elegant way to write the conditionals below? I can't see a shorter way of writing it but it feels pretty clunky, so any suggestions welcome!
// Check whether this page has any visuals
if (count($this->page->pagevisuals->find_all()) > 0)
{
// Ok to go ahead and assign
$visual = $this->page->pagevisuals->find_all();
}
// If this is a sub page, parent page may have visuals we can use
elseif (count($this->page->parent->pagevisuals->find_all()) > 0)
{
$visual = $this->page->parent->pagevisuals->find_all();
}
// If two levels deep, grandparent page might have visuals
elseif (count($this->page->parent->parent->pagevisuals->find_all()) > 0)
{
$visual = $this->page->parent->parent->pagevisuals->find_all();
}
You can write a loop instead:
$page = $this->page;
$visual = null;
while (!$visual && $page) {
$visual = $page->pagevisuals->find_all();
$page = $page->parent;
}
I believe this is equivalent, and will work no matter how many levels of parents/nesting you have.
You could assign $this->page to a variable and begin the statements with that, for a very slight improvement.
Alternatively, you could create nested ternary statements to assign $visual, but that's certainly not recommended practice.
A recursive approach:
function getVisuals($root) {
$visuals = $root->pagevisuals->find_all();
if(count($visuals) === 0 && isset($root->parent)) {
$visuals = getVisuals($root->parent);
}
return $visuals;
}
$visuals = getVisuals($this->page);
If you have control over whatever class $this->page is an instance of, then you can make it an instance method.
You could make a recursive method to get rid of those nasty conditionals. Also you're calling the find_all() method twice for every conditional branch which doubles the process time.
Here's an attempt at a recursive function (might not work though, recursive functions are always a bit tricky!). Beware of infinite loops.
<?php
$visual = $this->page->find_all_visuals();
class Page {
function find_all_visuals()
{
$found = $this->pagevisuals->find_all();
if (count($found) > 0) {
return $found;
} else if ($this->parent == null) {
return null;
} else {
return $this->parent->find_all_visuals();
}
}
}
?>
You might want make two changes in your code:
Ensure that getVisuals() returns an empty array instead of null in case there are no visuals
Consider making a null-object - a singleton page instance that has no visuals and has itself as a parent. It might have a method like isNull() so you can easily test if a given page is the null page.
If you make the two adjustments, most of the code concerning visuals will become easier to write and debug.
Getting all the visuals for two levels (I assume you don't want recursion):
$visuals = array_merge(
$this->page->pagevisuals->find_all(),
$this->page->parent->pagevisuals->find_all(),
$this->page->parent->parent->pagevisuals->find_all(),
);
Getting the visuals of the page OR of parent OR of grand parent:
($visuals = $this->page->pagevisuals->find_all()) ||
($visuals = $this->page->parent->pagevisuals->find_all()) ||
($visuals = $this->page->parent->parent->pagevisuals->find_all());
Recursive functions would be much simpler too (this is a method to add to the page object):
public function findRecursive(){
$my_visuals = $this->pagevisuals->find_all()
return $this->parent->isNull()?
$my_visuals
: array_merge($my_visuals, $this->parent->findRecursive());
}
$visual = $this->page->pagevisuals->find_all()
or $visual = $this->page->parent->pagevisuals->find_all()
or $visual = $this->page->parent->parent->pagevisuals->find_all();
What do you do if none of them match? In this code it will be set to the last one, which is not the same as what you did. (In your code $visual was not touched if none matched, in this code it will be set to zero. You could add or $visual = -1 or something similar.)
You can make a loop if you want to avoid all the ->parent, but you'll need some terminator.
$el = $this->page;
while(!$visual = $el->pagevisuals->find_all()) {
$el = $el->parent;
}
This could run forever if it never matches, but I don't know enough about your application to suggest a termination condition - you could add a counter, or something else.

Dealing with big IF statements in PHP

Is there any good alternative for the plain if statements in PHP? I know about switch, but I'll guess that there's some more refined alternative out there that comes handy when working with really big if statements.
Thanks a lot,
If you can't read your algorithm on one screen fold, there's a 99.9% chance you need to refactor your code toward more readability.
Change
if ($isHappening) {
// ... millions of lines of code
} else {
// .. another million lines of code
}
into
if ($isHappening) {
happen();
} else {
didntHappen();
}
function happen() {
// millions of lines of code
}
function didntHappen() {
// another million lines of code
}
There really is no magic hammer out there. Your best bet to making them manageable is to break nested ifs into their own functions to make them more readable.
Also, don't forget about array_filter. That can save you from having to write a for loop to filter out items.
Also, you can eliminate nesting by using guard statements. You basically invert your if and do a return instead (another reason to break conditions into functions).
If you want to improve readability only, then you can always split up the expressions inside the if statement:
$exp1 = is_array($var) && isset($var['key']);
$exp2 = is_object($var) && isset($var->key);
$exp3 = substr($string, 0, 4) == 'foo';
$exp4 = ($exp1 || $exp2) && $exp3;
if ($exp4) {}
instead of
if (((is_array($var) && isset($var['key'])) || (is_object($var) && isset($var->key))) && substr($string, 0, 4) == 'foo') {}
Obviously, these are simplified examples, but you get the idea...
Welcome to the world of Object Orientation :)
class Case1 {
function do() { echo "case 1"; }
}
class Case2 {
function do() { echo "case 2"; }
}
$object = new Case1();
$object->do();
And then, there is dispatching using an array:
$choices = array( "case1" => new Case1(), "case2" => new Case2(), ... );
$choices[ $_GET["case"] ]->do();
Well if is if, there is not much else out there. Of course switch is an alternative but depending on the conditions it might not be applicable.
If you are doing OOP, the state design pattern might be what you need.
Otherwise you have to give more information...
If by "big" you mean large, highly nested "ifs", this is a clear sign of code smell, and you should be looking at OOP and design patterns.

Categories