@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Minor formatting changes for some documentation

Summary: Self explanatory.

Test Plan: Eyeball it.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: epriestley, Korvin

Differential Revision: https://secure.phabricator.com/D13278

+163 -135
+163 -135
src/docs/flavor/php_pitfalls.diviner
··· 4 4 This document discusses difficult traps and pitfalls in PHP, and how to avoid, 5 5 work around, or at least understand them. 6 6 7 - = array_merge() in Incredibly Slow When Merging A List of Arrays = 7 + = `array_merge()` in Incredibly Slow When Merging A List of Arrays = 8 8 9 9 If you merge a list of arrays like this: 10 10 11 - COUNTEREXAMPLE 11 + COUNTEREXAMPLE, lang=php 12 12 $result = array(); 13 13 foreach ($list_of_lists as $one_list) { 14 14 $result = array_merge($result, $one_list); ··· 21 21 In a libphutil environment, you can use @{function@libphutil:array_mergev} 22 22 instead. 23 23 24 - = var_export() Hates Baby Animals = 24 + = `var_export()` Hates Baby Animals = 25 25 26 - If you try to var_export() an object that contains recursive references, your 26 + If you try to `var_export()` an object that contains recursive references, your 27 27 program will terminate. You have no chance to intercept or react to this or 28 - otherwise stop it from happening. Avoid var_export() unless you are certain 29 - you have only simple data. You can use print_r() or var_dump() to display 28 + otherwise stop it from happening. Avoid `var_export()` unless you are certain 29 + you have only simple data. You can use `print_r()` or `var_dump()` to display 30 30 complex variables safely. 31 31 32 - = isset(), empty() and Truthiness = 32 + = `isset()`, `empty()` and Truthiness = 33 33 34 34 A value is "truthy" if it evaluates to true in an `if` clause: 35 35 36 + lang=php 36 37 $value = something(); 37 38 if ($value) { 38 39 // Value is truthy. ··· 59 60 60 61 This is wrong because it prevents users from making the comment "0". //THIS 61 62 COMMENT IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD BETTER NOT 62 - BREAK IT!!!// A better test is probably strlen(). 63 + BREAK IT!!!// A better test is probably `strlen()`. 63 64 64 65 In addition to truth tests with `if`, PHP has two special truthiness operators 65 - which look like functions but aren't: empty() and isset(). These operators help 66 - deal with undeclared variables. 66 + which look like functions but aren't: `empty()` and `isset()`. These operators 67 + help deal with undeclared variables. 67 68 68 69 In PHP, there are two major cases where you get undeclared variables -- either 69 70 you directly use a variable without declaring it: 70 71 71 - COUNTEREXAMPLE 72 + COUNTEREXAMPLE, lang=php 72 73 function f() { 73 74 if ($not_declared) { 74 75 // ... ··· 84 85 } 85 86 } 86 87 87 - When you do either of these, PHP issues a warning. Avoid these warnings by using 88 - empty() and isset() to do tests that are safe to apply to undeclared variables. 88 + When you do either of these, PHP issues a warning. Avoid these warnings by 89 + using `empty()` and `isset()` to do tests that are safe to apply to undeclared 90 + variables. 89 91 90 - empty() evaluates truthiness exactly opposite of if(). isset() returns true for 91 - everything except null. This is the truth table: 92 + `empty()` evaluates truthiness exactly opposite of `if()`. `isset()` returns 93 + `true` for everything except `null`. This is the truth table: 92 94 93 - VALUE if() empty() isset() 95 + | Value | `if()` | `empty()` | `isset()` | 96 + |-------|--------|-----------|-----------| 97 + | `null` | `false` | `true` | `false` | 98 + | `0` | `false` | `true` | `true` | 99 + | `0.0` | `false` | `true` | `true` | 100 + | `"0"` | `false` | `true` | `true` | 101 + | `""` | `false` | `true` | `true` | 102 + | `false` | `false` | `true` | `true` | 103 + | `array()` | `false` | `true` | `true` | 104 + | Everything else | `true` | `false` | `true` | 94 105 95 - null false true false 96 - 0 false true true 97 - 0.0 false true true 98 - "0" false true true 99 - "" false true true 100 - false false true true 101 - array() false true true 102 - EVERYTHING ELSE true false true 106 + The value of these operators is that they accept undeclared variables and do 107 + not issue a warning. Specifically, if you try to do this you get a warning: 103 108 104 - The value of these operators is that they accept undeclared variables and do not 105 - issue a warning. Specifically, if you try to do this you get a warning: 106 - 107 - COUNTEREXAMPLE 108 - if ($not_previously_declared) { // PHP Notice: Undefined variable! 109 - // ... 110 - } 109 + ```lang=php, COUNTEREXAMPLE 110 + if ($not_previously_declared) { // PHP Notice: Undefined variable! 111 + // ... 112 + } 113 + ``` 111 114 112 115 But these are fine: 113 116 114 - if (empty($not_previously_declared)) { // No notice, returns true. 115 - // ... 116 - } 117 - if (isset($not_previously_declared)) { // No notice, returns false. 118 - // ... 119 - } 117 + ```lang=php 118 + if (empty($not_previously_declared)) { // No notice, returns true. 119 + // ... 120 + } 121 + if (isset($not_previously_declared)) { // No notice, returns false. 122 + // ... 123 + } 124 + ``` 120 125 121 - So, isset() really means is_declared_and_is_set_to_something_other_than_null(). 122 - empty() really means is_falsey_or_is_not_declared(). Thus: 126 + So, `isset()` really means 127 + `is_declared_and_is_set_to_something_other_than_null()`. `empty()` really means 128 + `is_falsey_or_is_not_declared()`. Thus: 123 129 124 - - If a variable is known to exist, test falsiness with if (!$v), not empty(). 125 - In particular, test for empty arrays with if (!$array). There is no reason 126 - to ever use empty() on a declared variable. 127 - - When you use isset() on an array key, like isset($array['key']), it will 128 - evaluate to "false" if the key exists but has the value null! Test for index 129 - existence with array_key_exists(). 130 + - If a variable is known to exist, test falsiness with `if (!$v)`, not 131 + `empty()`. In particular, test for empty arrays with `if (!$array)`. There 132 + is no reason to ever use `empty()` on a declared variable. 133 + - When you use `isset()` on an array key, like `isset($array['key'])`, it 134 + will evaluate to "false" if the key exists but has the value `null`! Test 135 + for index existence with `array_key_exists()`. 130 136 131 - Put another way, use isset() if you want to type "if ($value !== null)" but are 132 - testing something that may not be declared. Use empty() if you want to type 133 - "if (!$value)" but you are testing something that may not be declared. 137 + Put another way, use `isset()` if you want to type `if ($value !== null)` but 138 + are testing something that may not be declared. Use `empty()` if you want to 139 + type `if (!$value)` but you are testing something that may not be declared. 134 140 135 141 = usort(), uksort(), and uasort() are Slow = 136 142 137 143 This family of functions is often extremely slow for large datasets. You should 138 144 avoid them if at all possible. Instead, build an array which contains surrogate 139 145 keys that are naturally sortable with a function that uses native comparison 140 - (e.g., sort(), asort(), ksort(), or natcasesort()). Sort this array instead, and 141 - use it to reorder the original array. 146 + (e.g., `sort()`, `asort()`, `ksort()`, or `natcasesort()`). Sort this array 147 + instead, and use it to reorder the original array. 142 148 143 149 In a libphutil environment, you can often do this easily with 144 150 @{function@libphutil:isort} or @{function@libphutil:msort}. 145 151 146 - = array_intersect() and array_diff() are Also Slow = 152 + = `array_intersect()` and `array_diff()` are Also Slow = 147 153 148 154 These functions are much slower for even moderately large inputs than 149 - array_intersect_key() and array_diff_key(), because they can not make the 155 + `array_intersect_key()` and `array_diff_key()`, because they can not make the 150 156 assumption that their inputs are unique scalars as the `key` varieties can. 151 157 Strongly prefer the `key` varieties. 152 158 153 - = array_uintersect() and array_udiff() are Definitely Slow Too = 159 + = `array_uintersect()` and `array_udiff()` are Definitely Slow Too = 154 160 155 161 These functions have the problems of both the `usort()` family and the 156 162 `array_diff()` family. Avoid them. 157 163 158 - = foreach() Does Not Create Scope = 164 + = `foreach()` Does Not Create Scope = 159 165 160 - Variables survive outside of the scope of foreach(). More problematically, 161 - references survive outside of the scope of foreach(). This code mutates 166 + Variables survive outside of the scope of `foreach()`. More problematically, 167 + references survive outside of the scope of `foreach()`. This code mutates 162 168 `$array` because the reference leaks from the first loop to the second: 163 169 164 - COUNTEREXAMPLE 165 - $array = range(1, 3); 166 - echo implode(',', $array); // Outputs '1,2,3' 167 - foreach ($array as &$value) {} 168 - echo implode(',', $array); // Outputs '1,2,3' 169 - foreach ($array as $value) {} 170 - echo implode(',', $array); // Outputs '1,2,2' 170 + ```lang=php, COUNTEREXAMPLE 171 + $array = range(1, 3); 172 + echo implode(',', $array); // Outputs '1,2,3' 173 + foreach ($array as &$value) {} 174 + echo implode(',', $array); // Outputs '1,2,3' 175 + foreach ($array as $value) {} 176 + echo implode(',', $array); // Outputs '1,2,2' 177 + ``` 171 178 172 179 The easiest way to avoid this is to avoid using foreach-by-reference. If you do 173 180 use it, unset the reference after the loop: 174 181 175 - foreach ($array as &$value) { 176 - // ... 177 - } 178 - unset($value); 179 - 180 - = unserialize() is Incredibly Slow on Large Datasets = 182 + ```lang=php 183 + foreach ($array as &$value) { 184 + // ... 185 + } 186 + unset($value); 187 + ``` 181 188 182 - The performance of unserialize() is nonlinear in the number of zvals you 183 - unserialize, roughly O(N^2). 189 + = `unserialize()` is Incredibly Slow on Large Datasets = 184 190 185 - zvals approximate time 186 - 10000 5ms 187 - 100000 85ms 188 - 1000000 8,000ms 189 - 10000000 72 billion years 191 + The performance of `unserialize()` is nonlinear in the number of zvals you 192 + unserialize, roughly `O(N^2)`. 190 193 194 + | zvals | Approximate time | 195 + |-------|------------------| 196 + | 10000 |5ms | 197 + | 100000 | 85ms | 198 + | 1000000 | 8,000ms | 199 + | 10000000 | 72 billion years | 191 200 192 - = call_user_func() Breaks References = 201 + = `call_user_func()` Breaks References = 193 202 194 - If you use call_use_func() to invoke a function which takes parameters by 203 + If you use `call_use_func()` to invoke a function which takes parameters by 195 204 reference, the variables you pass in will have their references broken and will 196 205 emerge unmodified. That is, if you have a function that takes references: 197 206 198 - function add_one(&$v) { 199 - $v++; 200 - } 207 + ```lang=php 208 + function add_one(&$v) { 209 + $v++; 210 + } 211 + ``` 201 212 202 - ...and you call it with call_user_func(): 213 + ...and you call it with `call_user_func()`: 203 214 204 - COUNTEREXAMPLE 205 - $x = 41; 206 - call_user_func('add_one', $x); 215 + ```lang=php, COUNTEREXAMPLE 216 + $x = 41; 217 + call_user_func('add_one', $x); 218 + ``` 207 219 208 - ...`$x` will not be modified. The solution is to use call_user_func_array() 220 + ...`$x` will not be modified. The solution is to use `call_user_func_array()` 209 221 and wrap the reference in an array: 210 222 211 - $x = 41; 212 - call_user_func_array( 213 - 'add_one', 214 - array(&$x)); // Note '&$x'! 223 + ```lang=php 224 + $x = 41; 225 + call_user_func_array( 226 + 'add_one', 227 + array(&$x)); // Note '&$x'! 228 + ``` 215 229 216 230 This will work as expected. 217 231 218 - = You Can't Throw From __toString() = 232 + = You Can't Throw From `__toString()` = 219 233 220 - If you throw from __toString(), your program will terminate uselessly and you 234 + If you throw from `__toString()`, your program will terminate uselessly and you 221 235 won't get the exception. 222 236 223 237 = An Object Can Have Any Scalar as a Property = 224 238 225 239 Object properties are not limited to legal variable names: 226 240 227 - $property = '!@#$%^&*()'; 228 - $obj->$property = 'zebra'; 229 - echo $obj->$property; // Outputs 'zebra'. 241 + ```lang=php 242 + $property = '!@#$%^&*()'; 243 + $obj->$property = 'zebra'; 244 + echo $obj->$property; // Outputs 'zebra'. 245 + ``` 230 246 231 247 So, don't make assumptions about property names. 232 248 233 - = There is an (object) Cast = 249 + = There is an `(object)` Cast = 234 250 235 251 You can cast a dictionary into an object. 236 252 237 - $obj = (object)array('flavor' => 'coconut'); 238 - echo $obj->flavor; // Outputs 'coconut'. 239 - echo get_class($obj); // Outputs 'stdClass'. 253 + ```lang=php 254 + $obj = (object)array('flavor' => 'coconut'); 255 + echo $obj->flavor; // Outputs 'coconut'. 256 + echo get_class($obj); // Outputs 'stdClass'. 257 + ``` 240 258 241 259 This is occasionally useful, mostly to force an object to become a Javascript 242 - dictionary (vs a list) when passed to json_encode(). 260 + dictionary (vs a list) when passed to `json_encode()`. 243 261 244 - = Invoking "new" With an Argument Vector is Really Hard = 262 + = Invoking `new` With an Argument Vector is Really Hard = 245 263 246 - If you have some `$class_name` and some `$argv` of constructor 247 - arguments and you want to do this: 264 + If you have some `$class_name` and some `$argv` of constructor arguments 265 + and you want to do this: 248 266 249 - new $class_name($argv[0], $argv[1], ...); 267 + ```lang=php 268 + new $class_name($argv[0], $argv[1], ...); 269 + ``` 250 270 251 271 ...you'll probably invent a very interesting, very novel solution that is very 252 272 wrong. In a libphutil environment, solve this problem with 253 - @{function@libphutil:newv}. Elsewhere, copy newv()'s implementation. 273 + @{function@libphutil:newv}. Elsewhere, copy `newv()`'s implementation. 254 274 255 275 = Equality is not Transitive = 256 276 257 277 This isn't terribly surprising since equality isn't transitive in a lot of 258 - languages, but the == operator is not transitive: 278 + languages, but the `==` operator is not transitive: 259 279 260 - $a = ''; $b = 0; $c = '0a'; 261 - $a == $b; // true 262 - $b == $c; // true 263 - $c == $a; // false! 280 + ```lang=php 281 + $a = ''; $b = 0; $c = '0a'; 282 + $a == $b; // true 283 + $b == $c; // true 284 + $c == $a; // false! 285 + ``` 264 286 265 287 When either operand is an integer, the other operand is cast to an integer 266 - before comparison. Avoid this and similar pitfalls by using the === operator, 288 + before comparison. Avoid this and similar pitfalls by using the `===` operator, 267 289 which is transitive. 268 290 269 291 = All 676 Letters in the Alphabet = 270 292 271 293 This doesn't do what you'd expect it to do in C: 272 294 273 - for ($c = 'a'; $c <= 'z'; $c++) { 274 - // ... 275 - } 295 + ```lang=php 296 + for ($c = 'a'; $c <= 'z'; $c++) { 297 + // ... 298 + } 299 + ``` 276 300 277 - This is because the successor to 'z' is 'aa', which is "less than" 'z'. The 278 - loop will run for ~700 iterations until it reaches 'zz' and terminates. That is, 279 - `$c` will take on these values: 301 + This is because the successor to `z` is `aa`, which is "less than" `z`. 302 + The loop will run for ~700 iterations until it reaches `zz` and terminates. 303 + That is, `$c` will take on these values: 280 304 281 - a 282 - b 283 - ... 284 - y 285 - z 286 - aa // loop continues because 'aa' <= 'z' 287 - ab 288 - ... 289 - mf 290 - mg 291 - ... 292 - zw 293 - zx 294 - zy 295 - zz // loop now terminates because 'zz' > 'z' 305 + ``` 306 + a 307 + b 308 + ... 309 + y 310 + z 311 + aa // loop continues because 'aa' <= 'z' 312 + ab 313 + ... 314 + mf 315 + mg 316 + ... 317 + zw 318 + zx 319 + zy 320 + zz // loop now terminates because 'zz' > 'z' 321 + ``` 296 322 297 323 Instead, use this loop: 298 324 299 - foreach (range('a', 'z') as $c) { 300 - // ... 301 - } 325 + ```lang=php 326 + foreach (range('a', 'z') as $c) { 327 + // ... 328 + } 329 + ```