PHP na Linha de Comando

O PHP ficou famoso por gerar páginas web, mas ele também é um programa comum de terminal. Com o comando php você roda arquivos, testa uma linha de código, recebe informações de quem chamou o programa e devolve um "deu certo" ou "deu errado" para o terminal. Nesta aula você vai usar o PHP como se usa qualquer comando do Linux.

Você vai aprender
  • As três formas de rodar código: php arquivo.php, php -r e php -a.
  • Como o programa recebe informações na hora de rodar: $argv e $argc.
  • Como perguntar algo e ler o que a pessoa digitou: fgets(STDIN) e readline.
  • Como terminar o programa com um código de saída (exit) e conferir com echo $?.
  • Como transformar um arquivo PHP num comando, com #!/usr/bin/env php e chmod +x.

Os exemplos rodam no terminal da Ana, numa pasta chamada cli. Se você ainda não instalou o PHP, veja Instalar no Linux; as missões rodam direto no editor do guia.


A ideia: o mesmo cozinheiro, duas portas

Imagine um restaurante. O cozinheiro (o PHP) pode receber pedidos pela janela do salão, onde os clientes são os navegadores e a resposta é uma página web. Mas ele também atende pela porta da cozinha, onde quem pede é você, digitando no terminal, e a resposta é texto na tela. A comida é a mesma; muda quem pede e como a resposta é entregue.

A porta da cozinha se chama CLI (command line interface, "interface de linha de comando"). É por ela que rodam os scripts de manutenção, as tarefas agendadas, o Composer, os testes, e também cada exemplo desta parte do guia. A janela do salão você conhece em O Servidor Embutido e em PHP Dentro do HTML.


1. php arquivo.php: rodar um arquivo

É o jeito que você já usou em Olá, Mundo: o comando php seguido do nome do arquivo.

<?php
echo "Olá, mundo!\n";
$ php ola.php
Olá, mundo!
php
O programa PHP, que fica numa pasta do sistema (aqui, /opt/php/bin/php).
ola.php
O arquivo a rodar. O PHP lê o arquivo, executa e mostra a saída no terminal. Quando o programa acaba, o prompt $ volta.

O arquivo pode estar em outra pasta: php projetos/ola.php ou php /home/ana/cli/ola.php. Se o nome estiver errado, o PHP avisa:

$ php olaa.php
Could not open input file: olaa.php

Could not open input file = "não consegui abrir o arquivo de entrada". Confira o nome e a pasta onde você está (ls e pwd).


2. php -r: rodar uma linha, sem arquivo

Para testar uma coisa rápida, criar um arquivo dá trabalho. A opção -r (de run, "rodar") executa o código escrito ali mesmo, na linha de comando:

$ php -r 'echo 10 / 3, PHP_EOL;'
3.3333333333333
$ php -r 'echo strtoupper("ana"), "\n";'
ANA
$ php -r 'echo PHP_VERSION, "\n";'
8.5.8
-r
"Rode o código que vem a seguir."
'echo 10 / 3, PHP_EOL;'
O código, entre aspas simples. Sem <?php: no -r, tudo já é código PHP. O PHP_EOL é a quebra de linha (veja echo e print).

Duas armadilhas. A primeira: se você colocar <?php, o PHP reclama, porque no -r ele não espera essa marca:

$ php -r '<?php echo 1;'

Parse error: syntax error, unexpected token "<", expecting end of file in Command line code on line 1

Repare no lugar do erro: em vez de um arquivo, Command line code ("código da linha de comando"). Para ler mensagens de erro, veja Lendo Erros do PHP.

A segunda armadilha é usar aspas duplas por fora. O terminal (o Bash) troca tudo que começa com $ pelo valor de uma variável dele, antes de o PHP ver o código. Como o Bash não tem variável $nome, ela vira nada:

$ php -r "$nome = 'Ana'; echo $nome;"

Parse error: syntax error, unexpected token "=", expecting end of file in Command line code on line 1

O PHP recebeu = 'Ana'; echo ;, sem as variáveis. A regra: no php -r, aspas simples por fora, aspas duplas por dentro.

$ php -r '$nome = "Ana"; echo $nome;'
Anaana@notebook:~/cli$                « sem quebra de linha, o prompt grudou na saída

Viu o Anaana@notebook? O echo não pulou linha, então o prompt do terminal apareceu colado no Ana. Não é erro: é só o próximo $ do terminal. Por isso os exemplos terminam com "\n" ou PHP_EOL.


3. php -a: o modo interativo

A opção -a (de interactive) abre uma "conversa" com o PHP: você digita uma instrução, aperta Enter, e ele executa na hora, sem arquivo. É ótimo para experimentar uma função.

$ php -a
Interactive shell

php > echo 10 / 3;
3.3333333333333
php > $nome = "Ana";
php > echo "Olá, $nome!";
Olá, Ana!
php > echo strtoupper($nome);
ANA
php > exit
Interactive shell
"Modo interativo": o PHP avisa que entrou na conversa.
php >
O prompt do PHP (não do terminal): ele está esperando uma instrução. Escreva sem <?php, terminando com ;, como num arquivo, e aperte Enter.
$nome = "Ana";
Criar uma variável não mostra nada, mas ela continua existindo nas linhas seguintes, até você sair.
echo "Olá, $nome!";
Repare que não foi preciso \n: o modo interativo pula a linha sozinho antes do próximo php >.
exit
Sai do modo interativo e volta ao terminal (Ctrl+D também funciona).

As setas funcionam como no terminal: ↑ traz de volta a instrução anterior, para você corrigir e repetir. Quem cuida disso é a extensão readline ("ler linha"), que vem junto com o PHP nas instalações comuns. O que você faz no php -a some quando você sai; para guardar, use um arquivo. Para experimentar código deste guia, o editor das missões também serve: você muda o código e clica em Rodar quantas vezes quiser.

Qual usar?
Você quer…Use
Um programa de verdade, para guardar e rodar de novophp arquivo.php
Uma conta ou uma função, uma vez sóphp -r '…'
Experimentar várias linhas seguidas, vendo cada resultadophp -a ou o editor do guia

4. $argv e $argc: o que veio depois do nome do arquivo

Comandos do Linux recebem informações na própria linha: em mkdir fotos, fotos é um argumento. Seus programas PHP também podem receber argumentos. Tudo o que você escreve depois do nome do arquivo chega ao programa em duas variáveis prontas:

<?php
echo "Quantidade de itens: $argc\n";
echo "Item 0: $argv[0]\n";
echo "Item 1: $argv[1]\n";
echo "Item 2: $argv[2]\n";
$ php args.php Ana 29
Quantidade de itens: 3
Item 0: args.php
Item 1: Ana
Item 2: 29
$argv
argument values, "os valores dos argumentos". Um array, uma lista numerada a partir do zero. A posição 0 é o nome do arquivo; os argumentos de verdade começam na 1.
$argc
argument count, "a contagem de argumentos": quantos itens o $argv tem, contando o nome do arquivo. Por isso deu 3 para dois argumentos.
$argv[1]
O item da posição 1: Ana. Os colchetes escolhem a posição.

Quem separa os argumentos é o terminal, nos espaços. Para mandar um argumento com espaço dentro, use aspas. Para ver o array inteiro, o print_r ajuda (ele é explicado em var_dump e print_r):

<?php
print_r($argv);
$ php ver-args.php "Ana Souza" 29 --cor=azul
Array
(
    [0] => ver-args.php
    [1] => Ana Souza
    [2] => 29
    [3] => --cor=azul
)

Repare: "Ana Souza" chegou como um argumento (as aspas sumiram, foram só para o terminal); o 29 chegou como texto, não como número; e --cor=azul chegou como está: o PHP não interpreta nada, quem decide o que cada argumento significa é o seu programa.

E se a pessoa não mandar o argumento?

<?php
$nome = $argv[1];
echo "Olá, $nome!\n";
$ php saudacao.php Ana
Olá, Ana!
$ php saudacao.php

Warning: Undefined array key 1 in /home/ana/cli/saudacao.php on line 2
Olá, !

Sem argumento, não existe a posição 1 no array, e o PHP avisa: "chave de array não definida: 1". O jeito curto de dar um valor padrão é o operador ?? ("se não existir, use isto"), que você vai estudar em ?? e ?->:

<?php
$nome = $argv[1] ?? "visitante";
echo "Olá, $nome!\n";
$ php saudacao2.php
Olá, visitante!

Missão: esta caixa roda o programa como php main.php Ana 29. Complete a segunda linha para usar o segundo argumento (a idade) e mostrar Olá, Ana! Você tem 29 anos.

<?php
$nome = $argv[1];
$idade = "?";
echo "Olá, $nome! Você tem $idade anos.\n";
<?php
$nome = $argv[1];
$idade = $argv[2];
echo "Olá, $nome! Você tem $idade anos.\n";
$argv só existe no terminal

Numa página web não há "linha de comando", então $argv não serve. Lá, as informações chegam pelo endereço e por formulários, em $_GET e $_POST (veja $_GET e $_POST).


5. STDIN: perguntar e ler o que a pessoa digitou

Argumentos são decididos antes de o programa rodar. Às vezes você quer perguntar durante: "Qual é o seu nome?", e esperar a resposta. O teclado chega ao programa por um canal chamado STDIN (standard input, "entrada padrão"). A função fgets lê uma linha dele:

<?php
echo "Qual é o seu nome? ";
$nome = trim(fgets(STDIN));
echo "Olá, $nome!\n";
$ php nome.php
Qual é o seu nome? Ana                « o programa para e espera; a Ana digitou Ana e Enter
Olá, Ana!
echo "Qual é o seu nome? ";
A pergunta, sem \n, para o cursor ficar na mesma linha.
STDIN
Uma constante que o PHP cria sozinho no terminal: o canal de entrada (normalmente, o teclado).
fgets(STDIN)
file get string, "pegar um texto do arquivo": lê uma linha do canal, até o Enter. O programa fica parado esperando.
trim(…)
"Aparar": tira espaços e quebras de linha das pontas. Precisa dele porque o Enter que você apertou vem junto com o texto.

Veja o que acontece sem o trim. Aqui o echo Bruno | manda o texto Bruno (com o Enter) para a entrada do programa, como se alguém tivesse digitado:

$ echo Bruno | php nome-sem-trim.php
Qual é o seu nome? Olá, Bruno
!                                     « o Enter do "Bruno" veio junto e quebrou a frase
$ echo Bruno | php nome.php
Qual é o seu nome? Olá, Bruno!

(O Bruno não aparece depois da pergunta porque ninguém digitou: ele veio pelo cano |. Quando você digita, o terminal mostra as letras.)

A outra forma: readline

A função readline faz a pergunta e lê a resposta numa linha só, já sem o Enter no final:

<?php
$nome = readline("Qual é o seu nome? ");
echo "Olá, $nome!\n";
$ php nome-readline.php
Qual é o seu nome? Ana                « a Ana digitou Ana e Enter
Olá, Ana!

Sem trim, e a exclamação ficou na mesma linha: o readline já devolve o texto sem o Enter. Como no php -a, a pessoa pode usar as setas para corrigir o que digitou antes de apertar Enter. Mas há um detalhe: quando a resposta não vem do teclado, e sim de um cano (|), a pergunta não aparece:

$ echo Bruno | php nome-readline.php
Olá, Bruno!
FormaVantagemCuidado
trim(fgets(STDIN))Funciona em qualquer PHP de terminalPrecisa do trim e de um echo antes para a pergunta
readline("pergunta")Mais curta; permite usar as setas para corrigir o que digitouA pergunta não aparece quando a entrada vem de um cano (|)

Na dúvida, use trim(fgets(STDIN)). E lembre: o que chega do teclado é sempre texto, mesmo que a pessoa digite 29 (veja Conversão de Tipos).

Missão: esta caixa digita Bruno para o programa. Clique em Rodar: a exclamação caiu na linha de baixo. Conserte para sair Olá, Bruno! numa linha só, ainda lendo com fgets.

<?php
echo "Qual é o seu nome? ";
$nome = fgets(STDIN);
echo "Olá, $nome!\n";
<?php
echo "Qual é o seu nome? ";
$nome = trim(fgets(STDIN));
echo "Olá, $nome!\n";

6. exit: terminar e dizer como foi

Todo programa de terminal, ao terminar, entrega um número para o sistema: o código de saída. 0 quer dizer "deu tudo certo"; qualquer outro número (de 1 a 255) quer dizer "algo deu errado", e cada programa escolhe o que cada número significa. O terminal guarda o código do último comando na variável $?:

$ php ola.php
Olá, mundo!
$ echo $?
0

Um programa PHP que chega ao fim normalmente sai com 0. Com um erro fatal, sai com 255 (veja Lendo Erros). E você pode escolher o código, e parar o programa onde quiser, com exit:

<?php
echo "Conferindo o estoque...\n";
exit(3);
echo "Esta linha nunca aparece\n";
$ php sair.php
Conferindo o estoque...
$ echo $?
3
exit(3);
"Termine o programa agora, com o código 3." O que vem depois não roda.
echo $?
Comando do terminal (não do PHP): mostra o código de saída do último comando. Tem que ser o comando logo depois, porque cada comando novo troca o valor.

Se você passar um texto em vez de um número, o exit mostra o texto e sai com 0:

<?php
exit("Arquivo de estoque não encontrado.\n");
$ php sair2.php
Arquivo de estoque não encontrado.
$ echo $?
0

Cuidado: a mensagem é de erro, mas o código diz "deu certo". Para avisar um problema de verdade, mostre a mensagem e depois use exit com um número diferente de zero. Você também vai ver die(…) em códigos por aí: é outro nome para o mesmo exit.

Para que serve o código de saída?

Para outros programas saberem se o seu deu certo. No terminal, && roda o segundo comando só se o primeiro saiu com 0:

$ php ola.php && echo "deu certo"
Olá, mundo!
deu certo
$ php sair.php && echo "deu certo"
Conferindo o estoque...               « saiu com 3: o "deu certo" não rodou

É assim que scripts de instalação, tarefas agendadas e ferramentas de teste decidem se continuam ou param.

Mensagens de erro vão para STDERR

Assim como existe a entrada padrão, existem duas saídas: STDOUT (a normal, para onde vai o echo) e STDERR (a de erros). Mandar as mensagens de erro pela segunda permite separar o resultado dos avisos. Este programa exige um argumento:

<?php
$arquivo = $argv[1] ?? "";
if ($arquivo === "") {
    fwrite(STDERR, "Uso: php estoque.php ARQUIVO\n");
    exit(1);
}
echo "Lendo $arquivo...\n";

O if ("se") testa uma condição; você vai aprendê-lo em if, elseif, else. Aqui: se o nome do arquivo veio vazio, escreva o modo de usar em STDERR (fwrite = "escrever") e saia com 1.

$ php estoque.php
Uso: php estoque.php ARQUIVO
$ echo $?
1
$ php estoque.php 2>/dev/null          « 2>/dev/null joga fora só a saída de erros
$ php estoque.php produtos.csv
Lendo produtos.csv...
$ echo $?
0

7. Shebang: o arquivo PHP vira um comando

Até aqui você sempre escreveu php antes do arquivo. Com duas mudanças, o arquivo roda sozinho, como ls ou mkdir. A primeira é uma linha especial no topo, o shebang (o nome vem de #, "sharp", e !, "bang"):

#!/usr/bin/env php
<?php
echo "Oi! Eu sou um programa PHP.\n";
#!
Avisa o Linux: "para rodar este arquivo, use o programa que vem a seguir". Tem que ser os dois primeiros caracteres do arquivo, na linha 1.
/usr/bin/env php
"Procure o php nas pastas de programas e use-o." Assim o arquivo funciona onde quer que o PHP esteja instalado.
<?php
Continua necessário, na linha 2.

O nome do arquivo não precisa terminar em .php: um comando chamado oi é mais agradável de digitar. A segunda mudança é dar permissão de execução ao arquivo:

$ ./oi
bash: ./oi: Permissão negada
$ ls -l oi
-rw-rw-r-- 1 ana ana 63 out 16 20:50 oi
$ chmod +x oi
$ ls -l oi
-rwxrwxr-x 1 ana ana 63 out 16 20:50 oi
$ ./oi
Oi! Eu sou um programa PHP.
./oi
"Rode o arquivo oi desta pasta" (./ = a pasta atual). Na primeira vez, o Linux negou: o arquivo não tinha permissão de execução.
chmod +x oi
"Mude a permissão (change mode): acrescente (+) a execução (x)." Repare nos x que apareceram no ls -l.

E a linha do shebang não aparece na saída: quando o PHP roda um arquivo que começa com #!, ele pula essa primeira linha. Por isso o mesmo arquivo também funciona do jeito de sempre:

$ php oi
Oi! Eu sou um programa PHP.
E no Windows?

O Windows não usa shebang nem chmod: lá você sempre roda com php arquivo.php. Todo o resto desta aula (-r, $argv, STDIN, exit) funciona igual. Veja Instalar no Windows e macOS.


Quando dá errado

"Could not open input file"
$ php olaa.php
Could not open input file: olaa.php

O que significa: "não consegui abrir o arquivo de entrada". O nome está errado ou você está em outra pasta.

Como consertar: ls mostra os arquivos da pasta atual; pwd mostra onde você está. Use a tecla Tab para completar o nome.

php -r com aspas duplas: "unexpected token "=""
$ php -r "$nome = 'Ana'; echo $nome;"

Parse error: syntax error, unexpected token "=", expecting end of file in Command line code on line 1

O que significa: o terminal trocou $nome por nada antes de o PHP ver o código.

Como consertar: aspas simples por fora: php -r '$nome = "Ana"; echo $nome;'.

"Undefined array key 1" ao usar $argv
$ php saudacao.php

Warning: Undefined array key 1 in /home/ana/cli/saudacao.php on line 2
Olá, !

O que significa: o programa esperava um argumento que não foi passado.

Como consertar: passe o argumento (php saudacao.php Ana) ou dê um valor padrão: $argv[1] ?? "visitante".

A resposta digitada "quebra" a frase
$ echo Bruno | php nome-sem-trim.php
Qual é o seu nome? Olá, Bruno
!

O que significa: o fgets devolve a linha com o Enter no final.

Como consertar: trim(fgets(STDIN)).

"Permissão negada" ao rodar ./arquivo
$ ./oi
bash: ./oi: Permissão negada

O que significa: o arquivo não tem permissão de execução.

Como consertar: chmod +x oi. Confira também se o #!/usr/bin/env php está na primeira linha, sem espaço nem linha em branco antes.

Missão final: o programa da lojinha confere o estoque. Quando ele está zerado, deve mostrar Estoque vazio! e terminar com o código de saída 2, sem chegar à linha da venda. Complete a linha marcada.

<?php
$estoque = 0;
if ($estoque === 0) {
    echo "Estoque vazio!\n";
    // termine aqui, com o código 2
}
echo "Venda registrada.\n";
<?php
$estoque = 0;
if ($estoque === 0) {
    echo "Estoque vazio!\n";
    exit(2);
}
echo "Venda registrada.\n";

Referência

Comando ou recursoO que fazExemplo
php arquivo.phpRoda um arquivophp ola.php
php -r '…'Roda código direto (sem <?php; aspas simples por fora)php -r 'echo 2 ** 10, "\n";'
php -aModo interativo (prompt php >; sai com exit)php -a
php -l arquivo.phpConfere a sintaxe sem rodar (veja Erros)php -l ola.php
php -S localhost:8000Servidor web de teste (veja php -S)
php -v / php -mMostra a versão / as extensões instaladasphp -m
$argvArray: [0] = arquivo, [1] em diante = argumentos (sempre texto)$argv[1] ?? "padrão"
$argcQuantos itens o $argv tem (inclui o arquivo)php a.php x y → 3
trim(fgets(STDIN))Lê uma linha digitada, sem o Enter$nome = trim(fgets(STDIN));
readline("…")Pergunta e lê, já sem o Enter$nome = readline("Nome: ");
exit(N) / die(N)Termina com o código N (0 = certo)exit(1);
exit("texto")Mostra o texto e termina com código 0
fwrite(STDERR, "…")Escreve na saída de erros
echo $?(Terminal) código de saída do último comando
#!/usr/bin/env php + chmod +xArquivo PHP executável como comando./oi

Exercícios

1. Uma linha só

Complete para o PHP mostrar o resultado de 7 * 6 sem criar arquivo:

php

2. O segundo argumento

Em php nota.php Ana 9, complete para pegar o 9:

$nota =

3. Executável

Complete o comando que dá permissão de execução ao arquivo relatorio:

4. Quanto vale $argc em php compras.php pão leite café?

5. Por que se usa trim em trim(fgets(STDIN))?

6. Um programa termina com exit(1);. O que o echo $? logo depois mostra, e o que isso quer dizer?

7. Qual destes comandos funciona como esperado?


Resumo

  • php arquivo.php roda um arquivo; php -r '…' roda uma linha (sem <?php, aspas simples por fora); php -a abre o modo interativo, com o prompt php >.
  • $argv é a lista do que veio na linha de comando: [0] é o arquivo, os argumentos começam no [1], e tudo chega como texto. $argc conta os itens.
  • Para ler o teclado: trim(fgets(STDIN)). readline("…") é mais curto e já tira o Enter.
  • exit(N) termina o programa com o código N; 0 = certo, outro número = problema. No terminal, echo $? mostra o código.
  • Mensagens de erro vão para STDERR com fwrite(STDERR, "…").
  • #!/usr/bin/env php na linha 1 + chmod +x transformam o arquivo num comando: ./oi.