Formatting by Language

How to indent PHP code on Mac

You can do this with Note My in one pass. Here is how to re-indent PHP that arrived from a framework skeleton, a ticket or three other people — and how it protects your heredocs on the way through.

A PHP file that has passed through a few hands ends up indented several ways at once: four spaces in one class, a tab in the next method, a closing brace in the wrong column. You want one pass that puts every line at the depth it belongs at, in the brace style your project actually uses, without the formatter quietly rewriting the inside of a heredoc.

Note My does that from a single toolbar button. This guide covers where the PHP controls live, what the pass changes, what it protects, and how to read the result once it lands.

First, make sure the file is being read as PHP

Everything below depends on the document being read as PHP, because the sheet's PHP settings only apply once it is. The status bar shows the language the document is currently being treated as, so glance there before you start.

Most of the time there is nothing to do. Our advanced language identifier picks PHP up on its own, including in a template that opens with markup and only drops into PHP further down — a file like that is treated as PHP, not as HTML. All of it happens on your Mac: nothing is uploaded, there is no account to sign in to, and your code never leaves the machine.

A short pasted fragment is the one case worth a glance. If it arrives as Plain Text, click the Identify pill in the status bar. It opens the Language Inspector — "Identified programming language: X", an override pop-up, Confirm. Set it to PHP there and the PHP half of the Indentation sheet opens up: brace style, the elseif policy, heredoc preservation and the opening-tag setting. Do that before you re-indent and every behaviour described on this page is available to you on that document.

Re-indent a PHP file

  1. Save the file first

    Press ⌘S. A re-indent rewrites the whole document in one go, and a saved file gives you a one-keystroke way back to exactly what you had: File ▸ Reload from Disk (⌘R) drops the reformatted version in the window and reads the saved version straight back off disk. Reloading never changes the file on disk, so you can look at a result, decide against it and be back where you started in a second. Make that a habit on any file you care about, and you can try a brace style you have never used on a real codebase without a moment's hesitation.

  2. Open the Indentation sheet

    Click Indent in the toolbar — the curly-braces glyph. It opens a sheet and changes nothing on the way in, so you can open it on any file just to look at where your settings currently stand.

  3. Choose PHP in the language picker

    The picker offers a global default plus the languages the sheet supports. Selecting PHP snaps the brace style, indent width and tabs-or-spaces controls to your saved PHP values, and reveals the PHP section of the sheet below them.

  4. Set the brace style and the elseif policy

    Both are covered in detail below. The brace style is a pill switch near the top; the elseif policy sits in the PHP section with the heredoc and opening-tag settings.

  5. Press Apply

    Nothing in your document changes until you do. Cancel, or ⎋, closes the sheet and leaves your saved settings as they were. The preview card above the controls renders a canned if <condition> { … } snippet, regenerated live from your settings, so you can see the shape a choice produces before you bring it to your own file.

Note

Plan a re-indent as a whole-document operation. One Apply takes the file from its first line to its last, which is exactly what you want on something that has come back from three people in three shapes — the depth is consistent everywhere, not just in the part you happened to be looking at.

Brace style, scoped to PHP alone

There are exactly two brace styles: same line (K&R) and next line (Allman). Same line is the default. Brace style is one of three settings scoped to a single language, alongside indent width and tabs-or-spaces, so a choice made with PHP selected applies to PHP and leaves your other languages alone.

Pick the one your codebase already reads in and our indentation engine carries it right through the file: every function, every class, every control structure, in the same column, on the same side of the keyword. That is the point of the pass. A file that arrived with braces in two styles because two people wrote it comes back in one, and the diff you hand your reviewer is about the code rather than about whitespace.

On the unit, PHP starts you where PSR-12 asks: four spaces, no tabs, straight out of the box, with the elseif spelling to match. If your house style differs, set it once with PHP selected and every later pass on a PHP file honours it.

elseif or else if

PHP accepts both spellings, and they are not interchangeable inside alternative syntax. The sheet carries a policy with two options — elseif and else if — defaulting to elseif, the spelling PSR-12 asks for.

php
if ($status === 200) {
    return $body;
} else if ($status === 404) {
    return null;
}

With the default in force, the pass normalises that middle keyword:

php
if ($status === 200) {
    return $body;
} elseif ($status === 404) {
    return null;
}

Switch it to else if if your project has settled the other way. This one is a single decision that carries across every language you work in, so you settle the question once and every future pass follows it — worth knowing when you set it with PHP showing in the picker.

Heredocs are protected, not reformatted

This is the part most people are nervous about, and it is a setting you control rather than something you have to hope for. Heredoc preservation is a PHP setting in the sheet, and it is on by default, so there is nothing to switch on first. Leave it ticked unless you have a specific reason not to.

With it on, the whitespace inside a heredoc — which in a heredoc is content, not formatting — comes back exactly as you wrote it, while the code around the block moves to the depth it belongs at. That is the behaviour you want for an embedded SQL query, a mail body, or a chunk of templated markup, where the leading spaces are part of what the program outputs.

php
$sql = <<<TEXT
      SELECT id, name
        FROM accounts
       WHERE active = 1
    TEXT;

The alignment inside that block is yours to keep. If you have ever watched a formatter reflow an embedded query or template into a ragged column, this is the setting that stops it.

Important

The PHP section also carries a setting for where the opening tag sits, so give it a look with PHP showing in the picker before your first Apply on a template. Files that mix PHP and markup are covered further down, and the two decisions go together.

Indent width, tabs and spaces

The global indent width defaults to 4, over a sheet range of 1 to 12, and the unit control defaults to spaces. PHP takes those defaults as they stand, so out of the box you get four spaces — which is what PSR-12 asks for. Two other languages come set to a unit of their own: Go uses tabs, and Python can be pinned to four spaces. If your project wants tabs for PHP, or a width of two because that is what the framework skeleton shipped with, set it with PHP selected in the picker and it stays scoped to PHP, leaving your other languages alone. That is the whole point of the per-language scope: PHP on tabs and everything else on four spaces is a single afternoon's decision, made once and honoured on every file you open afterwards.

Carrying the same settings across a project

The sheet remembers what you chose. Once PHP is set up the way your project wants it — brace style, width, unit, the elseif spelling, heredoc preservation — those values are waiting for you the next time you open the sheet on a PHP file. The second file and the fiftieth are then the same two steps: Indent, then Apply.

That turns an inherited project into a steady walk rather than a negotiation. Open the files you mean to tidy in tabs, press ⌘S on each one before you run the pass, and work down them. The status bar tells you what the document in front of you is being read as, so you can see at a glance that you are on PHP before you apply anything. Where a result is not what you wanted for a particular file, ⌘R puts the saved version straight back in the window and you carry on to the next. By the end the whole set reads in one brace style at one depth, and the diff your reviewer opens is about the code.

Files that mix PHP and markup

A run formats the document as one language, and you choose which one in the picker — a decision you get to make per run, on the file in front of you. Choose PHP and the whole file is treated as PHP from top to bottom, markup included, which is what you want on a controller or a class file that happens to echo a little HTML.

Choose HTML for the same file and a different set of options comes into play: <pre> preservation, script and style formatting, attribute wrapping, all of them in the HTML section of the sheet. That is the better run for a view file that is mostly markup with a little PHP threaded through it, because the markup is the part with the structure worth straightening.

So a heavily mixed template is a two-minute experiment rather than a gamble. Save, run it as PHP, read the result. If the markup is what you wanted straightened, ⌘R back to the saved version and run the same file as HTML instead. You are comparing two real results on your own code, not guessing from a description.

Recovering code a comment swallowed

The sheet has a Recover damaged comments checkbox, off by default. It handles one specific kind of damage: a line where a comment marker has eaten real code, so that // Preserve state $x = 1 $y = 2 is a single commented line instead of a comment and two statements.

PHP is one of the languages it covers. The behaviour is deliberately conservative — single-line comments only, and doc markers (///, //!, /**, /*!) are always left alone, so a docblock is never split apart. Tick it for an inherited file that has that damage in it, and the pass puts the swallowed statements back on lines of their own as it works. Untick it again afterwards and every later run leaves your comments exactly as they are.

Reading the result

Indentation is invisible by definition, so give yourself something to look at.

  • Indentation guides mark each level. They are on by default, with ten styles in Settings ▸ Guides — Dots is the default. Toggle them from the toolbar Guides button while you are reading a result, or pick the style that suits your eye in that Settings pane and leave it there.
  • Show Invisibles in the toolbar draws each space as · and each tab as →. Four independent toggles live in Settings ▸ Invisibles, all off by default, with three marker styles: Symbols (the default), Code Points, Bytes. None of it is written into the file — the markers are drawn, not inserted.
  • Double-click a variable and every occurrence is highlighted, whole-identifier and Unicode-aware. PHP $vars get a colour of their own from our syntax engine, so they stand out before you even click.
  • Double-click a brace to fold the block under it. Folding covers {}, () and [] and marks the gutter, and it never changes the document or marks it as edited.

What one pass gives you

  • Every line at the depth it belongs at. Depth and brace placement are settled across the whole document in a single Apply, so the four-space class and the tab-indented method below it end up speaking the same language.
  • Statements that shared a line, unpacked. Two statements crammed onto one line come back on lines of their own, at the right depth, which is usually the moment an inherited file starts reading like code again.
  • Your brace style, applied uniformly. Same line or next line, chosen once with PHP selected, then carried through every function and control structure in the file.
  • Heredocs exactly as you wrote them. The embedded query keeps its alignment while the code around it moves. This is the setting that lets you run a formatter over a file full of templated output without holding your breath.
  • Auto-pairing while you type. Brackets and quotes pair as you write PHP — six pairs — so the braces are balanced before a re-indent ever has to reason about them.
  • Nothing reformats on your behalf. The pass runs when you press Apply and at no other moment. Open the sheet, change a setting, close it again, and your document is untouched.

That last point is worth sitting with, because it is what makes the rest usable on real work. A file you have not saved is never quietly rewritten, the sheet is safe to open on anything, and the settings you choose are yours until you change them. Combined with ⌘R as a way back to the saved version, it means you can run a pass on a codebase you inherited this morning and find out what your house style actually looks like on it, with no consequence you did not ask for.

Troubleshooting

I pressed Indent and nothing happened

The Indent button opens the Indentation sheet, which is where the pass gets set up: the language, the brace style, the unit and the PHP options. Nothing changes in the document until you press Apply, so that is the button to finish on. Cancel and ⎋ both close the sheet with your document and your saved settings exactly as they were — reopen it, choose what you want, and Apply.

My heredoc came back reformatted

Check that heredoc preservation is still ticked in the PHP section of the sheet. It ships on. Then check that the document really was formatted as PHP, since that is what brings the PHP settings with it. Set the language with the Identify pill and run the pass again — the next Apply moves the code around the block and hands the block back exactly as you wrote it.

The indentation changed depth partway down the file

That is the pass telling you something useful: it follows the braces, so a depth that shifts at a particular line points straight at an unbalanced brace just above it. Read that spot, ⌘R back to the saved version, close the brace, and run again — the second pass comes out clean. Plenty of people use a re-indent this way on purpose, as the quickest method of finding the brace they dropped in a long class.

Getting back to the version I saved

File ▸ Reload from Disk (⌘R) is the dependable way back, which is exactly why saving is the first step above. It reads your saved file off disk and puts it in the window, and it never changes the file on disk in the process. Our auto-recovery safety net is there for an unexpected quit as well, so a file you were partway through is not lost to a crash.

Working on a very large PHP file

Bring it in. Our large-file engine is what keeps a very large PHP file open, scrollable and editable in the first place, and a re-indent runs over the whole of it. A file of that size takes a moment to come back — the window is working through it — and then the entire document is at the right depth in one go, which is a good deal better than paging through it by hand.

Which settings are scoped to PHP

Brace style, indent width and tabs-or-spaces are the three scoped to a single language: set them with PHP showing in the picker and they apply to PHP alone. Every other switch in the sheet is a single decision that applies across the board, so you settle it once and it holds for whatever you open next.

A scan told me the file has mixed tab and space indentation

That is the two features working together. Our hidden-character scanner finds mixed tab and space indentation and lists every line it sits on — the kind of thing that is invisible until a diff turns strange. The re-indent pass is what resolves it: choose your unit, press Apply, and the whole file comes back on one of them.

Frequently asked questions

Where do I start a re-indent?

The toolbar Indent button — the curly-braces glyph. It opens the Indentation sheet, where you set the brace style, the unit and the PHP options, then press Apply to run the pass over the document.

What happens to the contents of my heredoc?

They come back exactly as you wrote them. Heredoc preservation is on by default, so the whitespace inside the block is treated as the content it is, while the code around the block is re-indented.

How close is the default to PSR-12?

The unit and the keyword spelling match it as shipped: four spaces, no tabs, and elseif. For braces you choose the style your project uses, and the pass applies your choice consistently from the first line of the file to the last.

Can I use tabs for PHP and spaces everywhere else?

Yes. Tabs-or-spaces is one of the three settings scoped to a single language, so set it with PHP selected in the picker and it stays scoped to PHP. Two languages come set to a unit of their own — Go uses tabs, Python can be pinned to four spaces — and PHP takes whichever unit you give it.

What is the preview card showing me?

A canned if <condition> { … } snippet, regenerated live from the current width, unit and brace style. It shows you the shape of a setting as you change it, so you can see what a choice will look like before you apply it to your own file.

Can I try a brace style before I commit to it?

Yes, and on your own code rather than a sample. Press ⌘S first, pick the style, press Apply, and read the result in the editor. If you would rather keep what you had, ⌘R reads the saved version back off disk and you are exactly where you started. Seeing a style on a real class settles the question far faster than reading about it.

Does any of this send my code anywhere?

Everything on this page happens on your Mac. There is no account and no sign-in, nothing is uploaded, and your files never leave the machine.