PHP para Iniciantes: Funções - Definidas pelo Usuário

Até aqui, já vimos uma série de exemplos sobre funções na prática, desde a criação até a usabilidade de uma, mas nada oficial como material de estudo, apenas como exemplo do assunto que estávamos falando na hora. Dito isso, precisamos dar dez passos para trás para mostrar o que exatamente é uma função definida pelo usuário...

Peraí... Quem é esse usuário, Kiko?

Você, ué. O usuário da compilação é quem escreveu o código a ser compilado. Então se você escreveu uma função no código, essa função é chamada função definida pelo usuário, afinal, como vimos no artigo anterior, já temos as funções internas.

Além disso, essas funções definidas também são conhecidas como funções nomeadas, onde obrigatoriamente cada função deve ter um nome único. As regras de escrita de uma função são as mesmas de uma variável, exceto que não colocamos o operador de variável na frente ($). Ou seja, o primeiro caractere deve ser uma letra ou um underline (_) e as demais podem ser, também, números.

No caso de funções, as letras são case insensitive, portanto, não importa a forma que você as escreva (tudo maiúsculo, tudo minúsculo, tudo bagunçado), desde que tenha as mesmas letras na mesma sequência, a função será chamada. Mesmo assim, é bom senso escrever exatamente do mesmíssimo jeito que definiu no momento da declaração, beleza?

Reforçando: funções nomeadas são constantes, isto é, você não pode redefini-la. No máximo criar outra com outro nome. Mesmo variando o maiúsculo e/ou minúsculo, como a declaração é case insensitive, ainda daria conflito.

E mais uma observação: as funções não são declaradas no momento da evaluação (leia-se interpretação com dados de entrada) do código e sim na compilação, ou seja, você pode chamar a função antes de definí-la, desde que ela realmente seja definida posteriormente sem nenhuma dependência da interpretação de uma expressão.

Além disso tudo tem outro ponto importantíssimo: toda função nomeada tem contexto GLOBAL. Mesmo que você a crie dentro de mil laços, ela ainda vai ser acessível do lado de fora de todos eles. Da mesma forma, você não vai conseguir declarar a função mais de uma vez.

E aqui acabamos com os litle details hehehehehehehehe.

Hehehehe

Calma, não se desespere, é claro que eu vou colocar tudo o que falei em prática. Vamos revisar cada ponto mencionado em forma de exemplos:

1 - Uma função deve ter um nome único

Considere o seguinte arquivo, responsável por definir a função nomeUnico():

<?php // nomeUnico.php

function nomeUnico() {
    echo 'Nome Único!';
}

E considere esse outro arquivo, que aciona essa declaração incluindo esse arquivo dentro de si:

<?php // header.php

require(__DIR__ . '/nomeUnico.php');

nomeUnico(); // imprime 'Nome Único!'

E então, esse outro arquivo que inclui o header.php... Mas acidentalmente incluíram, também, o nomeUnico.php:

<?php // index.php

require(__DIR__ . '/header.php');
require(__DIR__ . '/nomeUnico.php'); // Fatal error: Cannot redeclare nomeUnico()

Esse erro só aconteceu porque a função em si já havia sido declarada no primeiro require executado dentro do arquivo header.php. E para solucionar esse problema, tem três jeitos:

A - [Best Developer Choice] Pare de fazer descoberta de funções/classes na mão, use namespace com spl_autoload!

Slap like

Mas tudo bem se não quiser ver isso agora, afinal eu não expliquei nada disso. Porém essa é uma dica de ouro hein? Imagina só incluir uma função e/ou classe somente quando ainda não foi definida? Maravilhoso. E ainda garante um padrão PSR-4 no projeto, se fizer certinho... Ah, maravilha.

B - Limite a importação da função para uma única vez com require_once

Se o arquivo nomeUnico.php serve somente para declarar a função, você pode controlar bem o código ao limitar sua inclusão para somente uma única vez adicionando a terminologia _once no require, modificando os arquivos header.php e index.php para, respectivamente:

<?php // header.php
require_once(__DIR__ . '/nomeUnico.php');

nomeUnico(); // imprime 'Nome Único!'
<?php // index.php

require(__DIR__ . '/header.php');
require_once(__DIR__ . '/nomeUnico.php'); // nada acontece feijoada

Assim, desde que todo require desse arquivo esteja com essa defesa, nada irá declarar a função duas vezes.

C - [Best Beginner Choice] Verifique se a função foi declarada antes de declará-la

Se você sabe que há um risco enorme de outro desenvolvedor desavisado incluir a função sem a terminologia _once (e quiser retirar essa responsabilidade de conhecer tudo o que acontece em todo lugar), você pode deixar a declaração segura encapsulando-a com uma verificação simples usando a função function_exists(), que retorna true quando já existe uma função declarada no nome informado e false quando não.

Ou seja, modificando somente o arquivo nomeUnico.php para:

<?php // nomeUnico.php
if (!function_exists('nomeUnico')) {
    function nomeUnico() {
        echo 'Nome único!';
    }
}

Se você já lidou com WordPress, provavelmente já viu um monte desses espalhados por aí... Se encontrar de novo e já estiver manjando de spl_autoload, por favor, resolve esse débito técnico, ok?

Eu ouvi um ok??!

...

Não tem como eu ter ouvido um ok, mas tudo bem. Vamos para o próximo ponto!

2 - As regras de nomenclatura são as mesmas das variáveis

E aqui não tem o que discutir, apenas sentir

<?php

function ehUmNomeValido() {
    return 'oi';
}

function _tambemEhUmNomeValido() {
    return 'vc';
}

function Tambem3h_UmNomeValido() {
    return 'vem';
}

// daqui pra cima é sucesso
// daqui pra baixo é erro

function 1naoEhUmNomeValido() {
    return 'sempre';
}

function $tbmNao() {
    return 'aqui';
}

function mt Menos Assim() {
    return '?';
}

3 - O uso das funções é case insensitive

<?php

function nomeUnico() {
    echo 'Nome único!' . PHP_EOL;
}

// mesmo que você declare assim, com tudo minúsculo e o U maiúsculo
// você pode chamar do jeito que quiser
NOMEUNICO();
nomeunico();
NoMeUnIcO();
NOMEuNICO();

// você só não pode colocar caracteres diferentes da ordem da escrita
N0MEUNICO(); // erro
_nomeUnico(); // erro

Da mesma forma, respeitando o primero ponto, você não pode escrever outra função com a mesma nomenclatura mesmo variando as letras maiúsculas e minúsculas.

<?php

function nomeUnico() {
    echo 'Nome único!' . PHP_EOL;
}

// aqui vai dar erro
function nomeunico() {
    echo 'Nome único!' . PHP_EOL;
}

4 - As funções não são declaradas no momento da evaluação

Se você escreve uma função no escopo natural do código, sem nenhuma dependência (como aquele if que criei), você poderá escrever sua execução antes mesmo de definí-la sequencialmente. Por exemplo:

<?php

nomeUnico();


function nomeUnico() {
    return 'Funciona!';
}

Isso aqui só funciona porque o PHP dá uma compilada no seu código antes de atribuir valores à chamada. Por isso ele consegue saber que a função existe em algum lugar e chamá-la corretamente. Entretanto, se a declaração da sua função depender de uma expressão, aí não funciona, pois a resolução de expressões é feita no momento de evaluação, onde há dependência de um dado.

<?php

nomeUnico();

if (!function_exists('nomeUnico')) { // expressão dependente
    function nomeUnico() {
        return 'Não funciona =/';
    }
}

Mesmo em casos literais isso acontece, pois também são considerados expressões. Experimente trocar o !function_exists('nomeUnico') por true.

5 - Toda função tem contexto GLOBAL

E é fato:

<?php

function declaraNomeUnico() {
    function nomeUnico() {
        echo 'Funciona!';
    }
}

declaraNomeUnico();
nomeUnico(); // mesmo sendo declarada dentro da função, podemos chamá-la do lado de fora

Enfim, com todos esses exemplos, você deve ter percebido que a escrita segue esse passo-a-passo:

  1. começa com function;
  2. depois colocamos o nome único da função;
  3. daí abrimos parênteses ((), onde, nos exemplos acima, ficaram todos vazios;
  4. depois fechamos os parênteses ());
  5. e, em seguida, abrimos o bloco de código a ser executado pela função que estamos definindo, encapsulando com chaves ({}).

O que ficaria entre os parênteses, Kiko?

Os argumentos, que é o assunto do nosso próximo artigo, pois é bem extenso. Mas são como se fossem variáveis internas, onde você pode definir o tipo de dado esperado, um valor padrão, etc. Se você já estiver lidando com PHP 8, tem um recurso bem interessante chamado Named Arguments ou Argumentos Nomeados que também abordarei no próximo artigo. Por tanto, não esqueça de vir dar uma lida!

E por hoje é só! Curtiu?! Comenta e compartilha! Ainda falta um bocado de coisa para falar sobre funções, mas aqui já deu pra dar um overview básico de como escrevê-las. Nos próximos você só vai aprender como deixá-las mais complexas, hehehehehe. Ah, não esquece de responder minha pergunta subliminar que deixei nos códigos, hein?

Inté!!