Changeset View
Changeset View
Standalone View
Standalone View
src/docs/flavor/php_pitfalls.diviner
Show First 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | If a value is not truthy, it is "falsey". These values are falsey in PHP: | ||||
null // null | null // null | ||||
0 // integer | 0 // integer | ||||
0.0 // float | 0.0 // float | ||||
"0" // string | "0" // string | ||||
"" // empty string | "" // empty string | ||||
false // boolean | false // boolean | ||||
array() // empty array | array() // empty array | ||||
Disregarding some bizarre edge cases, all other values are truthy. Note that | Disregarding some bizarre edge cases, all other values are truthy. | ||||
because "0" is falsey, this sort of thing (intended to prevent users from making | |||||
empty comments) is wrong in PHP: | |||||
COUNTEREXAMPLE | |||||
if ($comment_text) { | |||||
make_comment($comment_text); | |||||
} | |||||
This is wrong because it prevents users from making the comment "0". //THIS | |||||
COMMENT IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD BETTER NOT | |||||
BREAK IT!!!// A better test is probably `strlen()`. | |||||
valerio.bozzolan: I moved this text under the section "Check for non-empty strings" | |||||
In addition to truth tests with `if`, PHP has two special truthiness operators | In addition to truth tests with `if`, PHP has two special truthiness operators | ||||
which look like functions but aren't: `empty()` and `isset()`. These operators | which look like functions but aren't: `empty()` and `isset()`. These operators | ||||
help deal with undeclared variables. | help deal with undeclared variables. | ||||
In PHP, there are two major cases where you get undeclared variables -- either | In PHP, there are two major cases where you get undeclared variables -- either | ||||
you directly use a variable without declaring it: | you directly use a variable without declaring it: | ||||
Show All 15 Lines | |||||
When you do either of these, PHP issues a warning. Avoid these warnings by | When you do either of these, PHP issues a warning. Avoid these warnings by | ||||
using `empty()` and `isset()` to do tests that are safe to apply to undeclared | using `empty()` and `isset()` to do tests that are safe to apply to undeclared | ||||
variables. | variables. | ||||
`empty()` evaluates truthiness exactly opposite of `if()`. `isset()` returns | `empty()` evaluates truthiness exactly opposite of `if()`. `isset()` returns | ||||
`true` for everything except `null`. This is the truth table: | `true` for everything except `null`. This is the truth table: | ||||
| Value | `if()` | `empty()` | `isset()` | | | Value | `if()` | `empty()` | `isset()` | | ||||
|-------|--------|-----------|-----------| | |---------------|--------|-----------|-----------| | ||||
| `null` | `false` | `true` | `false` | | | `null` | `false`| `true` | `false` | | ||||
| `0` | `false` | `true` | `true` | | | `0` | `false`| `true` | `true` | | ||||
| `0.0` | `false` | `true` | `true` | | | `0.0` | `false`| `true` | `true` | | ||||
| `"0"` | `false` | `true` | `true` | | | `"0"` | `false`| `true` | `true` | | ||||
| `""` | `false` | `true` | `true` | | | `""` | `false`| `true` | `true` | | ||||
| `false` | `false` | `true` | `true` | | |`false` | `false`| `true` | `true` | | ||||
| `array()` | `false` | `true` | `true` | | |`array()` | `false`| `true` | `true` | | ||||
| Everything else | `true` | `false` | `true` | | |Everything else| `true` | `false` | `true` | | ||||
valerio.bozzolanAuthorUnsubmitted Done Inline ActionsI just indented a bit valerio.bozzolan: I just indented a bit | |||||
The value of these operators is that they accept undeclared variables and do | The value of these operators is that they accept undeclared variables and do | ||||
not issue a warning. Specifically, if you try to do this you get a warning: | not issue a warning. Specifically, if you try to do this you get a warning: | ||||
```lang=php, COUNTEREXAMPLE | ```lang=php, COUNTEREXAMPLE | ||||
if ($not_previously_declared) { // PHP Notice: Undefined variable! | if ($not_previously_declared) { // PHP Notice: Undefined variable! | ||||
// ... | // ... | ||||
} | } | ||||
Show All 20 Lines | `is_falsey_or_is_not_declared()`. Thus: | ||||
- When you use `isset()` on an array key, like `isset($array['key'])`, it | - When you use `isset()` on an array key, like `isset($array['key'])`, it | ||||
will evaluate to "false" if the key exists but has the value `null`! Test | will evaluate to "false" if the key exists but has the value `null`! Test | ||||
for index existence with `array_key_exists()`. | for index existence with `array_key_exists()`. | ||||
Put another way, use `isset()` if you want to type `if ($value !== null)` but | Put another way, use `isset()` if you want to type `if ($value !== null)` but | ||||
are testing something that may not be declared. Use `empty()` if you want to | are testing something that may not be declared. Use `empty()` if you want to | ||||
type `if (!$value)` but you are testing something that may not be declared. | type `if (!$value)` but you are testing something that may not be declared. | ||||
= Check for non-empty strings = | |||||
As already mentioned, note that you cannot just use an `if` or `empty()` to | |||||
check for a non-empty string, mostly because "0" is falsey, so you cannot rely | |||||
on this sort of thing to prevent users from making empty comments: | |||||
COUNTEREXAMPLE | |||||
if ($comment_text) { | |||||
make_comment($comment_text); | |||||
} | |||||
This is wrong because it prevents users from making the comment "0". | |||||
//THE COMMENT "0" IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD | |||||
BETTER NOT BREAK IT!!!// | |||||
Another way //was// also `strlen()`: | |||||
COUNTEREXAMPLE | |||||
if (strlen($comment_text)) { | |||||
make_comment($comment_text); | |||||
} | |||||
valerio.bozzolanAuthorUnsubmitted Done Inline Actions↑ That part was just copy-pasted mostly from the already-existing block ↓ This part is new valerio.bozzolan: ↑ That part was just copy-pasted mostly from the already-existing block
↓ This part is new | |||||
But `strlen()` wastes too many CPU cycles to just check of a non-empty | |||||
string, and, `strlen()` also comes with these funny things: | |||||
| Check | Return | | |||||
|-----------------|--------| | |||||
| `strlen(false)` | `0` | | |||||
| `strlen(true)` | `1` | | |||||
| `strlen(null)` | deprecation exception since PHP 8.1 | | |||||
As solution, you can start using `is_string()` that requires a | |||||
variable of type string (and it also excludes null). | |||||
In short, this is a good way to check for non-empty strings: | |||||
```lang=php | |||||
if (is_string($value) && $value !== '') { | |||||
// do something | |||||
} | |||||
``` | |||||
If you like that method, this is a shortcut provided by Arcanist: | |||||
```lang=php | |||||
if (phutil_nonempty_string($value)) { | |||||
// do something | |||||
} | |||||
``` | |||||
But note, if you use `phutil_nonempty_string()`, this function is designed | |||||
to throw an exception if it receives `true`, `false`, an array, an object | |||||
or anything alien. This is an extra security measure to detect funny things | |||||
coming from input sources. Do your evaluations but probably this is what | |||||
you want. | |||||
= usort(), uksort(), and uasort() are Slow = | = usort(), uksort(), and uasort() are Slow = | ||||
This family of functions is often extremely slow for large datasets. You should | This family of functions is often extremely slow for large datasets. You should | ||||
avoid them if at all possible. Instead, build an array which contains surrogate | avoid them if at all possible. Instead, build an array which contains surrogate | ||||
keys that are naturally sortable with a function that uses native comparison | keys that are naturally sortable with a function that uses native comparison | ||||
(e.g., `sort()`, `asort()`, `ksort()`, or `natcasesort()`). Sort this array | (e.g., `sort()`, `asort()`, `ksort()`, or `natcasesort()`). Sort this array | ||||
instead, and use it to reorder the original array. | instead, and use it to reorder the original array. | ||||
▲ Show 20 Lines • Show All 181 Lines • Show Last 20 Lines |
Content licensed under Creative Commons Attribution-ShareAlike 4.0 (CC-BY-SA) unless otherwise noted; code licensed under Apache 2.0 or other open source licenses. · CC BY-SA 4.0 · Apache 2.0
I moved this text under the section "Check for non-empty strings"