|
| 1 | +--- |
| 2 | +title: Watches and Edits |
| 3 | +--- |
| 4 | + |
| 5 | +[Inspecting variables](./inspect-variables.md) tells you what the code is holding. |
| 6 | +This page covers the two things you can do beyond that: watch an expression so its |
| 7 | +value is in front of you at every stop, and change a value while the script is |
| 8 | +paused. |
| 9 | + |
| 10 | +Underneath, both are the same mechanism — the debugger runs a fragment of PHP |
| 11 | +inside your paused request — and that is worth knowing, because it explains the |
| 12 | +sharp edges on both. |
| 13 | + |
| 14 | +## Watch expressions |
| 15 | + |
| 16 | +A watch is an expression you hand to your editor once, which it then re-evaluates |
| 17 | +every time execution stops and shows alongside the variables. Anything valid in |
| 18 | +that scope will do: |
| 19 | + |
| 20 | +```php |
| 21 | +$order->getTotal() |
| 22 | +count($items) |
| 23 | +$user->isActive() && $user->getRole() === 'admin' |
| 24 | +$items[$i]['sku'] |
| 25 | +``` |
| 26 | + |
| 27 | +The value refreshes at every stop, so stepping through a loop with |
| 28 | +`$items[$i]['sku']` on watch turns "what is this iteration working on" into |
| 29 | +something you read rather than something you go digging for. |
| 30 | + |
| 31 | +Watches are the natural companion to stepping. A breakpoint tells you *when*, the |
| 32 | +variables panel tells you *what is here*, and a watch tells you *what the thing you |
| 33 | +actually care about is doing* — especially when it is buried several levels inside |
| 34 | +a structure you would otherwise be expanding by hand at every stop. |
| 35 | + |
| 36 | +### What a watch really is |
| 37 | + |
| 38 | +It is not a read. The debugger evaluates the expression as PHP, in the paused |
| 39 | +process, exactly as though the line appeared in your code at that point. Four |
| 40 | +consequences follow, and all four can surprise you: |
| 41 | + |
| 42 | +**Side effects are real.** `$collection->pop()` in a watch pops the collection — |
| 43 | +every time you stop. An expression that writes to the database writes to the |
| 44 | +database. Keep watches to questions, not actions. |
| 45 | + |
| 46 | +**Breakpoints inside a watch are ignored.** The debugger switches breakpoints off |
| 47 | +while it evaluates. Watch an expression that calls a function you have a breakpoint |
| 48 | +in, and that breakpoint stays silent — which is the right behaviour, since the |
| 49 | +alternative is stopping inside your own watch. |
| 50 | + |
| 51 | +**Errors are swallowed.** Error reporting is turned off for the evaluation, so a |
| 52 | +warning or notice your expression provokes goes nowhere. If a thrown exception is |
| 53 | +what ended it, your editor shows the exception message in place of the value. |
| 54 | + |
| 55 | +**Watches evaluate where execution stopped.** Selecting an outer frame in the call |
| 56 | +stack changes what the variables panel shows, but a watch is still evaluated in the |
| 57 | +frame the debugger actually paused in. An expression naming a local of the caller |
| 58 | +will not resolve just because you have that frame selected. |
| 59 | + |
| 60 | +Most editors also offer a one-off "evaluate expression" box for a question you only |
| 61 | +want to ask once. It is the same machinery with the same caveats — the only |
| 62 | +difference is that nothing is remembered for the next stop. |
| 63 | + |
| 64 | +## Editing values |
| 65 | + |
| 66 | +You can also change a value while the script is paused. In most editors it is a |
| 67 | +double-click on the value in the variables panel, or a "set value" item on its |
| 68 | +context menu. |
| 69 | + |
| 70 | +What happens is an assignment, executed in the scope of the frame you have selected |
| 71 | +— `$retries = 0`, run inside the paused request. Execution then carries on with the |
| 72 | +new value, exactly as if the code had put it there. |
| 73 | + |
| 74 | +This is the fastest way to reach a branch you cannot otherwise get to. A retry path |
| 75 | +that only runs on the third failure, an error handler that needs a malformed |
| 76 | +response, a discount that only applies over a threshold: rather than contriving the |
| 77 | +input, stop before the branch and set the value that takes you down it. |
| 78 | + |
| 79 | +Editors usually let you say what type you mean — boolean, integer, float or string |
| 80 | +— for the cases where `0` should not become the string `"0"`. |
| 81 | + |
| 82 | +Some edits will simply refuse: |
| 83 | + |
| 84 | +- a **typed property** given something its declared type rejects |
| 85 | +- a **readonly property** that has already been set |
| 86 | +- **constants**, which cannot be assigned to at all |
| 87 | + |
| 88 | +Your editor reports that the change did not take, though often quietly — if a value |
| 89 | +snaps back to what it was, that is what happened. |
| 90 | + |
| 91 | +:::warning[You are changing the run, not the code] |
| 92 | + |
| 93 | +An edit lives in the paused request and nowhere else. The file on disk is untouched |
| 94 | +and the value is gone when the request ends. |
| 95 | + |
| 96 | +That cuts both ways. Nothing you do here can damage your source — but a bug you |
| 97 | +"fixed" by editing a value is still in the code, and the next request will run it |
| 98 | +exactly as before. |
| 99 | + |
| 100 | +::: |
0 commit comments