PHP

About PHP basics and pitfalls

AND, && and &

Usually you use && in favor of AND. At least, I prefer! Cake code conventions state the same.
But they do exactly the same:
They "intelligently" match the conditions and abort as SOON as the first condition fails. This is important to know.

if ($cond1 && $cond2 && $cond3) {}

in this case $cond2 and $cond3 are only checked if the first one returns true, $cond3 only if both 1 and 2 return true. So it does make sense to order them from "fastest" to "most complicated" etc.

So why is there "&"?

if ($this->checkOne() & $this->checkTwo()) {}

& can be useful if you want to make sure ALL conditions are checked and the final result is returned afterwards. But this should only be used if the content of all conditional functions is vital to the following code in both cases (if any condition fails or if all pass).

Example for "&":

if ($this->cleanup() & $this->cleanupSomeWhereElse()) {...}

We do want to return the result, but AFTER all cleanup functions did their job. Using "&&" we would not call the second function if the first only already returned false. But they work independently – so why not trying to get everything done the first time? The result will show that something didn’t work and can be manually deleted or whatever.

An example for "&&" would be validation: Why checking 4 other validation rules if the first one (maybe "notEmpty") already failed? This speeds up the process.

Careful with ! (NOT)

BAD:

if (!$time = $this->Session->read('Board.time')) {
    $time = time();
    //some code
}

GOOD:

if (!($time = $this->Session->read('Board.time'))) {
    $time = time();
    //some code
}

The extra () brackets make all the difference in the world here.
In the first scenario time gets first "inversed" before trying to become the assigned value whereas in the second scenario we first assign time the correct value and afterwards check on "NOT condition".
So the bad example will not work or result in an unexpected outcome. The good one, though, will work as expected.

isset(), !empty(), …

Some coders just check on variables by using "if ($var)" or "if (!$var)".
Of course one can suppress the notices. But it’s really bad style. Many PHP scripts circulating the internet are written in such an unclean style.

If you are not sure whether a variable exists, use isset() or !empty().
Examples:

if (!empty($this->params['named']['xyz']) { ... }
if (isset($this->SomeModel) { ... }
if (!empty($myNewArray) { ... }

The main difference is that !empty() should not be used with integer values IF 0 is a valid input.
I find it interesting that it even works with arrays (empty arrays return false).

Result Table for those two:

$var1	=	0; 
$var2	=	null; 
$var3	=	false; // boolean 
$var4	=	true;	 // boolean 
$var5	=	''; 
$var6	=	' '; // whitespace 
$var7	=	'something'; 
$var8	=	array(); // empty array
$var9	= 	'0';
$var10	= 	"0";

# isset()
// var1: true
// var2: FALSE
// var3: true
// var4: true
// var5: true
// var6: true
// var7: true
// var8: true
// var9: true

# !empty()

You could ask "Why not always using isset()?". Well, most of the time we want to make sure that the variable contains some kind of information – not that it exists. So the rule to remember is: First try to use !empty(), then fallback to isset(), is_null(), …

And remember: good code never throws any notices. And if it does you know that something unexpected out of the ordinary happened and can respond and maybe fix the bug etc.

For details on this topic, see the type comparison tables.

Conclusions for CakePHP:
As you can see i also included " ", a single whitespace. It will return true even for !empty().
So always trim your post data. Otherwise notEmpty validation rules are easily overriden by nonsense-whitespaces.

Tip: Use the following snippet in your app_controller()

function beforeFilter() {
    parent::beforeFilter();

    /** DATA PREPERATION **/
    if (!empty($this->data)) {
        $this->data = $this->Common->trimDeep($this->data);
    }
}

and put this snippet in your "CommonComponent" or any other component you are using in every controller:

function trimDeep($value) {
    $value = is_array($value) ? array_map(array(&$this, 'trimDeep'), $value) : trim($value);
    return $value;
}

The sandbox contains a demo of this: sandbox.dereuromark.de/sandbox/tools-examples/trim.

Note: For CakePHP 3+ and immutable request this is handled now directly inside the CommonComponent.

normal functions and "mb" functions

Those mb (multi byte) functions are usally used/needed with UTF8 as app encoding.
The validation, for instance, needs them to check on minLength/maxLength. They count characters.
For example: mb_strlen(‘abc’) equals 3
The normal functions, like strlen() count bytes. They are usually used if non-utf8-characters are used or in combination with file operations.

What happens if you use them incorrectly?
Let’s say, we want to make sure the user input is exactly 4 characters long.
So
strlen('über') => 5 (!!!)
returns false results but
mb_strlen('über') => 4
will do it correctly.
Even if you don’t think anybody would ever input such special chars like "umlaute" (äöüÄÖÜ) or other UTF8 chars, you still should implement it so that it would work with them.

Summary:
Usage of strlen()

  • how long a string is in bytes (actual file size)
  • string operations where the length is irrelevant (e.g. replacing)
    Usage of mb_strlen()
  • validation of string lengths (chars)
  • cutting out pieces of a string

In general, mb_ functions are slower than the normal functions. But they should still be used wherever it makes sense.

Interesting:
If you wish to find the byte length of a multi-byte string when you are using mbstring.func_overload 2 and UTF-8 strings, then you can use the following:
mb_strlen($utf8_string, 'latin1'); (source)

2 Comments

  1. Of course that’s their main purpose.
    And of course && is usually the way to go!

    But IF you want to use & I did point out what the behavior would be like.

    One example of a rare case where this could come in handy:

    $is1 = $this->Model1->validate();
    $is2 = $this->Model2->validate();
    $is3 = $this->Model3->validate();
    if ($is1 && $is2 && $is3) {
    }

    is the same as

    if ($this->Model1->validate() & $this->Model2->validate() & $this->Model3->validate()) {
    }

    Guess which version is the better one here?
    We need all 3 validations because we want to display all error messages combined.

    if ($this->Model1->validate() && $this->Model2->validate() && $this->Model3->validate()) {
    }

    would only give as the first model validation errors if it fails – and is therefore quite annoying!

    What would you propose instead?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.