PHP para Iniciantes: Tipos de Dados Primitivos - Introdução

No artigo de hoje, iremos falar sobre um assunto polêmico: Primitive Data Types ou Tipos de Dados Primitivos, que apesar de soar tão complexo é na verdade uma forma de identificar um padrão de estrutura de dados. Olhando por cima, você consegue aprender facilmente o que é cada tipo primitivo mas uma boa forma de se tornar um desenvolvedor excelente é saber o que acontece por baixo desses tipos, identificando qual é mais apropriado para cada contexto do seu código.

Tá, Kiko, mas o que é um tipo de dado primitivo?

Eu gosto de dizer que é um padrão de dados binários. Cada tipo ocupa uma quantidade de bytes específicos e cada bit pode ter um significado diferente em cada um deles. Analogamente, suponha que você tem uma ficha cadastral com seu nome e sua altura. Seu nome estará escrito com letras alfabéticas, enquanto sua altura estará usando números. São símbolos diferentes, ocupando larguras diferentes na ficha. Então faz sentido dizer que são tipos diferentes: letras e números, como já separamos naturalmente na língua portuguesa.

Conhecer os tipos de dados primitivos nos ajuda a reduzir custos de armazenamento e criar formas de manipulação de dados extremamente eficazes para cada cenário. Por exemplo, sabendo matemática, você consegue somar números. Mas a operação de soma funcionaria na língua portuguesa? "o" + "i" seria "oi"? Bem, não parece ser exatamente uma soma (e tem até uma palavra mais adequada pra isso, mas não vem ao caso agora). Por isso, antes de sair dizendo que tudo é texto e pronto, que tal mergulharmos nas possibilidades que encontramos no PHP?

PS.: Os tipos de dados primitivos variam em cada linguagem. Por exemplo, em PHP nós temos uma distinção sobre os números inteiros (Int) e decimais (Float), enquanto em Javascript, tudo é número (Number).

PS.2: Sobre os exemplos acima, temos um assunto bem polêmico chamado Ponto Flutuante que não será profundamente abordado nos artigos para iniciantes (pois acredito que isso não é papo pra iniciante mesmo). Se você quiser se aprofundar nesse tema, sugiro que leia qualquer livro de cálculo que aborde "Arrendodamento e Ponto Flutuante". Somente depois vá buscar sobre as limitações de cada linguagem, beleza? No caso do PHP, a limitação depende do Sistema Operacional onde está rodando. Por isso que digo que não é papo pra iniciante.

Ok, eu acho que consegui te explicar o que é um tipo de dado primitivo. Mas nem todos os tipos são iguais. Na verdade, a gente costuma separar esses tipos em três categorias:

  • escalares;
  • compostos;
  • especiais.

Os tipos escalares são os padrões de dados que mencionei anteriormente. São tipos que representam uma informação, somente. Já os tipos compostos, são tipos feitos com os tipos escalares, podendo agregar mais de uma informação dentro de si.

Ok, e os tipos especiais?

São especiais, ora! Comportamentos que fogem do que mencionamos acima serão listados como tipos especiais. Ouso dizer que você vai entender melhor quando chegarmos lá, beleza? Além disso, nesse artigo iremos apenas ter uma visão superficial de cada um deles. Irei me aprofundar um pouco mais nos próximos.

Se você chegou até aqui, comenta tipagem bacana, tipagem legal, tipagem bonita!

1. Tipos Escalares

Então voltando aos tipos escalares, no PHP, temos quatro:

1.1. Booleanos (bool)

A tipagem booleana é a tipagem mais simples que existe, pois representa um valor binário: nós costumamos traduzir seus valores para "falso" e "verdadeiro" (duas opções, logo, binário). Toda vez que você pensar em criar alguma coisa cujas possibilidades são limitas a "sim" ou "não", você está criando um booleano.

"Usuário está logado"? Booleano. "Tem tarefa pendente?" Booleano. "Quem matou o Bozo?" Bool- epa! Não, esse não é não.

No PHP, um Booleano pode ser representado pelas palavras-chave "true" (verdadeiro) e "false" (falso).

1.2. Inteiros (int)

Essa tipagem pode ser uma das mais fáceis de se compreender pra quem estudou matemática até aquele assunto de Números Inteiros (Números Naturais, Números Racionais, etc). Por enquanto esse é o entendimento que você precisa ter, não tem como falar muito sem se aprofundar nesse tópico.

1.3. Números de Ponto Flutuante (float)

Essa, no entanto, já é um pouco mais complexa. Temos a péssima mania de associar esse tipo de dado aos Números Fracionais, tipo 1,5, 1,123, 123,12, mas não é bem assim. Pontos flutuantes tem vários problemas de precisão porque a forma que o dado é armazenado na memória difere de linguagem pra linguagem. Na realidade, é justamente essas estruturas de armazenamento que trazem as maiores dores de cabeça que existem na programação: cálculo com ponto flutuante.

Quer ver como é péssimo? Vou pegar um problema conhecido do PHP.

Se você fizer, no lápis, a conta 2.45 - 1.05 - 1.40, quanto vai dar?

0.

Afinal, 2.45 - 2.45 é zero mesmo. Porém, se você fizer um script em PHP para rodar

<?php
echo 2.45 - 1.05 - 1.40;

Você terá a resposta 2.2204460492503E-16 (2 elevado a -52, um número super próximo de zero).

Ué, Kiko, mas por que não dá zero?

Tem a ver com o fato de que a precisão padrão de ponto flutuante nos limita a ter um valor limite utilizado nas operações. "1 float é diferente de 1 int".

Sei que tu ficou curioso(a), mas teremos um artigo só sobre esse assunto em breve, ok? É um assunto bem legal e que realmente se aplica para todas as linguagens, pois cada uma desenvolve sua própria forma de lidar com isso.

1.4. Textos (string)

Se você achou Inteiros fácil de entender, imagine esse. Na maioria das linguagens, String é um tipo de dados composto (feitos por um conjunto de Chars, que são caracteres). Isso só não é verdade no PHP pois aqui não existe o tipo Char. Apesar de ter uma filosofia tão simples, existe sim algumas coisas iradíssimas sobre texto em PHP. Afinal, há um grande número de funções criadas especificamente para manipulação de texto.

Dito isso, pode esperar um artigo bem maluco sobre isso nos próximos episódios!


2. Tipos Compostos

Agora que você revisou os quatro tipos de dados primitivos escalares do PHP, nós podemos brincar com a mistura deles em tipos compostos, conforme mencionados a seguir.

2.1. Mapas ordenados (array)

Já dizia o Alceu Valença: Aaaaaaaaaarraaayyyy

Zoeira a parte, Array é uma das estruturas mais usadas EM TODO PROJETO. É difícil encontrar qualquer coisa que não use um único array. Então é estupidamente importante que você aprenda bem:

  • o que é um array?
  • pra que serve?
  • como eu manipulo?

E quando eu levanto essas questões, eu falo de forma lógica, independente da linguagem de programação onde for atuar. Se você realmente nunca ouviu falar disso, eu recomendo que pare para pesquisar um pouco sobre isso antes de chegarmos no artigo de aprofundamento, ok?

Tá, Kiko, mas cadê o resuminho?

Certo... Array é... complicado, porque, em PHP, todo array é um dicionário. Apenas recentemente ganhamos algumas funções para manipular arrays como listas, porém, independente da forma que fosse criado, você poderia misturar tudo e, por isso, ainda é certo dizer que array em PHP é dicionário.

Uma colinha rápida pra quem não sabe o que é dicionário / lista:

  • dicionário: é uma série de dados identificados por palavras-chave, assim como no Dicionário de Português (onde você busca uma palavra e descobre as informações relacionadas a ela);
  • lista: é uma série dados ordenados identificados por posições, assim como a lista de Pokémons na Pokedex (quem é o Pokémon 001?). Se você não sabe o que é Pokémon, outro exemplo mais simples seria a fila da lotérica (o primeiro da fila é quem chegou primeiro, certo?).

Sobre essas duas definições, não pretendo me aprofundar no artigo sobre arrays. Caso queira conhecer mais, também indico uma boa pesquisa sobre o assunto, beleza?

Mas Kiko, por que Array é um tipo composto?

Dicionário ou lista, array é e sempre será uma série de dados. Que dados são esses? Ora, algum outro tipo (não necessariamente escalar). Como outros tipos fazem parte de sua "composição", ele é um dado composto.

2.2. Objetos (object)

Se você nunca ouviu falar em Programação Orientado a Objeto, recomendo leitura sobre o assunto antes de entrar no artigo sobre esse tipo composto. Por ora, não tem problema não saber de nada sobre isso, apenas entenda que objetos tem uma filosofia bem similar a um array, exceto que a sua série de dados é bem mais definido - ou deveria ser. O lance é que no PHP é possível criar objetos sem ter uma definição sólida sobre os dados que irão existir nele.

Tem muitos métodos que chamamos de métodos mágicos no PHP que fazem uma definição que não existia antes, existir. Por essas e outras coisas que muita gente odeia a nossa linguagem: porque se alguém não seguir as boas práticas, tudo vai pro saco. E é verdade! Nós temos frameworks por aí que promovem mais métodos mágicos do que tudo e, o pior, é que são estupidamente fáceis de se usar.

Você acha errado usar esses frameworks, Kiko?

Não acho, eles estão aí pra facilitar a nossa vida no trabalho. Mas se você está começando, recomendo que use um total de ZERO frameworks para aprender o PHP real antes de encontrar as ideias de um grupo de experts em otimização de trabalho, beleza?

Ok, Kiko, mas o que é um objeto?

Lembra que mencionei sobre array ser um dicionário? Um objeto é como se fosse um, exceto que, na lógica, objeto foi feito pra dar a possibilidade de manipular esses dados internamente (afinal, manipular externamente também é algo que fazemos com arrays). Num exemplo bem chulo, imagine que temos o objeto $pessoa e nela temos um dado Booleano chamado estaViva inicialmente true. Se eu digo que a pessoa morreu, o que esperamos que aconteça?

<?php

$pessoa->estaViva = true;
$pessoa->morreu();
// $pessoa->estaViva ??

Esse é o tipo de coisa que não se espera ser feito em arrays, embora seja possível. Ugh, eu adoraria mentir dizendo que não dá pra que não cometam esse absurdo, mas gosto de falar a verdade.

Se ainda não entendeu o que é um objeto, eu juro que vou explicar melhor no artigo, beleza? É um dos tipos compostos mais abstratos que temos nas linguagens de computação, tanto que tem um paradigma exclusivo pra ele.

2.3. "Acionáveis" (callable)

Também chamados de callback, esse tipo de dado representa uma função *¹ ou o método de um objeto *².

*¹ vamos falar sobre isso em Funções, que ainda está longe.

*² vamos falar sobre métodos de objetos nos artigos de Classes e Objetos, que ainda está longe.

É um tipo de dado composto porque ele representa uma ação, por exemplo:

<?php
$acionavel = function () {
    echo "Ola, mundo";
}

$acionavel é uma função, portanto, é do tipo callable. Também nos aprofundaremos sobre esse assunto em um artigo específico mais para frente.

2.4. Iteráveis (iterable)

Um tipo relativamente recente (nasceu no PHP 7.1), feito para podermos identificar dados que funcionam como listas, isto é, se você tem um array ou um objeto (que implementa a interface Traversable *¹), você pode usar estruturas de iteração (foreach *² ou yield *³) para navegar entre os dados de forma mais simples.

*¹ vamos falar sobre o que é implementação de interfaces quando chegarmos nos artigos de Classes e Objetos, o que ainda está longe.

*² vamos falar sobre foreach em Estruturas de Controle, também longe.

*³ vamos falar sobre yield em Geradores, mais longe ainda.

PS.: se você não sabe o que é uma iteração, recomendo que pesquise sobre o assunto pois trata-se de um conceito lógico e é aplicável em todas as linguagens de programação, mesmo que não tenham estruturas de iteração (for, por exemplo), vão ter formas de aplicar isso (goto, famosíssimo).

Deu pra ver que isso aqui vai ser bem chato, né? Confesso que esse vai ser o tipo de dado que você só vai entender de verdade na prática.

Ainda assim, só pra encerrar: iterável é um tipo composto pelo mesmo motivo citado em Arrays e Objetos.


3. Tipos Especiais

Assim como citei, todos os tipos que não se encaixam nas definições mencionadas anteriormente serão definidos como tipos especiais. São eles:

3.1. Recursos (resource)

Esse tipo representa dados externos à aplicação. Por exemplo, um streaming de leitura binária, seja pra ler um arquivo ou acessar um banco de dados. O dado irá representar a conexão a um dado externo, ou seja, um recurso para a sua aplicação (daí o nome). É preciso ressaltar que você não simplesmente cria recursos (a menos que você participe da evolução da linguagem, eu acho, não é o meu caso).

Um bom exemplo pra quem ainda não explorou esse tipo de dado é trabalhar com manipulação de arquivos. Manipular um simples .csv traz a tona esse tipo de dado na hora que usar funções como fopen, que retorna um resource (veja a documentação). Você precisa de outros métodos que usam o resource como fonte de dados, é o caso da função fgetcsv (doc), que já é específica para puxar uma linha de um CSV.

Deu pra entender por que isso é um tipo de dado especial? Comenta aí!

3.2. Nulos (null)

Talvez o mais óbvio de todos: um tipo de dado que representa... nada! Um dado do tipo nulo é um dado especial porque ele pode ser aplicado em qualquer tipagem. Se você declara alguma coisa como inteiro, ele ainda pode ser nulo. Então tem muitos detalhes para falarmos sobre esse tipo e iremos nos aprofundar também em um artigo exclusivo.

O importante de hoje era você entender as formas que separamos os tipos de dados primitivos no PHP e o motivo de cada tipo ser segmentado assim, pois, dessa forma, quando eu começar a falar sobre determinados assuntos vocês já saberão a quais tipos se aplicam, quais não fazem sentido, etc.


Outra coisa que devo comentar antes de encerrar é...

PHP não é linguagem de tipagem forçada!!

O que é isso, Kiko?

Há diversas linguagens de programação mundo a fora que te forçam a definir as tipagens de TUDO. De forma que, se uma variável for do tipo Inteiro, você não pode colocar outro tipo de dado armazenado nela. Se fizer isso, irá dar um erro bem forte.

Isso serve para forçar os programadores a seguir as estruturas montadas ao longo do código e a perceberem rapidamente se fizeram mal-uso de algum dado, enxergando as limitações de manipulação facilmente pelo tipo onde está fazendo manutenção.

Ué, não é algo bom?

É... Mas lembra que PHP foi feito pra ser livre? Então não teria como ele nos limitar tanto assim. Porém é possível usar o PHP como uma linguagem de tipagem MAIS OU MENOS forçada, pelo menos nos paradigmas de Programação Orientada a Objeto e Programação Funcional. Você pode dizer em todas as funções e métodos o(s) tipo(s) de dados que espera. Caso algum tipo seja informado diferente do aceito, o PHP dará erro.

Ainda assim, isso não é tipagem forçada de verdade, pois não se aplica a variáveis, que possuem tipagem manipulada pelo runtime do interpretador. Por exemplo:

<?php

function soma(int $a, int $b): int
{
    return $a + $b;
}

Essa função tem suas tipagens muito bem definidas: recebe dois inteiros e retorna outro inteiro. Porém, se eu quisesse mudar a tipagem de $a dentro da função, não aconteceria nenhum erro, porque na visão interna ela não passa de uma variável e sua tipagem é volátil.

<?php

function soma (int $a, float $b): float
{
    $a = $a + $b;
    return $a;
}

Agora, $b é número de ponto flutuante. Portanto, qualquer soma com ele resultará em outro número de ponto flutuante. Se $a, que é inteiro, recebe a soma de $a + $b, ela muda de tipo para float. Em uma linguagem de tipagem forçada, isso seria um TABU.

Mas PHP é livre, e isso é MUITO COMUM. Não vejo problema nisso, afinal, há princípios que nos dizem que nem tudo o que acontece do lado de dentro precisa ser exposto. Você expõe somente o que precisa (os tipos de entrada e o tipo de saída). Se o que acontece dentro for de responsabilidade da função, tudo certo!

Ah, Kiko, não sei o que é função, não sei o que é variável...

Tenha paciência, estamo seguindo a ordem da documentação e por isso estou abordando alguns assuntos que ainda não falamos nesses exemplos, mas prometo que tudo fará sentido mais pra frente, tudo bem?

E não esquece de indicar o conteúdo para os seus amigos que querem aprender PHP, beleza?

Inté!