MODX getOption() with Snippet Properties

In the previous article, we looked at the various ways that $modx->getOption() can be used. In this one, we’ll look specifically at how it handles snippet properties placed in a snippet tag.

When you place properties in a snippet tag, their names (keys) and values arrive in the snippet in the $scriptProperties array. You can retrieve them directly from the array, but as we saw in the last article, it’s a lot more convenient to get them with $modx->getOption(). When you use getOption(), it helps to understand what it will get you depending on the actual properties sent in the tag.

MODX logo

The ScriptProperties Array

Suppose this is your snippet tag:

[[SnippetName? &color=`red` &size=`large` &length=`22`]]

Using the tag above, MODX will essentially place this code at the top of your snippet (though you won’t see it):

$scriptProperties = array(
     'color' => 'red',
     'size' => 'large',
     'length' => '22',
);

If you want the value of the color property, for example, and want the color set to gray if it’s not set, you can use this code:

$color = $modx->getOption('color', $scriptProperties, 'gray');

 

Test Your Knowledge

Take a look at the following snippet tag. Try to guess how each of the properties will look in the $scriptProperties array, and when retrieved in the Test snippet with $modx->getOption(). We’re going to test them to see if they are strings, are empty, == 0, == ‘0’, === 0, === ‘0’, == true, and == false. The code used to test them appears at the end of this article. See if you can predict the results

[[!Test?
    &One=0
    &Two=1
    &Three=x
    &Four=hello sailor
    &Five=`0`
    &Six=`1`
    &Seven=`a`
    &Eight=`0+`
    &Nine=`1+`
    &Ten=`hello sailor`
    &Eleven=``
    &Twelve=`missing`
    $Thirteen=`missing`
    &Fourteen=''
]]

 

A Word About Equality Tests in PHP

When you test equality with ==, PHP does its best to coerce the variables to the same type. The result of the test is always a boolean (true/false) value. If you do the comparison 123 == '123', PHP converts the value on the right to a number and will consider the comparison true.

If, on the other hand, you use === for the comparison, PHP will not coerce either variable to another type. It first compares the *types* of the variables (integer, string, boolean, array, etc.). If they are not the same, PHP will consider the expression false, regardless of the values. If they are of the same type, PHP will then compare the values. The comparison will be true if they are the same and false if they’re not.

You might wonder why our test below doesn’t include === true or === false. The answer is that these comparisons would *always* be false (i.e., there would never be a match) because the type of true and false is boolean and there’s no way to send an actual boolean value in a snippet property. That’s why a Yes/No property is always sent as `0` (for false) or `1` (for true), and why in the snippet, == is always the comparison operator for these properties.

 

The $scriptProperties Array

In our code below, the first thing we do is dump the $scriptProperties array. This is the result:

 

array
    'Two' => string '1' (length=1)
    'Three' => string 'x' (length=1)
    'Four' => string 'hello sailor' (length=12)
    'Five' => string '0' (length=1)
    'Six' => string '1' (length=1)
    'Seven' => string 'a' (length=1)
    'Eight' => string '0+' (length=2)
    'Nine' => string '1+' (length=2)
    'Ten' => string 'hello sailor' (length=12)
    'Eleven' => string '' (length=0)
    'Fourteen' => string '''' (length=2)

The first thing to notice is that they are all strings. This is a valuable thing to know in MODX: *all* $scriptProperties values are strings — always, with or without back-ticks. There’s no way to send a true integer-type value as a property. This is true for plugin properties as well. It’s also worth knowing that the return values of snippets are always strings.

You might think this would cause problems in snippets that need number values, but in almost all cases PHP’s loose typing takes care of it for you. If you set a variable in a snippet to the property ‘123’ and you need it to be a number (e.g., for a loop counter or a maximum value), PHP will convert it to a number when it needs to be one. You do, however, have to be careful about comparisons and true/false tests as we’ll see below.

The second interesting thing about the $scriptProperties array is that properties One, Twelve, and Thirteen are missing — more on that in a bit.

 

The Tests

In this section, we’ll look at the results of the tests our snippet performs on the values of the properties sent in the $scriptProperties array. The properties used in the snippet tag appear at the top in parentheses.


(&One=0)

This one didn’t even make it into the $scriptProperties array. This happens because its value is empty and not enclosed in back-ticks. Always use back-ticks on all properties.


(&Two=1)

Property: Two

Value: 1
Is a string
Is NOT empty
== true YES
== false NO
== 0 NO
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

Again, no back-ticks, but this one makes it through because it’s not empty. You might not have guessed that it’s a string, though, rather than a number. This makes sense if you realize that *all* properties are sent as strings in the $scriptProperties array, even if they’re not in back-ticks. As you’d expect, it’s never equal to 0 in any form, and it’s true. When coerced to a number, it will be (integer) 1, which PHP considers true.


(&Three=x)

Property: Three

Value: x
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

Still no back-ticks, but it arrives as a string. The possible surprise here is that it’s equal to 0 when tested with ==. To do the comparison, PHP coerces it to an integer, and since it’s text, the number it comes up with is 0. At the same time, though, it’s true when tested with ==. That’s because a different coercion occurs because the right-hand value is a boolean. In this case it’s coerced to a boolean value and since it’s not really 0 and it’s not empty, it evaluates to true.


(&Four=hello sailor)

Property: Four

Value: hello sailor
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

This property makes it through as a string, though it has no back-ticks and it’s not empty, but there’s something strange going on. When tested with ==, it’s true, but it’s also equal to 0. That’s because the first test is testing a boolean value against a non-empty string (which evaluates to true unless the string is all zeros). The second test is comparing the non-numeric string to an integer (0). When PHP coerces the string to an integer for the comparison, it’s converted to 0, so the two are considered the same.


(&Five=`0`)

Property: Five

Value: 0
Is a string
Is empty
== true NO
== false YES
== 0 YES
=== 0 NO
== ‘0’ YES,
=== ‘0’ YES

It might surprise you to find that this is empty, even though it’s not what you would normally consider an empty string. PHP considers the string ‘0’ to be empty. Not a lot of other surprises here. It’s false, and == 0. It’s not === 0, though, because it’s not an integer, but it passes the last two tests because it’s a string and it’s value is 0 whether it’s treated as a string or as a number.


(&Six=`1`)

Property: Six

Value: 1
Is a string
Is NOT empty
== true YES
== false NO
== 0 NO
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

No surprises at all here. It’s not empty, it’s true, and it’s never equal to 0, no matter how you test it.


(&Seven=`a`)

Property: Seven

Value: a
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

This one is a string and not empty. As we’ve seen above it’s both true and == 0. As before the first test coerces it to boolean true because it’s not empty, while the second one coerces it to integer 0 because the string doesn’t contain a number.


(&Eight=`0+`)

Property: Eight

Value: 0+
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

This one may be a little surprising. It’s true because the + makes it not empty, but it’s == 0 because when converted to an integer for the comparison, it evaluates to 0 (+0. -0, and 0- would give the same results). It fails the === ‘0’ test, however, because even though they’re the same type, the string ‘0’ is not equal to the string ‘0+’.


(&Nine=`1+`)

Property: Nine

Value: 1+
Is a string
Is NOT empty
== true YES
== false NO
== 0 NO
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

No surprises here. It’s not empty, it’s true, and it’s never equal to 0, no matter how you test it.


(&Ten=`hello sailor`)

Property: Ten

Value: hello sailor
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

By now, this one should be easy. It’s true because it’s not empty, but it’s ==0 because when converted to an integer, it’s 0.


(&Eleven=“)

Property: Eleven

Value:
Is a string
Is empty
== true NO
== false YES
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

This one is an empty string — the only truly empty string in our test. It’s false because it’s empty, but it’s == 0 because it’s 0 when converted to an integer. It’s not, however, equal to 0 in any other comparisons. It fails the === 0 test because it’s not the same type as integer 0. It fails the last two tests because ‘0’ and ” are different strings


(&Twelve=`missing`)

($Thirteen=`missing`)

What happened to these guys? Neither of them makes it into the $scriptProperties array. Thirteen is missing because it starts with dollar sign instead of an ampersand. This is an easy mistake to make if you’re switching back and forth between snippet code and the properties tag. It always confuses the tag parser. Often, all properties beyond that point are ignored. In this case, though, it has the odd effect of making the parser ignore the property *ahead* of it (Twelve). When you confuse the parser, there’s no telling what might happen.


(&Fourteen=”)

Property: Fourteen

Value: ”
Is a string
Is NOT empty
== true YES
== false NO
== 0 YES
=== 0 NO
== ‘0’ NO
=== ‘0’ NO

You might think this would be an empty string, but it’s not. Because we used single quotes instead of back-ticks, it comes through intact as a string containing two single quotes (see the $scriptProperties array above). It’s *not* empty. It’s true (because it’s not empty), and it passes the == 0 test because it evaluates to 0 when converted to an integer.

 

Using skipEmpty?

If you read the previous article, you might be wondering what the effect would be of sending trueas the fourth argument to getOption(). That tells MODX to use the default value if the property is missing or truly empty. The answer is that it has almost no effect in this case, since all the properties are set. The only change it makes to the results is that property Eleven gets the default value because it is the only property in the $scriptProperties array that is truly empty. Properties One, Twelve, and Thirteen would also get the default value, since they are missing from the $scriptProperties array. Because they’re not in the array, though, our test code would never try to get them.

 

The Code

Here’s the code of our Test snippet, in case you want to modify the snippet properties and try some tests of your own:

$output = '';
var_dump($scriptProperties);
$output .= '<br /><br />';
foreach($scriptProperties as $key => $value) {
    $val = $modx->getOption($key, $sp, 'default');
    $output .= '<hr><br />Property: ' . $key . '<ul>';
    $output .= '<li>Value: ' . $val;
    if (is_string($val)) {
        $output .= '<li>Is a string';
    } else {
        $output .= '<li>Is NOT a string';
    }
    if (empty($val)) {
        $output .= '<li>Is empty';
    } else {
        $output .= '<li>Is NOT empty';
    }
    if ($val == true) {
        $output .= '<li>== true YES';
    } else {
        $output .= '<li>== true NO';
    }
    if ($val == false) {
        $output .= '<li>== false YES';
    } else {
        $output .= '<li>== false NO';
    }
    if ($val == 0) {
        $output .= '<li>== 0 YES';
    } else {
        $output .= '<li>== 0 NO';
    }
    if ($val === 0) {
        $output .= '<li>=== 0 YES';
    } else {
        $output .= '<li>=== 0 NO';
    }
    if ($val == '0') {
        $output .= "<li>== '0' YES, ";
    } else {
        $output .= "<li>== '0' NO";
    }

    if ($val === '0') {
        $output .= "<li>=== '0' YES";
    } else {
        $output .= "<li>=== '0' NO";
    }
    $output .= '</ul>';
}
return $output;

 

Some Implications

These tests reveal some important information about how you should deal with snippet properties inside the snippet.

If, for example, your property is a number and it might be 0, you can’t test it with empty() unless you first test it with ==='0' to make sure it’s not a 0. The quotes around the 0 are essential here, because a 0 sent as a property will always be a string — a test for === 0 (without the quotes) will always fail.

Most of the time, using a snippet property as a number will work fine. There are a few occasions, however, where MODX really wants the variable to be an integer. In some older versions of MODX, for example makeUrl() will fail if the the ID you send in the first argument is a string. If you want to be sure a number is being sent as an argument, you can always convert it to one with intval(). It’s much faster, however, to use a “cast.” Putting the type you want in parentheses in front of the variable name, like this, will convert it:

$modx->makeUrl( (integer) $id, "", "", "full");
$modx->makeUrl( (int) $id, "", "", "full");

 

Here are the most common casts:

  • (int), (integer) – cast to integer
  • (bool), (boolean) – cast to boolean
  • (float), (double), (real) – cast to float
  • (string) – cast to string

 

Note that when casting a string with mixed numbers and letters to an integer, the result depends on which comes first.

$x = (int) '1abc'; /* $x = 1 */
$x = (int) 'abc1'; /* $x = 0 */

Similarly, if you need an actual true/false value, you can do something like this:

$x = (boolean) $x;

The PHP var_dump() function is handy if you want to see what the actual type of a variable is, because it will print it for you.

It doesn’t really hurt to cast a variable to the type you want, because it only takes PHP a few milliseconds to do the conversion. Casting is not necessary, though, if you are using any of these PHP operators for a test: ==, !=, <, >, <=, >=.

Another tricky situation occurs when the snippet property contains a comma-separated list of values that you convert to an array with explode() in the snippet. Even if an empty string (“) is sent as a snippet property, the result of explode() will never be empty. It’s common to use a foreach loop that you assume won’t execute on an empty array, but if the array is generated with explode(), the loop will always execute at least once.

The solution is to test the property value with empty(), *before* calling explode(). If it’s empty, skip the foreach loop.

If you’re sure that the property sent in the snippet tag will be `0` or `1`, there’s no need to do a comparison on it, you can just use an if() statement, because PHP will always consider '0' to be false and '1' to be true. For example, if you have a property called “doTotals” and you want it to default to false if the property is not sent in the snippet tag, you can do this:

$displayTotals = $modx->getOption(‘doTotals’, $scriptProperties, false);
if ($displayTotals) {
/* `1` (or something other than `0`) was sent */
}

 


For more information on how to use MODX to create a web site, see my web site Bob’s Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for quality MODX Web Hosting? Look no further than Arvixe Web Hosting!

Tags: , , , , , , | Posted under MODX, MODX | RSS 2.0

Author Spotlight

Bob Ray

Bob Ray

I am the author of MODX: The Official Guide and over 30 MODX add-on components. I host Bob's Guides, a source of valuable information for MODX users, and I've been very active in the MODX Forums with over 14,000 posts.

Leave a Reply

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


9 + 4 =

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>