Escreva \n dentro de uma string entre aspas duplas para imprimir uma nova linha: echo "Line 1\nLine 2";. A constante PHP_EOL faz o mesmo e fica mais legível no fim de uma linha: echo 'Done' . PHP_EOL;. Numa página web, uma quebra de linha não aparece como quebra; ali você precisa de <br> ou nl2br().
Aspas simples ou aspas duplas
Sequências de escape como \n e \t só funcionam entre aspas duplas. Entre aspas simples, '\n' são dois caracteres: uma barra invertida e a letra n. Esse é o motivo comum de "o \n não funciona".
A página sobre strings lista todas as sequências de escape que as aspas duplas entendem.
PHP_EOL ou "\n"
PHP_EOL é o fim de linha do sistema em que o PHP está rodando: "\n" no Linux e no macOS, "\r\n" no Windows. Use para saída no terminal e arquivos que você vai ler na mesma máquina. Quando um formato define o próprio fim de linha, use o dele: cabeçalhos HTTP e e-mail precisam de "\r\n", e a maioria das ferramentas lê "\n" sem problema em qualquer lugar.
"\r" é um retorno de carro, "\n" uma alimentação de linha. Arquivos do Windows terminam as linhas com os dois, "\r\n", e é por isso que texto de um usuário do Windows ou de um <textarea> muitas vezes contém caracteres \r inesperados.
Nova linha no HTML: br e nl2br
Um navegador junta as quebras de linha num único espaço, então echo "a\nb" mostra a b numa página. Imprima você mesmo o <br>, ou mantenha as quebras no texto e converta-as com nl2br(), que insere <br /> antes de cada uma. Rode e compare as abas Output e Page:
Na aba Output, os três parecem iguais. Na aba Page, o primeiro fica todo numa linha, o nl2br() o quebra, e o <pre> mantém as quebras como foram escritas.
Mostrar o texto de um textarea com as quebras de linha
O texto de um <textarea> mantém as linhas que o usuário digitou, mas você precisa escapá-lo antes de imprimi-lo no HTML. A ordem importa: primeiro htmlspecialchars(), depois nl2br(), senão as próprias tags <br /> seriam escapadas. Rode, digite algumas linhas na caixa e clique em Preview: o mesmo script roda de novo com $_POST preenchido.
Uma quebra de linha de textarea pode chegar como "\r\n" ou "\n"; as duas contêm "\n", então contar esse caractere funciona nos dois casos. O htmlspecialchars() explica por que escapar não é opcional aqui.
Dividir uma string por quebra de linha
explode("\n", $text) funciona para textos que você mesmo criou. Para textos de usuários ou de arquivos, os fins de linha podem ser "\r\n", "\n" ou "\r"; preg_split('/\R/', $text) lida com todos, porque \R bate com qualquer quebra de linha.
Remover quebras de linha de uma string
Para transformar um texto de várias linhas numa linha só, substitua todos os tipos de fim de linha. Substitua "\r\n" antes dos caracteres isolados, senão cada quebra de linha do Windows viraria dois espaços:
trim() e rtrim() removem quebras de linha nas pontas de uma string, mas nunca no meio, então são a ferramenta certa para uma linha lida de um arquivo e a errada para juntar linhas.
Perguntas frequentes
Como dou echo numa nova linha no PHP?
Coloque \n dentro de uma string entre aspas duplas: echo "Line 1\nLine 2";. Ou acrescente a constante: echo 'Line 1' . PHP_EOL;. Num navegador, a quebra de linha não aparece; use <br> ou nl2br() ali.
Por que o \n não funciona no PHP?
Ou a string está entre aspas simples, onde '\n' é uma barra invertida e um n, ou a saída é exibida como HTML, onde o navegador trata a quebra de linha como um espaço. Use "\n" entre aspas duplas para saída de texto e nl2br() ou <br> para páginas web.
O que é PHP_EOL?
Uma constante que guarda o fim de linha do sistema operacional em que o PHP roda: "\n" no Linux e no macOS, "\r\n" no Windows. Use para saída no terminal ou arquivos locais; para HTTP, e-mails e arquivos CSV, o formato normalmente define o fim de linha, qualquer que seja o sistema.
O que faz o nl2br no PHP?
nl2br($text) insere <br /> antes de cada quebra de linha da string, para que um texto digitado num <textarea> mantenha as linhas ao ser exibido em HTML. Escape antes: nl2br(htmlspecialchars($text)).
Como removo quebras de linha de uma string no PHP?
Substitua os três tipos de fim de linha: str_replace(["\r\n", "\r", "\n"], ' ', $text), ou com uma regex, preg_replace('/\R/', ' ', $text). Para remover só a quebra de linha final, use rtrim($text, "\r\n").