a simple php logic - php

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.

Related

There is a short way to write if(!is_null($foo) && !is_null($bar))?

So, I am writing a function that receives two arguments, and before I do anything with these variables, I want to check if they are not null.
if (!is_null($foo) && !is_null($bar)) {
/* do something */
}
The problem is that I think we are repeating code, and in some cases when the variable name is a little bit bigger, it becomes painful to write every time.
So, there is a way to shorten this code?
You can use isset which returns FALSE if variable is null. You can set there a lot of variables so code will be shorter:
if (isset($foo, $bar))
You can write your own function to check this:
function is_any_null() {
$params = func_get_args();
foreach($params as $param) {
if (is_null($param))
return true;
}
return false;
}
Now you can use it like this:
if (!is_any_null($foo, $bar)) {
/* do something */
}
is_null() checks a variable to determine if the value is NULL. It can be used in an if statement. It depends on what you would like to do in that if statement. For example, if you would like to do an echo, you can make it a bit shorter using the `elvis``operator:
echo (is_null($foo) && is_null($bar)) ?: 'The values are not null';
Or you could make it a one liner by leaving the { and } out of your code and putting everything on one line.
if (!is_null($foo) && !is_null($bar)) die("NULLLLL");

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();

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.

refactoring this block

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.

Do you consider foreach((array)$foo as $bar) a code smell?

Do you consider this a code smell?
foreach((array)$foo as $bar)
{
$bar->doStuff();
}
Should i use that instead?
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
Any other good practices to cover not set variables and assert an array?
They're both code smells. The second one is just evading all the error messages, kind of like turning off the fire alarm before you set your kitchen on fire. Both of those tell you that you have no idea what's in the variable $foo or if it's even been defined in the code above. You need to go back up through the code and find out exactly what's going on with $foo.
If it was my code, $foo would probably be always defined either as an array, or else false to indicate the array isn't needed:
if(do_we_need_an_array())
$foo = function_returning_an_array();
else
$foo = false;
[...snip...]
if($foo)
foreach($foo as $f) { ... }
If you are testing if variables are set, you can initialize them:
if (! $foo or !is_array($foo))
$foo = array();
foreach($foo as $bar)
{
$bar->doStuff();
}
Personally, I would never do the first method and always opt for the second.
If $foo should always be an array, then the second form would be much better if you did some kind of handling for the error case, e.g.:
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
else
{
// This should not happen, exit angrily.
exit("Oh crap, foo isn't an array!");
}
Of course you don't have to just exit the application, but do whatever is appropriate in that case, maybe logging or some alternate logic.
(array)$foo != if (isset($foo) && is_array($foo))
The (array) cast can be useful for casting objects to arrays or scalars to arrays so you can create consistent interfaces to variables that may contain single values or arrays.
(array)$foo == array($foo)
As defined in the PHP Manual for Array Types.
So if you need to always use an array then the first code snippet you presented would be the answer. However type casting rules still apply so you may not get what you want, so look to the manual for more info. Otherwise the second option would prevent accessing unset variables that are not arrays.
As far as a code smell, I would say that checking for unset variables can certainly be avoided, however always knowing that a variable is going to have an array is more often than not, going to creep up. So I would aim to keep code wrapped in is_array($foo) if-then statements to a minimum.
I usually do this to make sure a foreach can handle both scalars and collections:
<?php
foreach (makeSureTraversable($scalarOrCollection) as $val)
{
// Do something.
}
function
makeSureTraversable($anything)
{
if (is_array($anything) || ($anything instanceof Traversable))
{
return $anything;
}
else
{
return array($anything);
}
}
This way I also handle classes that implement Traversable (from the SPL), which means allowing them to be used in foreaches.
if (!isset($foo) && !is_array($foo)) {
throw new InvalidArgumentException('Wrong array passed');
// Or do something to recover lost array
}
foreach($foo as $bar) {
$bar->doStuff();
}
There's quite a few times that you'd like to write a function to take one or more values for a parameter:
function getNamesById($id) { }
In this case, it would make sense that if this function was called with an array of ids, it should probably return an array of names. Similarly, to save the calling code from having to wrap the input in an array and then unwrap the output, if you just pass a scalar, then a scalar should be returned. Consider the likely contents of the function designed to handle both scalar and array parameters:
function getNamesById($id) {
$returnAnArray = is_array($id);
$output = array();
foreach ((array)$id as $theId) {
// perform some logic
$output[] = someFunction($theId);
}
return $returnAnArray ? $output : $output[0];
}
You can see that in this case, casting to an array definitely makes things a lot easier for everyone. As they say, be liberal in what you accept... As long as it is documented that it is expected that a variable could be either, then I see no problem. PHP is a duck-typed language, which has both benefits and drawbacks, but this is one of the benefits, so enjoy it!

Categories