Thanks to visit codestin.com
Credit goes to pt.scribd.com

0% acharam este documento útil (0 voto)
13 visualizações675 páginas

85 Bits - PHP

Livros programação

Enviado por

msf6705
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
13 visualizações675 páginas

85 Bits - PHP

Livros programação

Enviado por

msf6705
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 675

CURSO COMPLETO DE

Versão Vídeo Aula disponível em:

https://www.youtube.com/channel/UC0aGMws3O0PEhxng-CxwLFg
Sobre o Professor (Dez 2019)
● Celso Araujo Fontes
● Mestre em Sistemas e Computação (IME-RJ)
● Coordenador de Projetos Estratégicos na PGE RJ
● Ex-Professor na graduação do Instituto Infnet

linkedin: https://www.linkedin.com/in/celsowm/
email: [email protected]
Tópicos do Curso
1. PHP Básico (sintaxe, variáveis, estruturas de controle...)
2. PHP Funções
3. PHP Arrays
4. PHP Orientado a Objetos
5. PHP Entrada de Dados e Formulários
6. Serialização
7. Tratamento de Exceções
8. PHP e Banco de Dados (pdo)
9. Estruturas de Dados com SPL (pilha, fila...)
10. Computação Gráfica com PDO
11. Design Patterns
PHP Básico
● Introdução ao PHP
● Sintaxe Básica
● Variáveis
● Operadores (string, aritméticos, comparação, lógicos,
incremento/decremento)
● Estruturas de Controle (if, switch, for, while)
● Função
● Constante
PHP
● Criado por Rasmus Lerdorf in 1994
● Originalmente Personal Home Page hoje PHP: Hypertext
Preprocessor
● Linguagem script (server-side)
● Tipagem dinâmica
● Multiparadigma (procedural, reflexiva, orientada a objetos,
funcional, imperativa)
● Influenciada por Perl, C, C++, Java e Tcl.
● Cross-platform (Windows, Linux, Mac OSX…)
PHP
Ferramentas (sugestões)
● Ferramentas Web:
○ http://ideone.com/
○ https://3v4l.org/
○ http://phpfiddle.org/
○ http://phptester.net/

● Ferramentas desktop:
○ http://www.wampserver.com/
○ https://notepad-plus-plus.org/
○ https://netbeans.org/
Exemplo de Arquitetura com PHP
Web Server

request
(exemplo.php)

os
response dad
sql
Arquitetura PHP
● Executável que interpreta e executa scripts PHP
● Suporta extensões escritas em C
○ Projetos PECL (PHP Extension Community Library) outros
● Pode utilizar bibliotecas de terceiros escritas em PHP
○ Projeto PEAR PHP Extension and Application Repository) e outros
● Precisa, normalmente, de um serviço HTTP para responder requisições Web
Sintaxe básica (HTML5 + PHP)
<!DOCTYPE html> O objetivo final da maioria dos
documentos web PHP será exibir
<html>
dinamicamente algum conteúdo HTML,
<head> pois esta é a linguagem de marcação
utilizada pelos navegadores para
<title>PHP Test</title>
exibição e formatação dos elementos de
</head> uma página web.
<body>
Este é um exemplo de uma página web
<?php echo '<p>Hello World</p>'; ?> em HTML5 com um trecho de instruções
para interpretação do PHP.
</body>
</html> O cliente não terá acesso ao código
PHP, apenas ao conteúdo final escrito
pelo mesmo.
Separação de instruções (; ?> )
Basicamente um código PHP
<?php começa com o símbolo de menor
seguido de um sinal de interrogação
echo 'Isto é um teste'; e o termo PHP.

?> Cada instrução é separada pelo


caractere ponto e vírgula.

Para fechar um bloco de códigos


<?php echo 'Isto é um teste' ?> PHP utilizamos o símbolo de
interrogação e o sinal de menor.

É válido salientar que a instrução de


<?php echo 'Nós omitimos a última fechamento do PHP é opcional
tag de fechamento'; quando não existe nenhum outro
tipo de código após o bloco de
instruções
Comentários ( //, /* */, #)
<?php

echo 'Isto é um teste'; // Estilo de

comentário de uma linha em c++ Assim como outras linguagens, o


PHP permite a escrita de
/* Este é um comentário de múltiplas linhas comentários, que permitem ao
desenvolvedor escrever textos que
ainda outra linha de comentário */ possam explicar a outros
desenvolvedores o que significa
echo 'Isto é ainda outro teste';
aquele código ou até documentar
echo 'Um teste final'; # Este é um comentário de forma estruturada elementos da
aplicação através do PHPdoc.
de uma linha no estilo shell

?>
require_once & include
● funcionam com caminhos
○ absolutos:
■ “c:\\apache24\\htdocs\\aula\\teste.php”
○ relativos:
■ “teste.php” “..\aula2\teste2.php”
● quando o arquivo não existe:
○ include: gera erro mas o script continua
○ require: erro fatal e o script é encerrado
● require_once:
○ A instrução require_once () é idêntica a require (), exceto que o PHP verificará se o arquivo já
foi incluído e, em caso afirmativo, não incluirá (require) novamente.
Variáveis e Tipos “básicos” O elemento básico de qualquer linguagem de
programação é variável.

<?php Variáveis são basicamente locais onde as


informações são definidas ou referenciadas.
No PHP as variáveis começam com o símbolo
$minha_var; // null do cifrão ($) seguido pelo nome da variável.

$texto = "Ola Mundo !"; // string Se nenhum valor é definido para esta variável,
ela começa automaticamente com o valor NULL
$x = 5; // integer ou nulo.
$y = 10.5; // float ou double
Para definir um valor basta utilizar o símbolo de
$verdadeiro = true; // bollean igual seguido do valor que pode ser um destes
tipos primitivos ou também um array ou uma
$nulo = null; // null referência a um objeto.

Como o PHP é uma linguagem de tipagem


dinâmica, não é necessário que o
desenvolvedor defina um tipo para a variável,
pois a mesma poderá ter seu tipo alterado de
acordo com o valores a serem definidos na
execução do código.
Variáveis (diferença entre caixas)
<?php
É válido salientar que os
nomes de variáveis no PHP
fazem distinção entre letras
//duas variáveis diferentes maiúsculas e minúsculas
$verdadeiro = true; (caixas alta e baixa).

$Verdadeiro = false; Portanto, neste exemplo,


existem duas variáveis
distintas pois uma possua uma
letra em caixa diferente da
outra.
Echo e Print
<?php Outro recurso fundamental de qualquer
echo("Olá Mundo!"); ambiente de desenvolvimento é a saída
padrão, isto é, uma forma para se exibir
echo "Olá Mundo !"; conteúdo para o usuário. No PHP
echo "Olá ", "Mundo!"; podemos utilizar tanto o echo e como o
print. Como nenhum deles é exatamente
uma função PHP, o uso do parêntese é
opcional.
<?php
print("Olá Mundo!"); As diferenças entre eles são que o echo
print "Olá Mundo!"; é ligeiramente mais rápido e pode
aceitar mais de um parâmetro enquanto
$ret = print "Hello World"; o print tem valor de retorno 1 e pode ser
print("Olá mundo! ".$ret); usado como uma função.
Strings (‘Single quoted’ e “Double quoted”)
<?php Para definir uma string ou texto, o PHP
permite utilizar dois diferentes tipos de
caracteres delimitadores, o apóstrofo ou
$versao = 7; as aspas.

Quando uma string é delimitada por


echo 'PHP versão $versao'; apóstrofos, nomes de variáveis serão
considerados partes integrantes do texto.
//saída: PHP versão $versao
Já com o uso das aspas, nomes de
variáveis serão interpretados e seus
echo "PHP versão $versao"; valores e não seus nomes é que serão
//saída: PHP versão 7 considerados para integrar a string.
‘NOWDOC’ e HEREDOC
<?php NOWDOC são para cadeias de
caracteres com aspas simples o que
$versao = '7'; HEREDOC são para cadeias de
caracteres com aspas duplas.
$nowdoc = <<<'NOW'
PHP versão $versao! Um NOWDOC é especificado de forma
NOW; semelhante a um HEREDOC, mas
nenhuma análise é feita dentro de um
$herec = <<<HERE NOWDOC.
PHP versão $versao!
HERE; A construção é ideal para incorporar
código PHP ou outros grandes blocos
echo $nowdoc; de texto sem a necessidade de usar
echo $herec; “escape” (escapar).
Operadores de String (concatenação “.” e ”.=”)

<?php Um importante recurso para a manipulação


de duas ou mais strings é o operador de
$a = "Olá "; concatenação.

$b = $a . "Mundo!"; O sinal de ponto no PHP é utilizado com este


propósito: permitir a união de uma ou mais
strings.

$a = "Hello "; Ele também pode ser utilizado com o símbolo


de igual para permitir que o PHP faça a
$a .= "World!"; concatenação do conteúdo já presente na
variável à esquerda do ponto com o conteúdo
a direita do sinal de igual.
Operadores Aritméticos
Operador Nome Exemplo Resultado

+ Adição $x = 2 + 2; 4 Outro conjunto de operadores


comumente disponíveis nas
- Subtração/Negação $x = 4 - 2; 2 linguagens de programação são os
operadores aritméticos.
* Multiplicação $x = 2 * 10; 20 Ressaltando que além das quatro
operações básicas da aritmética,
/ Divisão $x = 15 / 5 3
no PHP, existe também o operador
% Módulo (sobra da $x = 5 % 2 1 de módulo, que permite obter o
divisão) valor da sobra de uma divisão e o
operador de exponencial.
** Exponencial $x = 4 ** 4; 256
Ordem de precedência
<?php
//PHP usa a ordem de precedência
conhecida como BODMAS

$x = 1 + 2 * 3;
$y = (1 + 2) * 3;
echo $x; //7
echo $y; //9

fonte:http://www.famousarticlesolutionz.com/2015/03/what-is-bodmas-rule.html
unset()
<?php

$a = 1; O unset() destrói variáveis previamente


$b = $a; especificadas.
unset($a);
O comportamento de unset() dentro de
echo $a; //Notice: Undefined variable: a
uma função pode variar dependendo do
echo $b;
tipo de variável que você está tentando
destruir.
Estrutura de Controle (If)
● Como toda linguagem de alto nível, PHP possui um conjunto de
estruturas de controle que permite à aplicação reagir a diferentes
ações ou resultados;

● A primeira e mais importante é o IF (se);

● O if permite testar uma ou mais condições e definir quais serão os


códigos para executar caso a condição seja verdadeira ou falsa;

● Neste pequeno exemplo testamos se uma determinada variável


possui o conteúdo igual a string “85 bits”, se a condição for
verdadeira o código do bloco “então” será executado.
Estrutura de Controle (If)

● Sintaxe básica:
then/então falso
condição
if (condição) {
verdadeiro
//código para executar se a
condição for verdadeira;
código condicional
}

fim do código condicional


fim
Estrutura de Controle (If)

Neste pequeno exemplo testamos se uma determinada variável possui o


conteúdo igual a string “85 bits”, se a condição for verdadeira o código.
Estrutura de Controle (If)
● Exemplo:
falso
condição
<?php
verdadeiro

if ($nome == "85 bits") { código condicional

echo "Nome correto !";


}
fim
Operadores de comparação
Exemplo ($a =
Operador Nome
5; $b = 3;)
Resultado Nesta tabela é descrito os
operadores de comparação
== Igual if ($a == $b) false
disponíveis no PHP.
=== Idêntico if ($a === $b) false
Apesar da tipagem
!= Diferente if ($a != $b) true
dinâmica do PHP é
<> Diferente if ($a <> $b) true possível fazer uma
!== Não if ($a !== $b) true comparação mais criteriosa
idêntico utilizando os comparadores
> Maior if ($a > $b) true
idêntico e não idênticos
que onde não apenas os
valores são comparados
< Menor if ($a < $b) false
que mas o tipo também.
Operadores de comparação

Operador Nome Exemplo ($a = 5; $b = 3; $c = 0) Resultados

>= Maior ou Igual if ($a >= $b) true

<= Menor ou Igual if ($a <= $b) false

<=> Spaceship if ($a <=> $b) 1

?? Diferente (Null $a ?? $b ?? $c 5
coalescing)

Spaceship: Retornar 0 se os valores de ambos os lados são iguais. Retornar 1 se o valor à esquerda é
maior. Retornar -1 se o valor da direita é maior.
Já o operador diferente ou Null Coalescing funciona avaliando as variáveis da esquerda para a direita, e o
primeiro valor não nulo será o resultado da comparação.
Operadores Lógicos

Operador Nome Exemplo ($a = 5; $b = 3; $c = 0;) Resultados

&& E if ($a && $b) true

II OU if ($a || $b) true

xor Xor “ou exclusivo” if ($a xor $b) 1

and E if ($a > $b AND $c < $a) true

or OU if ($a > $b OR $a < $c) true

! Not if(!($a > $b)) false


Operadores Lógicos (“>” maior, igual “==” e “&&”)
Exemplo:

<?php Outro conjunto de operadores


do PHP são os operadores
$compra = 500; lógicos, estes operadores são
$saldo = 300; utilizados para combinar e
$tem_cheque_especial = false; testar mais de uma condição.

if( $compra >= $saldo && $tem_cheque_especial == false ) { Neste exemplo utilizamos o
print 'compra negada'; operador lógico E,
} representado pelos símbolos
duplos de e comercial para
testar se ambas as condições
são verdadeiras.
Operadores Lógicos (ou “II” e “>=” maior ou igual)
Exemplo 2:

<?php
Neste exemplo utilizamos o operador
$tempo_trabalho = 24; lógico OU, representado pelos
$idade = 66; símbolos duplos de pipe para testar
se uma das condições são
if( $idade > 65 || $tempo_trabalho >= 25 ) { verdadeiras.
echo 'Aposentadoria !';
}
Operadores Lógicos (diferente “!=” )
Exemplo 3:

<?php
Neste exemplo utilizamos o operador
$convite = 'econômico'; lógico de diferente, representado
pelos símbolos de exclamação e igual
para testar se uma variável ou
if($convite != 'VIP'){ expressão é diferente do valor testado
echo 'Acesso negado!'; à direita.
}
Operadores Lógicos (igual “==” e idêntico “===” )
Exemplo 4:
<?php
<?php
$x = '300.00'; $x = '';
$y = null;
$y = 300.00;
$z = 0;
$z = 300.00;
if($x == $y){
if($x == $y){ echo 'iguais'; //são idênticos
echo 'iguais'; //são idênticos }
if($x === $y){
}
echo 'idênticos'; //não são idênticos
if($x === $y){ }
echo 'idênticos'; //não são idênticos if($y === $z){
} echo 'idênticos'; //não são idênticos
if($y === $z){ }
echo 'idênticos'; //são idênticos
}
Estrutura de Controle (if...else)

verdadeiro falso
Na estrutura de controle IF também condição
é possível definir qual conjunto de
códigos será executado se as
código código
condições do if não forem condicional condicional
verdadeiras, para isto, podemos (verdadeiro) (falso)

utilizar a cláusula “else”.

fim
Estrutura de Controle (if...else)
<?php
$a = 1; $b = 2; verdadeiro
condição
falso

if ($a > $b) { THEN/ENTÃO


código código
echo "A é maior que B"; condicional condicional
(verdadeiro) (falso)
} else {
echo "A não é maior do que B";
}
fim
Estrutura de Controle (if...elseif)
<?php
Também podemos
if ($a > $b) { combinar mais uma
echo "a é maior que b"; estrutura de controle if
utilizando a cláusula elseif.
} elseif ($a == $b) {
echo "a é igual a b"; Neste exemplo utilizamos
apenas um elseif mas
} else { diversos elseif podem ser
echo "a não é maior do que b"; usados.

}
Operador Ternário (?:)
<?php Outra forma de escrita do if no PHP é o operador ternário. Nesta forma simplificada utilizamos o sinal de
interrogação para definir bloco do então e o sinal de dois pontos para o else.
O valor de resultado da operação pode ser impresso ou armazenado em uma variável, conforme fizemos neste
exemplo.
$a = 40;
then/então else/senão
$b = 50;

$resultado = ($a >= $b ? 'maior ou igual a b' : 'menor que b');

echo $resultado;
Operadores de Incremento/Decremento

Operador Nome

++ Incremento

-- Decremento

Incremento e decremento são operadores que as linguagens oferecem para que


o desenvolvedor possa manipular o valor de uma variável de forma rápida,
permitindo que o seu valor acrescido ou subtraído em 1.
Pré Incremento e Decremento
<?php

$x = 10;
$y = 10;

//pré incremento
echo "valor de x:".++$x; //11 - Incrementa $x mais 1 e então retorna $x
echo "valor de x:".$x; //11

//pré decremento
echo "valor de x:".--$y; //9 - Decrementa $y menos 1 e então retorna $y
echo "valor de x:".$y; //9
Pós Incremento e Decremento
<?php

$x = 10;
$y = 10;

//pós incremento
echo "valor de x:".$x++; //10 - Retorna $x e então incrementa $x mais 1
echo "valor de x:".$x; //11

//pós decremento
echo "valor de x:".$y--; //10 - Retorna $y e então decrementa $y menos 1
echo "valor de x:".$y; //9
Estrutura de Controle (switch)
expressão
Apesar das facilidades do if muitas vezes nos
deparamos com um número grande de possibilidades
V
para uma determinada variável, então podemos usar condição
código do case n0
uma outra estrutura de controle chamada de switch. n0

Nela podemos definir um conjunto de rotinas para cada F


resultado e até um conjunto para se caso nenhum dos condição V
n1
código do case n1
resultados for alcançado.
F
É válido ressaltar a importância do uso do break no final
de cada bloco pois se não utilizarmos esta cláusula o código do default
PHP continuará testando as outras condições inclusive a
default.
fim
Estrutura de Controle (switch)
<?php expressão
switch ($i) {
V
case 0: condição
código do case n0
ATENÇÃO ! n0
echo "i igual a 0";
F
break;
condição V
código do case n1
case 1: n1

echo "i igual a 1"; F


break; código do default
default:
echo "i não é igual nem a 0 ou 1";
fim
}
Estrutura de controle (match) PHP 8.0 >=

<?php ● O mecanismo de seleção de controle match foi


introduzido no PHP 8.0. É similar ao switch/case
$i = 1; e permite fazer uma comparação de múltiplos
valores com uma única variável de entrada;
echo match ($i) {
● Diferente da comparação de valores semelhantes,
0 => "i igual a 0 (int)", utilizada internamente pelo switch/case (==), o
'1' => "i igual a 1 (string)", match faz uma comparação de igualdade estrita
1 => "i igual a 1 (int)", (===);
2,3 => "i igual a 2 ou 3 (int)",
default => "i não é igual nem a 0 ou 1"
● Caso o match não contenha a opção default e o
valor da variável seja diferente das opções
};
definidas, um throwable do tipo
UnhandledMatchError será lançado;
Estrutura de Controle “loop”
Durante o desenvolvimento de uma aplicação é comum trabalhar com instruções
repetitivas, e na maioria das vezes para trabalhar com um conjunto de dados.

Para otimizar este tipo de tarefa utilizamos estruturas de controle conhecidas


popularmente como “laço” ou loop.

Este fluxograma representa um exemplo de loop onde o primeiro passo é a


inicialização, onde muitas vezes uma variável é usada para o controle do loop.
Em seguida, uma condição é testada, e enquanto esta condição for verdadeira
todo o bloco do código será executado.
Quando a condição não é mais atendida, o laço é interrompido e as instruções
posteriores serão executadas.
Estrutura de Controle “loop”
Inicialização

falso
condição parar/fim

verdadeiro

código do loop

incrementar/
decrementar
Estrutura de Controle “loop” (for)
Sintaxe:

Uma das estruturas de


for (inicialização; condição; incremento) { controle de laço do PHP é
o FOR.
//código
Esta é sintaxe para sua
} //fim utilização de forma mais
básica.
Estrutura de Controle “loop” (for)
<?php
O número é: 0
O número é: 1
O número é: 2
for ($x = 0; $x <= 10; $x++) { O número é: 3
O número é: 4
echo "O número é: $x <br />"; O número é: 5

} O número é: 6
O número é: 7
O número é: 8
O número é: 9
O número é: 10

* substituir o </ br> por \r\n se vc estiver usando o ideone ou php cli.
Estrutura de Controle “loop” (while)

Outra estrutura de laço é o while


falso
(enquanto), ele permite que um condição
conjunto de instruções sejam repetidas
inúmeras vezes “enquanto” uma verdadeiro

condição for verdadeira.


código do loop
Por isso, é comum utilizar alguma
variável de controle que será
modificada dentro do escopo do while
para limitar o número de repetições.
parar/fim
Estrutura de Controle “loop” (while)
<?php
falso
condição

$i = 1;
verdadeiro
while ($i <= 10) {
código do loop
echo $i++;

parar/fim
Estrutura de Controle “loop” (do...while)
<?php DO

$x = 1;
código do loop
do {
echo "O número é: $x </ br>";
$x++;
verdadeiro
} while ($x <= 5); condição

falso

parar/fim
While (sintaxe alternativa)
<?php <?php

$i = 1; $i = 1;

while ($i <= 10) { while ($i <= 10):

echo $i++; echo $i;

} $i++;

endwhile;
Função
Sintaxe básica:
Uma função é
<?php basicamente um bloco
de instruções que
podem ser utilizados
function nomeDaFuncao() {
repetidamente num
//código
programa.
}
Função
<?php Diferente das outras
function escreverMensagem() { instruções básicas, uma
função nunca será
echo "Olá Mundo!"; executada imediatamente
} quando uma página ou
script é carregado.

escreverMensagem(); Uma função só será


// executar/chamar a função executada apenas quando
é feito uma chamada pela
mesma.
Função (argumentos/parâmetros)
<?php
function imprimeNome($nome) { Uma informação pode ser
echo "Meu nome é $nome !"; passada para as funções por
meio de argumentos. Um
}
argumento é como uma variável
(inclusive utilizamos o $ antes do
imprimeNome("João"); nome).
imprimeNome("Maria");
Argumentos são especificados
após o nome da função, dentro
dos parênteses.
Função (argumentos)
<?php
function imprimeNome($nome, $sobrenome) {
Podemos adicionar
echo "Meu nome é $nome $sobrenome !";
}
quantos argumentos
quisermos, basta
separá-los com uma
imprimeNome("João","Oliveira"); vírgula.
imprimeNome("Maria","das Lurdes");
Função (argumentos com valor padrão “opcional”)
<?php
Muitos vezes é necessários que
function imprimeNome($nome = "Fulano") { alguns parâmetros de nossa função
echo "Meu nome é $nome !"; sejam opcionais.
}
Para isso, podemos definir valores
padrões para os parâmetros que
imprimeNome(); desejamos que se tornem opcionais.
imprimeNome("João"); Esta atribuição é feita através de
imprimeNome("Maria"); forma muito similar a atribuição de
um valor para uma variável usando o
sinal de igual seguido do valor
desejado.
Função (argumentos nomeados) (PHP 8.0 >=)
<?php ● Com o advento do PHP 8.0
tornou-se possível passar
function imprimeNome($nome, $sobrenome, $pronomeTratamento = "") { parâmetros em ordem arbitrária
echo "Meu nome é $pronomeTratamento $nome $sobrenome !".PHP_EOL;
através da indicação explícita
}
do nome e valor desejado para
cada argumento pré-definidos
imprimeNome(nome:"João",sobrenome:"Oliveira");
imprimeNome(sobrenome:"das Lurdes", nome:"Maria", pronomeTratamento: "Sra.");
na assinatura de funções ou
imprimeNome("Lucas",sobrenome:"Silva"); métodos;
//imprimeNome(nome:"Thiago"); //Erro !
● É possível combinar
argumentos posicionais com
argumentos nomeados,
contanto que estes sejam o(s)
últimos;
Lista de argumentos variável (Splat Operator “...”)
<?php A partir do PHP 5.6 tornou-se
function soma(...$numeros) { possível incluir o token reticências
para indicar uma lista de
echo array_sum($numeros); argumentos e assim informar que
a função aceita um número
} variável de argumentos.

Os argumentos serão passados


soma(1, 2, 3, 4); na forma de um array e por isso,
neste exemplo, utilizamos uma
função do php chamada
array_sum para somar os valores
dentro deste array.
Argument Unpacking (Splat Operator “...”)
<?php
function soma($a, $b, $c) {
O splat operator também
echo $a + $b + $c; pode ser utilizado para
} “desempacotar” um um
coleção em argumentos
separados.
$args = array(2, 3);
soma(1, ...$args);
Função (return)
<?php
function soma($x, $y) { O return permite que uma
função possa retornar
$z = $x + $y; algum valor.
return $z;
} O return retorna o controle
do programa para o trecho
que executou a chamada.
echo "5 + 10 = " . soma(5, 10)
;
Função Recursiva
● É basicamente uma função que chama a si mesma.

● Neste exemplo utilizaremos o resultado de cada iteração para multiplicar este


resultado vezes ele mesmo menos 1 em uma cadeia de recursividade que
será controlada por um “if” que irá garantir que a recursividade não seja
eterna.

● Atenção: Chamadas de função / método recursivas com mais de 100 a 200


níveis de recursão podem estourar a pilha e causar o encerramento do script
atual.

● A recursão infinita é considerada um erro de programação.


Função Recursiva
<?php

function fatorial($numero) {
=4*3
echo 'Numero atual'.$numero.' ';
= 4 * (3 * 2)
if ($numero < 2) { = 4 * (3 * (2 * 1))
return 1; = 4 * (3 * 2)
} else {
return ($numero * fatorial($numero-1)); =4*6
} = 24
}

echo 'Resultado:'.fatorial(4);
Type Hint
<?php Tipos usados no
TypeHint:

● bool
function soma(int $a, int $b) { ● float (php >=7)
return $a + $b; ● int (php >=7)
● string (php >=7)
} ● Classe/Interface
● callable
● self
echo soma(1,2); ● array
● mixed (php >= 8)
Type Hint + Splat Operator (...)
<?php
function soma(int ...$numeros) {
echo array_sum($numeros); O type hint ainda pode ser
usado em conjunto com o
} Splat Operator para especificar
qual será o tipo dos valores de
um determinado conjunto.
soma(1, 2, 3, 4);

soma(1, 2, 3, 4, 5, 6, 7);
Função (declaração de tipo de retorno “:”)
<?php
function soma($a, $b): int { A partir do PHP 7 também se tornou
possível definir o tipo do valor do retorno
return $a + $b; de uma função.

} Para utilizar este recurso, basta definir


tipo da saída (retorno) antecedido pelo
símbolo de dois pontos.

echo soma(1, 2.5);


declare (strict_types=1)
<?php
No PHP também é possível
controlar o nível de restrição do
declare(strict_types=1);
Type Hint, isto é, definir se a
restrição estará ligada “1” ou
function soma(int $a, int $b) {
desligada “0”.
return $a + $b;
}
Vale a pena salientar que o
strict_types precisar ser a primeira
echo soma(1,2); instrução no script
echo soma(1,2.5); //se o strict_types
// for 1 teremos um "TypeError"
Nullable types (php 7.1 >=) “?”
<?php
Com o advento do PHP 7.1, foi
possível determinar uma nova
function soma(int $x, ?int $y): ?string { condição de valor para o tipo de
return $x+$y; parâmetro (ou retorno de função),
} usando o “nullable type”;

O Nullable Type permite que além


echo soma(2,2).PHP_EOL;
de um valor com o tipo solicitado,
echo soma(2,null).PHP_EOL; o parâmetro também seja capaz
echo soma(2).PHP_EOL; /*Too few de receber null (ou um retorno
arguments to function soma() */ null).
Passagem por referência “&”
<?php
function somar(&$var) {
$var++; Com o uso do e comercial,
é possível determinar que
}
um parâmetro possa
modificar uma variável
$x=5; “mantendo a referência”
somar($x); para a mesma dentro do
escopo da variável.
echo $x;
Função Anônima (Closure)
<?php Funções anônimas, também
conhecidas como closures,
permitem a criação de funções que
$x = function ($txt) { não tem o nome especificado.

echo("Hello $txt !"); Elas são mais úteis como o valor de


}; parâmetros callback, mas podem
tem vários outros usos.

Vale apenas salientar que toda


$x('World');
função anônima atribuída a uma
$x('PHP'); variável termina com ponto e
vírgula.
Função Anônima (“use”)
<?php

$total = 40; O termo “use” permite


a utilização de
$soma = function ($x) use ($total) { variáveis externas
echo $x+$total;
};
dentro do escopo de
uma closure.
$soma(50);
Função Anônima como parâmetro
<?php

function add($x,$y){
return $x+$y();
}

echo add (3, function(){return 5;} );


Função Anônima como parâmetro
<?php

function teste_closure(callable $a) {


$b = 20;
return $a($b);
}

teste_closure(function($x){
echo($x+10);
});
Função Anônima como parâmetro (com iteração de coleção)
<?php

function calculaImposto(callable $formula){


$impostos = [10,22,34,46,58];

for($i = 0; $i < count($impostos); $i++){


$impostos[$i] = $formula($impostos[$i]);
}
return $impostos;
}

$resultado = calculaImposto(function($valor){
return $valor * 0.1;
});

var_dump($resultado);
Arrow Function (fn)
?php Recurso introduzido no PHP 7.4, as arrow functions
são uma sintaxe alternativa mais concisa para funções
$z = 4; anônimas.
function somar(int $x,callable $y){ As diferenças de sintaxes são:
● function é substituído por fn
return $x+$y(10);
● return é substituído por =>
} ● { } e ; são completamente eliminados.

echo somar(3, function($v) use ($z) {return $v*$z;} ); Funções anônimas e funções de seta são
echo PHP_EOL; implementadas usando a classe Closure.
echo somar(3, fn($v)=> $v*$z);
Arrow functions tem como vantagem o acesso
automático ao escopo de variáveis do pai, eliminando
assim a necessidade da clausura “use”.

Entretanto, uma desvantagem das arrow function é


sua limitação a apenas uma expressão.
never (tipo de retorno de função)
<?php

function redirecionar(string $url): never {


header("Location: $url");
exit(); //ou die();
}

class Teste {

public static function xpto() : never {


throw new \Exception('método não implementado ainda');
}
}

redirecionar('http://localhost/');
Teste::xpto();
Constantes
<?php

Diferente das variáveis, o valor de um


constante nunca muda e acessamos seu
define("MINHA_CONSTANTE", "nunca vai mudar"); valor sem o uso do cifrão.
define("OUTRA_CONSTANTE", "não importa o case", true);
define("PI", 3.141592); Para definir uma constante utilizamos a
função define, onde o primeiro parâmetro será
um string com o nome da constante, o
echo MINHA_CONSTANTE; segundo parâmetro será o valor da constante
echo outra_constante; e por último podemos definir se a constante
será insensível ao case, isto é, se a constante
echo PI; pode ser chamada através de letras
maiúsculas ou minúsculas independente de
como foi definido.
Date e time
<?php
//definindo TimeZone
date_default_timezone_set('America/Sao_Paulo');

//data Função date permite recuperar data (e hora) em


diversos formatos.
echo date("d");
echo date("d/m/y");

//hora
echo date("h:i:s");
echo date("d/m/y h:i:s"); //combinando data e hora
PHP Array
● Um array em PHP é na verdade um mapa ordenado. Um mapa é um tipo
que associa valores a chaves.
● Esse tipo é otimizado para vários usos diferentes; ele pode ser tratado como
uma matriz, lista (vetor), tabela de hash (uma implementação de um mapa),
dicionário, coleção, pilha, fila e provavelmente mais.
● Como os valores de array podem ser outros arrays, árvores e arrays
multidimensionais também são possíveis.
● Pode crescer dinamicamente e ser iterado em diferentes direções.
Array (sintaxe básica)
<?php
//No primeiro exemplo é feito utilizando a sintaxe tradicional do php até 5.3
$array = array("maria", "joão", 2, 3.5);
/*A partir do php 5.4 é possível utilizar esta sintaxe simplificada conforme o
segundo exemplo*/
$array = ["maria", "joão", 2, 3.5 ];

0 1 2 3

"maria" "joão" 2 3.5


Array (chaves)
<?php

//definindo manualmente as chaves


$array = [ 1=>"maria", 2=>"joão", 'valor'=>2 , null => 3.5 ];

echo $array[1]; 1 2 'valor' null


echo $array[2];
echo $array['valor'];
echo $array[null]; "maria" "joão" 2 3.5

//sem definição das chaves


$array2 = [ 'maria', 'joao' ]; 0 1

echo $array2[0]; "maria" "joão"


echo $array2[1];
Arrays Multidimensionais
<?php
0 1 2
$shop = [
["camisa", 9.25 , 15],
["short", 19.75 , 25], 0 1 2 0 1 2 0 1 2

["tênis", 89.15 , 7] camisa 9.25 15 short 19.75 25 tênis 89.15 7

];

<?php
echo $shop[0][0]." custa".$shop[0][1]." e existem ".$shop[0][2]." em estoque";
echo $shop[1][0]." custa ".$shop[1][1]." e existem ".$shop[1][2]."em estoque";
Arrays (Multidimensionais e Associativos)
<?php
$shop = [
[ 0 1 2
"Produto" => "camisa",
"Preco" => 9.25,
"Quantidade" => 15 Produto Preco Qua Produto Preco Qua Produto Preco Qua
], ntida ntida ntid
de de ade
[
"Produto" => "short", camisa 9.25 15 short 19.75 25 tênis 89.15 7
"Preco" => 19.75,
"Quantidade" => 25,
],
[
"Produto" => "tênis",
"Preco" => 89.15,
"Quantidade" => 7
] echo $shop[0]['Produto'];
];
Iteração/loop de Arrays (for)
<?php

$array = ["Brasil","Argentina","Bolívia","Venezuela"];

for ($i = 0; $i < count($array); $i++) {


print $array[$i];
}
Iteração de Arrays (foreach)
<?php

$array = ["Brasil","Argentina","Bolívia","Venezuela"];

foreach ($array as $pais) {


print $pais;
}
Iteração de Array (foreach chave - valor)
<?php

$array = [ 10 => "Brasil", 20 => "Argentina", 30 => "Bolívia", 99 => "Venezuela"];

foreach ($array as $key => $pais) {


print "$key - $pais";
}
Iteração de Arrays Multidimensionais
<?php

$pessoas = [
["nome" => "João", "cpf" => 123],
["nome" => "Maria", "cpf" => 456]
];

foreach ($pessoas as $pessoa) {


print "nome: ".$pessoa["nome"];
}
Iteração de Arrays Multidimensionais
<?php
$turmas = [
["nome" => "PHP", "alunos" => [
"João", "Maria", "Lucas"]],
["nome" => "BD", "alunos" => [
"Pedro", "Thiago", "João"]]
];

0 1

nome alunos nome alunos

"João" 0 1 2 "João" 0 1 2

"João" "Maria" "Lucas" "João" "Maria" "Lucas"


Iteração de Arrays Multidimensionais
<?php

foreach ($turmas as $turma) {


print "nome: ". $turma["nome"]."</ br>";
print "alunos:";
foreach($turma["alunos"] as $aluno){
print $aluno."</ br>";
}
}
Debug (print_r, var_dump, var_export)
<?php

$var1 = [ '', false, 42, ['42'] ];

print_r($var1);

var_dump($var1);

var_export($var1);
print_r, var_dump, var_export
print_r() var_dump() var_export()
Array array(4) { array (
( [0]=> 0 => '',
[0] => string(0) "" 1 => false,
[1] => [1]=> 2 => 42,
[2] => 42 bool(false) 3 =>
[3] => Array [2]=> array (
( int(42) 0 => '42',
[0] => 42 [3]=> ),
) array(1) { )
[0]=>
) string(2) "42"
}
}
Ordenação de Arrays
$carros = ["Chevete", "BMW", "Ferrari"];

● Ordem crescente (sort):


sort($carros);

● Ordem descrecente (rsort):


rsort($carros);
Ordenação de Arrays Associativos
$pessoas = ["Mateus"=>"35", "Marcos"=>"37", "Lucas"=>"43"];

● Ordem crescente “valor” (sort):


asort($pessoas);

● Ordem descrecente “valor” (rsort):


arsort($pessoas);
Ordenação de Arrays Associativos
$pessoas = ["Mateus"=>"35", "Marcos"=>"37", "Lucas"=>"43"];

● Ordem crescente “chave” (sort):


ksort($pessoas);

● Ordem descrecente “chave” (rsort):


krsort($pessoas);
Array para String (implode)
<?php

$array = ['uva', 'pera', 'abacate'];


$texto = implode(",", $array);

echo $texto; // uva,pera,abacate


String para Array (explode)
<?php

$texto = "uva pera abacate";


$array = explode(" ", $texto);

echo $array[0]; // uva


echo $array[1]; // pera
Checagem de valor no Array (in_array)
<?php

$frutas = ["uva", "pera", "abacaxi", "laranja"];

if (in_array("uva", $frutas)) {
echo "Tem uva !";
}
Checagem de chave no Array (array_key_exists)
<?php

$frutas = ["uva" => 10.99, "manga" => 4.00];

if (array_key_exists("uva", $frutas)) {
echo "a chave uva se encontra no array !";
}
Executar uma função em cada elemento (array_map)
<?php
$array = [
'PHP',
'Arrays',
$y = array_map(function ($x){
'Funções',
return "#".$x;
];
}, $array);
function hashtag($x){
return "#".$x; print_r($y);
}

print_r(array_map('hashtag', $array));
Filtrar elementos do array por condição (array_filter)

<?php

$alunos = [
['nome' => 'João', 'nota' => 8],
['nome' => 'Maria', 'nota' => 4.5],
['nome' => 'Thiago', 'nota' => 7]
];
$alunosAprovados = array_filter($alunos, function($aluno) {
return $aluno['nota'] >= 7;
});
var_dump($alunosAprovados);
Checagem de lista sequencial (array_is_list) (PHP 8.1 >=)
<?php ● array_is_list é uma função que retorna
array_is_list([]); // true true se o valor for um array e se todos
array_is_list([1, 2, 3]); // true os índices deste array são números
array_is_list(['josé', 2, 3]); // true
array_is_list(['josé', 'maria']); // true
inteiros em uma sequência começando
array_is_list([0 => 'josé', 'maria']); // true em 0 até N, sem intervalos entre esta
array_is_list([0 => 'josé', 1 => 'maria']); // true
sequência;
// Primeira chave não é 0 (zero)
array_is_list([1 => 'josé', 'maria']); // false ● A função array_is_list também retorna
// Chaves fora de ordem
array_is_list([1 => 'josé', 0 => 'maria']); // false true em arrays vazios (PHPWATCH,
// Chaves não inteiras 2022);
array_is_list([0 => 'josé', 'esposa' => 'maria']); //false
// Chaves não sequenciais
array_is_list([0 => 'josé', 2 => 'maria']); //false
Variáveis para array (compact)
<?php

$nome = "João";
$cpf = "123";
$rg = "8888";
$filhos = ["Maria", "Thiago"];

$result = compact("nome", "cpf", "rg", "filhos");


print_r($result);
Array para variáveis (list)
<?php

$info = ['Camiseta', 'Azul', 30.65];

// Listando todas as variáveis


list($produto, $modelo, $preco) = $info;

echo "A $produto $modelo custa $preco.\n";


Array collumn (recuperar dados por key)
<?php
$nomes = array_column($pessoas, 'nome');
$pessoas = [
print_r($nomes);
[
'id' => 1,
'nome' => 'João',
'idade' => 34,
],
[ 0 1 2
'id' => 2,
'nome' => 'Maria', “João” “Maria” “Lucas”
'idade' => 22,
],
[
'id' => 3,
'nome' => 'Lucas',
'idade' => 60,
]
];
Array collumn (recuperar dados por key com index)
<?php
$pessoas = [ $nomes = array_column($pessoas, 'nome', ‘id’);
[
'id' => 6789,
print_r($nomes);
'nome' => 'João',
'idade' => 34,
],
[ 6789 7089 3000
'id' => 7089,
'nome' => 'Maria', “João” “Maria” “Lucas”
'idade' => 22,
],
[
'id' => 3000,
'nome' => 'Lucas',
'idade' => 60,
]
];
Array como Pilha (array_push & array_pop)
//2
//3
<?php

$alunos = ["Maria", "João"]; //1


Lucas Lucas
X
array_push($alunos,"Carlos", "Lucas"); //2
print_r($alunos); //1
Carlos Carlos
array_pop($alunos); //3
print_r($alunos);
João João João

Maria Maria Maria


Array como Fila (array_shift)
<?php

//1
$alunos = ["Maria", "João"];
Maria João

array_push($alunos,"Carlos", "Lucas");
print_r($alunos);
//2
array_shift($alunos); Maria João Carlos Lucas
print_r($alunos);

//3
Maria João Carlos Lucas
PHP OO
● A programação orientada a objetos (POO) é um paradigma baseado no
conceito de "objetos", que podem conter dados, na forma de campos
(atributos/propriedades) e instruções de código, na forma de funções
(métodos).
● Consolidado no PHP 5, os recursos de Orientação a Objetos suportados pela
linguagem estão a inclusão de visibilidade, classes e métodos abstratos e
final, métodos mágicos, interfaces, clonagem e type hint.
● O PHP trata objetos da mesma maneira que referências ou manipuladores,
onde cada variável contém uma referência a um objeto ao invés de uma
cópia de todo o objeto.
Classe
● nome Pessoa

● atributos
● métodos
+ nome
+ sobrenome
+ dataNascimento
+ cpf

+ nomeCompleto()
+ idade()
Classe PHP
<?php Pessoa
class Pessoa {

public $nome;
+ nome
public $sobrenome; + sobrenome
public $dataNascimento; + dataNascimento
public $cpf; + cpf

public function nomeCompleto(){

return $this->nome." ".$this->sobrenome; + nomeCompleto()

}
Instanciando (new)
<?php
#1: Pessoa
class Pessoa { nome: null
cpf: null
public $cpf;
public $nome;

} $pessoa

$pessoa = new Pessoa();


Object Operator (arrow) “->”
<?php

class Pessoa {

public $cpf;
public $nome;
}

$pessoa = new Pessoa();

$pessoa->nome = 'maria';
$pessoa->cpf = '123';
Objetos (Instâncias)
#2: Pessoa
<?php
class Pessoa { #1: Pessoa nome: joão
public $cpf; cpf: 567
public $nome; nome: maria
} cpf: 123

$x = new Pessoa();
$y = new Pessoa();
$y
$x->nome = 'maria'; $x $z
$x->cpf = '123';

$y->nome = 'joão'; var_dump($x);


$y->cpf = '345'; var_dump($y);
var_dump($z);
$z = $y;
$z->cpf = '567';
clone

<?php

class Aluno {
$b = clone $a;
public $nome;
$b->nome = 'Juliana';
public $turma;
}
var_dump($a);
$a = new Aluno() ;
var_dump($b);
$a->nome = 'Maria';
$a->turma = 'PHP';
Métodos
<?php Pessoa

class Pessoa {

public $cpf; + cpf


public $nome; + nome

function exemplo() {
return 'olá mundooo!';
}
} + exemplo()

$pessoa = new Pessoa();

echo $pessoa->exemplo();
Referência ao Objeto ($this)
<?php

class Pessoa {
$pessoa = new Pessoa();
$pessoa->nome = 'João';
public $cpf;
$pessoa->sobreNome = 'da Silva';
public $nome;
public $sobreNome;
echo $pessoa->nomeCompleto();
function nomeCompleto(){
return $this->nome." ".$this->sobreNome;
}
}
Método Construtor (__construct)
<?php
class Pessoa {

public $cpf; $pessoa = new Pessoa();


public $nome; $pessoa->nome = 'maria';
$pessoa->cpf = '123';
function __construct() { print $pessoa->nome;
echo "nova pessoa";
}
}
Método Construtor com parâmetros (__construct)
<?php

class Pessoa {

public $cpf;
$pessoa = new Pessoa('123','João');
public $nome;
echo $pessoa->cpf;
function __construct($cpf, $nome){ echo $pessoa->nome;
$this->cpf = $cpf;
$this->nome = $nome;
}
}
Promotor de atributos no construtor (PHP 8.0 >=)

● Na utilização deste tipo de construtor o tipo dos


atributos são opcionais, porém, a visibilidade é
obrigatória (com exceção de atributos herdados);
<?php

class Pessoa { ● Similar ao construtor tradicional, também é possível


function __construct( definir valores defaults para atributos;
public $cpf,
public $nome
){} ● Atributos duplicados, isto é, declarados com o
} mesmo nome no escopo da classe e no promotor de
atributos no construtor são proibidos;
$p1 = new Pessoa('0123','José');
echo $p1->nome;
● Entretanto, é possível ter atributos declarados no
escopo combinado com outros atributos declarados
no promotor de atributos no construtor;
New no Promotor de atributos no construtor (PHP 8.1 >=)

<?php

class ContaBancaria {
function __construct(
public float $saldo = 0
){}
}

class Funcionario {
function __construct(
public string $cpf,
public string $nome,
public ContaBancaria $contaBancaria = new ContaBancaria()
){}
}

$p1 = new Funcionario('0123','José');


echo $p1->nome;
echo $p1->contaBancaria->saldo;
Método Destrutor (__destruct)
<?php
class Pessoa {

public $cpf;
public $nome; $pessoa = new Pessoa();
$pessoa->nome = 'maria';
function __construct() { $pessoa->cpf = '123';
echo "nova pessoa";
}
print $pessoa->nome;

function __destruct() {
echo "objeto removido";
}
}
Herança (extends)
Pessoa
<?php
class Pessoa { + nome
public $nome;
}

class PessoaFisica extends Pessoa {


public $cpf;
} PessoaFisica
+ cpf

$a = new PessoaFisica() ;
$a->nome = 'Maria';
$a->cpf = 123;

var_dump($a);
Promotor de atributos no construtor (herança)

<?php ● Na utilização deste tipo de construtor o


class Pessoa {
tipo dos atributos são opcionais, porém,
public function __construct( a visibilidade é obrigatória (com
public string $nome, exceção de atributos herdados);
) {}
}
● Atributos duplicados, isto é, declarados
class PessoaFisica extends Pessoa { com o mesmo nome no escopo da
public function __construct( classe e no promotor de atributos no
$nome, construtor são proibidos;
public string $cpf,
){
parent::__construct($nome); ● Entretanto, é possível ter atributos
} declarados no escopo combinado com
}
outros atributos declarados no promotor
$pf1 = new PessoaFisica('João','123'); de atributos no construtor;
echo $pf1->nome;
echo $pf1->cpf;
Method Overriding e Parent
<?php A substituição de métodos (method overriding), em é
class Pai { um recurso que permite que uma subclasse ou classe
filha forneça uma implementação específica de um
public function falar() {
método que já é fornecido por uma de suas
echo 'sou seu pai! ';
} superclasses ou classes pai.

} A implementação na subclasse substitui (override) a


class Filho extends Pai{
implementação da superclasse, fornecendo um método
public function falar() { que possui o mesmo nome, mesmos parâmetros ou
parent::falar(); assinatura, e o mesmo tipo de retorno que o método na
echo 'nãooooooooo'; classe pai.
}

} Apesar do recurso de overriding, ainda é possível que


a classe filha “execute” um método da classe pai
$filho1 = new Filho(); através do recurso parent.
echo $filho1->falar();
PHP OO (visibilidade)
public: torna uma variável / método disponível
de qualquer lugar, outras classes e instâncias
do objeto.

protected: escopo quando você quiser fazer o


seu / função variável visível em todas as
classes que estendem a classe atual, incluindo
a classe pai.

private: escopo quando você quer que seu /


função variável a ser visível apenas em sua
própria classe.

http://stackoverflow.com/questions/4361553/php-public-private-protected
Public (Visibilidade)
● Mesma classe que foi declarada
<?php ● Classes que herdam
class Pai {
public $publico; ● As classes que herdam a classe
protected $protegido; declarada acima.
private $privado;
} ● Quaisquer elementos estrangeiros
fora dessa classe também podem
class Filho extends Pai {}
acessar estes elementos
$pai = new Pai();
$filho = new Filho();
$pai->publico = 'abc';
$filho->publico = 123;

var_dump($pai);
var_dump($filho);
Protected (Visibilidade)
<?php
class Pai {
public $publico;
Quando você declara um método
protected $protegido; (função) ou uma propriedade (atributo)
private $privado;
} como protegido, esses métodos e
class Filho extends Pai { propriedades podem ser acessados por
public function alteraProtegido($valor){ a mesma classe que declarou isso.
$this->protegido = $valor;
}
} As classes que herdam a classe
$pai = new Pai(); declarada acima. Os membros de
$filho = new Filho();
outsiders não podem acessar essas
//$pai->protegido = 'xyz'; //Erro !
//$filho->protegido = 456; //Erro !
variáveis. "Outsiders" no sentido de que
eles não são instâncias de objetos da
$filho->alteraProtegido(456);
própria classe declarada.
var_dump($filho);
Private (Visibilidade)
<?php
class Pai {
public $publico;
protected $protegido;
private $privado;

public function exibePrivado(){ Quando você declara um método


return $this->privado;
} (função) ou uma propriedade (variável)
public function alteraPrivado($valor){ como privada, esses métodos e
$this->privado = $valor;
} propriedades podem ser acessados por
} a mesma classe que declarou isso.
class Filho extends Pai {}

$filho = new Filho();


$filho->alteraPrivado(789);

echo $filho->exibePrivado();
Atributos Estáticos
<?php Teste
class Teste {
+ status
+ nome
public static $status = 'online';
public $nome;
}
Na programação orientada a objetos, atributo estático
é conhecido como "variável de classe". Um atributo
$t1 = new Teste(); estático de uma determinada classe tem seus valores
$t1->nome = 'Teste 1'; compartilhados em todas as instâncias (objetos) ou
Teste::$status = 'offline'; diretamente na classe utilizando o operador ::

var_dump($t1);
var_dump(Teste::$status);
Métodos Estáticos
<?php Util

class Util {
+ hoje()

public static function hoje() {


De maneira similar, um método estático é
return date('d/m/y'); “procedimento da classe”. Um método estático de uma
} determinada classe pode ser acessado em todas as
} instâncias (objetos) ou diretamente na classe
utilizando o operador ::

É válido salientar que é proibido utilizar o $this dentro


echo Util::hoje(); (no contexto) de um método estático.
$util = new Util();
echo $util->hoje();
Class Constants
<?php

class Matematica echo Matematica::PI . "\n";


{
const PI = 3.14; $mat = new Matematica();
$mat->valorDePi();
function valorDePi() {
echo self::PI . "\n"; Similar as constantes usando o define(), o
} valor de uma constante nunca pode ser
alterado.
} O acesso é similar aos atributos estáticos,
utilizando o :: porém sem o $.
Class Constants (visibilidade)
<?php

class Matematica {

private const PI = 3.14; //disponível a partir do PHP 7.1

function valorDePi() {
echo self::PI . "\n";
}

//echo Matematica::PI . "\n"; erro !

$mat = new Matematica();


$mat->valorDePi();
Final Method
<?php
class ClasseBase {
public function teste() {
echo "teste";
O PHP 5 introduziu a palavra-chave
} final, que previne que classes filhas
final public function maisTeste() { sobrescrevam um método que esteja
echo "teste"; prefixado em sua definição com a
} palavra final.
}

class ClasseFilha extends ClasseBase {

//erro:
public function maisTeste() {
echo "mais teste";
}
Final Class
<?php

class Pessoa {
public $nome;
}
Se a própria classe estiver
final class Professor extends Pessoa { definida como final, ela
public $matricula; não pode ser estendida.
}

//erro:
class ProfessorSubstituto extends Professor {}
Imutabilidade “Readonly” (PHP 8.1 >=)
<?php <?php
//php8.1 >=
//php8.0 <= class Pedido {
class Pedido {
public readonly \DateTimeImmutable $dataEntrada;
private \DateTimeImmutable $dataEntrada;
public function __construct() {
public function __construct() { $this->dataEntrada = new DateTimeImmutable();
$this->dataEntrada = new \DateTimeImmutable(); }
} }

public function getDataEntrada(): \DateTimeImmutable { $p1 = new Pedido();


return $this->dataEntrada; echo $p1->dataEntrada->format('d/m/Y H:i:s');
} //Cannot modify readonly property Pedido::$dataEntrada
//$p1->dataEntrada = new DateTimeImmutable();
//violação de regra de domínio:
public function setDataEntrada(\DateTimeImmutable $dataEntrada){
$this->dataEntrada = $dataEntrada;
} A partir do PHP 8.1.0, uma propriedade pode
}
ser declarada com o modificador readonly, que
$p1 = new Pedido();
echo $p1->getDataEntrada()->format('d/m/Y H:i:s');
impede a modificação da propriedade após a
$p1->setDataEntrada(new \DateTimeImmutable()); inicialização.
Classe Abstrata
Pessoa
<?php
abstract class Pessoa { + nome
public $nome;
}

class PessoaFisica extends Pessoa {


public $cpf;
}
PessoaFisica PessoaJuridica
class PessoaJuridica extends Pessoa { + cpf + cnpj
public $cnpj;
}

$pf = new PessoaFisica();


$pj = new PessoaJuridica();
//$p = new Pessoa(); //erro !
Método Abstrato
<?php

abstract class Pessoa{


public $nome;
abstract function exibirDocumento(); $pf = new PessoaFisica();
} $pf->cpf = 123;
echo $pf->exibirDocumento();
class PessoaFisica extends Pessoa {
public $cpf;

//Métodos abstratos precisam ser implementados nas classes filhas


public function exibirDocumento(){
Métodos abstratos precisam ser
return $this->cpf; implementados nas classes filhas.
}
}
Null safe operator (PHP 8.0 >=)
class Pessoa {
private string $nome;
private ?Pessoa $dependente;

public function __construct(string $nome, Pessoa $dependente = null){


$this->nome = $nome;
$this->dependente = $dependente;
}

public function getNome() : string {


return $this->nome;
}

public function getDependente() : ?Pessoa {


return $this->dependente;
}
}

$p1 = new Pessoa('João');


$p2 = new Pessoa('Maria', $p1);
//echo $p1->getDependente()->getNome(); //Call to a member function getNome() on null
echo $p1->getDependente() ? $p1->getDependente()->getNome() : null; //if ternário
echo $p1->getDependente()?->getNome();
echo $p2->getDependente()?->getNome();
Interface
● Interfaces permitem a criação de códigos que especificam quais métodos
uma classe deve implementar, sem definir como esses métodos serão
tratados.
● Interfaces permitem também que classes de diferentes hierarquias possam
ter comportamentos similares.
● Interfaces são definidas da mesma forma que classes, mas com a
palavra-chave interface substituindo class e com nenhum dos métodos
tendo seu conteúdo definido.
● Todos os métodos declarados em uma interface devem ser públicos, essa é a
natureza de uma interface.
● Interfaces PHP suportam herança múltipla e constantes
Interface
<?php

interface Formatador {
<<interface>>
public function formatar(); Fomatador

}
+ formatar()
class Html implements Formatador {

//erro: é obrigatório implementar o método formatar !


//obs.: exceção se a classe Html fosse abstrata

}
Interface (herança múltipla)

<?php

interface Paginador{
public function paginar();
class TabelaPessoa implements ArvorePaginavel
}
{
//deverá implementar dois métodos
interface Arvore{ }
public function getNoRaiz();
}

interface ArvorePaginavel extends Paginador, Arvore {

}
Interface (usando typehint)
<?php

interface Sepultavel {
public function calcularValorDoCaixao(float $valor): float;
} Neste exemplo, classes de
class Felino implements Sepultavel { diferentes Taxonomias
public $peso; implementam uma mesma
public function calcularValorDoCaixao(float $valor): float{
return $valor * ($this->peso * 0.3); Interface
}
}

class Pessoa implements Sepultavel {


public $altura;
public $peso;
public $circunferenciaAbdominal;
public function calcularValorDoCaixao(float $valor): float{
return $valor * (($this->altura * $this->peso) / 2 +
$this->circunferenciaAbdominal);
}
}
Interface (usando typehint - parte 2)

<?php
Um método de uma classe (ou uma
class Sepultamento { função) pode usar o type int com o
nome de uma interface para que
public $cotacao = '4.05'; seja garantido que os
comportamentos de um objeto (que
public function enterrar(Sepultavel $sepultavel): float{ implementam esta interface)
return possam ser evocados.
$sepultavel->calcularValorDoCaixao($this->cotacao);
}

}
$gato = new Felino();
$gato->peso = 3.4;
$s = new Sepultamento();
echo $s->enterrar($gato);
Trait
● Traits são mecanismos para reutilização de código em linguagens de
herança únicas, como PHP.
● Uma Trait destina-se a reduzir algumas limitações de herança única,
permitindo que um desenvolvedor reutilizar conjuntos de métodos livremente
em várias classes independentes que podem viver em diferentes taxonomias
de classes.
● Não é possível instanciar um Trait por conta própria.
● É uma adição à herança tradicional e permite a composição horizontal do
comportamento, isto é, a aplicação de membros de classe sem exigir
herança.
● Traits podem conter métodos abstratos
Trait
<?php
trait MinhaTrait {
public $nome = 'trait';
public function ola() { $x = new Teste();
return 'ola '.$this->nome."!!!"; echo $x->nome;
} echo $x->ola();
}
$y = new MinhaTrait(); //Erro! Não é possível instanciar uma Trait
class Teste {
use MinhaTrait;
}
Trait - (múltiplo uso)
<?php
class Smartphone {
trait Telefone { use Telefone, Camera;
public function ligar() { public function videoChamada() {
echo 'alo…'; echo $this->filmar().$this->ligar();
} }
} }

trait Camera { $o = new Smartphone();


public function filmar() { $o->ligar();
echo 'gravando…'; $o->filmar();
} $o->videoChamada();
}
Trait (precedência/instead of)
<?php

trait Video {
public function tag() {
class Media {
echo '<video> ';
}
use Audio, Video {
}
Video::tag insteadof Audio;
} //sem esta resolução ocorrerá um Fatal Error.
}
trait Audio {
public function tag() {
$media = new Media();
echo '<audio>';
$media->tag(); //resultado: <video>
}
}
Trait (alias “as”)
<?php

trait Video { class Media {


public function tag() { use Audio, Video {
echo '<video> '; Video::tag insteadof Audio;
} Audio::tag as tagAudio;
}
}
}
trait Audio {
public function tag() { $media = new Media();
echo '<audio>';
}
$media->tag();
}
$media->tagAudio();
Trait (sintaxe para resolução de conflitos)
● Precedência:
○ ClasseA::metodo insteadof ClasseB
● Alias:
○ ClasseA::metodo as metodoN2
Trait (método abstrato)
<?php

trait MinhaTrait {
abstract function fazAlgo();
function mensagem(){
echo "método concreto";
}
}

class MinhaClass {
use MinhaTrait;
function fazAlgo(){
echo "implementação de fazAlgo em MinhaClass";
}
}
Classe Anônima
<?php

$pessoa = new class {


var_dump($pessoa);
public $nome = 'Maria';
echo $pessoa->nome;
public $cpf = '754';
echo $pessoa->cpf;
echo $pessoa->getNome();
public function getNome(){
return strtoupper($this->nome);
}
};
Classe Anônima (novas instâncias)

<?php

$pessoa = new class{


echo $pessoa->oi();
public $nome = 'Fulano'; $p1 = new $pessoa();
$p1->nome = 'Maria';
public function oi(){ echo $p1->oi();
return "Oi {$this->nome} !";
}
};
Classe Anônima (via function)
<?php

function pessoa_oo() { $pessoa = pessoa_oo();


echo $pessoa->nome;
return new class {
public $nome = 'Maria'; echo $pessoa->cpf;
public $cpf = '754'; echo $pessoa->getNome();
public function getNome(){
return strtoupper($this->nome);
}

};
}
Associação entre objetos
Na programação orientada a objetos, a associação define um relacionamento entre classes de objetos
que permite que uma instância de objeto faça com que outra execute uma ação em seu nome. Essa
relação é estrutural, porque especifica que objetos de um tipo estão conectados a objetos de outro e não
representam comportamento.

Pessoa Veiculo

possui 0..1 + placa


+ nome
+ modelo
Associação entre objetos
<?php
$p1 = new Pessoa();
$p1->nome = 'Maria';
class Pessoa { $p1->veiculo = new Veiculo();
public $nome;
public $veiculo;
} $p1->veiculo->placa = 'ABC123';
$p1->veiculo->modelo = 'Tesla X1';
class Veiculo{
public $placa; var_dump($p1);
public $modelo;
}
Composição
● Composição é um tipo de associação onde o objeto composto é o único
responsável pela disposição das partes componentes.
● O relacionamento entre o composto e o componente é um relacionamento
forte "tem um", pois o objeto composto apropria-se do componente.
● Isso significa que o composto é responsável pela criação e destruição das
partes componentes. Um objeto só pode fazer parte de um composto. Se o
objeto composto for destruído, todas as partes componentes devem ser
destruídas.
● A composição impõe o encapsulamento, pois as partes do componente
geralmente são membros do objeto composto.
● A composição é caracterizada na UML pelo uso do losango preenchido.
Composição
<?php
class Assunto {
public $texto;
}
class Corpo {
public $texto;
public $formato;
}
class Destinatario {
public $nome;
}
class Email {
private $assunto;
private $corpo;
private $destinatario;
public function __construct() {
$this->assunto = new Assunto();
$this->corpo = new Corpo();
$this->destinatario = new Destinatario();
}
}
Composição (Versão com classes anônimas)
<?php

class Email {
private $assunto;
private $corpo;
private $destinatario; public function setAssunto($texto){
$this->assunto->texto = $texto;
public function __construct() {
}
$this->assunto = new class {
public $texto; //implementar os próximos getters & setters
}; }
$this->corpo = new class {
public $texto; $email = new Email();
public $formato; $email->setAssunto('Curso de PHP');
};
$this->destinatario = new class {
public $nome;
};
}
Agregação
● Agregação é um tipo de associação que especifica um relacionamento
de todo / parte entre a parte agregada (todo) e o componente.
● Esse relacionamento entre o agregado e o componente é um
relacionamento "tem-um" fraco, pois o componente pode sobreviver ao
objeto agregado.
● O objeto componente pode ser acessado através de outros objetos
sem passar pelo objeto agregado. O objeto agregado não participa do
ciclo de vida do objeto de componente, o que significa que o objeto de
componente pode sobreviver ao objeto agregado. O estado do objeto
componente ainda faz parte do objeto agregado.
● A agregação é caracterizada na UML pelo uso do losango sem vazio.
Agregação Motor

<?php
abstract class Motor {}
class MotorV8 extends Motor { MotorV8

}
class Carro {
private $motor;
public function __construct(Motor $motor) {
$this->motor = $motor;
}
} Carro
$motorV8 = new MotorV8();
$carro1 = new Carro($motorV8);
Associação (1..*)

Uma associação um-para-muitos


(ou têm muitos) é um tipo de
associação que estabelece uma
vinculação onde uma instância da Editora Livro
classe do lado esquerdo tem zero publicacoes
ou mais instâncias de outra classe. + nome 1..* + titulo
+ website + edicao
Para representar esta
multiplicidade, podemos utilizar
uma coleção (ex.: array) para
“armazenar” / “intermediar” esta
relação.
Associação (1..*)

<?php
$livro1 = new Livro();
class Livro { $livro1->titulo = 'Aventuras do PHP';
public $titulo;
public $edicao; $livro2 = new Livro();
} $livro2->titulo = 'A vida';

class Editora { $editora1 = new Editora();


public $nome; $editora1->nome = 'Editora XPTO';
public $website; $editora1->publicacoes[] = $livro1;
public $publicacoes = []; $editora1->publicacoes[] = $livro2;
}
Associação (1..*)

0 1

Editora1

nome: Editora XPTO


website: null Livro1 Livro2
publicacoes:
titulo: Aventuras d.. titulo: A vida
edicao: null edicao: null
Classe Associativa
Uma classe associativa é uma classe que faz parte de um
relacionamento de associação entre duas outras classes.

É possível “anexar” uma classe associativa a um


relacionamento para fornecer informações adicionais
sobre o relacionamento. Uma classe de associação é
idêntica a outras classes e pode conter operações,
atributos e outras associações.

Por exemplo, um Ator pode atuar em muitos Filmes e um


Filme pode contar com a atuação de vários Atores. Em
cada Filme, o Ator pode atuar com um papel distinto. Por
isso, neste caso, uma classe associativa chamada
Atuação pode definir melhor esta associação fornecendo
informação adicional do papel que o Ator desempenhou
em um determinado Filme.

Como a figura a seguir ilustra, uma classe de associação é


conectada a uma associação por uma linha pontilhada.
Classe Associativa
<?php

class Ator {
public $nome;
public $atuacoes = []; $ator1 = new Ator();
} $ator1->nome = 'José da Silva';
class Atuacao {
public $ator; $ator2 = new Ator();
public $filme; $ator2->nome = 'Maria das Dores';
public $papel;
$filme1 = new Filme();
public function __construct(Ator $ator, Filme $filme, string $papel){ $filme1->titulo = 'Aventuras do barulho';
$this->ator = $ator;
$this->filme = $filme;
$this->papel = $papel; $filme1->atuacoes[] =
} new Atuacao($ator1, $filme1, 'Protagonista');
} $filme1->atuacoes[] =
new Atuacao($ator2, $filme1, 'Coadjuvante');
class Filme {
public $titulo;
public $atuacoes = [];
}
Autoload (Carregamento automático de Classes)
● Quando trabalhamos com mais de uma Classe, Interface e/ou Trait, é
interessante utilizar algum recurso que possibilite o carregamento
dinâmico/automático das estruturas em seus respectivos arquivos *.php.
● O PHP possui duas funções de registro automático: o __autoload() e o
spl_autoload_register()
● O __autoload() foi depreciado à partir do PHP 7.2.
● O spl_autoload_register() permite que vários carregadores automáticos sejam
registrados, que serão executados por sua vez até que uma classe, interface ou
trait correspondente seja localizada e carregada, ou até que todas as opções de
carregamento automático tenham sido esgotadas. Isso significa que, se você
estiver usando algum framework ou outras bibliotecas que implementam seus
próprios carregadores automáticos, não é necessário se preocupar com a
possibilidade de conflitos.
Autoload (spl_autoload_register)

classes\Pessoa.php exemplo.php
<?php <?php

class Pessoa { function meu_autoloader($class) {


public $nome; include 'classes/' . $class . '.php';
} }

spl_autoload_register('meu_autoloader');
classes\Veiculo.php
<?php $pessoa1 = new Pessoa();
$veiculo1 = new Veiculo();
class Veiculo {
public $placa;
}
Reflection
● Permite introspecção no código Orientado a objeto.
● O Reflection auxilia os desenvolvedores a criar bibliotecas genéricas de
software para exibir dados, processar diferentes formatos de dados, realizar
serialização, agrupar dados e muito mais.
● O Reflection pode ser usado para observar e modificar a execução do
programa em tempo de execução.
● Em linguagens de programação orientada a objeto, como PHP, o Reflection
permite a inspeção de classes, interfaces, campos e métodos em tempo de
execução sem conhecer os nomes das interfaces, campos, métodos em
tempo de execução.
● Também permite a instanciação de novos objetos e a invocação de métodos
de forma dinâmica.
Reflection (class)
<?php

<?php
class Aluno {
public $nome;
class Animal {} protected $endereco;
class Felino extends Animal {} private $telefone;
class Gato extends Felino {} }

$aluno1 = new Aluno();


$class = new ReflectionClass(Gato::class);
$reflect = new ReflectionClass($aluno1);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC |
while ($parent = $class->getParentClass()) { ReflectionProperty::IS_PROTECTED);
$parents[] = $parent->getName();
$class = $parent; foreach ($props as $prop) {
print $prop->getName() . "\n";
}
}

echo "Parents: " . implode(", ", $parents); var_dump($props);


Reflection (alterando visibilidade)
<?php
class Teste {
private $propriedade = 'ola'; Com o uso do Reflection é possível
“modificar” a visibilidade de um atributo
private function dizSegredo(){ ou método em tempo de execução
echo $this->propriedade.'! 1234'; através do setAcessible(true);
}
}
$class = new ReflectionClass("Teste"); Entretanto, o acesso aos
$property = $class->getProperty("propriedade"); valores/retornos precisam ser feitos
$property->setAccessible(true); através dos métodos getValue() ou
invoke().
$teste1 = new Teste();
//echo $teste1->propriedade; // Não funciona aqui No caso dos atributos é possível utilizar o
echo $property->getValue($teste1); setValue() para modificar o valor de um
$property->setValue($teste1, 'legal'); atributo também.
$method = new ReflectionMethod("Teste", 'dizSegredo');
$method->setAccessible(true);
echo $method->invoke($teste1);
Reflection (gerar instâncias e invocar métodos)

<?php

class Pessoa { //com reflection


$reflector = new ReflectionClass('Pessoa');
public function digaOi(){ $instancia = $reflector->newInstance();
return 'oi!'; $metodo = $reflector->getMethod('digaOi');
} echo $metodo->invoke($instancia);
}
Reflection (recuperar comentários)
<?php
/**
* Esta é uma classe de exemplo
*/
class Exemplo {
/**
* Esta é uma função de exemplo Com Reflection também é possível recuperar
*/ comentários das classes e métodos através do
public function fazNada(){} getDocComment().
}

$reflector = new ReflectionClass(Exemplo::class);

echo $reflector->getDocComment();
echo
$reflector->getMethod('fazNada')->getDocComment();
Namespace
● Os namespaces são basicamente uma maneira de organizar suas classes
PHP e evitar conflitos de código.
● Usamos a palavra reservada namespace
● Namespace não estão relacionados diretamente com a estrutura de
diretórios.
● Namespace usam “backslash” (\) como separador
● É possível referenciar a classe usando o use para evitar escrever o
namespace completo novamente
● Apelidos e Conflitos podem ser resolvidos com o uso do as
Namespace
Pessoa.php exemplo.php

<?php <?php

namespace Modelo; require "Pessoa.php";

class Pessoa {} // não funciona !


//$pessoa1 = new Pessoa();
$pessoa1 = new Modelo\Pessoa();
Namespace (modelos homônimos)
\Modelo\Literatura\Manga.php
<?php

namespace Modelo\Literatura; exemplo.php


class Manga { <?php
public $autor;
public $editora;
} require "Modelo\Fruta\Manga.php";
\Modelo\Fruta\Manga.php
require "Modelo\Literatura\Manga.php";
<?php
$manga1 = new Modelo\Literatura\Manga();
namespace Modelo\Fruta;
$manga2 = new Modelo\Fruta\Manga();
class Manga {

public $vitaminas = ['A','C'];

}
Namespace (use)

<?php

require "Modelo\Fruta\Manga.php";
Com a palavra reservada use, é
possível invocar o nome completo de
use Modelo\Fruta\Manga; uma classe para que a mesma possa
ser utilizada sem a necessidade do
namespace após o use.
$manga2 = new Manga();
Namespace (use + as)

<?php

require "Modelo\Fruta\Manga.php";

use Modelo\Fruta\Manga as FrutaManga;

$manga2 = new FrutaManga();


Namespace (resolução de conflitos)

<?php

require "Modelo\Fruta\Manga.php";
require "Modelo\Literatura\Manga.php";

use Modelo\Fruta\Manga as FrutaManga;


use Modelo\Literatura\Manga;

$manga1 = new Manga();


$manga2 = new FrutaManga();
Namespace + Autoloader
<?php

use classes\Pessoa; O nome do namespace (e consonância


use classes\Veiculo; com o nome dos diretórios) pode ser
utilizado para incluir automaticamente as
function meu_autoloader($class) { classes no código.
include_once $class . '.php';
} Isto não exime o desenvolvedor de utilizar
o use ou nome completo da classe (FQN -
spl_autoload_register('meu_autoloader'); Fully Qualified Name).

$pessoa1 = new Pessoa();


$veiculo1 = new Veiculo();
Namespace (Group use PHP >= 7.0)

<?php

use classes\{Pessoa, Veiculo};

function meu_autoloader($class) { A partir do PHP 7, tornou-se possível agrupar


include_once $class . '.php'; classes de um mesmo namespace utilizando a
} sintaxe de group use.

spl_autoload_register('meu_autoloader');

$pessoa1 = new Pessoa();


$veiculo1 = new Veiculo();
Iteração de atributos do Objeto
<?php

class Pessoa {
public $nome = 'Maria da Silva'; //Aqui só é possível iterar os atributos públicos
private $cpf = '123456'; foreach($pessoa as $key => $value) {
protected $idade = 30; print "$key => $value\n";
}
function iterar() { echo "\n";
foreach ($this as $key => $value) {
print "$key => $value\n"; //no método usando o $this é possível iterar todos
} os valores
} $pessoa->iterar();
}
$pessoa = new Pessoa();
Interface Iterator

<<interface>>
● Interface Iterator possui um conjunto abstrato de Iterator
métodos para iteradores externos ou objetos que
podem ser iterados internamente.
+ current ()
+ key()
+ next()
● Quando uma nova classe implementa esta esta + rewind
Interface, suas instâncias tornam-se iteráveis, + valid()

similar ao uso de um array.


<<interface>>
Interface Iterator Iterator

+ current ()
public function next() {
<?php $novo_atual = $this->atual;
+ key()
$this->atual += $this->anterior; + next()
class Fibonacci implements Iterator { $this->anterior = $novo_atual; + rewind
private $anterior = 1; $this->key++; + valid()
private $atual = 0; }
private $key = 0;
public function rewind() {
public function __construct (int $max = 100) { $this->anterior = 1;
$this->max = $max; $this->atual = 0;
} $this->key = 0;
}
public function current() {
return $this->atual; public function valid() {
} if($this->atual > $this->max) Fibonacci
return false;
public function key() { return true;
return $this->key; }
} }
Interface Iterator

$seq = new Fibonacci();


Quando um objeto implementa o Iterator, o
mesmo torna-se passível de ser “iterado”
foreach ($seq as $f) { de forma transparente, similar a um array,
por uma estrutura de laço dinâmica como o
“foreach”, graças aos métodos que
“garantem” o processo da iteração.
echo $f.PHP_EOL;

}
Generator
<?php

function meuGenerator() {

echo "Um".PHP_EOL; Quando uma função contém a palavra reservada


yield 1; yield, o PHP automaticamente interpreta a mesma
echo "Dois".PHP_EOL; como um objeto do tipo Generator.
yield 2;
echo "Três".PHP_EOL; Similar ao uso do Iterator, um generator permite que
yield 3; o objeto possa ser iterado.
}
Além disso, o yield “pausa” o fluxo natural do código
$iterator = meuGenerator(); da função e permite retorna um valor similar ao
return.
$value = $iterator->current();
$value = $iterator->next();
$value = $iterator->next();
Generator (yield from)
<?php function oito() {
yield 8;
function contar10() { }
yield 1;
yield 2; foreach (contar10() as $num) {
yield from [3, 4]; echo "$num ";
yield from new ArrayIterator([5, 6]); }
yield from sete_oito();
yield 9; Com o uso yield from é possível recuperar os
yield 10; valores de outros generators, iteradores ou arrays.
}

function sete_oito() {
yield 7;
yield from oito();
}
Iterator to Array

<?php

$iterator = meuGenerator();
$seq = new Fibonacci();
Com o uso da função
$array_fibonnaci = iterator_to_array($seq); iterator_to_array é possível
$array_generator = iterator_to_array($iterator); converter um objeto iterável para
array.
var_dump($array_fibonnaci);
var_dump($array_generator);
Classe DateTime

<?php Classe DateTime é uma alternativa


O.O. à função date().
$dateTime = new DateTime('2016-12-31'); Ela possui um conjunto de métodos
echo $dateTime->format('Y-m-d H:i:s'); que permitem realizar operações
complexas com datas e horários.
$dateTimeAgora = new DateTime(); Além disso, por sua natureza de
echo $dateTimeAgora->format('Y-m-d H:i:s'); Classe, é possível estender a
mesma e criar classes customizadas
de Data e Hora.
DateTimeZone (Fuso Horário)

<?php

date_default_timezone_set('America/Sao_Paulo'); É possível setar o fuso horário no


PHP através da função
$dateTimeAgora = new DateTime(); date_default_timezone_set()
passando como valor o fuso horário
echo $dateTimeAgora->format('d/m/Y H:i:s'); desejado.

Também é possível determinar o fuso


$dateTime = new DateTime('2016-12-31 12:03:00', horário individualmente no momento
new DateTimeZone('America/Sao_Paulo')); da criação do objeto DateTime
passando uma instância de
echo $dateTime->format('d/m/Y H:i:s'); DateTimeZone
Classe DateTime (createFromFormat)

<?php

$dateBr = DateTime::createFromFormat('d/m/Y', '31/12/2003');


echo $dateBr->format('d/m/Y');

Formato
da data Data no
Formato para formato
exibição
(string)
Classe DateTime (modificando data)
Adicionar um dia Segunda Feira desta semana
<?php <?php

$dateTime = new DateTime('2018-01-31'); $dateTime = new DateTime();


$dateTime->modify('+1 day'); $dateTime->modify('monday this week');
echo $dateTime->format('d/m/Y'); echo $dateTime->format('d/m/Y');

Adicionar dez dias úteis Adicionar um mês e “menos três dias”


<?php <?php

$dateTime = new DateTime(); $dateTime = new DateTime();


$dateTime->modify('+10 weekdays'); $dateTime->modify('+1 month -3 days');
echo $dateTime->format('d/m/Y'); echo $dateTime->format('d/m/Y');
Classe DateTime (diferença entre datas)

<?php

$date1 = new DateTime('2018-08-11 05:02:00');


$date2 = new DateTime('2018-10-13 10:12:34');
$interval = $date1->diff($date2);

echo "Meses: {$interval->m} dias: {$interval->d} horas: {$interval->h} minutos:


{$interval->i} segundos: {$interval->s}";
DateTime (comparação)

<?php

$hoje = new DateTime('today');


$ontem = new DateTime('yesterday'); É possível usar operadores de comparação
var_dump($hoje > $ontem); para comparar datas.
var_dump($hoje < $ontem);
var_dump($hoje == $ontem);
Typed Properties (PHP 7.4 >=)
<?php

class Veiculo {
public string $placa;
public string $modelo; Com o advento das typed properties no PHP 7.4,
} é possível especificar os tipos dos atributos de
uma classe e restringir os valores dos mesmos.
class Pessoa {
public string $nome; O comportamento é similar ao type hint em
public Veiculo $veiculo; parâmetros de métodos (funções) e ambos são
} afetados pelo nível do “strict_types” onde, o PHP
poderá (ou não) converter determinados tipos dos
$pessoa1 = new Pessoa(); valores em tempo de execução para o tipo
$pessoa1->nome = "Maria da Silva"; determinado na declaração na classe.
$veiculo1 = new Veiculo();
$veiculo1->placa = "ABC 123";
$pessoa1->veiculo = 1; //erro !
$pessoa1->veiculo = $veiculo;
Union Types (PHP 8.0 >=)
<?php
function somar(int|float $v1, int|float $v2): int|float{

return $v1+$v2;

echo somar(2,33.4);

function incrementar(int|float|string $valor) {


return ++$valor;
}

echo incrementar(1);
echo incrementar(11.3);
echo incrementar('a');
Union Types (PHP 8.0 >=) - parte 2
class Pessoa {
private string $nome;
private array|Pessoa $dependente;

public function __construct(string $nome){


$this->nome = $nome;
}
public function getNome() : string {
return $this->nome;
}
public function setDependente(Pessoa $dependente){
$this->dependente = $dependente;
}
public function setDependentes(Pessoa ...$dependente){
$this->dependente = $dependente;
}
public function getDependente() : array|Pessoa {
return $this->dependente;
}
}

$p1 = new Pessoa('João');


$p1->setDependente(new Pessoa('Maria'));
echo $p1->getDependente()->getNome();

$p2 = new Pessoa('Thiago');


$p2->setDependentes(new Pessoa('Joana'), new Pessoa('Lucas'));
echo $p2->getDependente()[1]->getNome();
Mixed Typed
<?php

function getLog(int $parametro): mixed {

if($parametro < 0 || $parametro > 3){


throw new \OutOfRangeException('apenas 0, 1, 2 ou 3 !');
}

$valores = ['sistema ok',200,true,(new \DateTime())];

return $valores[$parametro];

echo getLog(1);
Mixed Typed em Classes
<?php

declare(strict_types=1);

class Funcionario {

public string $nome;


public mixed $situacao;

public function __construct(string $nome, mixed $situacao){


$this->nome = $nome;
$this->situacao = $situacao;
}

$f1 = new Funcionario('José','ativo');


$f2 = new Funcionario('José',false);

var_dump($f1);
var_dump($f2);
Pure Intersection Types (PHP 8.1 >=)
<?php function salvarTextoZip(Compressao&Persistencia $object) {
$conteudo_zipado = $object->getConteudoComprimido();
interface Compressao { $object->salvar($conteudo_zipado, 'arquivo.bz');
public function getConteudoComprimido(): string; }
}
$t1 = new Teste("olá mundo!");
interface Persistencia { salvarTextoZip($t1);
public function salvar(string $conteudo, string $nome_arquivo): void;
}

class Teste implements Compressao, Persistencia {

public function __construct(


private string $conteudo
){}

public function getConteudoComprimido(): string {


return bzcompress($this->conteudo);
}

public function salvar(string $conteudo, string $nome_arquivo): void {


file_put_contents($nome_arquivo, $conteudo);
}
}
StdClass e Type Cast
<?php
//convertendo array para objeto
$array = ['nome'=>'José', 'idade'=>34];
$objeto = (object) $array; A classe nativa do PHP StdClass
var_dump($objeto); (Standard Class) tem o propósito de ser
uma classe para objetos frutos de
//convertendo json para objeto coerções.
$json = '{"nome":"Maria", "idade":45}';
$objeto2 = json_decode($json); Também permite que objetos genéricos
var_dump($objeto2); possam ser criados.

//criando um objeto genérico


$objeto3 = new StdClass();
$objeto3->nome = 'Legal';
$objeto3->vivo = true;
var_dump($objeto3);
WeakMaps
<?php :Pessoa :Pessoa
$obj1 = new \StdClass();
$obj1->nome = 'João'; unset($obj1);
+ nome = “João” + nome = “João”
$obj1->idade = 28; + idade = 28 + idade = 28

$map = [];
$map[$obj1->nome] = $obj1;

unset($obj1);
var_dump($map); $obj1 João … João …

<?php Pessoa
$obj1 = new \StdClass();
$obj1->nome = 'João'; unset($obj1); WeakMap
+ nome = “João”
$obj1->idade = 28;
+ idade = 28

$map = new \WeakMap();
$map[$obj1] = $obj1->nome;
WeakMap
unset($obj1);
var_dump($map); $obj1 0 …
Stringable Interface
<?php

class Pessoa { //implements Stringable (opcional)

public string $nome;

public function __construct(string $nome){


$this->nome = $nome;
}

public function __toString(): string {


return $this->nome;
}
}

function printarVermelho(string|Stringable $texto) {


echo "\033[31m{$texto}".PHP_EOL;
}

printarVermelho(new Pessoa('João'));
printarVermelho('Maria');
Attributes (Introdução)
● Até a versão 7.4, a única maneira de inserir anotações em classes, métodos ou
atributos capazes de decorar ou injetar novos recursos aos mesmos via PHP era
através da utilização de comentários estruturados como, por exemplo, o phpdoc;
● A partir do PHP 8.0, surge o recurso do Attributes (ou atributos em português), que é a
versão do PHP do que é conhecido em algumas linguagens como “annotations” ou
“decorators”.
● Os atributos oferecem a capacidade de adicionar informações de metadados
estruturados e legíveis por máquina em diferentes estruturas do programa como
Classes, métodos e funções e outros;
● Esses metadados definidos via atributos podem ser recuperados em tempo de
execução usando as APIs de reflexão do próprio PHP;
● Os atributos podem, portanto, ser vistos como uma linguagem de configuração
incorporada diretamente no código;
Attributes (Sintaxe)
#[AtributoEmClasse] ● A utilização de um atributo é sempre delimitada
class Exemplo {
por uma tralha seguida de um colchete de
#[AtributoEmPropriedade] abertura “#[“ e termina com colchete de
public int $valor; fechamento “]”;
#[AtributoEmConstante]
public const PADRAO = 1; ● Atributos podem ser aplicados em Classes,
#[AtributoEmMetodo]
propriedades, métodos, classes anônimas,
public function fazAlgo(){} funções, closures e parâmetros de métodos e/ou
} funções;
$x = new #[AtributoEmClasseAnonima] class{};
● O nome de um atributo pode ser não qualificado,
#[AtributoEmFuncao]
qualificado ou totalmente qualificado, seguindo
function fazAlgo() {}
as mesmas regras de namespaces e aliases
$closure = #[AtributoEmClosure] fn() => 1; utilizados em classes;
function fazAlgo2(#[AtributoEmParametro] $x, #[AtributoEmParametro] $y) {}
● Dois os mais Attributes podem ser utilizados e
#[ClassAttribute] #[ClassAttribute2] separados por espaço, quebra de linha ou sem
class Exemplo2 {} nenhum caractere de separação;
Attributes (Definição, Argumentos e Reflexão)
#[Attribute] ● Novos atributos são definidos através do meta-atributo #[Attribute]
class AtributoExemplo{ como instrução (linha) anterior a classe que representará o atributo;
function __construct(
public string $nome = "default",
public int $valor = 0 ● A utilização de um atributo é sempre delimitada por uma tralha seguida
){} de um colchete de abertura #[ e termina com colchete de fechamento ];
}

#[AtributoExemplo] ● O nome de um atributo pode ser não qualificado, qualificado ou


class TesteA{} totalmente qualificado, seguindo as mesmas regras de namespaces e
aliases utilizados em classes;
#[AtributoExemplo("prioridade",100)]
class TesteB{}
● Os argumentos para os atributos são opcionais, mas precisam ser
$reflection = new ReflectionClass(TesteA::class); //ou TesteB colocados entre parênteses (). Argumentos para atributos só podem ser
valores literais ou expressões constantes. A sintaxe de argumentos
foreach ($reflection->getAttributes() as $attribute) {
posicionais e nomeados podem ser utilizadas;
var_dump([
'nome' => $attribute->getName(),
'argumentos' => $attribute->getArguments(), ● Os argumentos dos atributos são repassados para o método construtor
'instância' => $attribute->newInstance() da classe atribuído ao Attribute. Sua instância pode ser acessada
]);
} através do uso de Reflection.
Attributes (Configuração)
<?php ● Por padrão, os atributos podem ser adicionados
#[Attribute(Attribute::TARGET_CLASS)] em vários lugares, conforme listado acima. É
class MeuAtributo1{ possível, no entanto, configurá-los para que
}
possam ser usados apenas em locais específicos
#[Attribute(Attribute::TARGET_FUNCTION)] (ROOSE, 2022)
class MeuAtributo2{
}

#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)]
● Para restringir o uso de um atributo customizado,
class MeuAtributo3{ basta passar como parâmetro, umas das
}
constantes de prefixo “TARGET” do Attribute.
#[MeuAtributo1]
class Teste{}
● Também é possível permitir a repetição do
#[MeuAtributo2] mesmo atributo através da constante
function fazAlgo(){}
Attribute::IS_REPEATABLE
#[MeuAtributo2] //erro !!!
class TesteErro{}
● É válido ressaltar que a validação destas “flags”
//erro fatal: ocorre apenas no momento em que o método
$attr = (new \ReflectionClass(TesteErro::class))->getAttributes()[0]->newInstance();
newInstance() é executado;
Enum (PHP 8.1 >=)
● Enumeração ou "Enum" é recurso que permite ao desenvolvedor definir um tipo personalizado
que está limitado a um número discreto de valores possíveis. Isso pode ser especialmente útil ao
definir um modelo de domínio, pois permite "tornar estados inválidos irrepresentáveis.";

● Enums aparecem em muitas linguagens com uma variedade de recursos diferentes. No PHP,
Enums são um tipo especial de objeto;

● A Enum em si é uma classe, e seus possíveis casos são todos objetos de instância única dessa
classe. Isso significa que casos Enum são objetos válidos e podem ser usados em qualquer lugar
em que um objeto pode ser usado, incluindo verificações de tipo;

● O exemplo mais popular de enumerações é o tipo embutido booleano, que é um tipo enumerado
com valores válidos true e false. Enums permitem que os desenvolvedores definam suas
próprias enumerações robustas (PHPGroup, 2022);
Enum (Sintaxe Básica e Uso)
<?php

enum EstadoCivil {
case SOLTEIRO;
case CASADO;
case SEPARADO;
case DIVORCIADO;
case VIUVO;
}

class Pessoa {
public function __construct(
public string $nome,
public EstadoCivil $estadoCivil,
) {}
}

$p1 = new Pessoa("José", EstadoCivil::CASADO);


//$p2 = new Pessoa("Maria", "CASADA"); //Erro: Argument #2 ($estadoCivil) must be of type EstadoCivil
//$p3 = new Pessoa("Joana", EstadoCivil::CASADA); //Erro: Undefined constant EstadoCivil::CASADA
Enum (métodos)
<?php

enum Mensagem {

case SUCESSO;
case ATENCAO;
case ERRO;

public function getCor(): string


{
return match($this)
{
self::SUCESSO => '#7FFFD4',
self::ATENCAO => '#FFFF00',
self::ERRO => '#DC143C'
};
}
}

$mensagem = Mensagem::SUCESSO;

echo "<div style='background-color:{$mensagem->getCor()}'> Sucesso ! </div>";


Enum (backed)
<?php

enum Mensagem : string { //type return obrigatório e apenas int ou string


case SUCESSO = '#7FFFD4';
case ATENCAO = '#FFFF00';
case ERRO = '#DC143C';
}

$cor = Mensagem::SUCESSO->value;
echo "<div style='background-color:$cor'> Sucesso ! </div>";

<?php

enum StatusHTTP : int {


case OK = 200;
case NOT_FOUND = 404;
case INTERNAL_SERVER_ERROR = 500;
}

$status_code = StatusHTTP::INTERNAL_SERVER_ERROR->value;
http_response_code($status_code);
Enum (métodos nativos)
<?php

enum StatusHTTP : int {


case OK = 200;
case NOT_FOUND = 404;
case INTERNAL_SERVER_ERROR = 500;
}

var_dump(StatusHTTP::from(200));
var_dump(StatusHTTP::from(200) instanceof StatusHTTP); // true
var_dump(is_object(StatusHTTP::from(200))); // true

var_dump(StatusHTTP::tryFrom(501)); //null
//var_dump(StatusHTTP::from(501)); //is not a valid backing value for enum
https://www.php.net/manual/pt_BR/
class.backedenum.php
var_dump(StatusHTTP::cases()); // array
Enum (interfaces)
<?php enum StatusHTTP : int implements MinhaInterface {
case OK = 200;
interface MinhaInterface { case NOT_FOUND = 404;
public function fazAlgo() : string; case INTERNAL_SERVER_ERROR = 500;
}
public function fazAlgo(): string {
enum EstadoCivil implements MinhaInterface { return "olá Enum Backed!";
}
case SOLTEIRO;
case CASADO; }
case SEPARADO;
case DIVORCIADO; echo EstadoCivil::VIUVO->fazAlgo();
case VIUVO; echo StatusHTTP::OK->fazAlgo();

public function fazAlgo() : string {


return "olá Enum!";
}
}
Entrada de dados

● Para que uma aplicação seja dinâmica, faz-se necessário permitir que os
usuários possam interagir com a mesma através de algum método de input
(entrada) de dados.

● Antes do surgimento dos sistemas operacionais gráficos (GUI), a entrada de


dados era feita através de interface de linha de comando (CLI).

● Com o surgimento da web, os dados são, em sua maioria, imputados através


de formulários HTML.
Entrada de dados (CLI)
cli.php
<?php

echo "Qual é o seu nome? " . PHP_EOL;


$input = fgets(STDIN);
echo "Seja bem vindo(a), $input";
Entrada de Dados (web)
formulario.htm cadastrar.php

<html>
<form action='cadastrar.php'>
<label>Nome:</label>
<input type="text" name="nome"/>
<?php
<label>Email:</label>
var_dump($_REQUEST);
<input type="email" name="email"/>
<input type="submit" value="Cadastrar"/>
</form>
</html>
Formulários e Requisições (Variáveis)
● $_POST: é um array de variáveis com os valores enviados via HTTP POST
como, por exemplo, o utilizado por formulários HTML.
● $_GET: é um array de variáveis com os parâmetros da URL, também
conhecido como “query string”.
● $_REQUEST: por default possui o conteúdo de $_GET, $_POST (e
$_COOKIE também).
● $_FILES: um array com as informações de arquivos enviados via HTTP
POST (utilizar o “multipart/form-data” no enctype do formulário)
Entrada de Dados (GET)
http://localhost/curso/get/index.php?nome=Maria&idade=33

URL campo valor

atribuição separador de
de valor campos

Separador de URL e Query String


Formulário (Array como parâmetro)
sanduba_formulario.php
<!DOCTYPE html>
<html>
<head>
<title>Formulário</title><meta charset="utf-8" /> sanduba.php
</head> <?php
<body>
<form action="sanduba.php" method="post"> foreach($_REQUEST['adicionais'] as $adicional){
echo $adicional."<br/>";
Quais adicionais você deseja?<br />
}
<input type="checkbox" name="adicionais[]" value="queijo" />Queijo<br />
<input type="checkbox" name="adicionais[]" value="bacon" />Bacon<br />
<input type="checkbox" name="adicionais[]" value="ovo" />Ovo<br />
<input type="submit"/>
</form>
</body>
</html>
Formulários (upload - $_FILES e multipart/form-data)
email_com_anexo.php
<!DOCTYPE html>
<html> <?php
<head>
$diretorio = "tmp".DIRECTORY_SEPARATOR;
<title>Formulário com upload</title>
<meta charset="utf-8" /> foreach($_FILES as $arquivo){
</head> $nome = $arquivo['name'];
<body> $conteudo = file_get_contents($arquivo['tmp_name']);
//salvando no disco
<form action="email_com_anexo.php"
file_put_contents($diretorio.$nome, $conteudo);
method="post" enctype="multipart/form-data"> }
Name: <input type="text" name="nome"><br>
E-mail: <input type="email" name="email"><br>
Anexo: <input type="file" name="anexo"><br>
<input type="submit"/>
</form>
</body>
</html>
Cookie
<!DOCTYPE html> Um cookie é um pequeno arquivo que o servidor
<?php incorpora no computador (navegador) do usuário.
$cookie_name = "usuario";
Cada vez que o mesmo computador solicita uma
$cookie_value = "João da Silva";
setcookie($cookie_name, $cookie_value, mktime(24), "/"); página com um navegador, ele também enviará o
?> cookie.
<html>
<body>
Com o PHP, é possível criar e recuperar valores de
<?php
if (!isset($_COOKIE[$cookie_name])) { cookie. O nome do cookie é automaticamente
echo "Cookie (" . $cookie_name . ") sem valor"; atribuído a uma variável com o mesmo nome.
} else {
echo "Cookie '" . $cookie_name . "' com valor!<br>"; A função setcookie() cria o cookie enquanto a
echo "valor atual é: " . $_COOKIE[$cookie_name];
} variável global $_COOKIE acessa os valores.
?>
</body> O cookie pode ser substituído por soluções mais
</html> modernas como o localstorage do Javascript.
Session
<?php session_start(); ?> Session (sessão) é uma maneira de
<!DOCTYPE html>
<html>
armazenar informações temporariamente
<body> que podem ser utilizadas em várias
<?php páginas da aplicação;
$_SESSION["usuario"] = "admin";
$_SESSION["senha"] = "1234"; Ao contrário de um cookie, as
?>
</body> informações não são armazenadas no
</html> computador do usuário e sim no servidor.

Apesar da informação (dado) estar no


<?php servidor, um pequeno cookie é criado no
session_start();
navegador do usuário para vincular a este
if(isset($_SESSION['usuario'])){ dado(s) na sessão.
echo "Seja bem vindo ! {$_SESSION['usuario']} !";
}else{ É possível destruir a sessão utilizando a
header("Location: index.php");
função: session_destroy();
}
PHP Assíncrono (PHP + Javascript)
● Com o fluxo tradicional “síncrono” do HTTP, faz-se necessário “atualizar/carregar” a
página toda vez que um informação é enviada do cliente para o servidor (ou o inverso),
o que, em muitas situações, pode prejudicar a interação do usuário com a aplicação.

● Com o advento do Ajax em 1999, os apps web tornaram-se capaz de enviar e


recuperar dados do servidor de forma assíncrona (em segundo plano) sem interferir na
exibição e no comportamento da página existente. Ao separar a camada de troca de
dados da camada de apresentação, o Ajax permite que uma página web possa alterar
seu conteúdo dinamicamente sem a necessidade de recarregar a página inteira.

● A partir de 2017, uma nova alternativa surgiu ao Ajax: o fetch, que permite que
requisições assíncronas possam ser feitas usando o conceito de promisses.
PHP Assíncrono (Ajax - XMLHttpRequest)
index.php
Com o uso do XMLHttpRequest do Javascript é
<!DOCTYPE html>
possível recuperar enviar dados e receber
<html lang="pt-BR">
<head> conteúdos de documentos web de forma
<title>Teste de Ajax</title> assíncrona.
<script src="curso.js"></script>
</head> curso.js
<body>
function meuAjax() {
<div id="mensagem"></div>
var xhr = new XMLHttpRequest();
<button onclick="meuAjax()">Ver mensagem</button>
xhr.open('GET', 'meu_ajax.php?nome=Maria');
</body>
xhr.onload = function () {
</html>
if (xhr.status === 200) {
document.getElementById('mensagem')
.innerHTML = xhr.responseText;
meu_ajax.php
} else {
<?php alert('Erro! Status: ' + xhr.status);
}
echo "Meu nome é ".$_REQUEST['nome']; };
echo " ".(new DateTime())->format('h:i:s'); xhr.send();
}
PHP Assíncrono (Fetch JS)
index.php
<!DOCTYPE html> A partir de 2017, o fetch permite que requisições
<html lang="pt-BR">
assíncronas possam ser feitas usando o conceito
<head>
<title>Teste de Ajax</title> de promisses.
<script src="curso.js"></script>
</head>
<body> curso.js
<div id="mensagem"></div> function meuFetch() {
<button onclick="meuFetch()">Ver mensagem</button>
</body> window.fetch("meu_fetch.php?nome=Maria")
</html> .then(response => response.text())
.then(data => {
document.getElementById('mensagem')
meu_fetch.php .innerHTML = data;
})
<?php
.catch(error => alert('Erro!' + error));
echo "Meu nome é ".$_REQUEST['nome']; }
echo " ".(new DateTime())->format('h:i:s');
PHP Assíncrono (FormData + Fetch JS)
index.php curso.js

<!DOCTYPE html> function meuFormData(event) {


<html lang="pt-BR"> event.preventDefault();
<head> const formData = new FormData(this);
<title>Formulário Assíncrono</title> window.fetch(this.getAttribute("action"), {
<script src="curso.js"></script> method: 'post',
</head> body: formData
<body> }).then(function (response) {
<form id="meu_form" action="meu_form_data.php"> return response.text();
<label>Nome:</label> }).then(function (text) {
<input type="text" name="nome"/> document.getElementById('mensagem')
<label>Email:</label> .innerHTML = text;
<input type="email" name="email"/> });
<input type="submit" value="Cadastrar"/> }
</form>
<div id="mensagem"></div>
<script> meu_form_data.php
const form = document.getElementById('meu_form');
form.addEventListener('submit', meuFormData); <?php
</script>
</body> echo "Email {$_REQUEST['email']} cadastrado com
</html> sucesso !";
PHP Assíncrono (Upload Múltiplo com Fetch)
index.php curso.js
<!DOCTYPE html> function meuFormData(event) {
<html lang="pt-BR"> event.preventDefault();
<head> <title>Formulário Assíncrono</title> const formData = new FormData(this);
<script src="curso.js"></script> </head>
<body> var fileInput = document.querySelector('input[type="file"]');
<form id="meu_form" action="meu_form_data.php" formData.delete('anexo');
enctype="multipart/form-data"> for (var i=0; i < fileInput.files.length; i++){
<label>Nome:</label> formData.append('anexo'+i, fileInput.files.item(i));
<input type="text" name="nome"/> }
<label>Email:</label>
<input type="email" name="email"/> window.fetch(this.getAttribute("action"), {
<input type="file" name="anexo" multiple="multiple"/> method: 'post',
<input type="submit" value="Cadastrar"/> body: formData
</form> }).then(function (response) {
<div id="mensagem"></div> return response.text();
<script> }).then(function (text) {
const form = document.getElementById('meu_form'); document.getElementById('mensagem')
form.addEventListener('submit', meuFormData); .innerHTML = text;
</script> });
</body> }
</html>
PHP Assíncrono (Upload Múltiplo com Fetch)
meu_form_data.php

<?php

echo "Email {$_REQUEST['email']} cadastrado com sucesso ! <br/>";

$diretorio = "anexos".DIRECTORY_SEPARATOR;

foreach($_FILES as $arquivo){
$nome = $arquivo['name'];
$conteudo = file_get_contents($arquivo['tmp_name']);
file_put_contents($diretorio.$nome, $conteudo);
if(file_exists($diretorio.$nome))
echo "Arquivo {$arquivo['name']} salvo com sucesso ! <br/>";
}
PHP Assíncrono (JSON + <select>)
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Formulário Assíncrono</title> <script src="curso.js"></script>
</head>
<body>
<form>
<label>Região:</label>
<select id="regioes">
index.php
<option value="">Selecione...</option>
<option value="centro-oeste">Centro Oeste</option>
<option value="sul">Sul</option>
</select>
<label>Estado:</label>
<select id="estados"></select>
</form>
<script>
const select = document.getElementById('regioes');
select.addEventListener('change', selectEstados.bind(this,'regioes', 'estados'), false);
</script>
</body>
</html>
PHP Assíncrono (JSON + <select>)
function selectEstados(fonte_id, alvo_id) {

fonte = document.getElementById(fonte_id);
alvo = document.getElementById(alvo_id);
alvo.length = 0;

let regiao_selecionada = fonte.options[fonte.selectedIndex].value;


if (regiao_selecionada == '') curso.js
return;
window.fetch("estados.php?regiao_selecionada=" + regiao_selecionada)
.then(response => response.json())
.then(data => {
for (var i = 0; i < data.length; i++) {
var option = document.createElement("option");
option.innerHTML = data[i].nome;
option.value = data[i].id;
alvo.options.add(option);
}
})
.catch(error => alert('Erro!' + error));
}
PHP Assíncrono (JSON + <select>)
<?php

header('Content-Type: application/json');

$regiao_selecionada = $_REQUEST['regiao_selecionada'];
$estados = [
'centro-oeste' => [
['id'=>'DF', 'nome' => 'Distrito Federal'],
estados.php
['id'=>'GO', 'nome' => 'Goiás'],
['id'=>'MT', 'nome' => 'Mato Grosso'],
['id'=>'MS', 'nome' => 'Mato Grosso do Sul']
],
'sul' => [
['id'=>'PR', 'nome'=> 'Paraná'],
['id'=>'RS', 'nome'=> 'Rio Grande do Sul'],
['id'=>'SC', 'nome'=> 'Santa Catarina']
]
];

echo json_encode($estados[$regiao_selecionada]);
Tratamento de Exceções/Erros
● O tratamento de exceção é usada para alterar o fluxo normal da execução de
código se ocorrer uma condição de erro específico (excepcional). Essa
condição é chamada de exceção.
● No PHP 7>= existem dois principais tipos de erros: os throwables e o não
throwables.
● Os throwables podem ser lançados via throw e tratados via try/catch. Nesta
categoria existem dois sub tipos: Exceptions e Errors.
● Os não throwables podem, em alguns casos, ser suprimidos via @,
error_reporting ou tratados utilizando o set_error_handler()
Taxonomia de Erros e Exceções (throwables)

Com o advento do PHP 7, foi


definido uma Interface
chamada Throwable, que se
tornou a interface base para
qualquer objeto “lançável”,
isto é, objetos passíveis de
serem disparados via throw
e/ou capturados via catch.

FONTE:
http://asika.windspeaker.co/post/3503-php-exceptions
Manipulação de Throwables
Bloco de código em
<?php TRY

try { código
Bloco Sim lança
// código aqui uma
CATCH Exceção/
} Error ?
catch (\Throwable $t) {
Não
echo $t->getMessage();
} Bloco FINALLY
Throwable
<?php Utilizando a Interface base como
// "capturando" exceptions typehint do catch, é possível capturar
try {
tanto as Exceptions como os Errors.
throw new Exception('exception');
} catch (Exception $e) {
echo('capturando exception: '.$e->getMessage().PHP_EOL);
}
// capturando errors (PHP 7>=)
try {
$obj = new \StdClass();
$obj->not_a_method();
} catch (Error $e) {
echo('capturando error: '. $e->getMessage().PHP_EOL);
}
// ambos (Exception e Error)
try {
throw new Exception('exception');
} catch (Throwable $e) {
echo('capturando Throwable: '. $e->getMessage().PHP_EOL);
}
Tratamento de Throwables (Finally [PHP 5.5>=])
<?php
try {
print "bloco de código";
Com o advento do PHP 5.5, tornou-se
possível colocar uma condição final que
} catch (\Throwable $t) { será executada tanto no caso da ocorrência
de uma Exception/Error quanto no caso de
print "Erro!"; sucesso das instruções do bloco try.

} finally {
print "Terminou!";
}
Lançamento de Throwables (throw new)
<?php Com o uso do operador throw seguido de
uma instância de Throwable é possível
function escolhaMcOferta (int $opcao) : ?string { “lançar” uma exceção/erro para que o
usuário de uma função/método possa
receber as informações necessárias do
$ofertas = ['bic mac', 'mc cheddar', erro e tratá-lo da melhor maneira possível;
'quarteirão', 'mc fish', 'mc chicken'];
Se o desenvolvedor tentar usar uma
if (!in_array($opcao, range(1,5))) { instância de um objeto que não
implementa throwable ocorrerá um erro
throw new OutOfRangeException("Oferta inválida");
fatal do tipo “Uncaught Error” descrevendo
} que o objeto não implementa a interface;
return $ofertas[--$opcao];
}

echo escolhaMcOferta(6);
Tratamento de Throwables (múltiplos)

<?php

function teste($x):int{
return $x;
}

try {
É possível especificar diferentes
intdiv(10,0);
tratamentos de Exceptions/Errors
teste('ss');
através do aninhamento de catchs
}
catch(DivisionByZeroError $e){
echo "não pode fazer essa divisão";
}
catch(TypeError $e){
echo "Erro de tipo!";
}
Non-capturing catches (PHP 8.0 >=)
<?php ● A partir do PHP 8.0 a definição de
uma variável para receber a
function teste($x) : int {
return $x; referência para o possível
} throwable levantado dentro do
bloco try, tornou-se opcional;
try {
teste('ss');

}
catch(TypeError){
echo "Erro de tipo!";
}
Tratamento de Throwables (grupo com pipe)

<?php

function teste($x):int{
return $x;
}
Com o advento do PHP 7.1 é possível
especificar um único tratamento para
try { um grupo de diferentes tratamentos de
intdiv(10,0); Exceptions/Errors utilizando o operador
pipe “I”;
teste('ss');
}
catch(DivisionByZeroError | TypeError $e){
echo "Erro de divisão ou de tipo";
}
Criando Exceções/Erros customizados (extends Exception)

<?php
class MeuException extends Exception {
public function __construct($message = null, $code = 0){
parent::__construct($message, $code);
file_put_contents('/tmp/log.txt',
$this->getTraceAsString().PHP_EOL ,
FILE_APPEND | LOCK_EX); É possível criar um Throwable customizado
} com uma especialização de Exception ou
}
Error. (Pois não é possível implementar a
function legal($x){ interface Throwable).
if($x == 0){
throw new MeuException();
}
}

try{
legal(0);
}catch(Exception $e){
print('erro');
}
Principais Tipos de Erros (não Throwable)
Tipo Constante Significado Interrompe a Pode ser Tempo
execução do suprimido de:
script? com @?

NOTICE E_NOTICE Aviso para indica que o script encontrou alguma NÃO SIM Execução
coisa que pode indicar um erro;

WARNING E_WARNING Erro não fatal; NÃO SIM Execução

DEPRECATED E_DEPRECATED Aviso de um recurso depreciado e será NÃO SIM Execução


futuramente removido nas próximas versões do
PHP.

ERROR E_ERROR Erro fatal em tempo de execução. Estes indicam SIM NÃO Execução
erros que não podem ser recuperados.

PARSER E_PARSER Erros gerados pelo interpretador devido a erro SIM NÃO Compilação
ERROR de sintaxe no script
Operador de controle de erro (supressão @)
<?php
O PHP suporta um operador de controle
echo @(10 / 0);
// suprimiu "Warning: Division by zero" de erro: o sinal 'arroba' (@).
$c = @$_POST["nome"] . @$_POST["sobrenome"];
// suprimiu "Notice: Undefined index: nome" Com uso do @ é possível suprimir um
// suprimiu "Notice: Undefined index: sobrenome"
@$newfunc = create_function('$a', 'return;');
notice ou um erro não fatal.
// suprimiu "Deprecated: Function create_function() is
deprecated"
@ $i / 0;
// suprimiu "Notice: Undefined variable: i"
// não suprimiu o "Warning: Division by zero"

$c = @funcaoNaoExiste(); //não suprimiu erro fatal


echo 'fim';
Controle de nível de erros não throwables
<?php Com o uso da função error_reporting()
podemos controlar, em tempo de
// Desligando todos os avisos de erros
execução quais avisos de erros o PHP
error_reporting(0);
poderá imprimir na tela.
// Ligando apenas para warning
error_reporting(E_WARNING); Essas mesmas constantes podem ser
setadas de uma maneira global no
// com o uso do pipe | é possível criar uma lista php.ini.
// fixa de avisos para erros
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// E_ALL é o equivalente a todos os tipos


error_reporting(E_ALL);

// Com o uso do ^ (not) é possível remover


// um item da lista (todos menos notice)
error_reporting(E_ALL ^ E_NOTICE);
set_error_handler() e restore_error_handler()
<?php Com o uso do set_error_handler() é possível
set_error_handler("manipuladorCustomizadoDeErros"); criar um manipulador customizado de erros
não throwables.
function manipuladorCustomizadoDeErros
($severity, $mensagem, $arquivo, $linha) {
if (error_reporting() & $severity) { É possível, inclusive, lançar uma exceção
throw new Exception($mensagem, 0); dentro de um manipulador customizado
} permitindo assim que erro possa ser tratado
} com um throwable, podendo ser capturado
via catch.
$array = ['maria','josé'];

try { O ideal é sempre no final do bloco de


$b = $array[2]; instruções restaurar o manipulador original
} catch (Exception $e) { do programa com o restore_error_handler().
echo "Posição não encontrada !";
} finally {
restore_error_handler();
}
Serialização
● É o processo de traduzir estruturas de dados ou estado de objeto em um
formato que pode ser armazenado (por exemplo, em um arquivo ou buffer de
memória) ou transmitido e reconstruído posteriormente (possivelmente em
um ambiente de computador diferente).
● Quando a série resultante de bits é relida de acordo com o formato de
serialização, ela pode ser usada para criar um clone semanticamente idêntico
do objeto original.
● Esse processo de serializar um objeto também é chamado de marshalling
(empacotamento de um objeto).
● A operação oposta, extraindo uma estrutura de dados de uma série de bytes,
é a desserialização (também chamada de unmarshalling).
Serialização (serialization)
Aluno.php Turma.php
<?php <?php

class Aluno { class Turma {

public $nome; public $nome;


public $matricula; public $data;
public $alunos;
public function __construct(string
$nome, int $matricula) { public function __construct(string
$this->nome = $nome; $nome, \DateTime $data, array $alunos = []) {
$this->matricula = $matricula; $this->nome = $nome;
} $this->data = $data;
} $this->alunos = $alunos;
}
}
Serialização (escrita)
<?php
include_once 'Turma.php';
include_once 'Aluno.php';
A função serialize converte toda estrutura
$turmas = [];
de arrays e objetos em uma string utilizando
$aluno1 = new Aluno('José', 123);
$aluno2 = new Aluno('Maria', 456);
um formato passível de recuperação pelo
$aluno3 = new Aluno('Thiago', 789); próprio PHP.

$turmas[] = new Turma('PHP', Já a função file_put_contents persiste


new \DateTime('today'), [$aluno1, $aluno2]); uma string em um arquivo texto.
$turmas[] = new Turma('CakePHP',
new \DateTime('-2 days'), [$aluno1, $aluno2, $aluno3]); Se as classes não forem carregadas o PHP
$turmas[] = new Turma('MySQL', irá gerar instâncias de
new \DateTime('yesterday'), [$aluno1, $aluno3]); __PHP_Incomplete_Class.

$serializacao = serialize($turmas);
file_put_contents('dados.db', $serializacao);
Desserialização (leitura)
<?php
include 'Turma.php';
include 'Aluno.php';

$serializacao = file_get_contents('dados.db');
$turmas = unserialize($serializacao); A função file_get_contents recupera o
conteúdo de um arquivo texto em uma
echo "<table border>"; string.
foreach ($turmas as $turma) {
echo "<tr>"; A função unserialize interpreta o conteúdo
echo "<td> {$turma->nome} </td>"; serializado em uma string e reconstrói os
echo "<td> {$turma->data->format('d/m/Y')} </td>"; objetos e arrays.
echo "<td>". implode(", ",
array_column($turma->alunos, 'nome'))."</td>";
echo "</tr>";
}
echo "</table>";
PHP e Bancos de Dados Relacionais
● Até o PHP 5.0, era necessário utilizar extensões PECL com conjuntos diferentes de funções
para cada acessar cada banco de dados (ex.: mysql_connect, mssql_connect etc…).

● Com o advento do PHP 5.1 surgiu o PDO (PHP Data Objects): uma camada de abstração de
acesso a banco de dados (DBAL). Uma DBAL (Database abstraction layer) é uma API que visa
unificar a comunicação entre um aplicações e bancos de dados.

● As DBALs reduzem a quantidade de trabalho para acessar diferentes bancos de dados


fornecendo uma API consistente ao desenvolvedor ocultando o máximo possível as
especificidades do banco de dados por trás dessa interface.

● O PDO fornece uma DBAL, o que significa que, independentemente de qual banco de dados, o
desenvolvedor utiliza as mesmas funções para emitir consultas e buscar dados. O PDO não
fornece uma abstração de SQL e nem emula os recursos ausentes de um banco de
dados.
Modelo Relacional
● O modelo relacional é uma abordagem para gerenciar dados usando uma
estrutura e linguagem consistente com lógica de predicados de primeira ordem,
descrita pela primeira vez em 1969 pelo cientista inglês Edgar F. Codd, onde os
dados são representados em termos de tuplas (linhas), agrupadas em relações
(tabelas).
● Um banco de dados organizado em termos do modelo relacional é um banco de
dados relacional.
● A maioria dos bancos de dados relacionais utiliza o SQL para definição de dados
e a linguagem de consulta; esses sistemas implementam o que pode ser
considerado como uma aproximação de engenharia ao modelo relacional.
● Além disso, os banco de dados permitem também criar relacionamento entre as
tabelas (entidades) através de chaves estrangeiras.
Modelo Relacional: Tabela, colunas, tipos e chave primária

● Uma tabela é composta de colunas (campos).


● Cada coluna possui um nome e um tipo de dado, ex.:
VARCHAR (string), INT, DOUBLE etc.
● É possível definir também se um campo poderá permitir
valores nulos ou não.
● Para garantir o unicidade de cada tupla (registro) é
possível utilizar o recurso da chave primária onde o
SGBD garante que aquele valor não possa ser repetido
Relacionamentos e restrições de integridade
● Um relacionamento, no contexto de bancos de dados relacionais, é uma situação que
existe entre duas tabelas quando uma possui uma chave estrangeira que faz
referência à chave primária da outra tabela. Os relacionamentos permitem que bancos
de dados relacionais dividam e armazenem dados em diferentes tabelas, enquanto
ligam itens de dados distintos.

● Existem os seguintes tipos de relacionamentos entre tabelas:


○ 1..1 (um para um): onde uma das duas tabelas faz referência para a outra.
○ 1..N (um para muitos): onde o lado N recebe faz referência para o lado um.
○ N..N (muitos para muitos): onde uma terceira tabela (associativa) precisa ser
criada para permitir que está faça referência para as duas tabelas da relação.
○ Auto relacionamento: onde uma tabela faz referência para ela mesma.
Drive PDO
● Um drive/extensão PDO define uma interface leve e consistente para acessar
bancos de dados no PHP. Cada driver de banco de dados que implementa a
interface do PDO pode expor recursos específicos do banco de dados como
funções de extensão regulares.

● Os drivers PDO também são extensões PECL e podem ser habilitadas no


php.ini, caso as mesmas estejam disponíveis em forma de *.dll ou *.so no
diretório de extensões do PHP.
PDO (getAvailableDrivers())

<?php

print_r(PDO::getAvailableDrivers());
O método estático getAvailableDrivers permite
verificar quais os drivers de PDO estão instalados no
PHP.
PDO Drivers

Banco (SGBD) Drive (.dll ou .so) Incluído no pacote PHP Download


Windows? adicional

MySQL php_pdo_mysql SIM -

Postgres php_pdo_pgsql SIM -

SQL Server php_pdo_sqlsrv NÃO Drive ODBC

Oracle php_pdo_oci SIM Instant Client


Classe PDO (instanciando)
<?php

$pdo = new PDO($dsn, $usuario, $senha, $opcoes);

● dsn: conexão com a fonte de banco de dados


● usuario: usuário do banco de dados
● senha: senha deste usuário
● opções: conjunto de opções em forma de chave e valores utilizando um
conjunto de constantes, alguns comuns entre os drivers e outros
específicos de cada um.
PDO Options (opções recomendadas)
Opção Valor Recomendado Resultado Suportado pelos
Drivers

PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION Lança Exceptions toda vez que uma MySQL, Postgres,
instrução SQL falhar. Sql Server e Oracle

PDO::ATTR_EMULATE_PRE false O motor do banco de dados fará o MySQL, Postgres e


PARES prepared statement ao em vez do Oracle
PDO e assim, consulta e os dados
reais são enviados separadamente,
aumentando a segurança.

PDO::ATTR_DEFAULT_FETC PDO :: FETCH_ASSOC É conveniente configurá-lo de forma MySQL, Postgres,


H_MODE global e depois omiti-lo em buscas Sql Server e Oracle
específicas.
DSN (Data Source Name)
● DSN ou data source name (nome de fonte de dados, algumas vezes conhecido como
nome de fonte de banco de dados, apesar de fontes de dados não serem limitadas a
bancos de dados), é uma estrutura de dados usada para descrever uma conexão a
uma fonte de dados.
● Cada Driver possui seu conjunto específico de parâmetros. Exemplos:
○ MYSQL: mysql:host=localhost;dbname=livraria;port=3306;charset=utf8mb4
○ POSTGRES: pgsql:host=localhost;port=5432;dbname=livraria;
○ SQL SERVER: sqlsrv:Server=localhost;Database=livraria
○ ORACLE:
oci:dbname=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT
_DATA=(SERVICE_NAME=LIVRARIA)))

Nome ou IP da máquina onde o Banco (SGGBD) está executando.


Nome da base onde as tabelas se encontram.
Porta de conexão do SGBD
PDO (Conexão MySQL)
conexao.php
<?php
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false, //para funcionar bind no limit
];

$servidor = "localhost";
$banco = "livraria";
$usuario = "root";
$senha = '';
$porta = 3306;
$dsn = "mysql:host=$servidor;port=$porta;dbname=$banco;charset=utf8";

$pdo = new PDO($dsn, $usuario, $senha, $options);


PDO (Conexão Postgres)
conexao_postgres.php
<?php

$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];

$servidor = "localhost";
$banco = "livraria";
$usuario = "postgres";
$senha = "admin";
$porta = 5432;
$dsn = "pgsql:host=$servidor;port=$porta;dbname=$banco;";

$pdo = new PDO($dsn, $usuario, $senha, $options);


PDO (Conexão SQL Server)
conexao_sqlserver.php
<?php

$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //ver erros de query
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];

$servidor = "localhost";
$banco = "livraria";
$usuario = "85bits";
$senha = "admin";
$dsn = "sqlsrv:Server=$servidor;Database=$banco";

$pdo = new PDO($dsn, $usuario, $senha, $options);


PDO (Conexão Oracle)
conexao_oracle.php
<?php
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_CASE => PDO::CASE_LOWER
];

$servidor = "localhost";
$usuario = "php";
$senha = "admin";
$service_name = "XE";
$sid = "XE";
$port = 1521;
$dbtns = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = $servidor)(PORT = $port)) (CONNECT_DATA =
(SERVICE_NAME = $service_name) (SID = $sid)))";

$pdo = new PDO("oci:dbname=" . $dbtns . ";charset=utf8", $usuario, $senha, $options);


Modelo de Banco de Dados Livraria
● Para os exemplos deste curso utilizaremos um banco de dados do domínio de uma Livraria.
O banco está disponível para 4 SGBDS diferentes e já conta com um número considerável de
registros nas tabelas.

● Além disso, este banco possui todos os tipos de associações:


a. (1..1): Funcionário tem uma Habilitação.
b. (1..N): Editora tem muitos Livros.
c. (N..1): Livros pertencem a uma Editora.
d. (N..N): Livros têm muitos Autores e Autores têm muitos Livros.
● E algumas Associações especiais:
a. Auto relacionamento: Funcionário tem gerente Gerente (Funcionário).
b. N..N com dados associativos: Pedido tem muitos Livros (através de ItemPedido e seus
dados).
c. Árvore: Gênero tem nós filhos, irmãos e pais.
Modelo de Exemplo (livraria)
SQL (livraria v0.3 - DUMP do Banco de Dados)
Disponível em:

https://gist.github.com/celsowm/9d0ffd735e92dc4fdff854f8847fcd39

https://gist.github.com/celsowm/067fe51dfa612697895c8ec3b5cb436d

https://gist.github.com/celsowm/b139713d65d6c42df084269b3f150a2d

https://gist.github.com/celsowm/219c130a18289b9378fa7642508c473b
PDO Query (PDO->query() e PDOStatement->fetch())

<?php O método query permite


submeter uma query SQL para o
include_once “conexao.php”; SGBD e retorna um objeto do tipo
PDOStatement.
$statement = $pdo->query("SELECT nome FROM funcionario");
$funcionario = $statement->fetch(); Para recuperar (a
próxima/primeira) linha do
echo $funcionario['nome']; resultado da query podemos
utilizar o método fetch(), que, no
caso do fetch padrão associativo,
retorna um array onde as chaves
são as colunas e os valores de
cada linha os valores do array.
PDO Query (PDO->query() e PDOStatement->fetch())

<?php Podemos iterar o resultado de


uma query invocando o método
include_once “conexao.php”; fetch até o mesmo retornar nulo,
isto é, até esgotar o número de
$statement = $pdo->query("SELECT nome FROM funcionario"); registros.
while($funcionario = $statement->fetch()){
echo $funcionario['nome']."</br>";
}
PDO Query (Transversable)

<?php
A classe PDOStatement
include_once “conexao.php”; implementa Traversable, o
que permite que objetos
$statement = $pdo->query('SELECT nome FROM funcionario'); desta classe possam ser
foreach ($statement as $linha){ iterados de forma
echo $linha['nome'] . "<br>"; transparente.
}
SQL Injection
"SQL Injection" é um subconjunto da vulnerabilidade de entrada do usuário não Exemplo:
verificada / não-tratada cujo a propósito é forçar o aplicativo a executar um código ' or '1'='1
SQL que não foi planejado.

<!DOCTYPE html> <?php


<html> include_once '../conexao.php';
<head>
<title>Login</title> $login = $_REQUEST['login'];
<meta charset="UTF-8"> $senha = $_REQUEST['senha'];
</head>
<body> $query = "SELECT * FROM usuario WHERE login = '$login' AND senha =
<form action="logar.php"> '$senha'";
<label>login:</label> //var_dump($query);
<input type="text" name="login" />
<label>senha:</label> $statement = $pdo->query($query);
<input type="password" name="senha" /> $usuario = $statement->fetch();
<input type="submit" value="logar" /> if($usuario){
</form> echo "Usuário {$usuario['login']} logado com sucesso !";
</body> }
</html>
Prepared Statements
● Para evitar SQL Injection podemos utilizar o recurso de prepared statement
(declaração/instrução preparada/parametrizada).
● Os prepared statements são resilientes à SQL injections porque os valores que
são transmitidos posteriormente usando um protocolo diferente e não são
compilados/interpretados com o código SQL original.
● Para utilizar este recurso com o PDO devemos utilizar o método prepare() com a
query desejada substituindo os valores por placeholders (caracteres substitutos).
● Antes de recuperar os dados (fetch) faz-se necessário vincular os valores aos
seus respectivos placeholders e executar (execute).
● Os Drivers PDO podem utilizar prepared statements de forma nativa (quando
suportado) ou emulados pelo PDO (PDO::ATTR_EMULATE_PREPARES)
PDO Binding (usando parâmetros no SQL)
include_once “conexao.php”;

$nome = 'Edson Wander';


$cpf = '54698715324';

//posicional
$statement = $pdo->prepare('SELECT * FROM funcionario WHERE nome = ? AND cpf = ?');
$statement->execute([$nome, $cpf]);
$funcionario = $statement->fetch();

//usando key e value


$statement = $pdo->prepare('SELECT * FROM funcionario WHERE email = :email AND status=:status');
$statement->execute([nome => $nome, 'cpf' => $cpf]);
$funcionario = $statement->fetch();
Prepared Statement (evitando SQL Injection)
<!DOCTYPE html> <?php
<html> include_once '../conexao.php';
<head>
<title>Login</title> $login = $_REQUEST['login'];
<meta charset="UTF-8"> $senha = $_REQUEST['senha'];
</head>
<body> $query = "SELECT * FROM usuario WHERE login = ? AND
<form action="logar_prepared.php"> senha = ? ";
<label>login:</label> //var_dump($query);
<input type="text" name="login" />
<label>senha:</label> $statement = $pdo->prepare($query);
<input type="password" $statement->execute([$login, $senha]);
name="senha" /> $usuario = $statement->fetch();
<input type="submit" value="logar" /> if($usuario){
</form> echo "Usuário {$usuario['login']} logado com sucesso !";
</body> }
</html>
PDO Binding (bindParam)
<?php Podemos utilizar o
método bindParam para
include 'conexao.php'; passar por referência o
valor de variáveis para
$nome = 'Edson Wander'; uma prepared statement.
$cpf = '54698715324';
No primeiro parâmetro
especificamos o nome ou
$stmt = $pdo->prepare('SELECT * FROM funcionario WHERE nome = ? AND
posição do placeholder;
cpf = ?'); no segundo, a variável
$stmt->bindParam(1, $nome,PDO::PARAM_STR); de referência e no
$stmt->bindParam(2, $cpf,PDO::PARAM_STR); terceiro o tipo desejado
$stmt->execute(); do valor (usando
$funcionario = $stmt->fetch(); constantes do PDO).

var_dump($funcionario);
PDO Binding (bindValue)

<?php Podemos utilizar o


método bindValue para
include 'conexao.php'; passar por um valor de
variáveis para uma
$stmt2 = $pdo->prepare('SELECT * FROM funcionario WHERE nome = ? AND prepared statement.
cpf = ?');
No primeiro parâmetro
$stmt2->bindValue(1, 'Edson Wander',PDO::PARAM_STR);
especificamos o nome ou
$stmt2->bindValue(2, '54698715324',PDO::PARAM_STR);
posição do placeholder;
$stmt2->execute(); no segundo, a variável
$funcionario2 = $stmt2->fetch(); de referência e no
terceiro o tipo desejado
var_dump($funcionario2); do valor (usando
constantes do PDO).
PDO (bindColumn)

<?php Com o uso do bindColumn é possível


passar por referência o valor de uma
include 'conexao.php'; coluna para uma variável para cada fetch
realizado.
$query = "SELECT id, nome, cpf FROM funcionario";
$statement = $pdo->query($query); A indicação da coluna pode ser feita de
$statement->bindColumn(1, $id); maneira posicional ou pelo nome da
$statement->bindColumn(2, $nome); coluna.
$statement->bindColumn('cpf', $cpf);
O ideal é utilizar sempre o estilo
while ($row = $statement->fetch(PDO::FETCH_BOUND)) { PDO::FETCH_BOUND que permite que o
echo "$id:" . $nome . " " . $cpf . "</br>"; PDO possa designar valores para
} variáveis que foram “vinculadas”
anteriormente usando bindColumn.
PDO Prepared Statement (com SQL like)

<?php O operador like do SQL, que permite


procurar por um determinado “padrão” em
include 'conexao.php'; um texto, pode ser utilizado também como
prepared statement.
$sql = "SELECT * FROM livro WHERE titulo LIKE ?";
$statement = $pdo->prepare($sql); Só é válido salientar que os operadores
$statement->execute(['%do%']); curingas precisam ser utilizados “fora” da
foreach($statement as $livro){ query, isto é, precisam ser enviados como
echo $livro['titulo']."</br>"; valores para os binds.
}
PDO Prepared Statement (com SQL IN() “literal” )
<?php Infelizmente o uso de
include 'conexao.php'; array como placeholders
não é suportado
$filtro = ["preco_minimo" => "1.98"];
nativamente no PDO,
$edicoes = [1,2,10];
então, faz-se necessário
$edicoes = array_combine( “replicar” um conjunto de
array_map(function($i){ return ':id'.$i; }, array_keys($edicoes)), placeholders que possa
$edicoes representar cada valor do
); conjunto do IN().
$in_placeholders = implode(',', array_keys($edicoes));
$sql = "SELECT * FROM livro WHERE preco >= :preco_minimo AND edicao IN É válido salientar que
($in_placeholders)";
muitos SGBDs possuem
$statement = $pdo->prepare($sql);
limitação no número de
$statement->execute(array_merge($filtro,$edicoes));
foreach($statement as $livro){ valores literais em um IN.
echo $livro['titulo']."</br>";
}
PDO (fetchCollumn)

<?php

include_once “conexao.php”;

//PDO fetchColumn
$statement = $pdo->query("SELECT id, titulo FROM livro"); O método fetchCollumn retorna uma
var_dump($statement->fetchColumn()); única coluna da próxima linha de um
var_dump($statement->fetchColumn(1)); conjunto de resultados ou FALSE se
não houver mais linhas.
PDO getColumnData (Introspecção)

<?php O método getColumnData permite


que recuperar os metadados dos
include 'conexao.php'; campos (colunas) de uma query
(inclusive campos virtuais).
$statement = $pdo->query('SELECT titulo, preco FROM livro');
$metadados = $statement->getColumnMeta(0); O parâmetro do método é a
posição da coluna em relação do
echo "<pre>"; descrito na query.
var_dump($metadados);
echo "</pre>"; Infelizmente o drive do Oracle
(pdo_oci) não suporta/implementa
este método.
PDO FetchAll
● PDOStatement::fetchAll() retorna um array contendo todas as linhas
restantes no conjunto de resultados. O array representa cada linha como
uma matriz de valores de coluna ou um objeto com propriedades
correspondentes a cada nome de coluna.
● Uma array vazio é retornado se houver zero resultados a serem obtidos ou
retorna FALSE em caso de falha.
● Usar esse método para buscar conjuntos de resultados grandes resultará em
uma grande demanda no sistema e possivelmente nos recursos da rede. Em
vez de recuperar todos os dados e manipulá-los no PHP, considere o uso do
servidor de banco de dados para manipular os conjuntos de resultados. Por
exemplo, use as cláusulas WHERE ou Paginação no SQL para restringir os
resultados antes de recuperá-los e processá-los com o PHP.
FetchAll (exemplo)

<?php

include_once “conexao.php”;

$statement = $pdo->query('SELECT * FROM funcionario');


$funcionarios = $statement->fetchAll();
foreach ($funcionarios as $funcionario) { O resultado do fetchAll() é um
echo $funcionario['nome']."</br>"; array com o resultado da query.
}
Fetch & Fetch All (estilos)
● Ao utilizar os métodos fetch ou fetchAll de um PDOStatement é possível
determinar qual o “formato” dos dados resultantes da query.

● Alguns estilos possuem argumentos (similar a uma função). E os


valores destes argumentos influenciam na formatação dos dados.

● O estilo dos fetchs podem ser determinados das seguintes maneiras :


○ De maneira global no options do PDO (PDO::ATTR_DEFAULT_FETCH_MODE)
○ Através do segundo parâmetro dos métodos fetch e fetchAll do PDOStatement
○ Utilizando o método setFetchMode() em cada PDOStatement antes do
fetch/fetch/execute.
PDO::FETCH_ASSOC

<?php

include 'conexao.php';
PDO::FETCH_ASSOC:
$statement = $pdo->query('SELECT nome FROM funcionario'); retorna um array indexado
$funcionarios = $statement->fetchAll(PDO::FETCH_ASSOC);
foreach ($funcionarios as $funcionario) { pelo nome da coluna
echo $funcionario['nome']."</br>"; conforme o retorno da
}
query.
$statement = $pdo->query('SELECT nome FROM funcionario');
$funcionario = $statement->fetch(PDO::FETCH_ASSOC);
echo $funcionario['nome']."</br>";
PDO::FETCH_NUM

<?php
PDO::FETCH_NUM:
include 'conexao.php'; retorna um array 2D
$statement = $pdo->query("SELECT nome, id FROM funcionario");
onde as chaves
//$statement = $pdo->query("SELECT titulo, id FROM livro"); assumem as posições
$dados = $statement->fetchAll(PDO::FETCH_NUM);
foreach ($dados as $dado) {
das colunas e os
echo "nome: $dado[0] | id: $dado[1] <br/>"; valores são os dados
}
de cada registro nesta
coluna.
PDO::FETCH_BOTH

<?php
PDO::FETCH_BOTH:
include 'conexao.php'; retorna um array indexado
$statement = $pdo->query('SELECT * FROM funcionario'); duplicando o número
$funcionario = $statement->fetch(PDO::FETCH_BOTH); colunas onde permitindo
var_dump($funcionario);
que os dados sejam
acessados tanto pelo nome
da coluna como pelo índice
(número) da mesma.
PDO::FETCH_KEY_PAIR

<?php PDO::FETCH_KEY_PAIR:
retorna um array 2D onde
include_once "conexao.php";
as chaves assumem os
valores da primeira coluna
$statement = $pdo->query("SELECT id, nome FROM funcionario");
$dados = $statement->fetchAll(PDO::FETCH_KEY_PAIR); e os valores são os dados
echo "<select>"; da segunda.
foreach($dados as $key => $dado){
echo "<option value='$key'>$dado</option>"; Se uma terceira coluna for
} colocada na projeção, o
echo "</select>"; PDO lançará uma exceção
PDO::FETCH_UNIQUE

<?php
PDO::FETCH_UNIQUE:
include_once “conexao.php”; retorna um array de
arrays onde as chaves
$statement = $pdo->query('SELECT id,nome,cpf FROM funcionario'); assumem os valores da
$funcionarios = $statement->fetchAll(PDO::FETCH_UNIQUE); primeira coluna e os
foreach ($funcionarios as $id => $funcionario) {
valores (com colunas em
echo $id.":".$funcionario['nome']."</br>";
} keys) em subarrays.
PDO::FETCH_NAMED
<?php
PDO::FETCH_NAMED:
retorna um array
$statement = $pdo->query('
SELECT * FROM editora
multidimensional onde campos
LEFT JOIN livro ON livro.editora_id = editora.id'); com nomes repetidos são
$registros = $statement->fetchAll(PDO::FETCH_NAMED);
colocados dentro de um
mesmo array onde, a ordem
foreach ($registros as $registro) {
echo "editora id {$registro['id'][0]} e
dos mesmos é referente a
livro {$registro['id'][1]} <br/>"; ordem de suas respectivas
}
tabelas na própria query.
JOIN SQL
LEFT JOIN: junção de A B, INNER JOIN: junção de A B, RIGHT JOIN: junção de A B,
porém, recupere tudo de A porém recupere apenas se porém, recupere tudo B
mesmo se não houver uma houver registro A e B com mesmo se não houver uma
referência em B (ficando referências. referência em A (ficando
nulo valores para B sem nulo valores para A sem
referência para A). referência para B).
A B A B A B

SELECT * FROM funcionario SELECT * FROM pedido SELECT * FROM livro


LEFT JOIN habilitacao ON INNER JOIN cliente ON RIGHT JOIN editora ON editora.id
funcionario.id = cliente.id = pedido.cliente_id = livro.editora_id
habilitacao.funcionario_id
PDO::FETCH_GROUP
<?php

include 'conexao.php';

$statement = $pdo->query("SELECT edicao, id, titulo PDO::FETCH_GROUP:


FROM livro order by edicao");
retorna os registros
$livrosPorEdicao = $statement->fetchAll(PDO::FETCH_GROUP); agrupados pela primeira
foreach ($livrosPorEdicao as $key => $livros) { coluna da query em arrays
echo "Livros na $key ª edição: </br>"; multidimensionais.
foreach ($livros as $livro) {
echo "-- {$livro['titulo']} </br>";
}
echo "<br/>";
}
PDO::FETCH_LAZY

<?php PDO::FETCH_LAZY: Faz um busca


tardia/lenta/por demanda e retornar a
include_once “conexao.php”; próxima linha como um objeto anônimo com
nomes de colunas como atributos. O
$statement = $pdo->query('SELECT * FROM livro'); desempenho costuma ser notável ao
$livro = $statement->fetch(PDO::FETCH_LAZY); recuperar uma grande massa de dados
echo "ISBN: {$livro['isbn']} : {$livro->titulo} </br>"; (linhas).

Devido à própria natureza de


busca “tardia” (e por demanda)
dos registros do banco de dados,
o FETCH_LAZY não funciona
com o fetchAll().
PDO::FETCH_BOUND

<?php

include 'conexao.php';
PDO::FETCH_BOUND:
$query = "SELECT id, nome, cpf FROM funcionario"; Permite que o PDO possa
$statement = $pdo->query($query); designar valores para variáveis
$statement->bindColumn(1, $id); que foram “vinculadas”
$statement->bindColumn(2, $nome);
anteriormente usando
$statement->bindColumn('cpf', $cpf);
bindColumn.
while ($row = $statement->fetch(PDO::FETCH_BOUND)) {
echo "$id:" . $nome . " " . $cpf . "</br>";
}
PDO::FETCH_COLUMN (Estilos com argumentos)

<?php PDO::FETCH_COLUMN:
include_once "conexao.php";
retorna um array 2D onde
$statement = $pdo->query('SELECT nome, id FROM funcionario'); os valores são os dados de
$nomes = $statement->fetchAll(PDO::FETCH_COLUMN);
uma única coluna e uma
foreach ($nomes as $nome) {
echo $nome."</br>"; chave sequencial.
}
Seu parâmetro é similar o
$statement = $pdo->query('SELECT id, nome, cpf FROM funcionario');
fetchColumn, onde um
$cpfs = $statement->fetchAll(PDO::FETCH_COLUMN, 2);
foreach ($cpfs as $cpf) { inteiro é utilizado para
echo $cpf."</br>"; indicar a coluna desejada.
}
PDO::FETCH_FUNC (Estilos com argumentos)
<?php

include_once “conexao.php”;

$statement = $pdo->query('SELECT preco, titulo FROM livro'); PDO::FETCH_FUNC:


$livros = $statement->fetchAll(PDO::FETCH_FUNC, function($preco, $titulo){ Permite que uma
$preco_no_cartao = round($preco + ($preco * 0.1),2);
return "$titulo: R$ {$preco} à vista e no cartão R$ {$preco_no_cartao}";
função/closure receberá
}); em forma de parâmetros
as colunas da query.
foreach ($livros as $preco) {
echo $preco."</br>";
}
PDO e ORM (Object Relational Mapper)
● Alguns estilos do PDO permitem um mapeamento primitivo entre registros de
uma tabela (modelo relacional) e o instâncias/objetos de uma classe (modelo
orientado a objetos).

● As maiores limitações começam a surgir quando é necessário


representar/mapear relacionamentos entre objetos.

● Para tarefas mais complexas de mapeamento é recomendável utilizar


bibliotecas ORM de terceiros.
PDO (Fetch Class)
Livro.php
<?php O comportamento padrão do
class Livro { FETCH_CLASS é chamar o construtor
public $id; depois de colocar os valores nos atributos.
public $titulo;
private $edicao; }

//recuperando dados
include_once “conexao.php”;
include_once “Livro.php”;

$sth = $pdo->prepare("SELECT * FROM livro");


$sth->execute();

$result = $sth->fetchAll(\PDO::FETCH_CLASS, 'Livro');


print_r($result);
PDO FETCH_CLASS + FETCH_CLASSTYPE

<?php

include_once “conexao.php”; PDO::FETCH_CLASSTYPE:


Combinado com FETCH_CLASS permite
class Autor {} que a primeira coluna da query seja
class Funcionario {}
utilizada para definir qual classe utilizar
$statement = $pdo->query( para instância o registro.
"SELECT 'Autor', nome FROM autor
UNION Pode ser muito útil para o caso de
SELECT 'Funcionario', nome FROM funcionario"); associações polimórficas com simulação
$objeto = $statement->fetchAll(PDO::FETCH_CLASS | de herança no modelo relacional.
PDO::FETCH_CLASSTYPE);
print_r($objeto);
PDO::FETCH_CLASS + PDO::FETCH_PROPS_LATE
<?php <?php
class Funcionario {
class Habilitacao {
public $id;
public $nome;
public $numero;
public $habilitacao;
public $categoria;
public function __construct() {
$this->habilitacao = new Habilitacao(); }
}

public function __set($name, $value) { Podemos utilizar a flag


if (array_key_exists($name, get_object_vars($this->habilitacao))) { FETCH_PROPS_LATE para que o
$this->habilitacao->$name = $value; PDO comece a “colocar” os valores
} else { do objeto após a chamada do método
$this->$name = $value; construtor.
} Com isso podemos decorar nosso
} objeto e mapear um relacionamento
1..1, como neste exemplo.
}
PDO::FETCH_CLASS + PDO::FETCH_PROPS_LATE
include 'conexao.php';
include ‘Funcionario.php’;
include ‘Habilitacao.php’;

$statement = $pdo->prepare(
"SELECT * FROM funcionario "
. "LEFT JOIN habilitacao "
. "ON funcionario.id = habilitacao.funcionario_id");
$statement->execute();

$funcionarios = $statement->fetchAll(\PDO::FETCH_CLASS | \PDO::FETCH_PROPS_LATE,


Funcionario::class);

foreach ($funcionarios as $funcionario) {


echo "{$funcionario->nome} com Habilitação nº {$funcionario->habilitacao->numero} <br/>";
}
PDO::FETCH_OBJ
<?php

include_once "conexao.php";
PDO::FETCH_OBJ:
Retorna os registros em
$statement = $pdo->query('SELECT * FROM livro');
forma de objetos
$livros = $statement->fetchAll(PDO::FETCH_OBJ);
genéricos (instâncias de
foreach ($livros as $livro) {
StdClass).
echo "instância de ".get_class($livro).":";
echo $livro->titulo."</br>";
}
fetchObject
<?php Vale a pena salientar que o fetchObject
cria a instância e seta os valores antes
include 'conexao.php'; mesmo do método construtor ser
executado.
class Livro {

public function __construct($etiqueta = null) {


$this->etiqueta = $etiqueta;
}

$statement = $pdo->query("SELECT * FROM livro");


$livro = $statement->fetchObject(Livro::class, [123456]);
var_dump($livro);
PDO:FETCH_INTO
<?php
class Livro { PDO::FETCH_INTO:
public $titulo; Permite atualizar uma instância existente da
public $preco; classe solicitada, mapeando as colunas do
public $isbn; conjunto da query para os atributos nomeadas
public $edicao;
public $ano_publicacao; na classe.
}

$livro = new Livro();

$statement = $pdo->query('SELECT * FROM livro'); Atenção: Diferente do


$statement->setFetchMode(PDO::FETCH_INTO, $livro); FETCH_CLASS, o FETCH_INTO
$statement->fetch(); não é capaz de modificar valores de
atributos privados ou protegidos
var_dump($livro);
PDO::FETCH_CLASS + PDO::FETCH_SERIALIZE
<?php $statement = $pdo->query('SELECT dados FROM log');
$statement->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE,
include 'conexao.php'; Log::class);
$logs = $statement->fetchAll();
class Log implements \Serializable { foreach ($logs as $log) {
public $id; echo "id: $log->id | ";
public $dados; echo "dados: " . var_export($log->dados, true);
echo "<br/>";
public function serialize() { }
return serialize((array) $this);
}
public function unserialize($serialized): void {
foreach (unserialize($serialized) as $p => $v) { PDO::FETCH_SERIALIZE: Tem efeito similar ao
$this->{$p} = $v;
} FETCH_INTO porém é utiliza os
}
}
comportamentos da serialização (interface) nos
objetos.
PDO (operações com cursor)
● Quando um cursor é criado para uma consulta, é possível iterar sobre o
conjunto de linhas sem obter o resultado inteiro da mesma de uma só vez.
● Com um cursor não rolável (forward-only) é possível efetuar FETCH em cada
linha no máximo uma vez, e o cursor se move automaticamente para a linha
seguinte.
● Já os cursores roláveis (scrollable) permitem iterar o resultado em diversas
direções, inclusive para trás e por isso podem acessar a mesma linha no
conjunto do resultados várias vezes. Assim, modificações de dados (inserir,
atualizar, excluir operações) de outras transações podem ter um impacto no
conjunto de resultados.
PDO Cursores (Exemplo Pedidos)
<?php
include_once '../conexao_postgres.php'; //sqlserver, oracle e postgres
$query = "select * from pedido order by id";
$statement = $pdo->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]);
Em SGBDs que
$statement->execute(); suportam o recurso
de cursor scroll, é
$pedido = $statement->fetch(PDO::FETCH_LAZY, PDO::FETCH_ORI_FIRST); possível, no momento
print "Primeiro pedido (id:{$pedido['id']}) ocorreu em : {$pedido['data']}<br/>"; do método fetch(),
posicionar o cursor
$pedido = $statement->fetch(PDO::FETCH_LAZY, PDO::FETCH_ORI_NEXT);
print "Próximo pedido (id:{$pedido['id']}) ocorreu em : {$pedido['data']}<br/>";
em diferentes pontos
do resultado, bem
$pedido = $statement->fetch(PDO::FETCH_LAZY, PDO::FETCH_ORI_LAST); como movimentá-lo
print "Último pedido (id:{$pedido['id']}) ocorreu em: {$pedido['data']}<br/>"; para frente ou para
trás.
$pedido = $statement->fetch(PDO::FETCH_LAZY, PDO::FETCH_ORI_PRIOR);
print "Penúltimo pedido (id:{$pedido['id']}) ocorreu em: {$pedido['data']}<br/>";
No SQL Server o ABS
$pedido = $statement->fetch(PDO::FETCH_LAZY, PDO::FETCH_ORI_ABS, 3); começa com zero.
print "Terceiro pedido (id:{$pedido['id']}) em: {$pedido['data']}<br/>";
PDO Exception

<?php
O PDO também faz parte da Taxonomia de
include_once 'conexao.php'; Throwable e possui uma Exception com
informações diretamente específicas do
try{ SGBD que podem auxiliar no tratamento do
erro.
$pdo->query('SELECT * FROM nao_existe');

} catch (\PDOException $t) {

echo "mensagem:".$t->getMessage()."<br/>". Atenção: Para que o PDO lance


"código:".$t->getCode(); exceções, é necessário utilizar o
} atributo de conexão
PDO::ATTR_ERRMODE com o valor
PDO::ERRMODE_EXCEPTION.
SQL DML: Inserção, Atualização e Remoção
● Os recursos da linguagem SQL são normalmente divididos em conjuntos
onde cada um destes possui um propósito específico.

● Um destes conjuntos é chamado de DML (Data Manipulation Language /


Linguagem de Manipulação de Dados).

● O DML é composto pelas seguintes instruções (statements): INSERT,


UPDATE e DELETE.

● É fortemente recomendado utilizar prepared statement ao executar qualquer


instrução DML no PDO.
PDO (Inserção)
<?php

include_once “conexao.php”;

try { A instrução
$statement = $pdo->prepare("INSERT INTO funcionario (nome, cpf)” (statement) INSERT
. “VALUES (?,?)"); SQL permite adicionar
$statement>execute([$nome, $cpf]); um ou mais registros
a qualquer tabela
} catch (\PDOException $t) { única em um banco
de dados relacional.
echo "mensagem:".$t->getMessage()."<br/>".
"código:".$t->getCode();
}
PDO (Atualização)
<?php

include_once “conexao.php”; A instrução


(statement) UPDATE
try { SQL permite alterar
os dados de um ou
$stmt = $pdo->prepare("UPDATE livro SET preco += 1 WHERE id= :id "); mais registros em
$stmt->execute([‘id’=>$id]); uma tabela. Todas as
linhas podem ser
} catch (\PDOException $t) { atualizadas ou um
subconjunto pode ser
echo "mensagem:".$t->getMessage()."<br/>". escolhido usando
"código:".$t->getCode(); uma condição
} (WHERE).
PDO (Remoção)
<?php A instrução (statement)
DELETE SQL permite
include_once “conexao.php”; remover um ou mais
registros de uma tabela.
Um subconjunto pode
try { ser definido para
$stmt = $pdo->prepare("DELETE FROM livro WHERE id= :id "); exclusão usando uma
$stmt->execute([‘id’=>$id]); condição (WHERE),
caso contrário, todos os
} catch (\PDOException $t) { registros serão
removidos. Alguns
echo "mensagem:".$t->getMessage()."<br/>". SGBDs, como o MySQL,
"código:".$t->getCode(); permitem a exclusão de
} linhas de várias tabelas
com uma instrução
DELETE (multi-table
delete)
Conjunto de DMLs (sem transação)
UPDATE conta_corrente SET saldo = saldo - 200 WHERE cliente_id = 1;
select sleep(30); -- se durante este tempo o SGBD cair, a próxima instrução nunca ocorrerá
UPDATE conta_corrente SET saldo = saldo + 200 WHERE cliente_id = 2;

É muito comum a utilização de duas ou mais instruções SQL DML de forma sequencial para
cumprir a lógica de um caso de uso da aplicação.
Um exemplo seria o caso de uso: “transferência bancária”
Neste exemplo, R$ 200 serão “transferidos” da conta do cliente 1 para o cliente 2.
PDO x Transação
● Uma transação, no contexto de um banco de dados, é uma unidade lógica que é executada de
forma independente para recuperação ou atualização de dados.
● No SQL ANSI uma transação começa com BEGIN TRANSACTION e, após a mesma, todas as
instruções serão parte desta transação.
● Para “confirmar” a transação, faz-se necessário utilizar a instrução COMMIT
● Para “cancelar” todas as instruções da transação e retornar o banco ao estado original, faz-se
necessário utilizar o comando ROLLBACK
● Em bancos de dados relacionais, as transações do banco de dados devem ser atômicas,
consistentes, isoladas e duráveis - resumidas como o acrônimo ACID.
● Alguns SGBDs como o MySQL, utiliza outra sintaxe (START ao invés de BEGIN). Utilizando o PDO
estas diferenças são suprimidas pois o mesmo possui um método único para todos.
Transação ACID
● Atomicidade: Atomicidade garante que cada transação seja tratada de forma "unitária" e, se alguma das
instruções que constituem uma transação não for concluída, a transação inteira falhará e o banco de dados
permanecerá inalterado;

● Consistência: A consistência garante que uma transação só pode trazer o banco de dados de um estado
válido para outro, mantendo as regras do banco: quaisquer dados gravados no banco de dados devem ser
válidos de acordo com todas as regras definidas, incluindo restrições, triggers e qualquer combinação dos
mesmos. Isso evita corrupção do banco de dados por uma transação ilegal, mas não garante que uma
transação esteja correta;

● Isolamento: As transações geralmente são executadas simultaneamente (por exemplo, ler e gravar em
várias tabelas ao mesmo tempo). O isolamento garante que a execução simultânea de transações deixe o
banco de dados no mesmo estado que teria sido obtido se as transações fossem executadas
sequencialmente. O isolamento é o principal objetivo do controle de concorrência;

● Durabilidade: A durabilidade garante que uma vez que uma transação tenha sido confirmada, ela
permanecerá comprometida mesmo no caso de uma falha no sistema (por exemplo, falta de energia ou
falha). Isso geralmente significa que as transações concluídas (ou seus efeitos) são registradas na
memória não volátil.
PDO Transaction (Inserção)
<?php Neste exemplo, dois
inserts são
include_once “conexao.php”; executados dentro de
uma transação,
try { porém, apenas com a
$pdo->beginTransaction(); confirmação feita com
$stmt = $pdo->prepare("INSERT INTO funcionario (nome) VALUES (?)"); o commit, é que as
foreach (['José da Silva','Maria das Dores'] as $name){ mesmas serão de
$stmt->execute([$name]); facto executadas.
}
$pdo->commit(); Para que a transação
}catch (Exception $e){ não fique aberta em
$pdo->rollback(); caso de erro,
throw $e; utilizaremos o rollback
} dentro do catch
PDO Transaction (Atualização)
<?php

include_once “conexao.php”;

try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("UPDATE livro SET preco += 1 WHERE id= :id ");
$stmt->execute([‘id’=>$id]);
$pdo->commit();
}catch (Exception $e){
$pdo->rollback();
throw $e;
}
PDO Transaction (Remoção)
<?php

include_once “conexao.php”;

try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("DELETE FROM livro WHERE id= :id ");
$stmt->execute([‘id’=>$id]);
$pdo->commit();
}catch (Exception $e){
$pdo->rollback();
throw $e;
}
PDO::lastInsertId
● Muitas vezes, na lógica da aplicação, faz-se necessário persistir
mais de uma entidade e manter o relacionamento entre elas.

● Para isso, é preciso recuperar a chave primária da entidade forte e


utilizar como chave estrangeira na entidade fraca.

● O PDO possui um método chamado lastInsertId() que permite


recuperar a última chave primária persistida no banco. Infelizmente
nem todos os bancos/drivers suportam esse recurso e faz-se
necessário utilizar algum recurso proprietário dos mesmos.
PDO::lastInsertId
<?php if($pdo->getAttribute(PDO::ATTR_DRIVER_NAME) != 'oci'){
include 'conexao.php'; $funcionario_id = $pdo->lastInsertId();
try {
}
$pdo->beginTransaction();
$sql = "INSERT INTO funcionario (nome, cpf) VALUES(:nome,:cpf) "; $sql2 = "INSERT INTO habilitacao (numero, categoria,
if($pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == 'oci'){
funcionario_id) VALUES(:numero,:categoria,:funcionario_id) ";
$sql .= 'RETURNING id INTO :last_id';
} $statement2 = $pdo->prepare($sql2);
$statement2->execute(['95685512398','B',$funcionario_id]);
$statement = $pdo->prepare($sql);
$pdo->commit();
$statement->bindValue('nome','Jaqueline');
$statement->bindValue('cpf','12269736044');
} catch (PDOExecption $e) {
if($pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == 'oci'){ $dbh->rollback();
$statement->bindParam('last_id', $funcionario_id,
print "Error!: " . $e->getMessage() . "</br>";
PDO::PARAM_INT, 8);
} }
$statement->execute();
PDO x BLOB
● A maioria dos SGDS tem suporte ao armazenamento de BLOBs
● Um Binary Large Object (BLOB) é uma coleção de dados binários armazenados como uma
entidade única em um sistema de gerenciamento de banco de dados. Os blobs são tipicamente
imagens, áudio ou outros objetos multimídia, embora às vezes o código executável binário seja
armazenado como um blob.
● Com o PDO o bind (vinculação) de um valor BLOB é feito utilizando a tipagem como LOB através
do PARAM_LOB
● Alguns bancos como o SQL Server exigem o uso de um constante proprietária para indicar
corretamente ao Drive ODBC o uso recurso.
● Outros bancos como o Oracle exigem a necessidade da criação de um BLOB vazio via SGBD e,
através de um ponteiro virtual de arquivo (resource) a “persistência” dos dados.
PDO: Inserção de Binário (formulário)
<!DOCTYPE html>
<html>
<head>
<title>Incluir Nova Foto</title>
<meta charset="utf-8"/>
</head>
<body>
<form action="blob_action.php" method="post" enctype="multipart/form-data">
Funcionário: <select name="funcionario_id">
<?php include 'conexao.php'; Neste exemplo, será submetido via
$funcionarios = $pdo->query('SELECT id, nome FROM funcionario') formulário o id do funcionário e uma
->fetchAll(PDO::FETCH_KEY_PAIR); imagem que será persistida na tabela
foreach($funcionarios as $key => $value){ foto.
echo "<option value='$key'>$value</option>";
}
?>
</select>
Foto: <input type="file" name="foto" accept="image/*"><br>
<input type="submit" value="cadastrar"/>
</form>
</body>
</html>
PDO: Inserção de Binário (action - parte 1)
<?php

include 'conexao.php';

try {
$pdo->beginTransaction();

$funcionario_id = $_REQUEST['funcionario_id'];
Devido às peculiaridades do Oracle,
$arquivo = $_FILES['foto']['tmp_name']; faz-se necessário particularizar o bind
$binario = file_get_contents($arquivo); dos parâmetros, primeiramente,
$mimetype = $_FILES['foto']['type']; criando um BLOB vazio com a função
Oracle EMPTY_BLOB() e recuperar o
$sql = "INSERT INTO foto (binario, mimetype, funcionario_id) "; resource do mesmo no placeholder
$sql_values = " VALUES (:binario, :mimetype, :funcionario_id)"; :binario

$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
if ($drive == 'oci') {
$sql_values = " VALUES (EMPTY_BLOB(), :mimetype, :funcionario_id)"
. " RETURNING binario INTO :binario";
}
PDO: Inserção de Binário (action - parte 2)
$statement = $pdo->prepare($sql . $sql_values);
switch ($driver) {
case 'sqlsrv':
$statement->bindParam('binario', $binario, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
break;
case 'oci':
$statement->bindParam('binario', $binario_resource, PDO::PARAM_LOB);
$binario_resource = fopen($arquivo, 'rb');
break;
default:
$statement->bindParam('binario', $binario, PDO::PARAM_LOB);
break;
}
$statement->bindValue('mimetype', $mimetype, PDO::PARAM_STR);
$statement->bindValue('funcionario_id', $funcionario_id, PDO::PARAM_INT);
$statement->execute();
$pdo->commit();
} catch (\PDOException $ex) {
$pdo->rollback();
echo $ex->getMessage();
}
Tipo Resource
● Um resource é uma variável especial, mantendo uma referência a um
recurso externo. Recursos são criados e usados por funções especiais.
● Resources normalmente funcionam como identificadores (referências)
especiais para arquivos abertos, conexões de banco de dados, áreas de tela
de imagem e semelhantes.
● Devido a natureza dos resources, a conversão dos mesmos não é possível.
Recuperação de binários do Banco (blob/varbinary)
<!DOCTYPE html> <?php
<html lang="pt-BR"> include 'conexao.php';
<head>
<title>Foto de BLOB</title> $id = $_REQUEST['funcionario_id'];
<meta charset="UTF-8">
try {
</head>
$statement = $pdo->prepare("SELECT * FROM
<body>
<img src="carrega_foto.php?funcionario_id=7" alt="foto do funcionário" /> foto WHERE funcionario_id = ? ");
</body> $statement->execute([$id]);
</html> $foto = $statement->fetch(PDO::FETCH_ASSOC);
} catch (Exception $exc) {
echo $exc->getTraceAsString();
}
Neste exemplo recuperamos uma imagem armazenada no banco. Alguns
drivers utilizam um link em forma de resource (recurso) para acessar o binário
ob_clean();
de forma otimizada. Para estes casos, utilizamos o stream_get_contents()
header("Content-type: {$foto['mimetype']}");
para recuperar o conteúdo binário.
if (is_resource($foto['binario'])) {
echo stream_get_contents($foto['binario']);
ob_clean() é uma função do PHP para ajudar a limpar o “output buffer” e evitar
} else {
algum caráter indesejável na composição do echo do binário, o que poderia
echo ($foto['binario']);
corromper o mesmo
}
PDO - Formatação de datas (dd/mm/YYYY)
<?php
include 'conexao.php';
switch ($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql': Normalmente, os SGBDs armazenam as
$data = "DATE_FORMAT(data, '%d/%m/%Y')"; datas utilizando o padrão YYYY-MM-DD.
break;
case 'sqlsrv': //>= 2012 Existem diferentes maneiras para
$data = "FORMAT(data, 'd', 'pt-BR')"; recuperar datas no formato brasileiro,
break; dentre elas, podemos utilizar funções
case 'pgsql':
case 'oci':
específicas de SQL de cada SGBD (t-sql,
$data = "TO_CHAR(data, 'dd/mm/yyyy') as"; plsql etc.) para já recuperar, diretamente
break; da query, a data formatada.
}
$statement = $pdo->query("SELECT $data data FROM pedido"); Lembrando que esta formatação também
$pedidos = $statement->fetchAll(); pode ser feita utilizando o format() da
classe Date conforme demonstrado aqui.
foreach ($pedidos as $pedido) {
echo $pedido['data']."<br/>";
}
PDO (paginação)
● O recurso/técnica de paginação em repositórios de dados permite que
uma parte dos dados seja carregada por demanda do usuário.

● Com a paginação do lado do servidor, o número de registros de uma


consulta também será limitada pelo limite estabelecido na paginação.

● O número e o tamanho das páginas digitais em um documento são


limitados pela quantidade de dados no repositório de dados, não pelos
dispositivos de vídeo ou pela quantidade de “papel”.
PDO paginação (parte 1)
<?php
include_once 'conexao.php';

$pagina = (isset($_REQUEST['pagina'])) ? $_REQUEST['pagina'] : 1;


$limit = 5;
$inicio = $pagina * $limit;
$offset = ($pagina - 1) * $limit;

$query = "SELECT id, titulo, preco, isbn, edicao, ano_publicacao FROM livro ";
$query_total = $pdo->query("SELECT COUNT(*) FROM ($query) q");
$total = $query_total->fetchColumn();
$statement = limit($pdo, $query, $limit, $offset);
$livros = $statement->execute();
PDO (paginação parte 2)
function limit($pdo, string $query, int $limit, int $offset, string $order = 'id') : \PDOStatement{

$query_limit = "";
switch ($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'sqlsrv': //>= 2012
case 'oci': //>= 12c
$query_limit = "$query ORDER BY $order OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY";
break;
default: //mysql e postgres
$query_limit = "$query ORDER BY $order LIMIT :limit OFFSET :offset";
break;
}
$statement = $pdo->prepare($query_limit);
$statement->bindValue(':offset', (int) $offset, PDO::PARAM_INT);
$statement->bindValue(':limit', (int) $limit, PDO::PARAM_INT);
return $statement;

}
PDO paginação (parte 3)

function montaLinha(array $row, $tag = 'td'){


return "<tr>".implode('',array_map(function($row) use ($tag){
return "<$tag>" . $row . "</$tag>";
}, $row))."</tr>";
}

echo "<table border>";


echo montaLinha(['ID','Título','Preço','ISBN','Edição','Ano'], 'th');
while ($row = $statement->fetch()){
echo montaLinha($row);
}
echo "</table>";

echo (($pagina-1) > 0) ? "<a href='index.php?pagina=".($pagina-1)."'>Anterior</a>" : "Anterior";


echo "&nbsp";
echo (($pagina)*$limit < $total) ? "<a href='index.php?pagina=".($pagina+1)."'>Próximo</a>" : "Próximo";
PDO - Paginação com Filtro
● Muitas vezes é necessário permitir que o usuário possa recuperar um
subconjunto específico dos dados persistidos pelo sistema
● Com o recurso do formulário é possível permitir que o usuário possa escolher
e/ou digitar determinados valores que, aplicados à query, afetará o resultado
final exibido na lista/tabela
● Utilizando paginação síncrona, faz-se necessário persistir o filtro nos links da
paginação permitindo que, via get, os valores possam ser reutilizados na
próxima página pela query.
PDO Paginação com Filtro (parte 1)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"> Neste formulário,
<title></title> usaremos o PHP
</head> para recuperar os
<body>
<form> valores dos campos
<label>Nome:</label> de pesquisa, caso
<input name="nome" value="<?php echo isset($_REQUEST['nome']) ? $_REQUEST['nome'] : '';?>"/> os mesmos tenham
<label>CPF:</label> sido usados (e
<input name="cpf" value="<?php echo isset($_REQUEST['cpf']) ? $_REQUEST['cpf'] : '';?>"/>
<input type="submit" value="filtrar" />
repassados pelos
</form> links da paginação
e/ou submissão do
próprio formulário).
PDO Paginação com Filtro (parte 2)
<?php
// put your code here
include 'conexao_oracle.php';

function limit(\PDO $pdo, string $query, int $limit, int $offset, string $order = 'id'): \PDOStatement {
$query_limit = "";
switch ($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'sqlsrv':
case 'oci':
$query_limit = "$query ORDER BY $order OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY";
break;
default:
$query_limit = "$query ORDER BY $order LIMIT :limit OFFSET :offset";
break;
}
$statement = $pdo->prepare($query_limit);
$statement->bindValue('offset', (int) $offset, PDO::PARAM_INT);
$statement->bindValue('limit', (int) $limit, PDO::PARAM_INT);
return $statement;
}
PDO Paginação com Filtro (parte 3)
$pagina = (isset($_REQUEST['pagina'])) ? $_REQUEST['pagina'] : 1;
unset($_REQUEST['pagina']);
$parametros = [];
$nome = "";
$cpf = "";
$limit = 5;
$inicio = $pagina * $limit;
$offset = ($pagina - 1) * $limit;
$query = "SELECT * FROM funcionario WHERE 1=1 ";
if(!empty($_REQUEST['nome'])){
$nome = $_REQUEST['nome'];
$parametros['nome'] = $nome;
$query .= " AND nome LIKE :nome";
}
if(!empty($_REQUEST['cpf'])){
$cpf = $_REQUEST['cpf'];
$parametros['nome'] = $cpf;
$query .= " AND cpf LIKE :cpf";
}
PDO Paginação com Filtro (parte 4)
$query_total = $pdo->prepare("SELECT COUNT(*) FROM ($query) q");
$query_total->execute($parametros);
$total = $query_total->fetchColumn();
$statement = limit($pdo, $query, $limit, $offset);
(!$nome) ?: $statement->bindValue('nome', "%$nome%");
(!$cpf) ?: $statement->bindValue('cpf', "%$cpf%");
$funcionarios = $statement->execute();
echo "<table border>";
echo "<tr><th>Nome</th><th>CPF</th></tr>";
while($funcionario = $statement->fetch()){
echo "<tr>";
echo "<td>{$funcionario['nome']}</td>";
echo "<td>{$funcionario['cpf']}</td>";
echo "</tr>";
}
echo "</table>";
$parametrosUrl = http_build_query($_REQUEST);
echo (($pagina - 1) > 0 ) ? "<a href='paginacao_1.php?pagina=" . ($pagina-1) . "&$parametrosUrl'>Anterior</a>" : "Anterior";
echo "&nbsp";
echo (($pagina) * $limit < $total) ? "<a href='paginacao_1.php?pagina=". ($pagina+1) . "&$parametrosUrl'>Próximo</a>" :
"Próximo";
?>
</body>
</html>
PDO e Information Schema
● Com o Information Schema é possível fazer uma introspecção nas tabelas do
SGBD.

● Informações de metadados como campos obrigatórios, tipos e ou até limites


de tamanho podem ser obtidos para, por exemplo, validar entrada dos
usuários de forma dinâmica.

● Tal recurso é primordial para tornar dinâmico algumas tarefas repetitivas


como validação de campos e essencial na construção de uma biblioteca
ORM.
PDO e Information Schema
<?php
include 'conexao_oracle.php';

$driver = $pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
Neste exemplo será
recuperado os
$table = 'editora'; metadados de cada
$colunas = 'COLUMN_NAME, IS_NULLABLE, DATA_TYPE, coluna da tabela
CHARACTER_MAXIMUM_LENGTH'; editora.
$from = 'information_schema.COLUMNS';
if($driver == 'oci'){
Como o Oracle não
$table = strtoupper($table);
$from = 'all_tab_columns'; suporta o Information
$colunas = 'column_name, nullable as IS_NULLABLE, data_type, data_length as Schema ANSI, faz-se
CHARACTER_MAXIMUM_LENGTH'; necessário uma
} adaptação para
$query = "SELECT $colunas " utilizar seu
. " FROM $from WHERE table_name = ?";
equivalente.
$statement = $pdo->prepare($query);
$statement->execute([$table]);
$schema = $statement->fetchAll();
PDO e Information Schema
echo "<table>";
echo "<tr><th>name</th><th>is null?</th><th>type</th><th>size</th></tr>";
foreach($schema as $coluna_schema){
$coluna_schema = array_change_key_case($coluna_schema, Para compatibilizar a
CASE_UPPER); caixa utilizada por
echo "<tr>"; alguns SGBDs, o
echo "<td>{$coluna_schema['COLUMN_NAME']}</td>"; array_change_key_c
echo "<td>{$coluna_schema['IS_NULLABLE']}</td>"; ase com a constante
echo "<td>{$coluna_schema['DATA_TYPE']}</td>"; CASE_UPPER
echo "<td>{$coluna_schema['CHARACTER_MAXIMUM_LENGTH']}</td>"; manterá todos os
echo "</tr>"; índices do resultado
} do fetchAll() em caixa
echo "</table>"; alta (padrão ansi do
information schema)
Estruturas de Dados com Classes SPL (Standard PHP Library)

● A Biblioteca Padrão do PHP (SPL) é uma coleção de interfaces e classes que


se destinam a solucionar problemas comuns.
● Nenhuma biblioteca externa é necessária para construir essa extensão e está
disponível e compilada por padrão desde o PHP 5.0.
● A SPL fornece um conjunto de:
○ Estrutura de dados (array, pilha, fila etc.)
○ Iteradores para percorrer objetos,
○ Interfaces,
○ Exceptions
SplFixedArray
● A classe SplFixedArray fornece as principais funcionalidades do array. A
principal diferença entre um SplFixedArray e um array PHP normal é que o
SplFixedArray é de tamanho fixo e permite apenas inteiros como valores para
os índices.

● Notoriamente o SplFixedArray consome um número menor de memória RAM


e, muitas vezes, também é mais rápido quando comparado ao array
“tradicional” do PHP.
SplFixedArray
<?php
0 1 2
$fixed = new SplFixedArray(3);

$fixed[0] = 'A';
$fixed[1] = 'B';
$fixed[2] = 'C'; 0 1 2

foreach ($fixed as $item) { A B C


echo $item, PHP_EOL;
}
0 1
$fixed->setSize(2); //diminuindo o array
A B
foreach ($fixed as $item) {
echo $item, PHP_EOL;
}
SplDoublyLinkedList (Lista Duplamente Encadeada)
● Classe genérica que representa a estrutura de uma fila duplamente encadeada.
● Permite a iteração bidirecional da lista e implementa as mesmas interfaces que o SplFixedArray.
● Ela também serve como a classe base para as classes SplStack e SplQueue, que também
implementam as estruturas de dados de pilha e fila.
● Através do método setIteratorMode, o Lista encadeada pode ser organizada usando FIFO ou LIFO.
● Esta implementação não é circular, por isso, não existe uma referência entre os elementos das
extremidades.

head tail

A B C
NULL NULL
SplDoublyLinkedList (iteração, cabeça e cauda)

<?php $prev = null;


$dll = new \SplDoublyLinkedList(); $dll->rewind(); //rebobinando

$dll->push("laranja"); while ($dll->valid()) {


$dll->push("banana"); $current = $dll->current();
$dll->push("limão"); echo 'Anterior: '.$prev, "<br/>";
$dll->push("maçã"); echo 'Atual: '.$current, "<br/>";
$dll->push("uva"); $prev = $current;
$dll->push("abacaxi"); $dll->next();
$next = $dll->current();
head echo "Cabeça: ". $dll->bottom(). "<br/>"; echo 'Próximo: '.$next. "<br/>"; tail
echo "Cauda: ". $dll->top(). "<br/>"; echo "<br/>";
}

laranja banana limão maça uva abacaxi


NULL
NULL
SplDoublyLinkedList (métodos)
<?php function imprimir(\SplDoublyLinkedList &$dll) {
$dll = new \SplDoublyLinkedList();
$dll->unshift(200); //no início $dll->rewind();
imprimir($dll); $values = [];
$dll->unshift(100); //no início while ($dll->valid()) {
imprimir($dll); $values[] = $dll->current();
$dll->push(34); //no fim $dll->next();
imprimir($dll); }
$dll->push(35); //no fim echo "[ " . implode(' , ', $values) . " ] </br>";
imprimir($dll); }
$dll->add(2, 3); //posição específica
imprimir($dll);
$dll->unshift(670); //no início
imprimir($dll);
$dll->add(3, 450); //posição específica
imprimir($dll);
$dll->pop(); //remove o último
imprimir($dll);
$dll->shift(); //remove o primeiro
imprimir($dll);
$dll->offsetUnset(1); //remove de posição específica (2ª)
imprimir($dll);
FIFO e LIFO
● FIFO: Comportamento First in, first out (primeiro a entrar, primeiro a sair) [Fila / Queue];
● Exemplos de FIFO:

○ Scheduling de CPU e Disco.


○ Fila da impressora: os trabalhos enviados para a impressora são impressos na ordem de chegada.
○ Transferindo dados de maneira assíncrona entre dois processos (E / S de Arquivo, Pipes, buffers de E / S,
etc.)
○ App de gerenciamento fila em bilheteiras, hospitais etc.

● Comportamento Last in, first out (última a entrar, primeiro a sair). [Pilha / Stack]
● Exemplos de LIFO:

○ Mecanismo Desfazer / Refazer em editores de texto e outras aplicações (CTRL+Z);


○ Verificação de sintaxe do compilador para chaves correspondentes
○ Uma pilha de pratos ou livros ou cadeiras.
○ Backtracking: Este é um processo quando você precisa acessar o elemento de dados mais recente em uma
série de elementos.
○ Inverter uma palavra
SplDoublyLinkedList (Modo FIFO)

<?php

//FIFO
$fifo = new \SplDoublyLinkedList();

$fifo->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO);

$fifo->push("laranja");
$fifo->push("banana");
$fifo->push("limão");
$fifo->push("maçã");
$fifo->push("uva");
$fifo->push("abacaxi");

foreach ($fifo as $value) {


echo $value."<br/>";
}
SplDoublyLinkedList (Modo LIFO)

<?php

//FIFO
$fifo = new \SplDoublyLinkedList();

$fifo->setIteratorMode(\SplDoublyLinkedList::IT_MODE_LIFO);

$fifo->push("laranja");
$fifo->push("banana");
$fifo->push("limão");
$fifo->push("maçã");
$fifo->push("uva");
$fifo->push("abacaxi");

foreach ($fifo as $value) {


echo $value."<br/>";
}
SplDoublyLinkedList (Modo Delete)
<?php
$dll = new \SplDoublyLinkedList();

$dll->setIteratorMode(\SplDoublyLinkedList::IT_MODE_DELETE);

$dll->push("laranja"); Com o modo de Iteração Delete, os


$dll->push("banana"); item são removidos da lista
$dll->push("limão");
$dll->push("maçã");
duplamente encadeadas, conforme
$dll->push("uva"); os mesmos são iterados.
$dll->push("abacaxi");

foreach ($dll as $value) { O modo default é KEEP, que


echo $value."<br/>"; mantém os itens na lista.
echo "Tamanho atual: ".$dll->count();
}
echo "Tamanho atual: ".$dll->count();
Pilha (SplStack) / LIFO
<?php

$stack = new SplStack();


3
$stack[] = 1;
$stack[] = 2;
2
$stack[] = 3;

foreach ($stack as $item) {


1
echo $item, PHP_EOL;
}
Fila (SplQueue) / FIFO
<?php

$queue = new SplQueue();

$queue[] = 1;
1 2 3
$queue[] = 2;
$queue[] = 3;

foreach ($queue as $item) {


echo $item, PHP_EOL;
}
SplHeap
● Heap é uma estrutura de dados baseada em árvore especializada que é
essencialmente uma árvore quase completa que satisfaz a propriedade heap: em P
um heap máximo, para qualquer nó dado C, se P é um nó pai de C, então a chave (o
valor) de P é maior que ou igual à chave de C. Em uma pilha mínima (MinHeap), a
chave de P é menor ou igual à chave de C. C
● O nó no "topo" do heap (sem pais) é chamado de nó raiz.
● O heap é uma implementação maximamente eficiente de um tipo de dados abstrato chamado de
fila de prioridade e, na verdade, as filas de prioridade são geralmente chamadas de "heaps",
independentemente de como elas podem ser implementadas. Em um heap, o elemento de
prioridade mais alto (ou mais baixo) é sempre armazenado na raiz.
● No entanto, um heap não é uma estrutura classificada; pode ser considerado parcialmente
ordenado.
● Um heap é uma estrutura de dados útil quando é necessário remover repetidamente o objeto/item
com a prioridade mais alta (ou mais baixa).
Spl Heap (Exemplo)
<?php $heap = new CampeonatoBrasileiro();
class CampeonatoBrasileiro extends SplHeap { $heap->insert(['nome' => 'Remo', 'pontuacao' => 22, 'vitorias' => 6]);
$heap->insert(['nome' => 'Santa Cruz', 'pontuacao' => 28, 'vitorias' => 7]);
protected function compare($value1, $value2): int { $heap->insert(['nome' => 'Atlético AC’, 'pontuacao' => 30, 'vitorias' => 9]);
$heap->insert(['nome' => 'Botafogo PB', 'pontuacao' => 26, 'vitorias' => 6]);
if ($value1['pontuacao'] == $value2["pontuacao"]) { $heap->insert(['nome' => 'Náutico', 'pontuacao' => 31, 'vitorias' => 9]);
return $value1['vitorias'] <=> $value2['vitorias']; $heap->insert(['nome' => 'Confiança', 'pontuacao' => 23, 'vitorias' => 5]);
} $heap->insert(['nome' => 'Globo', 'pontuacao' => 22, 'vitorias' => 4]);
return $value1['pontuacao'] <=> $value2['pontuacao'];
} //Campeão:
echo "Campeão da Série C 2018: {$heap->top()['nome']} <br/>";
} //Resultados
$total = $heap->count();
foreach ($heap as $key => $time) {
echo ($total - $key) . " º {$time['nome']} <br/>";
}
MinHeap (SplMinHeap)
<?php

$minHeap = new \SplMinHeap();


$minHeap->insert(30);
$minHeap->insert(50);
$minHeap->insert(10);
$minHeap->insert(105);
$minHeap->insert(99);
$minHeap->insert(88);

echo "mínimo:".$minHeap->top();
echo "<br/>";
foreach ($minHeap as $value) {
echo $value."<br/>";
}
MaxHeap (SplMaxHeap)
<?php

$maxHeap = new \SplMaxHeap();


$maxHeap->insert(30);
$maxHeap->insert(50);
$maxHeap->insert(10);
$maxHeap->insert(105);
$maxHeap->insert(99);
$maxHeap->insert(88);

echo "max:".$maxHeap->top();
echo "<br/>";
foreach ($maxHeap as $value) {
echo $value."<br/>";
}
SplPriorityQueue (Fila de Prioridade)
● Fila de prioridade é uma extensão da fila com as seguintes características:

○ Cada item tem uma prioridade associada a ele.


○ Um elemento com alta prioridade é retirado da fila antes de um elemento com baixa
prioridade.
○ Se dois elementos tiverem a mesma prioridade, eles serão atendidos de acordo com sua
ordem na fila.

● Filas de prioridade são estruturas de dados úteis em simulações, particularmente para manter um
conjunto de eventos futuros ordenados pelo tempo, para que possamos recuperar rapidamente o
que acontecerá. Eles são chamados filas de prioridade porque permitem recuperar itens não pelo
tempo de inserção (como em uma pilha ou fila), nem por uma correspondência de chave (como em
um dicionário), mas por qual item tem a prioridade mais alta de recuperação.
SplPriorityQueue
<?php

$prQueue = new SplPriorityQueue();

$prQueue->insert('A',1);
$prQueue->insert('B',9);
$prQueue->insert('C',7);
$prQueue->insert('D',5);
$prQueue->insert('E',2);
$prQueue->insert('F',4);

foreach ($prQueue as $item) {


echo $item, <br/>;
}
SplPriorityQueue (Flags)
<?php <?php
//flags //flags
$prQueue = new SplPriorityQueue(); $prQueue = new SplPriorityQueue();
$prQueue->insert('A',1); $prQueue->insert('A',1);
$prQueue->insert('B',9); $prQueue->insert('B',9);
$prQueue->insert('C',7); $prQueue->insert('C',7);
$prQueue->insert('D',5); $prQueue->insert('D',5);
$prQueue->insert('E',2); $prQueue->insert('E',2);
$prQueue->insert('F',4); $prQueue->insert('F',4);
$prQueue->insert('G',5); $prQueue->insert('G',5);

$prQueue->setExtractFlags(SplPriorityQueue::EXTR_PRIORITY); $prQueue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
foreach ($prQueue as $item) { foreach ($prQueue as $item) {
echo $item."<br/>"; echo "prioridade: {$item['priority']} ; dado:{$item['data']}<br/>";
} }

Com o uso do método setExtractFlags é possível determinar qual será o resultado extraído ao recuperar os
dados de um SplPriorityQueue. Seja com os dados (default), apenas o valor da priorização (priority) ou
ambos (both).
SplStorageObject
● A classe SplObjectStorage fornece um mapa de objetos para dados ou, ignorando dados, um
conjunto de objetos. Esse duplo propósito pode ser útil em muitos casos, envolvendo a
necessidade de identificar objetos de forma exclusiva.

● Diferente de um hashmap, o SplObjectStorage não atua como um armazenamento de chave-valor,


mas apenas um conjunto de objetos. Algo está no set ou não, mas sua posição não é importante.

● A "chave" de um elemento no SplObjectStorage é, na verdade, o hash do objeto. Isso faz com que
não seja possível adicionar várias cópias da mesma instância de objeto a um SplObjectStorage,
para que você não precise verificar se já existe uma cópia antes de adicionar.

● A principal vantagem do SplObjectStorage é o fato de que você ganha muitos métodos para lidar e
interagir com diferentes conjuntos (contains(), removeAll(), removeAllExcept () etc).
SplObjectStorage

<?php $storage->attach($o1);
$storage->attach($o2);
class Pessoa { $storage->attach($o3);
public $nome; $storage->attach($o1); //já existe, mesmo hash
} $storage->attach($o4); //estados iguais, objetos diferentes
$storage = new SplObjectStorage(); $storage->detach($o3); //removendo

$o1 = new Pessoa(); //var_dump($storage[0]); //nulo !


$o2 = new Pessoa(); echo "contém o1? ".var_export($storage->contains($o1), true)."</br>";
$o3 = new Pessoa(); foreach($storage as $key => $o){
$o4 = new Pessoa(); var_dump($o);//aqui funciona!
}
$o1->nome = 'João';
$o2->nome = 'Maria';
$o3->nome = 'Thiago';
$o4->nome = 'Maria';
SPLEnum (Conjunto finito de identificadores)
<?php
class MesesDoAno extends SplEnum {

const __default = self::Janeiro; Enum é um tipo de dado definido pelo usuário


const Janeiro = 1; const Fevereiro = 2; que consiste em um conjunto de constantes
const Marco = 3; const Abril = 4; nomeadas chamadas enumeradores.
const Maio = 5; const Junho = 6;
const Julho = 7; const Agosto = 8;
const Setembro = 9; const Outubro = 10;
const Novembro = 11; const Dezembro = 12;
}

echo new MesesDoAno(MesesDoAno::Junho) . "<br/>";


echo MesesDoAno::Novembro . "<br/>"; O SPLEnum faz parte de uma extensão PECL
print_r((new MesesDoAno())->getConstList(true)); chamada SPLTypes. Infelizmente esta
extensão não é mais compatível com PHP 7
try { >=, por isso, faz-se necessário utilizar algum
new MesesDoAno(13); fork (e compilar) ou um polyfill como por
} catch (UnexpectedValueException $uve) {
exemplo o duck-projects.
echo $uve->getMessage() . PHP_EOL;
}
Manipulação de String
● Na maioria das aplicações existe a necessidade de editar, manipular e/ou
comparar textos;

● O PHP fornece muitas funções internas para manipular strings, como calcular
o tamanho de uma string, encontrar substrings ou caracteres, substituir parte
de uma string por caracteres diferentes, separar uma string e muitos outros;

● Além disso, o PHP também possui implementações nativas de algoritmos


avançados de comparação/métrica de string como, por exemplo, o
Levenshtein;
Caixa Baixa e Alta - (Manipulação de String)
<?php
$nome = "José da Silva";
echo "caixa baixa:".mb_strtolower($nome);
O PHP possui um conjunto
echo "caixa alta:".mb_strtoupper($nome);
de funções que permite
echo "caixa alta 1º char :".ucfirst($nome); alterar as caixa (case) de
echo "caixa baixa 1º char :".lcfirst($nome); uma string, seja em sua
totalidade ou em partes dela.
echo "caixa alta 1º char de cada palavra :".ucwords($nome);
Posição e Tamanho de string
<?php

$nome = "José da Silva Silveira";


echo "tamanho:".strlen($nome); strlen: retorna o tamanho de uma string
strrpos: encontra a posição da última ocorrência
echo "existe:".strstr($nome, "s"); de um caractere em uma string
echo "existe:".stristr($nome, "silva"); strripos: o mesmo do strrpos, porém, insensível à
echo "encontrar:".strrpos($nome, "s"); caixa.
strspn: encontra o comprimento do segmento
echo "encontrar:".strripos($nome, "O");
inicial combinando com a máscara.
$digitos = '0123456789'; strrchr: Encontra a última ocorrência de um
$telefone = '02199999999a'; caractere em uma string
if (strlen($telefone) != strspn($telefone,$digitos)){
echo "telefone inválido";
}
Substring
<?php
Uma substring é uma seqüência
$string = "85 bits"; contígua de caracteres dentro de
uma string. Por exemplo, "o melhor
$frase = "o sabiá sabia assobiar"; dos" é uma substring de "Foi o
$path = '/www/public_html/index.html'; melhor dos tempos".

substr: Retorna uma parte de uma


echo substr($string, 1)."\n"; // 5 bits string
echo substr($string, 0, 2)."\n"; // 85 substr_count: Conta o número de
echo substr($string, -4, 4). "\n"; // bits ocorrências de uma substring
strrpos: retorna a posição da ultima
echo substr_count($frase, 'bi'); // 3
ocorrência de uma substring
$arquivo = substr($path, strrpos($path, '/') + 1);
echo $arquivo;
Substituição de String
<?php

$noticias = str_replace("aberto", "fechado", "Hoje o comércio está aberto !");


$vogais = array("a", "e", "i", "o", "u");
$titulo = str_replace($vogais, "", "Olá Seja Bem Vindo ao PHP !");
$conselho = "Você deveria comer frutas, vegetais e fibra todos os dias.";
$saudavel = array("frutas", "vegetais", "fibra");
$gorduroso = array("pizza", "bacon", "sorvete");
$novo_conselho = str_replace($saudavel, $gorduroso, $conselho);
$var = 'ola chuva !';
echo substr_replace($var, 'guarda', 4) . "<br />\n";
echo substr_replace($var, 'guarda-', 4, 0) . "<br />\n";
Funções para Strings com HTML
<?php htmlentities: converte
todos os caracteres
$original = "Eu <i>gosto</i> de estudar <b>PHP</b> !"; aplicáveis em entidades
$original = "Muito &lt;b&gt;bom!&lt;/b&gt;"; html.
html_entity_decode: é o
echo htmlspecialchars($original); oposto da função
echo htmlspecialchars_decode($str); htmlentities() no que
echo htmlspecialchars_decode($str, ENT_NOQUOTES); converte todas as
$a = htmlentities($original); entidades HTML para os
$b = html_entity_decode($a); seus caracteres de string.
echo $a."<br>";
echo $b;
$str = '<p>this -&gt; &quot;</p>';
echo nl2br("Vamos quebrar\n linhas !");
$html = '<p>Um parágrafo</p><!-- Comentário --> <a href="#">Link</a>';
echo strip_tags($html) . "<br>";
echo strip_tags($html, '<p><a>');
Formatação e limpeza
<?php wordwrap: permite “quebrar” string
limitando o número de caracteres em
$string = "Uma palavra enorme em português: inconstitucionalissimamente";
echo wordwrap($string,20,"<br>\n")."<br>\n"; grupos, comumente linhas, através de
$string_com_espacos = " olá "; um separador.
echo ltrim($string_com_espacos)."<br>\n"; ltrim: remove os espaços à esquerda
echo rtrim($string_com_espacos)."<br>\n"; rtrim: remove os espaços à direita
echo trim($string_com_espacos)."<br>\n"; trim: remove os espaços à esquerda e
à direita
Formatação de String
● "Printf" é o nome de uma das principais funções de saída da linguagem C e
significa "impressão formatada".
● O PHP, assim como outras linguagens de programação, também possui uma
implementação do printf, mantendo uma compatibilidade razoável com printf
do C.
● Essa função utiliza o conceito de um parâmetro de “formato”, que é composto
de um conjunto de um ou mais especificadores que começam com um
caractere “%”, indicando o local e o método para converter os dados de
entrada no formato desejado de saída em uma string.
● Importante reforçar que incompatibilidades entre os especificadores de
formato e o tipo de dados podem causar falhas e outras vulnerabilidades.
Formatação (printf)
especificador Tratado como Resultado
e notação científica em caixa baixa
notação científica
E notação científica em caixa alta
f número de ponto flutuante (reconhecimento de localidade)
float
F número de ponto flutuante (sem reconhecimento de localidade)
s string string
u decimal sem sinal
x hexadecimal (caixa baixa)
X hexadecimal (caixa alta)
b Inteiro binário
c caractere ASCII correspondente
d decimal com sinal de positivo ou negativo
o número octal
Printf (Exemplo)
printf (preenchimento)
<?php

printf( "%04d", 12 ); // Resultado: "0012"


printf( "%04d", 1234 ); // Resultado: "1234"
printf( "%04d", 12345 ); // Resultado: "12345"
printf( "% 10s", "Hello" ); // Resultado: " Hello"
printf( "%10s", "Hello" ); // Resultado: " Hello"
printf( "%'*10s", "Hello" ); // Resultado: "*****Hello"
printf( "%'*-10s", "Hello" ); // Resultado: "Hello*****"
Troca de argumento
<?php
//ordem natural:
printf('Existe uma diferença entre o %s and %s', 'bem', 'mal');
//informando posição usando % e a formatação com $:
printf( 'O Brasil possui %2$d regiões e %1$d estados', 27, 5 );
//retornando uma string (e não printando diretamente no output)
$string = sprintf('%3$02d/%2$02d/%1$04d', 1998, 2, 1);
print($string);
Comparação Avançada de Strings

<?php levenshthein: calcula a “distância”


entre duas strings. Esta distância é
$nome1 = "tiago da silva"; definida como o mínimo de caracteres
$nome2 = "thyago da silva"; (passos) necessários para transformar
echo "distâncias: ".levenshtein($nome1, $nome2)."<br/>"; a string1 na string2. Sua
echo "comparação fonética 1:" complexidade é O (m*n);
.soundex($nome1), "&nbsp", soundex($nome2)."<br/>"; soundex: calcula a “chave sonora” de
echo "comparação fonética 2:" uma string.
.metaphone($nome1), "&nbsp", metaphone($nome2)."<br/>"; metaphone: similar ao soundex,
similar_text($nome1, $nome2, $porcentagem); porém mais precisa pois utiliza os
echo "similaridade de textos: " principios básicos da pronuncia de
. $porcentagem." % <br/>"; palavras em inglês.
similar_text: calcula a distância entre
as duas strings, utilizando o algoritmo
de Oliver. Sua complexidade é
O(n**3).
Expressões Regulares
● Comumente conhecidas como "regex" ou "RegExp", expressões regulares
são sequências de caracteres especialmente formatadas para encontrar
padrões em textos.
● Expressões regulares são uma das ferramentas mais poderosas disponíveis
atualmente para processamento e manipulação de textos eficazes e
eficientes.
● Podem ser usadas, por ex., para verificar se o formato dos dados, ou seja,
nome, email, número de telefone etc. digitado pelo usuário estava correto ou
não, localizar ou substituir a sequência correspondente no conteúdo de texto.
● Com o advento do PHP 5.3, a linguagem passou a dar suporte a expressões
regulares no estilo Perl através de sua família preg_ de funções.
Regex no PHP
● O PHP possui um conjunto de funções que trabalham com expressões
regulares, todas com o prefixo “preg”

● PREG é um mnemônico para Perl REGular expression.

● A sintaxe dos padrões usados nessas funções se parece muito com o Perl. A
expressão deve estar entre os delimitadores, uma barra (/), por exemplo.

● O suporte a expressões regulares utilizando a sintaxe POSIX com funções de


prefixo “ereg” foram depreciadas no PHP 7.
Regex (Delimitadores)
Os delimitadores podem ser qualquer caractere
<?php ASCII não alfanumérico e sem espaço em
branco, exceto a barra invertida (\) e o byte
nulo. Se o caractere delimitador precisar ser
$regex_valido = "/a/"; usado na própria expressão, ele precisará ser
$regex_valido = "%a%"; escapado por uma barra invertida.
$regex_valido = "*a*"; Delimitadores correspondentes ao estilo Perl (), {},
$regex_valido = "!a!"; [] e <> também podem ser usados.
Funções de Regex no PHP
<?php

//busca
● preg_match($pattern, $subject, $matches);
● preg_match_all($pattern, $subject, $matches);
● preg_grep($pattern, $input);
● preg_split($pattern, $subject, $limit, $flags);

//substituição
● preg_filter($pattern, $replacement, $subject);
● preg_replace_callback($pattern, $callback, $subject);
● preg_replace_callback_array($patterns_and_callbacks, $subject);

//auxiliares
● preg_last_error();
● preg_quote($str, $delimiter);
Regex: preg_match
<?php
preg_match: busca um padrão ($pattern)
$regex = "/85/"; específico em uma string ($subject) e,
$subject = "Bem vindo ao 85 bits developer"; quando o padrão é encontrado pela
$matches = []; primeira vez, ele para de procurá-lo. Ele é
capaz de preencher um array por
if (preg_match($regex, $subject)) {
echo "encontrado"; referência (&$matches), onde
} else { $matches[0] conterá o texto que
echo "não encontrado"; corresponde ao padrão completo,
} $matches[1] terá o texto que
correspondeu ao primeiro sub-padrão
//passando array por referência
preg_match($regex, $subject, $matches); capturado entre parênteses, e assim por
var_dump($matches); diante.
Regex: preg_match_all
<?php

$regex = "/\w+(ala)/";
$subject = "uma mala com uma bala jogada em uma cala";
$matches = [];
preg_match_all: busca todas
preg_match_all($regex, $subject, $matches); as correspondências em uma
var_dump($matches);
string e as produz em um array
multidimensional ($matches)
ordenado de acordo com as
sinalizações ($flags).
Regex: preg_grep
<?php

$regex = "/Silva/i";
$array = [
'Maria da Silva', 'Luiz Oliveira',
'José da Silva', 'Thiago Silva'
]; preg_grep: retorna os itens do
var_dump(preg_grep($regex, $array));
array que combinaram com o regex.
Regex: preg_split

<?php

$regex = "/\./";
$string = "pudim.com.br";
var_dump(preg_split($regex, $string)); preg_split: divide a string em substrings
dentro de um array utilizando uma
expressão regular.
Regex: preg_replace
<?php
$regex = '/[aeiou]/';
$replacement = 'e';
$string = "O sapo não lava o pé, não lava porque não quer";
var_dump(preg_replace($regex, $replacement, $string)); preg_replace: realiza uma
pesquisa por uma
expressão regular e a
substitui.
Regex: preg_filter
<?php

$regex = '/a/';
$replacement = 'o';
$subject = 'luiz';

var_dump(preg_filter($regex, $replacement, $subject)); preg_filter: é idêntico a preg_replace(),


var_dump(preg_replace($regex, $replacement, $subject));
exceto que apenas retorna o $subject
(possivelmente transformado) onde
houve uma correspondência.
Regex: preg_replace_callback
<?php

$string = '2014-02-22';
$regex = '/(\d{4})-(\d{2})-(\d{2})/';
$resultado = preg_replace_callback($regex, 'dataBr', $string);

function dataBr ($matches) { O comportamento desta função é


return $matches[3].'/'.$matches[2].'/'.$matches[1]; similar ao da preg_replace(), exceto
}
pelo fato que ao invés do parâmetro
echo $resultado; $replacement, é utilizado uma função
ou closure com a responsabilidade de
executar a transformação/substituição.
Regex: preg_quote

<?php

$regex = 'etc… / legal';


$regex = preg_quote($regex, '/');
echo $regex;
Regex: preg_last_error()
PREG_PATTERN_ORDER: 1

PREG_SET_ORDER: 2

PREG_OFFSET_CAPTURE: 256

PREG_SPLIT_NO_EMPTY: 1

PREG_SPLIT_DELIM_CAPTURE: 2

PREG_SPLIT_OFFSET_CAPTURE: 4

PREG_NO_ERROR: 0

PREG_INTERNAL_ERROR: 1
PCRE
● O motor de expressões regulares do PHP é a implementação da biblioteca
PCRE (Perl Compatible Regular Expressions).

● A biblioteca PCRE possui um conjunto de recursos que implementam a


correspondência de padrões de expressão regular usando uma mesma
sintaxe e semântica que o Perl 5, com apenas algumas poucas diferenças.

● Este conjunto de recursos é em sua vasta maioria representado por


caracteres especiais (tokens) que, em caso de necessidade de utilizá-los
como parte integrante da string de busca, as mesmas deverão ser
“escapadas”.
Regex (âncoras)
<?php
//^ começa com...
$subject = "eles gostam de PHP";
$pattern = "/^ele/"; //começa com
Âncoras ou asserções atômicas de
var_dump(preg_match($pattern, $subject));
largura zero, especificam uma
posição na cadeia de caracteres em
que uma correspondência deve
//$ termina com... ocorrer.
$subject = "Eu gosto de estudar";
$pattern = "/estudar$/";
var_dump(preg_match($pattern, $subject)); Quando utilizamos âncora em uma
expressão de pesquisa, o
mecanismo de expressões regulares
não avança pela cadeia de
//começa e termina com...
$subject = "PHP"; caracteres ou consome caracteres,
$pattern = "/^PHP$/"; ele procura uma correspondência
var_dump(preg_match($pattern, $subject)); apenas na posição especificada.
Regex (modificador multi linha)
<?php
//multi linha
$regex = '/^ele/m'; $matches = [];
$string = "eles disseram:\nele gosta sim !";
preg_match_all($regex, $string, $matches);
var_dump($matches);

//apenas inicio da string (mesmo multi linha) Com o uso o modificador m (após
$regex = '/\Aele/m'; $matches = []; os delimitadores da regex), é
$string = "eles disseram:\nele gosta sim !"; possível usar âncoras e outros
preg_match_all($regex, $string, $matches); tokens mesmo quando existir
var_dump($matches); quebra de linhas na string.

//termina com (multi linha)


$regex = '/sim$/m'; $matches = [];
$string = "gosto sim\n gosto disso sim";
preg_match_all($regex, $string, $matches);
var_dump($matches);
Regex (modificador multi linha)
//\z string termina com (mesmo multi linha)
$regex = '/sim\z/m';
$string = "gosto sim\n gosto disso sim";
$matches = [];
preg_match_all($regex, $string, $matches);
var_dump($matches);

Com o uso o modificador m (após os


delimitadores da regex), é possível
usar âncoras e outros tokens mesmo
quando existir quebra de linhas na
string.
Regex (grupos de captura/ subpattern)

<?php

//sem grupos
$string = 'comi muita uva';
preg_match('/uva/', $string, $matches); Ao colocar parte de uma expressão regular
var_dump($matches); entre parênteses, é possível agrupar essa
//com grupos
parte da expressão regular. Isso permite
preg_match('/(u)(v)(a)/', $string, $matches); aplicar um quantificador a todo o grupo ou
var_dump($matches); restringir a alternância a parte da regex.
//grupo "nomeado"
Também é possível nomear/identificar um
$string = 'havia 2 homens e 4 crianças no local';
$regex = '/?P<numeros>\d)(/'; grupo através do uso do
preg_match_all($regex, $string, $matches); ?P<nome_do_grupo>.
var_dump($matches['numeros']);
Regex (quantificadores)
<?php

// * zero ou mais do caracter à esquerda de *


$subject = "goal"; //ou gol
$pattern = "/goa*l/";
var_dump(preg_match($pattern, $subject));

// + um ou mais do caracter à esquerda de +


$subject = "gooooool";
$pattern = "/go+l/";
var_dump(preg_match($pattern, $subject));

// ? zero ou um à esquerda de ?
$subject = "thiago"; // ou tiago
$pattern = "/th?iago/";
var_dump(preg_match($pattern, $subject));

// {n} N repetições do caracter à esquerda


$subject = "golll";
$pattern = "/gol{3}/";
var_dump(preg_match($pattern, $subject));
Regex (quantificadores)
// {nx, ny} mínimo nx e máximo ny repetições do caracter à esquerda
$subject = "goll";
$pattern = "/gol{1,3}/"; //começa com
var_dump(preg_match($pattern, $subject));

// {nx, } mínimo nx de repetições do caracter à esquerda


$subject = "golllllllllllllllllllllllllll";
$pattern = "/gol{1,}/"; //começa com
var_dump(preg_match($pattern, $subject));

// * zero ou mais repetições da string dentro dos parênteses


$subject = "muhahahahahaha";
$pattern = "/mu(ha)*/"; //começa com
var_dump(preg_match($pattern, $subject));

// * zero ou mais repetições da string dentro dos parênteses


$subject = "muhahaha";
$pattern = "/mu(ha){1,3}$/";
var_dump(preg_match($pattern, $subject));
Greedy vs Lazy
<?php Um quantificador greedy (ganancioso)
tenta corresponder a um elemento tantas
$html = '<b>Eu sou bold</b> <i>Eu sou itálico</i> <b>Eu também sou bold</b>';
vezes quanto possível.

//greedy Um quantificador não greedy/ Lazy (lento)


preg_match_all('#<b>(.+)</b>#', $html, $bolds); tenta corresponder a um elemento o
print_r($bolds[1]);
menor número de vezes possível.
//lazy
preg_match_all('#<b>(.+?)</b>#', $html, $bolds); É possível transformar um quantificador
print_r($bolds[1]); greedy em um quantificador lazy
simplesmente adicionando um token ? ou
//lazy com modificador U
preg_match_all('#<b>(.+)</b>#U', $html, $bolds); utilizar o modificador U depois do
print_r($bolds[1]); delimitador final.

A busca lazy é bastante útil ao tentar


executar uma análise simplista de HTML.
Regex (Operador “ou”)
<?php O operador “ou” permite criar
a alternância de matches.
// | seguido pela string a ou b
$string = "ele"; Com uso do operador pipe é
$regex = "/el(e|a)/"; //começa com possível alternar entre
var_dump(preg_match($pattern, $string)); caracteres dentro de um
grupo.

Sem o uso do pipe, basta


// mesmo que o anterior porém sem grupo colocar os caracteres de
$string = "ela"; forma contígua dentro de
$regex = "/el[ea]/"; brackets
var_dump(preg_match($regex, $string));
Regex (classe de caractere)
<?php
// \d dígito (mesmo que [0-9]))
$subject = "tem numero 1 aqui";
$pattern = "/\d/"; //começa com
var_dump(preg_match($pattern, $subject));

// \w caractere alfanumérico + underscore (mesmo que [a-zA-Z0-9_])


$subject = "número ou letra ou underscore 1_";
$pattern = "/\w/"; //começa com
var_dump(preg_match($pattern, $subject));

// \s caractere de espaço (incluindo tab e quebra de linha)


$subject = "tem espaço aqui";
$pattern = "/\s/"; //começa com
var_dump(preg_match($pattern, $subject));

// . qualquer caractere
$subject = "oi1 _";
$pattern = "/./"; //começa com
var_dump(preg_match($pattern, $subject));
Regex (classe de caractere [negação])
//--- Negação ---

// \D precisa ter um não dígito


$subject = "precisa ter um 'não' dígito aqui 123";
$pattern = "/\D/"; //começa com
var_dump(preg_match($pattern, $subject));

// \W precisa ter um não alfa


$subject = "precisa ter um 'não' alfa aqui 123";
$pattern = "/\W/"; //começa com
var_dump(preg_match($pattern, $subject));

// \W precisa ter um não espaço


$subject = "precisa ter um 'não' espaço aqui";
$pattern = "/\S/"; //começa com
var_dump(preg_match($pattern, $subject));

// \W precisa ter um não espaço


$subject = "precisa ter um 'não' espaço aqui";
$pattern = "/\S/"; //começa com
var_dump(preg_match($pattern, $subject));
Regex (ranges “-”)
<?php
//range para verificar se contém números 0 à 9 (todos os números) //range para letras acentuadas independente da caixa
$string = "3 quartos, 2 banheiros, 4 tvs e 18 copos"; $string = "Último açaí às 18 horas";
$regex = "/[0-9]/"; $regex = "/[À-ú]/u";
var_dump(preg_match($regex, $string)); preg_match_all($regex, $string, $matches);
var_dump($matches);
//range para verificar se contém números de 0 à 3
$regex = "/[0-3]/";
var_dump(preg_match($regex, $string));

//range para verificar se contém letras (caixa baixa e não acentuadas)


$regex = "/[a-z]/";
var_dump(preg_match($regex, $string));

//range para verificar se contém letras (caixa alta e não acentuadas)


$regex = "/[A-Z]/";
var_dump(preg_match($regex, $string));
Regex Unicode
<?php //Símbolo de pontuação "outros"
//categorias em https://www.fileformat.info/info/unicode/category/index.htm $string = "Ele gritou: – Você fez isso ?!? ";
//Símbolo de moeda $regex = "/\p{Po}/u";
$string = "Sua dívida era de R$ 40,78 e £ 200,00"; preg_match_all($regex, $string, $matches);
$regex = "/\p{Sc}/u"; var_dump($matches);
preg_match_all($regex, $string, $matches);
var_dump($matches);

//Símbolo matemático
$string = "solidão + auto-estima ÷ zombaria ÷ condenação × culpa ...";
$regex = "/\p{Sm}/u";
preg_match_all($regex, $string, $matches);
var_dump($matches);

//Símbolos de pontuação de abertura (Ps) e fechamento (Pe)


$string = 'tem coisas (muitas) e outras também [1] e assim {bom demais}';
$regex = "/\p{Ps}(.*?)\p{Pe}/u";
preg_match_all($regex, $string, $matches);
var_dump($matches);
Regex \b (word bondaury)

<?php

$regex = '/\bflor\b/';
$string = "uma flor na janela";
$string2 = "hoje visitei uma floricultura";
var_dump(preg_match($regex, $string)); //1
var_dump(preg_match($regex, $string2)); //0
Asserções
Uma asserção é um teste para os caracteres que seguem ou precedem o ponto de
correspondência atual que realmente não consome nenhum caractere.

Asserções avançadas são codificadas como subpadrões. Existem dois tipos: aqueles que
olham à frente da posição atual na string e aqueles que olham atrás dela. Um subpadrão de
asserção é correspondido da maneira normal, exceto que não faz com que a posição de
correspondência atual seja alterada.

Os sub-padrões de asserção não estão capturando sub-padrões e podem não ser repetidos,
porque não faz sentido afirmar a mesma coisa várias vezes. Se qualquer tipo de asserção
contiver subpadrões de captura dentro dele, eles serão contados com a finalidade de
numerar os subpadrões de captura em todo o padrão. No entanto, a captura de substring é
realizada apenas para afirmações positivas, porque não faz sentido para afirmações
negativas.
Regex Asserção lookhead

<?php

$string = "achei quebra; tem aqui;";

//(?=) positive lookahead


$regex = "/\w+(?=;)/";
preg_match_all($regex, $string, $matches);
var_dump($matches);

//(?!) negative lookahead /


$regex = "/\b\w+\b(?!;)/";
preg_match_all($regex, $string, $matches);
var_dump($matches);
Regex Asserção lookbehind

//(?<=) - positive lookbehind


$string = "dra Julia \ndr Marcos \ndr Mateus \ndra Ana";
$regex = "/(?<=dra\s)(\w+)/im";
preg_match_all($regex, $string, $matches);
var_dump($matches);

//(?<!) - negative lookbehind


$regex = "/(?<!^dra\h)\b\w+\b\h*$/im";
preg_match_all($regex, $string, $matches);
var_dump($matches);
Regex Recursividade

<?php

$string = "kkk!!!";
$regex = "/k(?R)?!/";
preg_match_all($regex, $string, $matches);
var_dump($matches);
Regex Recursividade
palíndromos:

^((\w)(((\w)(?5)\5?)*|(?1)|\w?)\2)$

com três ou mais consoantes seguidas:

\b\w*?([b-df-hj-np-tv-z]){3,}\w*?\b
Conditional Subpattern

<?php

$regex = "/(fato)??(?(1) é verdadeiro| é falso)/"; Se o grupo de captura 1 foi


$string = "este fato é verdadeiro"; correspondido até agora,
preg_match_all($regex, $string, $matches); corresponde ao padrão antes da
barra vertical.
var_dump($matches);
Caso contrário, corresponde ao
padrão após a barra vertical.
Biblioteca GD (Manipulação de Imagem)
● Nas aplicações modernas, muitas vezes, faz-se
necessário manipular imagens ou até criar novas em
tempo de execução.
● Para esta tarefa podemos utilizar uma extensão do
php, comumente presente em suas distribuições
chamada de PHP GD.
● GD (Graphical Draw) Graphics é uma biblioteca de
software gráfico criada por Thomas Boutell para
manipulação dinâmica de imagens. É nativamente
escrito em ANSI C, mas possui interfaces para muitas
outras linguagens de programação.
Instalação e Verificação
● No Windows, faz-se necessário habilitar a DLL do GD2 php_gd2.dll no php.ini (Esta
DLL já é disponibilizada no download windows oficial)
○ Bundles como Wamp e Xamp já incluem o php_gd2.dll por default

● No linux, dependendo da distribuição (e da versão do PHP) a instalação poderá ser


feita via gerenciadores de pacotes como:
○ apt-get install php7.3-gd
○ yum install php-gd

● Independente do S.O., sempre será necessário reiniciar o Apache após a


instalação/habilitação do php_gd.
● Após os procedimentos acima, basta executar o php_info() e verificar a inclusão da
extensão GD no mesmo.
GD: criação de imagem e preenchimento de fundo

<?php

// cria uma imagem de 800x600 pixels


$image = imagecreatetruecolor(800, 600);

// preenche com fundo branco (R,G,B)


$branco = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $branco);

// envia a imagem
header("Content-type: image/png");
imagepng($image);
imagedestroy($image);
GD: Tipos de formato para geração de imagens
gif, jpeg e png

wbmp foi depreciado


GD Texto (imagestring)
<?php

// cria uma imagem de 800*600


$image = imagecreatetruecolor(800, 600);

// fundo branco e texto azul


$branco = imagecolorallocate($image, 255, 255, 255);
$azul = imagecolorallocate($image, 0, 0, 255);
imagefill($image, 0, 0, $branco);

// escreve a string em cima na esquerda


// por default, sem font específica, o tamanho é de 1 à 5
imagestring($image, 5, 0, 0, "85 Bits", $azul);

// envia a imagem
header("Content-type: image/png");
imagepng($image);
imagedestroy($image);
GD Texto Vertical (imagecharup)
<?php

$string = '85 Bits';


$font_size = 5;
$img = imagecreate(20,90);
$bg = imagecolorallocate($img,225,225,225);
$preto = imagecolorallocate($img,0,0,0);

$len = strlen($string);
for ($i=1; $i<=$len; $i++) {
imagecharup($img, $font_size, 5,
imagesy($img)-($i*imagefontwidth($font_size)), $string, $preto);
$string = substr($string,1);
}
header('Content-type: image/png');
imagepng($img);
imagedestroy($img);
GD Texto com fonte (imagettftext)
<?php
//esse path só funciona com windows :(
$font_path = getenv('WINDIR') . DIRECTORY_SEPARATOR . "Fonts" .
DIRECTORY_SEPARATOR;
$font = 'arial.ttf';
// Criando a imagem
$image = imagecreatetruecolor(800, 600);

// fundo branco e texto azul


$branco = imagecolorallocate($image, 255, 255, 255);
$azul = imagecolorallocate($image, 0, 0, 255);
imagefill($image, 0, 0, $branco);

// escreve a string em cima na esquerda


imagettftext($image, 50, 0, 100, 200, $azul, $font_path.$font, "85 bits");

// envia a imagem
header("Content-type: image/png");
imagepng($image);
imagedestroy($image);
GD Texto com fonte (ângulos)
<?php

//esse path só funciona com windows :(


$font_path = getenv('WINDIR') . DIRECTORY_SEPARATOR . "Fonts" .
DIRECTORY_SEPARATOR;
$font = 'arial.ttf';
// Create the image
$image = imagecreatetruecolor(800, 600);
// fundo branco e texto azul
$branco = imagecolorallocate($image, 255, 255, 255);
$azul = imagecolorallocate($image, 0, 0, 255);
imagefill($image, 0, 0, $branco);
// escreve a string em cima na esquerda em 90º
imagettftext($image, 50, 90, 100, 200, $azul, $font_path.$font,
"85 bits");
// envia a imagem
header("Content-type: image/png");
imagepng($image);
GD retângulo

<?php
$image = imagecreatetruecolor(800, 600);
$branco = imagecolorallocate($image, 255, 255, 255);
$verde = imagecolorallocate($image, 0, 153, 0);

//retangulo ou quadrado sem preenchimento


imagerectangle($image, 20, 100, 400, 400, $branco);
//retangulo ou quadrado com preenchimento
imagefilledrectangle($image, 410, 100, 800, 400, $verde);

header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
GD Círculo
<?php

$image = imagecreatetruecolor(800, 600);


$branco = imagecolorallocate($image, 255, 255, 255);
$verde = imagecolorallocate($image, 0, 153, 0);

imageellipse($image, 200, 150, 300, 300, $branco);

imagefilledellipse($image, 510, 150, 300, 300, $verde);

// desenhando a imagem
header("Content-type: image/png");
imagepng($image);
imagedestroy($image);
GD Linha

<?php

$image = imagecreate(800, 600);


imagecolorallocate($image, 15, 142, 210);
$preto = imagecolorallocate($image, 0, 0, 0);
imagesetthickness($image, 5);
imageline($image, 0, 0, 800, 600, $preto);

header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
GD Linha tracejada

<?php

$image = imagecreatetruecolor(150, 150);


$branco = imagecolorallocate($image, 255, 255, 255);
//espessura da linha
imagesetthickness($image, 5);
imagedashedline($image, 150, 0, 0, 150, $branco);

header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
GD Polígono
<?php

$image = imagecreatetruecolor(800, 600);


$branco = imagecolorallocate($image, 255, 255, 255);

// Polígono sem preenchimento


imagepolygon($image, [
0, 0,
100, 200,
300, 200
],
3,
$branco);

header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
GD Polígono aberto
<?php

$image = imagecreatetruecolor(800, 600);


$branco = imagecolorallocate($image, 255, 255, 255);

imageopenpolygon($image, [
0, 0,
100, 200,
300, 200
],3,$branco);

header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
GD (imagefilledarc)

<?php
$image = imagecreatetruecolor(800, 600);
$branco = imagecolorallocate($image, 255, 255, 255);

//tipos de arcos
imagefilledarc($image, 50, 50, 300, 300, 0, 45, $branco, IMG_ARC_ROUNDED);
imagefilledarc($image, 310, 50, 300, 300, 0, 45, $branco, IMG_ARC_CHORD);
imagefilledarc($image, 50, 210, 300, 300, 0, 45, $branco, IMG_ARC_EDGED);
imagefilledarc($image, 310, 210, 300, 300, 0, 45, $branco, IMG_ARC_NOFILL);
imagefilledarc($image, 510, 210, 300, 300, 0, 45, $branco, IMG_ARC_PIE);

header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
GD gráfico de barras
<?php

$largura = 800;
$altura = 600;

$font_path = getenv('WINDIR') . DIRECTORY_SEPARATOR . "Fonts" .


DIRECTORY_SEPARATOR;
$font = 'arial.ttf';

$dados = ['jan' => 30, 'fev' => 40, 'mar' => 90, 'abr' => 77];

$colunas = count($dados);
$padding = ($largura + $altura) / 100;

$largura_colunas = $largura / $colunas;

$image = imagecreate($largura, $altura);


$cinza = imagecolorallocate($image, 0xcc, 0xcc, 0xcc);
$preto = imagecolorallocate($image, 0, 0, 0);
$cinza_claro = imagecolorallocate($image, 0xee, 0xee, 0xee);
$cinza_escuro = imagecolorallocate($image, 0x7f, 0x7f, 0x7f);
$branco = imagecolorallocate($image, 0xff, 0xff, 0xff);
GD Gráfico de barras (parte 2)
imagefilledrectangle($image, 0, 0, $largura, $altura, $branco);
$maxv = max($dados);
imagefilledrectangle($image, $x1, $y1, $x2, $y2,
$array_values = array_values($dados); $cinza);
$array_keys = array_keys($dados); imagettftext($image, $tamanho_fonte, 0, $x1 + 2, $y2 +
for ($i = 0; $i < $colunas; $i++) { $tamanho_fonte + $padding, $preto, $font_path . $font,
$string);
$coluna_altura = ($altura / 100) * (( $array_values[$i] / $maxv) * 100); //efeito de sombra
$string = $array_keys[$i]; imageline($image, $x1, $y1, $x1, $y2, $cinza_claro);
imageline($image, $x1, $y2, $x2, $y2, $cinza_claro);
$x1 = $i * $largura_colunas; imageline($image, $x2, $y1, $x2, $y2, $cinza_escuro);
$y1 = $altura - $coluna_altura; }
$x2 = (($i + 1) * $largura_colunas) - $padding;
$y2 = $altura - ($padding * 4); header("Content-type: image/png");
$tamanho_fonte = ($altura - $y2) / 2.5; imagepng($image);
$maxChars = ($tamanho_fonte * 2) / $padding; imagedestroy($image);

if (strlen($string) > ($maxChars)) {


$string = substr($string, 0, $maxChars) . '...';
}
GD captcha $tamanho_fonte = floor((40 / 100) * $altura);
$i = 0;

<?php foreach (range(0, $largura, $espaco_por_char) as $x) {


$largura = 142; if ($x < $largura) {
$altura = 40; $char = $random_char();
$max_chars = 6; $captcha .= $char;
$padding = ($largura / $altura); $angulo = rand(0, 45);
$image = imagecreatetruecolor($largura, $altura); imagettftext($image,$tamanho_fonte,$angulo,
$espaco_por_char = $largura / $max_chars; $x + ($tamanho_fonte / 2), ($tamanho_fonte * rand(1.5, 2)) +
$padding,
$background = imagecolorallocate($image, 0x66, 0xCC, 0xFF); rand(0, 1) ? $preto : $branco,
imagefill($image, 0, 0, $background); $path . $fonts[array_rand($fonts)], $char);
$azul_escuro = imagecolorallocate($image, 0x33, 0x99, 0xCC); }
$preto = imagecolorallocate($image, 0, 0, 0); }
$branco = imagecolorallocate($image, 255, 255, 255);
for ($i = 0; $i < 3; $i++) {
$random_char = function () { imagesetthickness($image, rand(1, 2));
return chr(rand(65, 90)); imageline($image, rand(0, $largura), 0, rand(0, $largura), $altura,
}; $azul_escuro);
$path = realpath('.') . DIRECTORY_SEPARATOR . 'fonts' . }
DIRECTORY_SEPARATOR; session_start();
$fonts = array_diff(scandir($path), array('.', '..')); $_SESSION['captcha'] = $captcha;
$captcha = ""; header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
Manipulação de imagens
● Além da possibilidade de criar imagens, o php-gd também permite manipular
imagens pré-existentes.
● Para recuperar dinamicamente informações sobre os arquivos de imagens, o
PHP possui um API EXIF que permite extrair um conjunto significativo de
metadados.
● Com uma imagem carrega como resource, é possível rotacionar,
redimensionar, cortar e muito mais.
● O GD também conta com recursos de filtros que permitem aplicar uma vasta
gama de efeitos em imagens.
Exif

● Exchangeable image file format (Exif) é uma especificação seguida por fabricantes de câmeras
digitais que gravam informações sobre as condições técnicas de captura da imagem junto ao
arquivo da imagem propriamente dita na forma de metadados etiquetados. A especificação usa
os formatos de imagem JPEG, TIFF rev.6.0 e o formato de áudio wave RIFF. O Exif não está
suportado nos formatos JPEG 2000, PNG, GIF e BMP. (WIKIPEDIA, 2019).

● O PHP possui um conjunto de funções que permitem recuperar os metadados EXIFs em


arquivos de imagens.
Exif (metadados)

<?php

$metadata = exif_read_data("levi.jpg");
var_dump($metadata);
Tipo da imagem (exif_imagetype)
<?php

$fileType = exif_imagetype("levi_bubble_waffle.jpg");

header("Content-type: ".image_type_to_mime_type($fileType));

echo file_get_contents("levi_bubble_waffle.jpg");
Thumbnail (exif)
<?php

$filename = "levi_bubble_waffle.jpg";
$fileType = exif_imagetype($filename);
$thumbnail = exif_thumbnail($filename);
if($thumbnail != false){
header("Content-type: ".
image_type_to_mime_type($fileType));
echo $thumbnail;
}else{
echo "Sem thumbnail disponível";
}
Imagecreatefrom
GD (Rotação de imagem)
<?php
$nome_arquivo = 'levi.jpg';
$grau_rotacao = 90;
$fileType = exif_imagetype($nome_arquivo);
header("Content-type: ".image_type_to_mime_type($fileType));
switch ($fileType) {
case IMAGETYPE_PNG:
$image_source = imagecreatefrompng($nome_arquivo);
$fundo_alpha = imagecolorallocatealpha($image_source, 255, 255, 255, 127);
$image_rotate = imagerotate($image_source, $grau_rotacao, $fundo_alpha);
imagesavealpha($image_rotate, true);
imagepng($image_rotate);
break;
default:
$image_source = imagecreatefromjpeg($nome_arquivo);
$image_rotate = imagerotate($image_source, $grau_rotacao, 0);
imagejpeg($image_rotate);
break;
}
imagedestroy($image_source);
imagedestroy($image_rotate);
GD (Virar imagem)
<?php
$nome_arquivo = 'logo.png';
$flip_mode = IMG_FLIP_HORIZONTAL;
$fileType = exif_imagetype($nome_arquivo);
header("Content-type: ".image_type_to_mime_type($fileType));

switch ($fileType) {
case IMAGETYPE_PNG:
$image = imagecreatefrompng($nome_arquivo);
$fundo_alpha = imagecolorallocatealpha($image, 255, 255, 255, 127);
imageflip($image, $flip_mode);
imagesavealpha($image, true);
imagepng($image);
break;
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($nome_arquivo);
imageflip($image, $flip_mode);
imagejpeg($image);
break;
}
imagedestroy($image);
GD (Recorte de Imagem)
<?php
$nome_arquivo = 'levi.jpg';
$corte_rect = ['x' => 80, 'y' => 100,
'width' =>220, 'height' => 300];
$fileType = exif_imagetype($nome_arquivo);
header("Content-type: ".image_type_to_mime_type($fileType));

switch ($fileType) {
case IMAGETYPE_PNG:
$image_source = imagecreatefrompng($nome_arquivo);
$output = function($image_croped){imagepng($image_croped);};
break;

case IMAGETYPE_JPEG:
$image_source = imagecreatefromjpeg($nome_arquivo);
$output = function($image_croped){imagejpeg($image_croped);};
break;
}
$image_croped = imagecrop($image_source, $corte_rect);
$output($image_croped);
imagedestroy($image_source);
GD (Ampliação e Redução de imagem)
<?php
$nome_arquivo = 'levi_bubble_waffle.jpg';
$nova_altura = 80; $nova_largura = 80;
$fileType = exif_imagetype($nome_arquivo);
header("Content-type: ".image_type_to_mime_type($fileType));
switch ($fileType) {
case IMAGETYPE_PNG:
$image = imagecreatefrompng($nome_arquivo);
$image_resized = imagescale($image, $nova_largura, $nova_altura);
imagealphablending($image_resized, false);
imagesavealpha($image_resized, true);
imagepng($image_resized);
break;
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($nome_arquivo);
$image_resized = imagescale($image, $nova_largura, $nova_altura);
imagejpeg($image_resized);
break;
}
imagedestroy($image);
imagedestroy($image_resized);
GD (gerando thumbnail)
<?php
$nome_arquivo = 'ultrawide.jpg';
$largura_maxima = 1000; $altura_maxima = 300;
$fileType = exif_imagetype($nome_arquivo);
header("Content-type: ".image_type_to_mime_type($fileType));
list($largura, $altura) = getimagesize($nome_arquivo);
if ($largura > $largura_maxima || $altura > $altura_maxima) {
$proporcao = min($largura_maxima / $largura, $altura_maxima / $altura);
$largura = round($largura * $proporcao, 0);
$altura = round($altura * $proporcao, 0);
}
switch ($fileType) {
case IMAGETYPE_PNG:
$image = imagecreatefrompng($nome_arquivo);
$image_resized = imagescale($image, $largura, $altura);
imagealphablending($image_resized, false);
imagesavealpha($image_resized, true);
imagepng($image_resized);
break;
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($nome_arquivo);
$image_resized = imagescale($image, $largura, $altura);
imagejpeg($image_resized);
break;
}
imagedestroy($image);
imagedestroy($image_resized);
GD (Sobreposição de imagens)

<?php
$image = imagecreatefromjpeg('levi.jpg');
$image_sobrepor = imagecreatefromjpeg('pencil.jpg');

// Copiar e sobrepor
imagecopy($image, $image_sobrepor,
10, 10, //posição inicial da sobreposição
0, 0, //posição inicial da cópia
100, 200); //tamanho da cópia

header('Content-Type: image/jpg');
imagejpeg($image);
imagedestroy($image);
imagedestroy($image_sobrepor);
GD (Merge de Imagens)

<?php
$image = imagecreatefromjpeg('levi.jpg');
$image_sobrepor = imagecreatefromjpeg('pencil.jpg');
$opacidade = 50;

// Copiar e mesclar
imagecopy($image, $image_sobrepor,
10, 10, //posição inicial da sobreposição
0, 0, //posição inicial da cópia
100, 200, //tamanho da cópia
$opacidade); // opacidade

header('Content-Type: image/jpg');
imagejpeg($image);
imagedestroy($image);
imagedestroy($image_sobrepor);
GD (marca d’água com transparência)
<?php
$nome_arquivo = 'levi.jpg';
$marca = 'logo.png';
$opacidade = 50;
$imagem = imagecreatefromjpeg($nome_arquivo);
$imagem_marca = imagecreatefrompng($marca);
list($largura_arquivo, $altura_arquivo) = getimagesize($nome_arquivo);
list($largura_marca, $altura_marca) = getimagesize($marca);
$margem = 20;
$centerX = $largura_arquivo - $largura_marca - $margem;
$centerY = $altura_arquivo - $altura_marca - $margem;

$cut = imagecreatetruecolor($largura_marca, $altura_marca);


imagecopy($cut, $imagem, 0, 0, $centerX, $centerY, $largura_marca,
$altura_marca);
imagecopy($cut, $imagem_marca, 0, 0, 0, 0, $largura_marca, $altura_marca);
imagecopymerge($imagem, $cut, $centerX, $centerY, 0, 0, $largura_marca,
$altura_marca, $opacidade);

header('Content-type: image/png');
imagepng($imagem);
imagedestroy($imagem);
GD (Filtros)
GD (Filtro de Brilho, Contraste e Inversão de cores)
<?php

imagefilter($image, IMG_FILTER_BRIGHTNESS, 100); //positivo ou negativo


imagefilter($image, IMG_FILTER_CONTRAST, 50); //positivo ou negativo
imagefilter($image, IMG_FILTER_NEGATE);
GD (Filtro de Coloração e Escala de Cinza)
<?php
$nome_arquivo = 'levi.jpg';
$image = imagecreatefromjpeg($nome_arquivo);

imagefilter($image, IMG_FILTER_GRAYSCALE);

header('Content-type: image/jpeg');
imagejpeg($image);
imagedestroy($image);
GD (Filtros de realce de arestas)
GD (Filtros de desfoque e suavização)
GD (Filtros de Esboço e Pixelização)
GD (Filtro de Sépia
Filtro matriz de convolução
● A convolução vem do latim convolvere, que significa 'rolar
juntos'. Na computação gráfica, a convolução é uma maneira
de calcular um novo valor para cada pixel na imagem para
então transformá-lo.
● O filtro de convolução permite modificar uma imagem
utilizando como parâmetro um array (matriz) multidimensional
de 3x3. Este array é chamado de kernel (configuração) ou
matriz convolução. fonte:
● Ao multiplicar um conjunto de pixels com esse kernel, o filtro https://www.researchgate.net/figure/Matrix-convolution-filter-a
lgorithm-as-a-graphic-diagram-12_fig18_304526867
produz a saída para o pixel "focado" (o pixel no meio do
conjunto).
● Com essa matriz, é possível aplicar efeitos de desfoque,
nitidez, relevo, detecção de bordas e muito mais.
● Um filtro de convolução passa por todos os pixels da imagem
de tal maneira que, em um determinado momento, é
recuperado um 'produto escalar' do filtro de convolução e os
fonte:
pixels da imagem. https://rickwierenga.com/blog/fast.ai/FastAI2019-6.html
GD (função imageconvolution)
● O GD fornece a função imageconvolution() para aplicar uma matriz de convolução 3x3 a um
resource de imagem.
● O parâmetro $matrix é uma matriz de três matrizes, cada uma das quais contém três valores
flutuantes - ou seja, é uma matriz 3x3. O primeiro elemento da primeira matriz é multiplicado pelo
valor da cor do pixel superior esquerdo. Da mesma forma, o segundo elemento da primeira matriz é
multiplicado pelo valor da cor do pixel diretamente na parte superior do pixel central. A cor final do
pixel é obtida adicionando o resultado de todas essas multiplicações e dividindo-o por $div para
normalização.
● O parâmetro $div é usado como um divisor para que o resultado da convolução normalize seu
valor. A normalização geralmente mantém o valor final da cor abaixo de 255.
● O parâmetro $offset, por outro lado, é usado para especificar um valor de deslocamento para todas
as cores.
GD (Image Convolution)
<?php
$image = imagecreatefromjpeg("levi.jpg");
$sharpen = [[0, -1, 0], [-1, 5, -1], [0, -1, 0]];
$emboss = [[-2, -1, 0], [-1, 1, 1], [0, 1, 2]];
$edge_detection = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]];
imageconvolution($image, $sharpen, 1, 0);
header("Content-type: image/jpg");
imagejpeg($image);
Design Patterns & Princípios de Design de Software
● Durante o ciclo de desenvolvimento de software, umas etapas/atividades
mais importantes é o design.
● Nesta atividade o arquiteto de software pode identificar um problema comum
de design que já tenha sido resolvido no passado;
● A resolução deste problema comum de design de software, quando é
amplamente testada e utilizada por muitos desenvolvedores, pode ser
promovida para o status de um padrão.
● A reutilização destes padrões pode ajudar a acelerar o processo de
desenvolvimento de software, além de reduzir custos também (WIKIPEDIA,
2019)
Design e Design de Software
● Design é “uma especificação de um objeto, manifestada por algum agente,
tendendo a atingir objetivos, em um ambiente específico, usando um
conjunto de componentes primitivos, satisfazendo um conjunto de requisitos,
sujeito a algumas restrições” (Ralph e Wand, 2009);
● Segundo a ISO 24765 (2017), no contexto de engenharia de software, design
é:
○ “processo de definição da arquitetura, componentes, módulos, interfaces e dados
de software para um sistema de software para atender aos requisitos
especificados”;
○ “o resultado desse processo”;
Design de Software e Princípios
● “[...]princípio é usado como sinônimo de valor fundamental ("É uma questão
de princípio"), como um elemento da noção básica (os princípios da ética, da
matemática, da física etc.) ou como uma abstração progressiva generalizada
de uma série de dados e casos particulares (GUIDO, 1994);

● “Os princípios de design de software estão preocupados em fornecer meios


para lidar com a complexidade do processo de design de maneira eficaz. O
gerenciamento eficaz da complexidade não apenas reduzirá o esforço
necessário para o design, mas também reduzirá o escopo da introdução de
erros durante o design” (JAVATPOINT, 2018);
5 Princípios de Design de Software OO (S.O.L.I.D)
● SOLID é um acrônimo mnemônico para cinco princípios de design,
destinados a tornar os projetos de software mais compreensíveis, flexíveis e
sustentáveis.
● Os princípios foram introduzidos pelo engenheiro e instrutor de software
americano Robert C. Martin em seu artigo de 2000 intitulado Design
Principles and Design Patterns.
● Os cinco princípios são:
a. Princípio da responsabilidade Única
b. Princípio aberto/fechado
c. Princípio da substituição de Liskov
d. Princípio da segregação de Interface
e. Princípio da inversão de dependência
Single Responsibility Principle

“Princípio da responsabilidade única:


só porque você pode, não significa que
você deve”
Single Responsibility Principle
● “Uma classe deve ter apenas um motivo para mudar”. (MARTIN, 2003);

● O princípio da responsabilidade única afirma que toda classe deve ter


responsabilidade sobre uma única parte da funcionalidade fornecida pelo
software e que a responsabilidade deve ser totalmente encapsulada pela
classe. Todos os seus métodos devem estar estritamente alinhados com
essa responsabilidade.

● “Se uma classe tem mais de uma responsabilidade, esta responsabilidade


fica incorporada a classe. Mudanças em uma responsabilidade podem
prejudicar ou inibir a capacidade da classe de atender as outras”. (MARTIN,
2003);
Single Responsibility Principle (antes)
<?php
class Funcionario {
private $nome;
private $matricula;
private $salarioBruto;
public function __construct(string $nome, string $matricula, float $salarioBruto) {
$this->nome = $nome;
$this->matricula = $matricula;
$this->salarioBruto = $salarioBruto;
} Neste exemplo a classe Funcionário possui
public function calcularINSS(): float{
$inss = 0; um acúmulo de responsabilidades pois, além
switch(true){ dos comportamentos do Funcionário
case($this->salarioBruto <= 1751.81):
$inss = 8; diretamente em si, a mesma possui
break;
case($this->salarioBruto >= 1751.82 && $this->salarioBruto <= 2919.72):
responsabilidades sobre o cálculo de
$inss = 9; desconto sobre o valor do salário do
break;
default: funcionário.
$inss = 11;
break;
}
return round(($inss/100)*$this->salarioBruto,2);
}
}
$f1 = new Funcionario('José', '123', 3000.00);
echo $f1->calcularINSS();
Single Responsibility Principle (depois)
<?php class CalculadoraSalarial {
class Funcionario { private $funcionario;
private $nome; public function __construct(Funcionario $funcionario) {
private $matricula; $this->funcionario = $funcionario;
private $salarioBruto; }
public function __construct(string $nome, string $matricula, float public function calcularINSS(): float{
$salarioBruto) { $salarioBruto = $this->funcionario->getSalarioBruto();
$this->nome = $nome; $inss = 0;
$this->matricula = $matricula; switch(true){
$this->salarioBruto = $salarioBruto; case($salarioBruto <= 1751.81):
} $inss = 8;
public function getSalarioBruto(): float{ break;
return $this->salarioBruto; case($salarioBruto >= 1751.82 && $salarioBruto <= 2919.72):
} $inss = 9;
} break;
default:
$inss = 11;
$f1 = new Funcionario('José', '123', 3000.00); break;
$c1 = new CalculadoraSalarial($f1); }
echo $c1->calcularINSS(); return round(($inss/100)*$salarioBruto,2);
}

public function calcularIRRF(): float{


Aplicando o princípio SRP, separamos a //...
responsabilidade para outra classe; }
}
Open/Closed Principle

“Princípio aberto fechado:


cirurgia no cérebro não é necessária ao
colocar um chapéu”
Open/Closed Principle
● “Classes deve ser abertas para extensões mas fechadas para modificações”.
(MARTIN, 2003);
● Princípio estabelecido por Bertrand Meyer nos anos 90 seu livro Object-Oriented
Software Construction:
○ “A abertura é uma preocupação natural dos desenvolvedores de software, pois
eles sabem que é quase impossível prever todos os elementos - dados,
operações - que um módulo precisará em sua vida útil; portanto, eles desejam
manter o máximo de flexibilidade possível para futuras alterações e extensões”.
○ “Se nunca fechamos um módulo até termos certeza de que ele inclui todos os
recursos necessários, nenhum software com vários módulos chegaria à
conclusão: todo desenvolvedor sempre aguardaria a conclusão do trabalho de
outra pessoa. Com técnicas tradicionais, os dois objetivos são incompatíveis”.
(MEYER, 1997);
Open/Closed Principle (antes)
<?php public function getValorFrete(): float{
class Item { if($this->tipoFrete == 'PAC'){
public float $valor; if($this->getValorItems() > 120.00){
public float $peso; return 0;
public function __construct(float $valor, float $peso) { }
$this->valor = $valor; return ($this->getPesoTotal() * 1.5);
$this->peso = $peso; }
} if($this->tipoFrete == 'SEDEX'){
} return ($this->getPesoTotal() * 3);
class Pedido { }
private \DateTimeInterface $data; }
private array $items; public function getDataEntrega(): \DateTimeImmutable {
private string $tipoFrete; if($this->tipoFrete == 'PAC'){
return $this->data->modify('+ 15 days');
public function __construct(\DateTime $data, array $items, string $tipoFrete) { }
$this->data = \DateTimeImmutable::createFromMutable($data); if($this->tipoFrete == 'SEDEX'){
$this->items = $items; return $this->data->modify('+5 days');
$this->tipoFrete = $tipoFrete; }
} }
public function getPesoTotal() : float{ }
return array_reduce($this->items, function($soma, Item $item){ $pedido = new Pedido(new \DateTime(), [
return $soma + $item->peso; new Item(40.50, 2), new Item(90.20, 18)], 'SEDEX');
}); echo $pedido->getValorFrete()."<br/>";
} echo $pedido->getDataEntrega()->format('d/m/Y');
public function getValorItems(){
return array_reduce($this->items, function($soma, Item $item){
return $soma + $item->valor;
});
}
Open/Closed Principle (depois - parte1)
class Pedido { abstract class Frete {
private \DateTimeInterface $data;
private array $items; protected Pedido $pedido;
private Frete $frete;
public function __construct(\DateTime $data, array $items, Frete $frete) { function getPedido(): Pedido {
$this->data = \DateTimeImmutable::createFromMutable($data); return $this->pedido;
$this->items = $items; }
$this->frete = $frete; function setPedido(Pedido $pedido): void {
$this->frete->setPedido($this); $this->pedido = $pedido;
} }
public function getPesoTotal() : float{ public abstract function getValorFrete(): float;
return array_reduce($this->items, function($soma, Item $item){ public abstract function getDataEntrega(): \DateTimeImmutable;
return $soma + $item->peso; }
});
}
public function getValorItems(){
return array_reduce($this->items, function($soma, Item $item){ class Item {
return $soma + $item->valor; public float $valor;
}); public float $peso;
}
public function getData() { public function __construct(float $valor, float $peso) {
return $this->data; $this->valor = $valor;
} $this->peso = $peso;
public function getItems() { }
return $this->items; }
}
function getFrete() {
return $this->frete;
}
}
Open/Closed Principle (depois - parte 2)
class Pac extends Frete {
public function getDataEntrega(): \DateTimeImmutable {
return $this->pedido->getData()->modify('+15 days');
}
public function getValorFrete(): float {
if ($this->pedido->getValorItems() > 120.00) {
return 0;
}
return ($this->pedido->getPesoTotal() * 1.5);
}
}

class Sedex extends Frete {


public function getDataEntrega(): \DateTimeImmutable {
return $this->pedido->getData()->modify('+5 days');
}
public function getValorFrete(): float {
return ($this->pedido->getPesoTotal() * 3);
}
}

$pedido = new Pedido(new \DateTime(),


[new Item(40.50, 2), new Item(90.20, 18)], new Sedex());
echo $pedido->getFrete()->getValorFrete()."<br/>";
echo $pedido->getFrete()->getDataEntrega()->format('d/m/Y');
Liskov (Princípio de Substituição de Liskov)

“Princípio de Substituição de Liskov:


se parece com um pato, grasna como um
pato mas precisa de baterias: você
provavelmente tem a abstração errada”
Liskov (Princípio de Substituição de Liskov)
● Este princípio criado por Barbara Liskov, ganhadora do prêmio Turing de
2008, onde os “Subtipos devem ser substituíveis por seus tipos de base”;

● “Se para cada objeto o1 do tipo S houver um objeto o2 do tipo T, de modo


que, para todos os programas P definidos em termos de T, o
comportamento de P seja inalterado quando o1 for substituído para o2
então S é um subtipo de T”;

● Este princípio também impõe alguns padrões de requisitos tipos de


entrada e saída de método e também no caso de exceções;

● Além dos requisitos de assinatura, o subtipo deve atender a várias


condições comportamentais. Eles são detalhados em uma terminologia
semelhante à da metodologia do Design por contrato.
Liskov (Checklist)
● Objetos de subclasses podem ser substituídos por objetos de suas classes de
base?
● A contravariância não é violada nos parâmetros dos métodos?
● A covariância não é violada nos tipos de retorno dos métodos?
● Nenhuma nova exceção é lançada, a menos que as exceções sejam subtipos de
exceções lançadas pelo pai?
● Nenhuma pré-condição foi reforçada ?
● Nenhuma pós-condição foi enfraquecida ?
● Todos os Invariantes foram preservados nas subclasses?
● O Histórico de Restrição não é violado ?
Covariância (PHP 7.4 >=)
<?php
class AbrigoGato implements AbrigoAnimal {
abstract class Animal { public function adotar(string $nome): Gato {
No âmbito do Orientação a
protected string $nome; return new Gato($nome); Objetos, covariância permite
public function __construct(string $nome) { }
$this->nome = $nome; } que um método de uma classe
} class AbrigoCachorro implements AbrigoAnimal {
abstract public function fazerSom(); public function adotar(string $nome): Cachorro { filha retorne um tipo mais
} return new Cachorro($nome); específico do que o tipo de
class Cachorro extends Animal { }
public function fazerSom() { } retorno do método de seu pai.
echo $this->nome . ": au au !";
} $gatinho = (new AbrigoGato)->adotar("Darwin");
} $gatinho->fazerSom(); Também existe a possibilidade
class Gato extends Animal { echo "<br />";
public function fazerSom() { $doguin = (new AbrigoCachorro)->adotar("Bolota"); de covariância de tipos de
echo $this->nome . ": miau !"; $doguin->fazerSom();
} parâmetros, porém, no PHP, o
}
interface AbrigoAnimal {
mesmo só é possível no
public function adotar(string $nome): Animal; método construtor.
}
Contravariância (PHP 7.4 >=)
<?php class AbrigoGato implements AbrigoAnimal {
class Alimento {} public function adotar(string $nome): Gato {
class AlimentoAnimal extends Alimento {} return new Gato($nome); Contravariância, por outro
}
abstract class Animal { } lado, permite que um tipo
protected string $nome;
public function __construct(string $nome) { class AbrigoCachorro implements AbrigoAnimal {
de parâmetro de um
$this->nome = $nome; public function adotar(string $nome): Cachorro { método seja menos
} return new Cachorro($nome);
public function comer(AlimentoAnimal $alimento){ } específico em um método
echo $this->nome . ": nhac nhac comendo" . }
get_class($alimento); filho do que o de seu pai.
} $gatinho = (new AbrigoGato)->adotar("Darwin");
} $whiskas = new AlimentoAnimal();
class Cachorro extends Animal { $gatinho->comer($whiskas); É o caso do método
public function comer(Alimento $alimento){ echo "<br />";
echo $this->nome . ": chomp chomp comendo " . $doguin = (new AbrigoCachorro)->adotar("Bolota"); comer na classe Filha
get_class($alimento); $arroz = new Alimento(); Cachorro, que utiliza um
} $doguin->comer($arroz);
} tipo mais genérico que no
class Gato extends Animal {} original de sua classe Pai.
interface AbrigoAnimal {
public function adotar(string $nome): Animal;
}
Exceptions com herança
<?php class DonoFelino extends Dono {
class ComidaBaixaQualidadeException extends Exception {} public function alimentar(Animal $animal, Comida $comida){
class ComidaGatoBaixaQualidadeException extends if (!$comida->isConsumivel())
ComidaBaixaQualidadeException {} throw new ComidaGatoBaixaQualidadeException();
class ComidaSemQualidadeException extends Exception {} }
}
class Animal{} class DonoGenerico extends Dono {
public function alimentar(Animal $animal, Comida $comida){
class Comida { if (!$comida->isConsumivel())
private \DateTime $dataValidade; throw new ComidaSemQualidadeException();
public function __construct(\DateTime $dataValidade){ }
$this->dataValidade = $dataValidade; }
} function testar(Dono $dono){
public function isConsumivel(): bool { try{
return ($this->dataValidade >= (new DateTime('today'))); $dono->alimentar(new Animal(), new Comida(new \DateTime('yesterday')));
} }catch(ComidaBaixaQualidadeException $e){
} echo "ocorreu uma exception !";
}
class Dono { }
public function alimentar(Animal $animal, Comida $comida){
if (!$comida->isConsumivel()) $dono = new DonoFelino();
throw new ComidaBaixaQualidadeException(); $dono2 = new DonoGenerico();
} testar($dono2);
}

No princípio de Liskov: nenhuma nova exceção deve ser lançada pelos métodos herdados nas sub classes, exceto quando
essas exceções são iguais ou subtipos das exceções lançadas pelos métodos da super classe.
Pré-condição em herança (enfraquecimento e reforço)
<?php
class CalculadoraPrazo {
public function data(int $dias): DateTimeInterface {
if($dias > 0)
return (new DateTime())->modify("+$dias days");
throw new \InvalidArgumentException("Prazo precisa ser maior que zero ");
}
} “Quando alguém substitui um
class CalculadoraPrazoCLT extends CalculadoraPrazo {
public function data(int $dias): DateTimeInterface { método, não deve reforçar a
if ($dias >= 0)
return (new DateTime())->modify("+$dias days"); pré-condição.” (BREUGEL, 2007).
throw new \InvalidArgumentException("Prazo precisa ser igual ou maior que zero");
}
}
http://www.cse.yorku.ca/~buildIt/n
class CalculadoraPrazoCPC extends CalculadoraPrazo { otes/6/pre.pdf
public function data(int $dias): DateTimeInterface {
if (in_array($dias, range(1,30)))
return (new DateTime())->modify("+$dias days");
throw new \InvalidArgumentException("Prazo precisa ser entre 1 e 30 ");
}
}
echo (new CalculadoraPrazo)->data(31)->format("d/m/Y")."<br/>";
echo (new CalculadoraPrazoCLT)->data(31)->format("d/m/Y")."<br/>";
echo (new CalculadoraPrazoCPC)->data(31)->format("d/m/Y");
Pós-condição em herança (reforço e enfraquecimento)
<?php
class ContaBancaria {
protected float $saldo;
public function __construct(float $saldoInicial){
$this->saldo = $saldoInicial;
}
public function sacar(float $valor) : float { //retorna apenas valores positivos
if(($this->saldo - $valor) >= 0)
$this->saldo -= $valor;
return $this->saldo;
}
} “Uma subclasse que enfraquece uma
class ContaBancariaVip extends ContaBancaria { pós-condição está dizendo que ela não
private const TAXA = 10.00;
public function sacar(float $valor) : float { pode fazer tudo o que sua superclasse
if(($this->saldo - $valor) >= self::TAXA) pode fazer”. (MA, 2009)
$this->saldo -= $valor;
return $this->saldo;
}
}
class ContaBancariaIlimitada extends ContaBancaria {
public function sacar(float $valor) : float {
$this->saldo -= $valor;
return $this->saldo;
}
}
Invariância
<?php $gp = new BoaPessoa("João", "Joãozinho");
class Pessoa { echo $gp->apelido;
protected string $nome; echo $gp->getNomeCompleto();
protected string $apelido;
$bp = new MalvadaPessoa("João", "João");
protected function invariant(){
assert(($this->nome != $this->apelido)); echo $bp->apelido;
}
public function __construct(string $nome, string $apelido){
$this->nome = $nome;
$this->apelido = $apelido;
● Na matemática, um invariante é uma propriedade de um objeto
} matemático (ou uma classe de objetos matemáticos) que permanece
public function __set($nome, $value){ inalterada, após operações ou transformações de um determinado tipo
$this->invariant(); serem aplicadas aos objetos (WIKIPEDIA, 2019).
$this->$nome = $value;
} ● Na OOP, invariância é um conjunto de asserções que sempre devem
public function __get($nome){ ser verdadeiras durante a vida de um objeto para que o programa seja
$this->invariant(); válido (NODET, 2010)
return $this->$nome;
}
● Neste exemplo foi utilizado uma assertion, que uma ferramenta de
} desenvolvimento e um recurso de linguagens usadas para verificar se
class BoaPessoa extends Pessoa { uma expressão condicional é avaliada como verdadeira quando o
public function getNomeCompleto(){ programa é executado
return $this->nome." ".$this->apelido. "!!!";
} ● Segundo Liskov, Invariantes do supertipo devem ser preservados em
} um subtipo.
class MalvadaPessoa extends Pessoa { ● Este princípio é similar ao conceito da programação por contrato ou
protected function invariant(){
assert(($this->nome != ""));
design por contrato.
} ● Algumas linguagens de programação como o D possuem recursos
} explícitos para implementação de invariância (link deste exemplo em D)
History Constraint or History Rule
<?php
class Funcionario {
protected string $matricula;
public string $nome;
public function __construct(string $nome){
$this->nome = $nome;
$this->matricula = uniqid();
}
public function getMatricula() : string {
return $this->matricula;
}
}
class Gerente extends Funcionario {
//violação
public function setMatricula(string $matricula){
$this->matricula = $matricula;
}
}
$f1 = new Gerente('José');
$f1->setMatricula('123');
echo $f1->getMatricula();
Liskov (antes - parte 1)
<?php class PatoBrinquedo extends Pato {
class RacaoPato {
public $sabor; protected $cores = ['amarelo'];
public function __construct(string $sabor) { private $audios = ['pato1.mp3', 'pato2.mp3'];
$this->sabor = $sabor;
} public function nadar(float $metros) {
} echo "Flutando $metros metros e gastando pilha";
class Pato { }
protected $cores = [];
public function nadar(float $metros) { public function grasnar() {
echo "nadando distância de $metros metros"; return $audios; //array e não string
} }
public function grasnar() {
return " quack! quá, quá, quá! quac! quac!"; public function comer(RacaoPato $racao) {
} throw \BadMethodCallException('Método não implementado !');
public function comer(RacaoPato $racao) { }
echo "comendo ração de sabor {$racao->sabor}";
} }
}
class PatoCayuga extends Pato {
protected $cores = ['verde', 'preto']; Excesso de overrides para se adequar a situações
}
específicas podem levar a problemas de manutenção.
Liskov (antes - parte 2)
function testar_pato(Pato $pato){
echo "nadar 10 metros: </br>";
$pato->nadar(10);
$racao = new RacaoPato('xpto');
echo "<br/> comer ração de pato xpto: <br/>";
$pato->comer($racao);
}

$o1 = new Pato();


$o2 = new PatoBrinquedo();
testar_pato($o1);
Interface Segregation Principle

“Princípio da Segregação de Interface:


Adapte interfaces para as necessidades
individuais dos clientes”.
Interface Segregation
● Segundo MARTIN (2003):
○ “Os clientes não devem ser forçados a depender dos métodos que não
usam”.
○ “Quando os clientes são forçados a depender dos métodos que não
usam, esses clientes estão sujeitos a alterações nesses métodos. Isso
resulta em um acoplamento inadvertido entre todos os clientes. Dito de
outra maneira, quando um cliente depende de uma classe que contém
métodos que o cliente não usa, mas que outros clientes usam, esse
cliente será afetado pelas alterações que esses outros clientes impõem à
classe. Gostaríamos de evitar esses acoplamentos sempre que possível
e, portanto, queremos separar as interfaces.”
Interface Segregation (antes)
<?php class CSVDengue implements Leitura {
interface Leitura { const URL =
public function getConteudo(); 'https://zenodo.org/api/files/613e9628-aa46-4a8e-bb95-ad6a4740c5a9/W_Table_dat
public function getTamanho(): int; a.csv';
public function getDono(): string;
} //problemas com o princípio de liskov aqui também
class LogTexto implements Leitura { public function getConteudo(): array {
private $nome_arquivo; return array_map('str_getcsv',
public function __construct(string $nome_arquivo) { explode(PHP_EOL, file_get_contents(self::URL))
if (is_file($nome_arquivo)) { );
$this->nome_arquivo = $nome_arquivo; }
} else { public function getDono(): string {
throw new \InvalidArgumentException('arquivo não existe !'); throw \BadMethodCallException('Método não implementado !');
} }
} public function getTamanho(): int {
public function getConteudo(): string { throw \BadMethodCallException('Método não implementado !');
return file_get_contents($this->nome_arquivo); }
} }
public function getDono(): string {
return fileowner($this->nome_arquivo); $t = new LogTexto('log.txt');
} var_dump($t->getDono());
public function getTamanho(): int { $c = new CSVDengue();
return filesize($this->nome_arquivo); var_dump($c->getConteudo());
}
}
Interface Segregation (depois)
<?php class CSVDengue implements Leitura, ArrayCSV {
interface Leitura {
public function getConteudo() : string; const URL =
} 'https://zenodo.org/api/files/613e9628-aa46-4a8e-bb95-ad6a4740c5a9/
interface ArrayCSV { W_Table_data.csv';
public function getAsArray() : array;
} public function getConteudo(): string {
interface Metadados { return file_get_contents(self::URL);
public function getTamanho(): int; }
public function getDono(): string;
} public function getAsArray(): array {
class LogTexto implements Leitura, Metadados { return array_map('str_getcsv',
private $nome_arquivo; explode(PHP_EOL, $this->getConteudo() )
public function __construct(string $nome_arquivo) { );
if (is_file($nome_arquivo)) { }
$this->nome_arquivo = $nome_arquivo; }
} else {
throw new \InvalidArgumentException('arquivo não existe !');
}
}
public function getConteudo(): string {
return file_get_contents($this->nome_arquivo);
}
public function getDono(): string {
return fileowner($this->nome_arquivo);
}
public function getTamanho(): int {
return filesize($this->nome_arquivo);
}
}
Dependency Inversion Principle

“Princípio da Inversão de dependência:


Você soldaria uma lâmpada diretamente
na fiação elétrica em uma parede?”
Dependency Inversion Principle
Segundo MARTIN (2003):

1. Módulos de alto nível não devem depender de módulos de baixo nível.


Ambos devem depender de abstrações.
2. As abstrações não devem depender de detalhes. Os detalhes é quem devem
depender da abstração

Spasojevic (2019) descreve que “A idéia básica por trás do Princípio de Inversão
de Dependência é que devemos criar os módulos de nível superior com sua
lógica complexa de forma a ser reutilizável e não afetada por qualquer
alteração dos módulos de nível inferior em nosso aplicativo. Para atingir esse
tipo de comportamento em nossos aplicativos, apresentamos abstrações que
separam mais alto dos módulos de nível inferior”.
Dependency Inversion Principle
Segundo Spasojevic (2019):

Os módulos de baixo nível contêm componentes individuais mais específicos,


com foco em detalhes e em partes menores do aplicativo. Esses módulos são
usados dentro dos módulos de alto nível em nosso aplicativo.

Os módulos de alto nível descrevem as operações em nosso aplicativo que têm


natureza mais abstrata e contêm lógica mais complexa. Esses módulos
orquestram os módulos de baixo nível em nosso aplicativo.
Dependency Inversion Principle (antes)
<?php class Relatorio {
class Funcionario {
public string $nome; private Departamento $departamento;
public string $sexo; public function __construct(Departamento $departamento) {
public string $cargo; $this->departamento = $departamento;
public function __construct(string $nome, string $sexo, string $cargo) { }
$this->nome = $nome;
$this->sexo = $sexo; public function getTotalGerentesSexoFeminino(): int {
$this->cargo = $cargo; return array_reduce($this->departamento->getFuncionarios(),
} function ($v, Funcionario $f) {
} return $v + ($f->sexo == 'F' && $f->cargo == 'Gerente');
class Departamento { }, 0);
private array $funcionarios; }
public function __construct() { }
$this->funcionarios = []; $departamento = new Departamento();
} $departamento->addFuncionario(new Funcionario('Maria','F', 'Gerente'));
public function addFuncionario(Funcionario $funcionario) { $departamento->addFuncionario(new Funcionario('Luiza','F', 'Gerente'));
$this->funcionarios[] = $funcionario; $departamento->addFuncionario(new Funcionario('José','M', 'Gerente'));
} $empSt = new Relatorio($departamento);
public function getFuncionarios(): array { echo $empSt->getTotalGerentesSexoFeminino();
return $this->funcionarios;
}
}
Dependency Inversion Principle (depois)
<?php public function getTotalPorGeneroECargo(string $sexo, string $cargo):
interface Enumeravel { Iterable {
public function getTotalPorGeneroECargo( return array_filter($this->funcionarios, fn($emp) =>
string $genero, string $cargo) : Iterable; ($emp->sexo == $sexo && $emp->cargo == $cargo));
} }
}
class Funcionario {
public string $nome; public string $sexo; class Relatorio {
public string $cargo; private Enumeravel $enumeravel;
public function __construct(string $nome, string $sexo, string $cargo){ public function __construct(Enumeravel $enumeravel){
$this->nome = $nome; $this->enumeravel = $enumeravel;
$this->sexo = $sexo; }
$this->cargo = $cargo; public function getTotalGerentesMulheres() : int {
} return count($this->enumeravel->getTotalPorGeneroECargo('F',
} 'Gerente'));
class Departamento implements Enumeravel { }
private array $funcionarios; }
public function __construct() { $departamento = new Departamento();
$this->funcionarios = []; $departamento->addFuncionario(new Funcionario('Maria','F', 'Gerente'));
} $departamento->addFuncionario(new Funcionario('Luiza','F', 'Gerente'));
public function addFuncionario(Funcionario $funcionario) { $departamento->addFuncionario(new Funcionario('José','M', 'Gerente'));
$this->funcionarios[] = $funcionario; $empSt = new Relatorio($departamento);
} echo $empSt->getTotalGerentesMulheres();
Design Patterns - Definições
● “Os padrões de design são soluções típicas para problemas comuns no design
de software. Eles são como modelos pré-fabricados que você pode personalizar
para resolver um problema de design recorrente em seu código”. (SHVETS,
2019);
● “Um pattern é uma apresentação de uma solução para um problema recorrente.
A solução é apenas a solução. O pattern é a apresentação, que leva você a uma
discussão válida sobre a qualidade da apresentação”. (COOKBURN, 2004);

● Design Patterns são templates que auxiliam na construção de softwares melhores


e com maior escalabilidade e extensibilidade, permitindo que futuras alterações
sejam aplicadas de forma menos custosa, mais rápidas e mais organizadas;
Gang of Four Design Patterns
O conjunto mais famoso de Design Patterns
é compilado do livro de 1994, o Design
Patterns: Elements of Reusable
Object-Oriented Software. Seus autores são:

Erich Gamma, que trabalha desde 2003 na


Microsoft como líder de desenvolvimento do
VSCode;

Richard Helm, que atualmente trabalha


como consultor de TI;

Ralph Johnson, professor na Universidade


de Illinois;

John Vlissides, pesquisador na IBM que


infelizmente faleceu em 2005 vítima de um
tumor no cérebro;
Grupos de Design Patterns GoF
1. Criacionais (5):
Abstract Factory, Builder, Factory Method, Prototype e Singleton;

2. Estruturais (7):
Adapter, Bridge, Composite, Decorator, Facade, Flyweight e Proxy;

3. Comportamentais (11):
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento
Observer, State, Strategy, Template e Visitor;
Design Patterns Criacionais
São padrões de design de software orientado a objetos cuja finalidade é prover
mecanismos para criação de novos objetos encapsulando detalhes da
instanciação dos mesmos;

Inúmeros domínios podem exigir um conjunto de passos, restrições ou até


associações na criação de determinados objetos de classes ou família de classes;

Ao utilizar um pattern criacional, o designer evitará erros do desenvolvedor se o


mesmo tentasse instanciar, em todas as situações, utilizando o operador
tradicional “new”.
Factory Method

O Factory Method é um padrão criacional que


utiliza “métodos” para lidar com o problema ao
criar objetos sem precisar especificar a classe
exata do objeto que será criado.

Isso é feito criando objetos chamando um


método “factory” especificado em uma
interface e implementado por classes filhas, ou
implementado em uma classe base e
opcionalmente substituído por classes
derivadas - em vez de chamar o construtor
(WIKIPEDIA, 2020).
Factory Method (Conceitual)
<?php
interface Produto{
public function fazerAlgo();
}
class ProdutoConcretoA implements Produto {
public function fazerAlgo() {
}
}
class ProdutoConcretoB implements Produto {
public function fazerAlgo() {
}
}
abstract class Criador {
public function rodarOperacao(){
$this->criarProduto()->fazerAlgo();
}
public abstract function criarProduto(): Produto;
}
class CriadorConcretoA extends Criador {
public function criarProduto(): ProdutoConcretoA {
}
}
class CriadorConcretoB extends Criador {
public function criarProduto(): ProdutoConcretoB {
}
}
Factory Method (Exemplo)
<?php
interface Produto {
public function getNome(): string;
public function setValor(float $valor);
}
class SmartphoneImportado implements Produto {
private float $valor;
public function getNome(): string {
return "Smartphone Importado XPTO";
}
public function setValor(float $valor) {
$this->valor = $valor;
}
}
class PacoteViagem implements Produto {
private \DateTimeInterface $data;
private float $valor;
public function __construct(\DateTimeInterface $data) {
$this->data = $data;
}
public function getNome(): string {
return "Pacote de Viagem";
}
public function setValor(float $valor) {
$this->valor = $valor;
}
}
Factory Method (Exemplo - continuação)
abstract class ProdutoFactory { $factory = new SmartphoneImportadoFactory();
public abstract function criarProduto(float $valor): Produto; $produto = $factory->criarProduto(100);
} var_dump($produto);
class SmartphoneImportadoFactory extends ProdutoFactory {
private const URI_API = $factory2 = new PacoteViagemFactory();
'https://api.exchangeratesapi.io/latest?base=USD&symbols=BRL'; $produto2 = $factory2->criarProduto(100);
public function criarProduto(float $valor): SmartphoneImportado { var_dump($produto2);
$cotacao_json = json_decode(file_get_contents(self::URI_API));
$cotacao = $cotacao_json->rates->BRL;
$produto = new SmartphoneImportado();
$produto->setValor($valor * $cotacao);
return $produto;
}
}
class PacoteViagemFactory extends ProdutoFactory {
const MESES_ALTA_TEMPORADA = [1, 7, 12];
public function criarProduto(float $valor): PacoteViagem {
$hoje = new \DateTime();
$produto = new PacoteViagem($hoje);
if (in_array($hoje->format('m'), self::MESES_ALTA_TEMPORADA)) {
$produto->setValor($valor * 2);
} else {
$produto->setValor($valor);
}
return $produto;
}
}
Abstract Factory
Abstract Factory permite produzir famílias de
objetos relacionados sem especificar suas classes
concretas.

O cliente não sabe (ou se importa) quais objetos


concretos obtém de cada uma dessas fábricas
internas, pois usa apenas as interfaces genéricas
de seus produtos.
Abstract Factory (conceitual)
<?php
interface ProdutoFamiliaA {
public function fazerAlgoX();
}
class ProdutoConcretoA1 implements ProdutoFamiliaA {
public function fazerAlgoX() {}
}
class ProdutoConcretoA2 implements ProdutoFamiliaA {
public function fazerAlgoX() {}
}
interface ProdutoFamiliaB {
public function fazerAlgoX();
}
class ProdutoConcretoB1 implements ProdutoFamiliaB {
public function fazerAlgoX() {}
}
class ProdutoConcretoB2 implements ProdutoFamiliaB {
public function fazerAlgoX() {}
}
abstract class AbstractFactory {
public abstract function criarProdutoA(): ProdutoFamiliaA;
public abstract function criarProdutoB(): ProdutoFamiliaB;
}
class FabricaConcreta1 extends AbstractFactory {
public function criarProdutoA(): \ProdutoConcretoA1 {}
public function criarProdutoB(): \ProdutoConcretoB1 {}
}
class FabricaConcreta2 extends AbstractFactory {
public function criarProdutoA(): \ProdutoConcretoA2 {}
public function criarProdutoB(): \ProdutoConcretoB2 {}
}
class Client {}
Abstract Factory (Exemplo)
<?php
abstract class Financiamento {//Família A
protected float $taxaDeJuros;
protected float $valor;
public function __construct(float $taxaDeJuros, float $valor) {
$this->taxaDeJuros = $taxaDeJuros;
$this->valor = $valor;
}
public abstract function getValorMensal(int $quantidadeParcelas): float;
}
class FinanciamentoHabitacional extends Financiamento { //conc. A1
public function getValorMensal(int $quantidadeParcelas): float {
$taxa = $this->taxaDeJuros / 100;
$valParcela = $this->valor * pow((1 + $taxa), $quantidadeParcelas);
$resultado = $valParcela / $quantidadeParcelas;
return $resultado;
}
}
class FinanciamentoVeicular extends Financiamento { //concreto A2
public function getValorMensal(int $quantidadeParcelas) : float {
$taxa = $this->taxaDeJuros / 100;
$m = $this->valor * (1 + $taxa * $quantidadeParcelas);
$resultado = $m / $quantidadeParcelas;
return $resultado;
}
}
Abstract Factory (Exemplo - parte 2)
//Familia B
abstract class Seguro {
protected float $valorAvaliado;
protected array $coberturas;
function __construct(float $valorAvaliado, array $coberturas) {
$this->valorAvaliado = $valorAvaliado;
$this->coberturas = $coberturas;
}
public abstract function getValorMensal();
}

//concreto B1
class SeguroResidencial extends Seguro {
public function getValorMensal() {
$adicionais = count($this->coberturas) * 100;
return (($this->valorAvaliado * 0.01) + $adicionais) / 12;
}
}

//concreto B2
class SeguroVeicular extends Seguro {
public function getValorMensal() {
$adicionais = count($this->coberturas) * 80;
return (($this->valorAvaliado * 0.1) + $adicionais) / 12;
}
}
Abstract Factory (Exemplo - parte 3)
//abstract factory class Cliente {
interface AbstractBancoFactory { private AbstractBancoFactory $factory;
public function getFinanciamento(float $valor): Financiamento; public function __construct(string $tipo) {
public function getSeguro(float $valorAvaliado, array $coberturas): Seguro; switch ($tipo) {
} case 'casa':
//fábricas concreta A $this->factory = new BancoCaseiro();
class BancoCaseiro implements AbstractBancoFactory { break;
private const TAXA_JUROS = 0.5; case 'veiculo':
public function getFinanciamento(float $valor): FinanciamentoHabitacional { $this->factory = new BancoMotorizado();
return new FinanciamentoHabitacional(self::TAXA_JUROS, $valor); break;
} default:
public function getSeguro(float $valorAvaliado, array $coberturas): new InvalidArgumentException('Opção inválida');
SeguroResidencial { }
return new SeguroResidencial($valorAvaliado, $coberturas); }
} function getFactory(): AbstractBancoFactory {
} return $this->factory;
//fábricas concreta B }
class BancoMotorizado implements AbstractBancoFactory { }
private const TAXA_JUROS = 0.7; $cliente = new Cliente('casa');
public function getFinanciamento(float $valor): FinanciamentoVeicular { echo $cliente->getFactory()
return new FinanciamentoVeicular(self::TAXA_JUROS, $valor); ->getFinanciamento(1000)
} ->getValorMensal(10);
public function getSeguro(float $valorAvaliado, array $coberturas): echo "<br/>";
SeguroVeicular { $cliente2 = new Cliente('casa');
return new SeguroVeicular($valorAvaliado, $coberturas); echo $cliente2->getFactory()
} ->getSeguro(200000, ['enchente','terremoto'])
} ->getValorMensal();
Builder
Builder é um pattern que separa a construção de um objeto
complexo de sua representação para que o mesmo processo de
construção possa criar representações diferentes.

Os participantes deste pattern são:

Builder: fornece ao Diretor uma interface para construir um


Produto. A interface permite que o Construtor (Builder) oculte a
representação e a estrutura interna do produto. Ele também
oculta como o produto é montado.
Builder Concreto: contém todo a lógica para criar e montar
algum um tipo específico ou variação do Produto.
Diretor: responsável por utilizar os Construtores e construir o
produto passo a passo sob controle do Diretor, que sabe a
ordem certa destes passos pois é ele que notifica o Construtor
sempre que uma parte do produto deve ser construída. o
Cliente: solicita ao Construtor o Produto ou Resultado da
Variação de acordo com o Construtor e o Diretor utilizados.
Builder GoF x Builder Bloch
● Em seu livro intitulado de “Java Effective” (2008), Joshua
Bloch propôs uma versão própria de “Builder” baseado no
Builder GoF.

● Esta versão tem um foco maior na eliminação de métodos


construtores “telescópicos” e o uso de “interface fluente”
para a construção de objetos complexos.

● Devido à sinonímia de ambos, é comum à confusão na


implementação dos mesmos.
Builder (Conceitual)
<?php
interface Builder {
public function construirParteOuExecutarPasso();
public function getProduto() : Produto;
}
class Diretor {
public function construir(Builder &$builder){
//executar os passos
}
}
class BuilderConcreto implements Builder {
private Produto $produto;
public function __construct() {
$this->produto = new Produto();
}
public function construirParteOuExecutarPasso() {
//produto->variacao
}
public function getProduto(): Produto {
return $this->produto;
}
}
class Produto {
private $atributo;
}
$builder = new BuilderConcreto();
$diretor = new Diretor();
$diretor->construir($builder);
var_dump($builder->getProduto());
Builder (Exemplo - parte 1)
abstract class Builder { abstract class Diretor {

protected string $resultado = ""; protected Builder $builder;

abstract function incluirCabecalho(array $header); public function __construct(Builder $builder) {


abstract function incluirLinha(array $line); $this->builder = $builder;
abstract function finalizar(); }
public abstract function construir(string $inputFileName);
public function getResultado() : string { }
return $this->resultado;
}
}
Builder (Exemplo - parte 2)
class HtmlBuilder extends Builder { class CsvBuilder extends Builder {

private \DOMDocument $document; private array $csvArray = [];


private \DOMElement $table;
public function incluirCabecalho(array $header) {
public function __construct() { $this->csvArray[] = $header;
$this->document = new \DOMDocument('1.0', 'utf-8'); }
$this->document->appendChild($this->document->createElement('html')); public function incluirLinha(array $line) {
$this->table = $this->document->createElement('table'); $this->csvArray[] = $line;
$this->table->setAttribute('border', 1); }
$this->document->firstChild->appendChild($this->table); public function finalizar() {
} foreach ($this->csvArray as $line) {
private function criarTableRow(array $line, $tipo = 'td'){ $this->resultado.= implode(",",$line).PHP_EOL;
$tr = $this->document->createElement('tr'); }
array_map(fn($v) => }
$tr->appendChild($this->document->createElement($tipo, $v)), }
$line);
$this->table->appendChild($tr);
}
public function incluirCabecalho(array $header) {
$this->criarTableRow($header, 'th');
}
public function incluirLinha(array $line) {
$this->criarTableRow($line);
}
public function finalizar() {
$this->resultado = $this->document->saveHTML();
}
}
Builder (Exemplo - parte 3)
class DiretorXml extends Diretor { class DiretorJson extends Diretor {

public function construir(string $inputFileName) { public function construir(string $inputFileName) {


$document = new \DOMDocument(); $jsonArray = json_decode(file_get_contents($inputFileName));
$document->preserveWhiteSpace = false; $this->builder->incluirCabecalho(array_keys((array) $jsonArray[0]));
$document->load($inputFileName); foreach ($jsonArray as $jsonObject) {
$root = $document->firstChild; $this->builder->incluirLinha((array) $jsonObject);
$item1 = iterator_to_array($root->firstChild->childNodes); }
$this->builder->incluirCabecalho(array_column($item1, 'tagName')); $this->builder->finalizar();
foreach($root->childNodes as $child){ }
$item = iterator_to_array($child->childNodes);
$this->builder->incluirLinha(array_column($item1, 'nodeValue')); }
}
$this->builder->finalizar();
}

}
Builder (Exemplo - parte 4)
//json to html
//https://gist.github.com/celsowm/45a9e92eb29c4580f971d528a0b61ab9
$input = "clientes.json";
$builder = new HtmlBuilder();
$diretor = new DiretorJson($builder);
$diretor->construir($input);
file_put_contents("clientes.html", $builder->getResultado());

//json to csv
$builder = new CsvBuilder();
$diretor = new DiretorJson($builder);
$diretor->construir($input);
file_put_contents("clientes.csv", $builder->getResultado());

//xml to html
//https://gist.github.com/celsowm/4ad318f0217953e550e9694baab8aa37
$input2 = "clientes.xml";
$builder = new HtmlBuilder();
$diretor = new DiretorXml($builder);
$diretor->construir($input2);
file_put_contents("clientes2.html", $builder->getResultado());

//xml to csv
$builder = new CsvBuilder();
$diretor = new DiretorJson($builder);
$diretor->construir($input);
file_put_contents("clientes2.csv", $builder->getResultado());
Prototype
Protótipo é um pattern que visa permitir copiar (clonar)
objetos existentes sem tornar seu código dependente de
suas classes (SHVETS, 2019).

O padrão Prototype delega o processo de clonagem para


os objetos reais que estão sendo clonados. O padrão
declara uma interface comum para todos os objetos que
suportam a clonagem. Essa interface permite clonar um
objeto sem acoplar seu código à classe desse objeto.
(SHVETS, 2019).

Em muitas aplicações, faz-se necessário criar cópias de


um mesmo objetos inúmeras vezes. Então, faz sentido
economizar na criação dos mesmos, principalmente se a
criação exigir muito tempo ou recursos.
Método “mágico” __clone
<?php ● Além da instrução clone, o PHP permite que
class Pessoa { instruções customizadas possam ser executadas
durante o processo de clonagem através do
public string $cpf;
public string $nome;
método mágico __clone().

public function __construct(string $nome, string $cpf){


● Quando o mesmo é implementado em um
$this->nome = $nome;
$this->cpf = $cpf; determinada classe, o código contido neste
} método será executado no momento que algum
public function __clone(){ objeto desta classe for clonada.
$this->nome.= " (cópia)";
}
● Se o método mágico __clone for definido como
} abstrato em uma classe abstrata, as classes
$p1 = new Pessoa("José","123");
concretas derivadas serão obrigadas a
$p2 = clone $p1; implementá-lo.
var_dump($p2);
Shallow Copy vs Deep Copy
<?php
class Aluno {
public string $nome;
public string $matricula;
public function __construct(string $nome, string
$matricula){
$this->nome = $nome; Existem duas estratégias básicas no processo de
$this->matricula = $matricula; copiar um objeto:
}
}
abstract class Turma { Cópia rasa (shallow): as possíveis referências para
public string $codigo; outros objetos que o objeto original dispunha são
public $alunos = [];
public function __construct(string $codigo){ simplesmente duplicadas e repassadas ao novo objeto
$this->codigo = $codigo; clonado.
}
}
class ShallowTurma extends Turma {} Cópia profunda (deep): os objetos que se relacionam
class DeepTurma extends Turma { com o objeto a ser clonado também são clonados e não
public function __clone(){
$this->alunos = array_map(fn ($o) => clone $o,
apenas referenciados em duplicidade.
$this->alunos);
}
}
Shallow Copy vs Deep Copy (parte 2)
$t0 = new ShallowTurma("patterns2020");
$t1 = new DeepTurma("php2020");
$t0->alunos = [
new Aluno("Tiago", "111"),
new Aluno("Ana", "222")
];
$t1->alunos = [
new Aluno("José", "001"),
new Aluno("Maria", "002")
];

$t2 = clone $t0;


$t3 = clone $t1;

$t2->codigo = "js2020";
$t2->alunos[0]->matricula = "999";

$t3->codigo = "cakephp2020";
$t3->alunos[0]->matricula = "888";
Prototype (conceitual)
<?php
class ProdutoFilho {
public string $nome;
function __construct(string $nome) {
$this->nome = $nome;
}
}
abstract class Prototype {
protected string $id;
protected ProdutoFilho $filho;
function __construct(string $id, ProdutoFilho $filho) {
$this->id = $id;
$this->filho = $filho;
}
function getId(): string {
return $this->id;
}
function getFilho(): ProdutoFilho {
return $this->filho;
}
function setId(string $id): void {
$this->id = $id;
}
function setFilho(ProdutoFilho $filho): void {
$this->filho = $filho;
}
abstract function __clone();
}
Prototype (conceitual - parte 2)
class ShallowCopyPrototype extends Prototype {
public function __clone() {}
}
class DeepCopyPrototype extends Prototype {
public function __clone() {
$this->setFilho(clone $this->getFilho());
}
}
$p1 = new ShallowCopyPrototype('p001', new ProdutoFilho('filho1'));
$p2 = new DeepCopyPrototype('p002', new ProdutoFilho('filho2'));
$p3 = clone $p1;
$p4 = clone $p2;
$p3->getFilho()->nome = 'filho 3';
$p4->getFilho()->nome = 'filho 4';
var_dump($p1);
var_dump($p2);
var_dump($p3);
var_dump($p4);
Prototype (exemplo - parte 1)
<?php
abstract class TermoContratual {

private string $nomeContratada;


private string $numeroContrato;
private string $template;

function __construct(string $nomeContratada, string $numeroContrato) {


$this->nomeContratada = $nomeContratada;
$this->numeroContrato = $numeroContrato;
$this->template = file_get_contents($this->getURITemplate());
}
function getNomeContratada(): string {
return $this->nomeContratada;
}
function setNomeContratada(string $nomeContratada) {
$this->nomeContratada = $nomeContratada;
}
function setNumeroContrato(string $numeroContrato) {
return $this->numeroContrato = $numeroContrato;
}
function getConteudo(): string {
$hashs = ["#contratada","#numero_contrato","#data"];
$replaces = [$this->nomeContratada,$this->numeroContrato,(new \DateTime())->format("d/m/Y")];
return str_replace($hashs,$replaces, $this->template);
}
abstract protected function getURITemplate() : string;
public abstract function __clone();
}
Prototype (exemplo - parte 2)
class DocumentoTermoCondicao extends TermoContratual {

public function __clone() {}


protected function getURITemplate(): string {
return "https://pastebin.com/raw/JNc3NVDy";
}

class RepresentanteLegal {
private string $nome;
private string $cargo;
function __construct(string $nome, string $cargo) {
$this->nome = $nome;
$this->cargo = $cargo;
}
function getNome(): string {
return $this->nome;
}
function getCargo(): string {
return $this->cargo;
}
public function setNome(string $nome): void {
$this->nome = $nome;
}
public function setCargo(string $cargo): void {
$this->cargo = $cargo;
}
}
Prototype (exemplo - parte 3)
class DocumentoTermoConfidencialidade extends TermoContratual {

private RepresentanteLegal $representanteLegal;

public function __construct(string $nomeContratada, string $numeroContrato, RepresentanteLegal $representanteLegal) {


parent::__construct($nomeContratada, $numeroContrato);
$this->representanteLegal = $representanteLegal;
}
public function setRepresentanteLegal(RepresentanteLegal $representanteLegal) {
$this->representanteLegal = $representanteLegal;
}
public function getRepresentanteLegal() : RepresentanteLegal{
return $this->representanteLegal;
}
public function getConteudo(): string {
return str_replace("#representante",$this->representanteLegal->getNome(),parent::getConteudo());
}
public function __clone() {
$this->representanteLegal = clone $this->representanteLegal;
}
protected function getURITemplate(): string {
return "https://pastebin.com/raw/fvxp0W6Z";
}
}
Prototype (exemplo - parte 4)
<?php
$docTC = new DocumentoTermoCondicao("Lorem Ipsum", "0000000");

$docConf = new DocumentoTermoConfidencialidade("Lorem Ipsum", "11111111",


new RepresentanteLegal("Fulano da Silva", "Cargo de Exemplo"));

$cloneTC = clone $docTC;


$cloneConf = clone $docConf;

$cloneTC->setNumeroContrato("2222222");
$cloneConf->setNumeroContrato("3333333");
$cloneConf->getRepresentanteLegal()->setNome("Beltrano da Silva");

echo "originais:";
var_dump($docTC);
var_dump($docConf);
echo "cópias:";
var_dump($cloneTC);
var_dump($cloneConf);
Singleton
O Singleton é um padrão de design de software que
restringe a instanciação de uma classe a uma "única
instância”. Isso é algo que pode ser considerado útil
quando exatamente um objeto é necessário para
coordenar diversas ações no sistema.

Normalmente, isso é feito através da declaração de


todos os construtores da classe como privados; e
fornecendo um método estático que retorna uma
referência à instância.

(WIKIPEDIA, 2020)
Singleton (conceitual)
<?php

class MinhaClasse {

private static $singleton;

private function __construct() {}

public static function getInstance(): ?self {


if (self::$singleton == null) { 1..1
self::$singleton = new self();
}
return self::$singleton;
}

}
Singleton (exemplo - parte 1)
<?php
class Configuracao {
const OPTIONS = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false
];
const SERVIDOR = "localhost";
const BANCO = "livraria";
const USUARIO = "root";
const SENHA = '';
const PORTA = 3306;
public static function getDSN(): string {
return "mysql:host=".self::SERVIDOR.";port=".self::PORTA.";dbname=".self::BANCO.";charset=utf8";
}
}
Singleton (exemplo - parte 2)
class Conexao {
private static $singleton;
private \PDO $pdo;
private function __construct(\PDO $pdo) {
$this->pdo = $pdo;
}
public static function getInstance(): ?self {
if (self::$singleton == null) {
self::$singleton = new self(
new \PDO(
Configuracao::getDSN(),
Configuracao::USUARIO,
Configuracao::SENHA,
Configuracao::OPTIONS
));
}
return self::$singleton;
}
public function getPdo(): ?\PDO{
return $this->pdo;
}
}
var_dump(Conexao::getInstance()->
getPdo()->query("SELECT * FROM livro")->fetchAll());
Design Pattern: Padrões Estruturais
● Os padrões de design estrutural auxiliam na maneira de como as classes
e objetos podem ser compostos para formar estruturas maiores,
mantendo essas estruturas flexíveis e eficientes (SHVETS, 2019).

● Os padrões de design estrutural simplificam a estrutura, identificando os


relacionamentos (JAVAPOINT, 2020).
Adapter
O Adapter é um pattern estrutural que
visa permitir que objetos com interfaces
incompatíveis colaborem.

Um adaptador (adapter) “empacota” um


dos objetos para ocultar a complexidade
da conversão que ocorre nos bastidores.
O objeto empacotado nem mesmo tem
conhecimento da adaptação.

(SHEVTS, 2019).
Adapter (conceitual - parte 1)
<?php

interface Cliente {
public function metodoEsperadoDeCliente();
}

class UsuarioDeCliente {
public static function fazAlgo(Cliente $cliente){
$cliente->metodoEsperadoDeCliente();
}
}

class ClienteConcretoNormal implements Cliente {


public function metodoEsperadoDeCliente() {
echo "oi!";
}
}
Adapter (conceitual - parte 2)
<?php
class ClasseTerceiro {

public function metodoSimilar(){


echo "oiii!!!";
}
}
class Adaptador implements Cliente {
private ClasseTerceiro $adaptado;

public function __construct(ClasseTerceiro $adapatado){


$this->adapatado = $adapatado;
}
public function metodoEsperadoDeCliente() {
$this->adapatado->metodoSimilar();
}
}

$normal = new ClienteConcretoNormal();


$incompativel = new ClasseTerceiro();
$adaptador = new Adaptador($incompativel);
UsuarioDeCliente::fazAlgo($normal); //ok
UsuarioDeCliente::fazAlgo($adaptador); //ok tb
Adapter (exemplo - parte1)
<?php
interface ContaBancaria {
public function getSaldo(): float;
public function temChequeEspecial(): bool;
public function depositar(float $valor);
public function sacar(float $valor);
}

class TransferenciaBancaria {
public static function transferir(float $valor, ContaBancaria
$origem, ContaBancaria $destino){
if($origem->getSaldo() >= $valor){
$origem->sacar($valor);
$destino->depositar($valor);
}
}
}
Adapter (exemplo - parte 2)
abstract class AbstractContaBancariaNacional implements ContaBancaria {

private float $saldo;


private bool $temChequeEspecial;

public function __construct(float $saldo) {


$this->saldo = $saldo;
}
public function getSaldo(): float {
return $this->saldo;
}
public function temChequeEspecial(): bool {
return $this->temChequeEspecial;
}
public function setChequeEspecial(bool $temChequeEspecial) {
$this->temChequeEspecial = $temChequeEspecial;
}
public function depositar(float $valor) {
$this->saldo += $valor;
}
public function sacar(float $valor) {
if ($this->saldo >= $valor || $this->temChequeEspecial()) {
$this->saldo -= $valor;
}
}
}
Adapter (exemplo - parte 3)
class PlatinumContaBancaria extends AbstractContaBancariaNacional {

public function __construct(float $saldo) {


parent::__construct($saldo);
$this->setChequeEspecial(true);
}

class ContaBancariaPadrao extends AbstractContaBancariaNacional {

public function __construct(float $saldo) {


parent::__construct($saldo);
$this->setChequeEspecial(false);
}

}
Adapter (exemplo - parte 4)
<?php
class ContaBancariaOffShore {
private float $saldo;
private float $valorTributo;
public function __construct(float $saldo, float $valorTributo) {
$this->saldo = $saldo;
$this->valorTributo = $valorTributo;
}
public function getValorTributo(): float {
return $this->valorTributo;
}
public function getOffshoreSaldo(): float {
return $this->saldo;
}
public function depositar(float $valor) {
$this->saldo += $valor;
}
}

class ContaBancariaOffShoreAdapter extends AbstractContaBancariaNacional {

private ContaBancariaOffShore $contaBancariaOffShore;

public function __construct(ContaBancariaOffShore $contaBancariaOffShore) {


parent::__construct($contaBancariaOffShore->getOffshoreSaldo());
$this->contaBancariaOffShore = $contaBancariaOffShore;
}
public function getSaldo(): float {
$valor_tributo = $this->contaBancariaOffShore->getValorTributo();
$bruto = parent::getSaldo();
$saldo_com_taxas = $bruto * $valor_tributo;
$liquido = $bruto - $saldo_com_taxas;
return $liquido;
}
}
Adapter (exemplo - parte 5)
$origem = new ContaBancariaPadrao(2000);
$destino = new ContaBancariaOffShoreAdapter(
new ContaBancariaOffShore(2000, 0.04));
TransferenciaBancaria::transferir(100, $origem, $destino);
var_dump($origem->getSaldo());
var_dump($destino->getSaldo());
Bridge
O Bridge é um pattern estrutural que permite dividir
uma classe grande ou um conjunto de classes
estreitamente relacionadas em duas hierarquias
separadas: abstração e implementação, que
podem ser desenvolvidas independentemente uma
da outra (SHVETS, 2019).

O Bridge é consideravelmente útil quando a classe


e o que ela faz variam com freqüência. A própria
classe pode ser considerada como a abstração e o
que a classe pode fazer como a implementação. O
Bridge também pode ser pensado como duas
camadas de abstração (WIKIPEDIA, 2020)
Herança e Bridge
● Usualmente, o recurso da herança é utilizado como
forma de abstração para separar o código do cliente de
suas implementações.
● Definimos uma interface ou uma classe abstrata e
criamos hierarquias de herança, uma para cada uma das
várias implementações possíveis.
● Abstrações por herança nem sempre são flexíveis.
Quando usamos herança, vinculamos permanentemente
a implementação à abstração. Como resultado, qualquer
alteração feita em um afeta o outro.
● Uma maneira mais flexível é separar a abstração e a
implementação, e é aí que entra o padrão da ponte.
● O Bridge faz isso separando a abstração e a
implementação em hierarquias de classes separadas. A
ponte entre as hierarquias de classe é alcançada através
da agregação (Thompson, 2020);
Bridge (conceitual - parte 1)
abstract class Abstracao {

private Implementador $implementador; //bridge


public function __construct(Implementador $implementador) {
$this->implementador = $implementador;
}
public function setImplementador(Implementador $implementador){
$this->implementador = $implementador;
}
public function fazAlgo(string $texto){
$this->implementador->fazAlgoPraValer($texto);
}
}

interface Implementador {
public function fazAlgoPraValer(string $texto);
}

class Concreto extends Abstracao {

public function fazAlgo(string $texto) {


parent::fazAlgo($texto);
}
}

class ImplementadorConcretoA implements Implementador {


public function fazAlgoPraValer(string $texto) {
echo "fiz $texto!";
}
}
Bridge (conceitual - parte 2)

class ImplementadorConcretoB implements Implementador {

public function fazAlgoPraValer(string $texto) {


echo "fiz $texto diferente !";
}
}

$concreto = new Concreto(new ImplementadorConcretoA());


$concreto->fazAlgo("nada");
$concreto->setImplementador(new ImplementadorConcretoB());
$concreto->fazAlgo("algo");
Bridge (exemplo - parte 1)
<?php

abstract class Mensagem {

protected ServicoComunicacao $servicoComunicacao;


protected string $assunto;
protected string $texto;

function __construct(string $assunto, string $texto) {


$this->assunto = $assunto;
$this->texto = $texto;
}
function setServicoComunicacao(ServicoComunicacao $servicoComunicacao) {
$this->servicoComunicacao = $servicoComunicacao;
}
public final function enviar() {
if ($this->servicoComunicacao) {
$this->_enviar();
} else {
new \Exception('Sem serviço setado');
}
}
protected abstract function _enviar();
}
Bridge (exemplo - parte 2)
class MensagemPadrao extends Mensagem {

protected function _enviar() {


$this->servicoComunicacao->enviarMensagem($this->assunto, $this->texto);
}

class MensagemUsuario extends Mensagem {

private array $comentariosUsuario;

function __construct(string $assunto, string $texto, array $comentariosUsuario) {


parent::__construct($assunto, $texto);
$this->comentariosUsuario = $comentariosUsuario;
}

private function getComentariosString() : string {


return implode("; <br/>", $this->comentariosUsuario);
}

protected function _enviar() {


$fulltexto = sprintf("%s <br/>comentários:<br/> %s", $this->texto,
$this->getComentariosString());
$this->servicoComunicacao->enviarMensagem($this->assunto, $fulltexto);
}

}
Bridge (exemplo - parte 3)
interface ServicoComunicacao {
public function enviarMensagem(string $assunto, string $texto);
}

class ServicoEmail implements ServicoComunicacao {


const EMAIL_ADM = '[email protected]';
public function enviarMensagem(string $assunto, string $texto) {
//mail(self::EMAIL_ADM, $assunto, $texto); //implementação real
printf("Email %s %s <br/>", $assunto, $texto);
}
}

class ServicoMSMQ implements ServicoComunicacao {


public function enviarMensagem(string $assunto, string $texto) {
//$msgQueue = new COM("MSMQ.MSMQQueue"); //implementação real
printf("MSQMQ %s %s <br/>", $assunto, $texto);
}
}

class ServicoSMS implements ServicoComunicacao {


private const TELEFONE_ADM = '9999999999';
public function enviarMensagem(string $assunto, string $texto) {
//Implementação real
//file_get_contents("http://api.clickatell.com/http/sendmsg?to=self::TELEFONE_ADM&ms
g=$texto");
printf("SMS %s %s <br/>", $assunto, $texto);
}
}
Bridge (exemplo - parte 4)
$email = new ServicoEmail();
$msmQ = new ServicoMSMQ();
$sms = new ServicoSMS();

$mensagem = new MensagemPadrao("Festa de


Confraternização", "Olá, todos estão convidados !");

$mensagem->setServicoComunicacao($email);
$mensagem->enviar();
$mensagem->setServicoComunicacao($msmQ);
$mensagem->enviar();
$mensagem->setServicoComunicacao($sms);
$mensagem->enviar();

$mensagemUsuario = new MensagemUsuario("Chamado


nº123", "Os seguintes assentamentos foram executados:",
["Análise","Correção","Entrega"]);
$mensagemUsuario->setServicoComunicacao($email);
$mensagemUsuario->enviar();
Composite
Composite é um pattern estrutural que
permite compor objetos em estruturas
de árvores e trabalhar com essas
estruturas como se fossem objetos
individuais (SHVETS, 2019).

“Componha objetos em estruturas de


árvore para representar hierarquias de
partes inteiras. Composite permite que
os clientes tratem objetos individuais e
composições de objetos de maneira
uniforme” (GAMMA et al., 1994).
Composite (conceitual - parte 1)
<?php

interface Componente {
public function fazerAlgo();
}

class Composite implements Componente {

private \SplObjectStorage $componentes;

public function __construct() {


$this->componentes = new \SplObjectStorage();
}
public function fazerAlgo() {
array_map(fn(Componente $f) => $f->fazerAlgo(), $this->getFilhos());
}
public function adicionar(Componente ...$componentes){
array_map(fn($o) => $this->componentes->attach($o), $componentes);
}
public function remover(Componente $componente){
$this->componentes->detach($object);
}
public function getFilhos() : array {
return iterator_to_array($this->componentes);
}
}
Composite (conceitual - parte 2)
<?php

class Folha implements Componente {

private int $indice;

public function __construct(int $indice) {


$this->indice = $indice;
}

public function fazerAlgo() {


echo "fazendo algo da folha {$this->indice} </br>".PHP_EOL;
}

//trocar para componenteRaiz


$compositeRaiz = new Composite();
$compositeRaiz->adicionar(new Folha(1), new Folha(2), new Folha(3));
$compositeIntermediario = new Composite();
$compositeIntermediario->adicionar(new Folha(4));
$compositeRaiz->adicionar($compositeIntermediario);
$compositeRaiz->fazerAlgo();
Composite (exemplo - parte 1)
interface Recurso {
public function getNome(): string;
public function getCustoTotal(): float;
}

class Departamento implements Recurso {


private string $nome;
private \SplObjectStorage $recursos;
public function __construct(string $nome) {
$this->recursos = new \SplObjectStorage();
$this->nome = $nome;
}
public function getNome(): string {
return $this->nome;
}
public function adicionar(Recurso ...$recursos) {
array_map(fn($o) => $this->recursos->attach($o), $recursos);
}
public function remover(Recurso $recurso): void {
$this->recursos->detach($recurso);
}
public function getRecursos() : array {
return iterator_to_array($this->recursos);
}
public function getCustoTotal(): float {
return array_reduce($this->getRecursos(),
fn($total, Recurso $r) => $total + $r->getCustoTotal(), 0);
}
}
Composite (exemplo - parte 2)
class Funcionario implements Recurso {
protected string $nome;
protected float $salario;
private const IMPOSTO = 0.4;

function __construct(string $nome, float $salario) {


$this->nome = $nome;
$this->salario = $salario;
}
public function getNome(): string {
return $this->nome;
}
public function getSalario(): float {
return $this->salario;
}
public function getCustoTotal(): float {
return $this->salario + ($this->salario * self::IMPOSTO);
}
}

class FuncionarioTerceirizado extends Funcionario {

const CUSTO_EMPRESA = 2;
public function getCustoTotal(): float {
return $this->salario * self::CUSTO_EMPRESA;
}
}
Composite (exemplo - parte 3)

$empresa = new Departamento('Empresa XPTO');


$gti = new Departamento('GTI');
$gti->adicionar(
new Funcionario('José', 1200.00),
new Funcionario('Thiago', 2000.00),
new Funcionario('Lucas', 3000.00)
);
$suporte = new Departamento('Suporte TI');
$suporte->adicionar(new FuncionarioTerceirizado('Maria', 1200.10));
$gti->adicionar($suporte);
$empresa->adicionar($gti);

echo "A Empresa {$empresa->getNome()} tem o custo total de


{$empresa->getCustoTotal()}";
echo "<br/>".PHP_EOL;
echo "O Departamento {$suporte->getNome()} tem o custo total de
{$suporte->getCustoTotal()}";

$gti->remover($suporte);

echo "<br/>".PHP_EOL;
echo "A Empresa {$empresa->getNome()} agora tem o custo total de
{$empresa->getCustoTotal()}";
Decorator
● O padrão Decorator permite que
seja adicionado novas
funcionalidades a um objeto
existente sem alterar sua estrutura.

● Esse padrão cria uma classe


“decoradora” que envolve a classe
original e fornece funcionalidades
adicionais mantendo a assinatura
de métodos da classe intacta.
Decorator (conceitual - parte 1)
<?php
interface Componente {
public function fazAlgo() : string;
}

class ComponenteBase implements Componente {


public function fazAlgo() : string {
return "Olá Mundo !";
}
}

abstract class Decorator implements Componente {

private Componente $decorado;


public function __construct(Component $decorado) {
$this->decorado = $decorado;
}
public function fazAlgo() : string {
return $this->decorado->fazAlgo();
}
}
Decorator (conceitual - parte 2)
class DecoratorConcretoA extends Decorator {
public function fazAlgo() : string {
return parent::fazAlgo() . " !!! ";
}
}

class DecoratorConcretoB extends Decorator {


public function fazAlgo() : string {
return parent::fazAlgo() . " @@@ ";
}
}

$original = new ComponenteBase();


echo $original->fazAlgo();
echo "<br>".PHP_EOL;
$decorado = new DecoratorConcretoA(
new DecoratorConcretoB(
new ComponenteBase()
)
);
echo $decorado->fazAlgo();
Decorator (exemplo - parte 1)
<?php
interface Sanduiche {
public function calcularPreco(): float;
public function getDescricao(): string;
}

class RecheioRosbife implements Sanduiche {


public function calcularPreco(): float{
return 7.00;
}
public function getDescricao(): string{
return 'recheio de rosbife';
}
}

class RecheioFrango implements Sanduiche {


public function calcularPreco(): float{
return 6.00;
}
public function getDescricao(): string{
return 'recheio de frango';
}
}
Decorator (exemplo - parte 2)
abstract class SanduicheDecorator implements Sanduiche {

protected Sanduiche $sanduiche;

public function __construct(Sanduiche $sanduiche){


$this->sanduiche = $sanduiche;
}
}

class QueijoExtra extends SanduicheDecorator {


private const PRECO = 4.00;
public function calcularPreco(): float {
return $this->sanduiche->calcularPreco() + self::PRECO;
}
public function getDescricao(): string {
return $this->sanduiche->getDescricao() . ' com queijo extra';
}
}

class Bacon extends SanduicheDecorator {


private const PRECO = 3.50;
public function calcularPreco(): float {
return $this->sanduiche->calcularPreco() + self::PRECO;
}
public function getDescricao(): string {
return $this->sanduiche->getDescricao() . ' e bacon';
}
}
Decorator (exemplo - parte 3)

$sanduiche = new RecheioRosbife();


$sanduiche = new Bacon($sanduiche);
$sanduiche = new QueijoExtra($sanduiche);

echo $sanduiche->calcularPreco();
echo PHP_EOL;
echo $sanduiche->getDescricao();
Decorator (2º exemplo - parte 1)
<?php
interface DataSource {
public function gerar($texto): string;
public function recuperar($texto): string;
}

class TextoBase implements DataSource {


public function gerar($texto): string {
return $texto;
}
public function recuperar($texto): string {
return $texto;
}
}

abstract class Decorator implements DataSource {


private DataSource $decorado;
public function __construct(DataSource $decorado) {
$this->decorado = $decorado;
}
public function gerar($texto): string {
return $this->decorado->gerar($texto);
}
public function recuperar($texto): string {
return $this->decorado->recuperar($texto);
}
}
Decorator (2º exemplo - parte 2)
class CriptoDecorator extends Decorator {
const KEY = 'vDIa5JdknBqfrKOu8d7UpddnBMCH1vza'; //32
const NONCE = 'Ra5LeH7ntW2rvkz3dmqI5Stx'; //24
public function gerar($texto): string {
return $this->encrypt(parent::gerar($texto));
}
public function recuperar($texto): string {
return parent::recuperar($this->decrypt($texto));
}
public function encrypt($data) {
return sodium_crypto_secretbox($data, self::NONCE, self::KEY);
}
private function decrypt(string $data): string {
return sodium_crypto_secretbox_open($data, self::NONCE, self::KEY);
}
}
class Base64Decorator extends Decorator {
public function gerar($texto): string {
return $this->codificar(parent::gerar($texto));
}
public function recuperar($texto): string {
return parent::recuperar($this->decodificar($texto));
}
private function codificar(string $stringData): string {
return base64_encode($stringData);
}
private function decodificar(string $stringData): string {
return base64_decode($stringData);
}
}
Decorator (2º exemplo - parte 3)
class CompressaoDecorator extends Decorator {

public function gerar($texto): string {


return $this->comprimir(parent::gerar($texto));
}
public function recuperar($texto): string {
return parent::recuperar($this->descomprimir($texto));
}
private function comprimir(string $stringData): string {
return gzcompress($stringData);
}
private function descomprimir(string $stringData): string {
return gzuncompress($stringData);
}

$texto = "olá mundo !";


$decorado = new CompressaoDecorator(
new Base64Decorator(
new CriptoDecorator(
new TextoBase()
)));
$texto_decorado = $decorado->gerar($texto);
echo PHP_EOL;
echo $decorado->recuperar($texto_decorado);
Facade
Facade é um pattern estrutural que fornece uma
interface simplificada para uma biblioteca, uma
estrutura ou qualquer outro conjunto complexo de
classes (SHEVTS, 2019).

De maneira análoga a uma fachada na arquitetura,


uma fachada (Facade) é um objeto que serve como
uma interface frontal, mascarando um código
subjacente ou estrutural mais complexo
(WIKIPEDIA 2020).
Facade (conceitual - parte 1)
namespace SubSistemaComplexo;

class ClasseA {

private string $x = "";


public function fazAlgoX(ClasseB $classeB) {
$this->x = $classeB->fazAlgoY(true);
}
public function getX(): string {
return $this->x;
}
}

class ClasseB {

public function fazAlgoY(bool $parametro) : string {


return $parametro;
}
}

class ClasseC {
private string $x = "";
public function setX(string $x) {
$this->x = $x;
}
public function getY(): string {
return $this->x . "!";
}
}
Facade (conceitual - parte 2)
namespace FacadeConceitual;

use SubSistemaComplexo\ClasseA;
use SubSistemaComplexo\ClasseB;
use SubSistemaComplexo\ClasseC;

class Facade {

private ClasseA $a;


private ClasseB $b;
private ClasseC $c;

public function __construct() {


$this->a = new ClasseA();
$this->b = new ClasseB();
$this->c = new ClasseC();
}

public function fazerAlgo(){


$this->a->fazAlgoX($this->b);
$this->c->setX($this->a->getX());
echo $this->c->getY();
}

$facade = new Facade();


$facade->fazerAlgo();
Facade (exemplo - parte 1)
<?php
namespace Singleton;
class Configuracao {//mesmo código usado no exemplo do singleton
}
class Conexao {//mesmo código usado no exemplo do singleton
}

namespace BackEndLivraria;
class PedidoDao {
private \PDO $pdo;
public function __construct(\PDO $pdo) {$this->pdo = $pdo;}
public function recuperarPorIsbn(string $isbn) {
$statement = $this->pdo->prepare(<<<SQL
SELECT p.data, i.quantidade
FROM pedido p
INNER JOIN item_pedido i ON p.id = i.pedido_id
INNER JOIN livro l ON l.id = i.livro_id
WHERE isbn = :isbn AND p.data IS NOT NULL
SQL);
$statement->execute(['isbn' => $isbn]);
return $statement->fetchAll();
}
}
Facade (exemplo - parte 2)
namespace OpenLibrary;
class OpenLibraryREST {

private const URI = 'https://openlibrary.org/api/books';

public function recuperarPorIsbn(string $isbn){


$uri = self::URI."?bibkeys=ISBN:$isbn&jscmd=data&format=json";
return json_decode(file_get_contents($uri));
}

}
Facade (exemplo - parte 3)
namespace MeuSistema;
use OpenLibrary\OpenLibraryREST;
use BackEndLivraria\PedidoDao;
use Singleton\Conexao;

class Pedido {
public \DateTimeInterface $data;
public int $quantidade;
public function __construct(\DateTimeInterface $data, int
$quantidade) {
$this->data = $data;
$this->quantidade = $quantidade;
}
}
class Livro {
public string $isbn;
public string $titulo;
public function __construct(string $isbn, string $titulo) {
$this->isbn = $isbn;
$this->nome = $titulo;
}
}
Facade (exemplo - parte 4)
class LibraryFacade {

private OpenLibraryREST $libraryRest;


private PedidoDao $pedidoDao;

public function __construct() {


$this->libraryRest = new OpenLibraryREST();
$this->pedidoDao = new PedidoDao(Conexao::getInstance()->getPdo());
}

public function recuperarLivroPorIsbn($isbn): ?Livro {


$livro = null;

$resultadoRest = $this->libraryRest->recuperarPorIsbn($isbn);
if ($resultadoRest) {
$livro = new Livro($isbn, $resultadoRest->{"ISBN:$isbn"}->title);
$livro->pedidos = array_map(fn($v) => new Pedido(
\DateTime::createFromFormat('Y-m-d H:i:s', $v['data']),
$v['quantidade']),
$this->pedidoDao->recuperarPorIsbn($isbn));
}
return $livro;
}
}

$facade = new LibraryFacade();


var_dump($facade->recuperarLivroPorIsbn('9780201485370'));
Flyweight
Flyweight é um pattern estrutural que permite
ajustar mais objetos à quantidade disponível de
RAM, compartilhando partes comuns do estado
entre vários objetos, em vez de manter todos os
dados em cada objeto (SHVETS, 2019).

Um flyweight é um objeto compartilhado que


pode ser usado em vários contextos
simultaneamente. Os flyweights não podem fazer
suposições sobre o contexto em que operam. O
conceito-chave aqui é a distinção entre estado
intrínseco e extrínseco (GAMMA et at, 1994).
Intrínseco (invariante) x Extrínseco (variante)
Dado imutável de um objeto geralmente é chamado de estado intrínseco. Ele vive dentro
do objeto; outros objetos podem apenas lê-lo, não alterá-lo. O restante do estado do objeto,
frequentemente modificado "por fora" por outros objetos, é chamado de estado extrínseco.
O padrão Flyweight sugere que você pare de armazenar o estado extrínseco dentro do
objeto. Em vez disso, você deve passar este estado para métodos específicos que
dependem dele. Somente o estado intrínseco permanece dentro do objeto, permitindo a sua
reutilização em diferentes contextos (SHVETS, 2019).

O estado intrínseco é armazenado no flyweight; consiste em informações independentes


do contexto do flyweight, tornando-o compartilhável. O estado extrínseco depende e varia
de acordo com o contexto do flyweight e, portanto, não pode ser compartilhado. Os objetos
do cliente são responsáveis por passar o estado extrínseco para o flyweight quando
necessário (GAMMA et al, 1994).
Flyweight (conceitual - parte 1)
class Flyweight {

private string $id = "";


public function __construct(string $id) {
var_dump("construção de $id");
$this->id = $id;
}
public function fazAlgo(string $extrinsico) {
echo "fazendo {$extrinsico} algo com o {$this->id} intrínseco";
}
}
class Extrinseco {

private int $valor;


private Flyweight $intrinseco;
public function __construct(Flyweight $intrinseco, int $valor) {
$this->intrinseco = $intrinseco;
$this->valor = $valor;
}
}
Flyweight (conceitual - parte 2)
class FlyweightFactory {

private array $flyweights = [];

public function getFlyweight(string $id): Flyweight {

if(!isset($this->flyweights[$id])){
$this->flyweights[$id] = new Flyweight($id);
}
return $this->flyweights[$id];

}
public function getTotalInCache(): int {
return count($this->flyweights);
}
}
Flyweight (conceitual - parte 3)
class Cliente {
private FlyweightFactory $flyweightFactory;
private array $dados;
public function __construct() {
$this->flyweightFactory = new FlyweightFactory();
}
public function adicionar(string $id, int $valor){
$this->dados[] = new Extrinseco(
$this->flyweightFactory->getFlyweight($id), $valor);
}
public function getDados() : array {
return $this->dados;
}
public function getTotalInCache() : int {
return $this->flyweightFactory->getTotalInCache();
}
}
$cliente = new Cliente();
$cliente->adicionar("A", 1);
$cliente->adicionar("B", 2);
$cliente->adicionar("C", 3);
$cliente->adicionar("A", 4);
$cliente->adicionar("C", 4);
echo count($cliente->getDados());
echo "<br/>";
echo $cliente->getTotalInCache();
Flyweight (exemplo - parte 1)
class Produto { //flyweight em si (intrínseco)
private string $nome;
public function __construct(string $nome) {
$this->nome = $nome;
}
public function __toString(): string {
return $this->nome;
}
}
class Pedido { //contexto (extrínsico)
private int $numeroPedido;
private Produto $produto;
private \DateTimeInterface $data;
public function __construct(int $numeroPedido, Produto $produto) {
$this->numeroPedido = $numeroPedido;
$this->produto = $produto;
$this->data = new \DateTimeImmutable();
}
public function processar() {
echo "Encomendando " . $this->produto . " do pedido nº " . $this->numeroPedido .
" no dia " . $this->data->format('d/m/Y h:i:s') . "</br>" . PHP_EOL;
}
}
Flyweight (exemplo - parte 2)
class Catalogo { //Fábrica Flyweight
private array $produtos = [];
public function getProduto(string $nomeProduto): Produto {
if (!isset($this->produtos[$nomeProduto])) {
$this->produtos[$nomeProduto] = new Produto($nomeProduto);
}
return $this->produtos[$nomeProduto];
}
public function getTotalProdutosFabricados(): int {
return count($this->produtos);
}
}
class Inventario { //Client Flyweight
private Catalogo $catalogo;
public array $pedidos = [];
public function __construct() {
$this->catalogo = new Catalogo();
}
function executarPedido(string $nomeProduto, int $numeroPedido) {
$produto = $this->catalogo->getProduto($nomeProduto);
$pedido = new Pedido($numeroPedido, $produto);
$this->pedidos[] = $pedido;
}
function processar() {
foreach ($this->pedidos as $key => $pedido) {
$pedido->processar();
unset($this->pedidos[$key]);
}
}
function getTotalProdutos(): int {
return $this->catalogo->getTotalProdutosFabricados();
}
Flyweight (exemplo - parte 3)
function formatBytes($size) {
$base = log($size) / log(1024);
$suffix = ["B", "KB", "MB", "GB", "TB"];
$f_base = floor($base);
return round(pow(1024, $base - floor($base)), 1) . $suffix[$f_base];
}
function testar(Inventario $inventario, $flyweight = true, $total = 10000) {
$produtosNome = ["Notebook Gamer XPTO", "Fone Bluetooth YY", "Sega Dreamcast"];
foreach (range(0, $total, count($produtosNome)) as $x) {
foreach ($produtosNome as $key => $nome) {
if($flyweight){
$inventario->executarPedido($nome, $x + $key);
}else{
$inventario->pedidos[] = new Pedido($x + $key, new Produto($nome));
}
}
}
}
$inventario = new Inventario();
testar($inventario);
echo "<br>" . PHP_EOL;
echo "total : " . formatBytes(memory_get_usage());
echo "<br>" . PHP_EOL;
$inventario->processar();
echo $inventario->getTotalProdutos();
Proxy
O Proxy é pattern estrutural que visa
permitir o fornecimento de um
placeholder (substituto) para outro
objeto. Um proxy controla o acesso ao
objeto original, permitindo que algo seja
feito antes ou depois do pedido chegar
ao objeto original (SHVETS, 2019).
Tipos Principais de Proxy
Existem quatro variações principais no padrão de proxy:

● Virtual: cria objetos custosos sob demanda (lazy load);


● Proteção: controla o acesso ao objeto original. Os proxies de proteção são úteis
quando os objetos devem ter direitos de acesso diferenciados;
● Remoto: fornece um representante local para um objeto localizado em outro endereço;
● Referência Inteligente: é uma substituição de um ponteiro simples que executa ações
quando um objeto é acessado. Os usos típicos incluem:
○ contar o número de referências ao objeto real para que ele possa ser liberado automaticamente quando não
há mais referências.
○ carregar um objeto persistente na memória quando ele é referenciado pela primeira vez.
○ verificar se o objeto real está bloqueado antes de ser acessado para garantir que nenhum outro objeto pode
alterá-lo.

(GAMMA et al, 1994) (SHVETS, 2019).


Proxy (conceitual - parte 1)
<?php

interface ProdutoOuServico {
public function fazAlgo();
}

class Usuario {
public int $nivelPermissao;
public function __construct(int $nivelPermissao){
$this->nivelPermissao = $nivelPermissao;
}
}

class ServicoReal implements ProdutoOuServico {

private string $nome;


public function __construct(string $nome) {
$this->nome = $nome;
$this->operacaoLocal();
}
private function operacaoLocal() {
echo "carregando {$this->nome} <br/>" . \PHP_EOL;
}
public function fazAlgo() {
echo "executando {$this->nome} <br/>" . \PHP_EOL;
}
}
Proxy (conceitual - parte 2)
class ProxyVirtual implements ProdutoOuServico {
private ?ProdutoOuServico $servico = null;
private string $nome;
public function __construct(string $nome) {
$this->nome = $nome;
}
public function fazAlgo() { //lazy
if ($this->servico === null) {
$this->servico = new ServicoReal($this->nome);
}
$this->servico->fazAlgo();
}
}
class ProxyProtecao implements ProdutoOuServico {
private ProdutoOuServico $produto;
private Usuario $produtoAssociado;
private const VALOR_MINIMO = 10;
public function __construct(string $nome, Usuario $produtoAssociado) {
$this->produtoAssociado = $produtoAssociado;
$this->produto = new ServicoReal($nome);
}
public function fazAlgo() {
if($this->produtoAssociado->nivelPermissao >= self::VALOR_MINIMO){
$this->produto->fazAlgo();
}else{
echo "Sem permissão";
}
}
}
Proxy (conceitual - parte 3)

//primeira implementação
$servico1 = new ProxyVirtual("Servico ABC 123");
$servico2 = new ProxyVirtual("Servico XPTO 9999");
$servico1->fazAlgo(); // Carregamento necessário
$servico1->fazAlgo(); // Carregamento desnecessário
$servico2->fazAlgo(); // Carregamento necessário
$servico2->fazAlgo(); // Carregamento desnecessário
$servico1->fazAlgo(); // Carregamento desnecessário
//segunda implementação
$servico3 = new ProxyProtecao("Servico Fulano 171", new Usuario(11));
$servico3->fazAlgo();
$servico3->fazAlgo();
Proxy (exemplo - parte 1)
<?php

interface FormaPagamento {
public function pagar(float $valor): bool;
}

class ContaBancaria implements FormaPagamento {


private float $saldo;
public function __construct(float $saldo = 0){
$this->saldo = $saldo;
}
public function pagar(float $valor): bool {
if ($this->saldo >= $valor) {
$this->saldo -= $valor;
return true;
}
return false;
}
public function getSaldo() : float {
return $this->saldo;
}
}
Proxy (exemplo - parte 2)
class CartaoDebito implements FormaPagamento {

private ContaBancaria $contaBancaria;


private \DateTimeInterface $validade;

public function __construct(ContaBancaria $contaBancaria, \DateTimeInterface


$validade) {
$this->contaBancaria = $contaBancaria;
$this->validade = $validade;
}

public function pagar(float $valor): bool {


if ($this->validade >= (new \DateTime())) {
return $this->contaBancaria->pagar($valor);
}else{
throw new \Exception('Cartão vencido !!!');
}

$contaBancaria = new ContaBancaria(1000);


$cartaoDebito = new CartaoDebito($contaBancaria, (new \DateTime('+1 week')));
var_dump($cartaoDebito->pagar(100));
Padrões Comportamentais

Os Padrões comportamentais estão relacionados aos algoritmos e à atribuição


de responsabilidades entre os objetos (GAMMA et at, 1994).

Os Padrões comportamentais descrevem interações entre objetos e enfocam


como os objetos se comunicam uns com os outros. Eles podem reduzir
fluxogramas complexos a meras interconexões entre objetos de várias classes.
Os Padrões comportamentais também são usados para fazer com que o
algoritmo utilizado em uma classe seja como um simples parâmetro ajustável em
tempo de execução (GOFPATTERNS, 2021).

Padrões comportamentais são voltados aos algoritmos e a designação de


responsabilidades entre objetos (SHVETS, 2019).
Chain of Responsibility
Chain of responsability é um pattern comportamental que
permite passar solicitações ao longo de uma cadeia de
manipuladores. Ao receber uma solicitação, cada
manipulador decide processar a solicitação ou passá-la
para o próximo manipulador na cadeia (SHVETS, 2019)

Evita acoplar o remetente de uma solicitação ao


destinatário, dando a mais de um objeto a chance de lidar
com a solicitação. Encadeie os objetos de requisição e
passe a solicitação ao longo da cadeia até que um objeto
lide com isso (GAMMA et al, 1994).

Chain of responsability é uma versão orientada a objeto do


idioma if ... else if ... else if ....... else ... endif, com o
benefício de que os blocos condição-ação podem ser
dinamicamente reorganizados e reconfigurados em tempo
de execução (WIKIPEDIA, 2020).
Chain of Responsability (problema)
$solicitacao = "A";
class Teste {

static function fazAlgo($solicitacao){


if($solicitacao == "A"){
return "processando $solicitacao com A !";
}elseif($solicitacao == "B"){
return "processando $solicitacao com B !";
}elseif($solicitacao == "C"){
return "processando $solicitacao com C !";
}
}
static function fazAlgo2($solicitacao){
switch($solicitacao){
case "A":
return "processando $solicitacao com A !";
break;
case "B":
return "processando $solicitacao com B !";
break;
case "C":
return "processando $solicitacao com C !";
break;
}
}
static function fazAlgo3($solicitacao){
return match($solicitacao){
"A" => "processando $solicitacao com A !",
"B" => "processando $solicitacao com B !",
"C" => "processando $solicitacao com C !",
};
}
}
echo Teste::fazAlgo($solicitacao);
echo Teste::fazAlgo2($solicitacao);
echo Teste::fazAlgo3($solicitacao);
Chain of Responsability (conceitual)
interface Manipulador { //Handler
public function setProximoManipulador(Manipulador $proximoManipulador): Manipulador;
public function podeManipular($solicitacao): bool;
}

abstract class ManipuladorBase implements Manipulador {

private ?Manipulador $proximoManipulador = null;


public final function manipular($solicitacao) {
if ($this->podeManipular($solicitacao)) {
$this->processar($solicitacao);
} else {
if ($this->proximoManipulador != null) {
$this->proximoManipulador->manipular($solicitacao);
}
}
}

public final function getproximoManipulador(): ?Manipulador {


return $this->proximoManipulador;
}
public final function setProximoManipulador(Manipulador $proximoManipulador): Manipulador {
$this->proximoManipulador = $proximoManipulador;
return $proximoManipulador; //fluent interface
}
protected abstract function processar($solicitacao);
}
Chain of Responsability (conceitual - parte 2)
class ManipuladorConcretoA extends ManipuladorBase {

public function podeManipular($solicitacao): bool {


if ($solicitacao == "A") {
return true;
}
return false;
}

protected function processar($solicitacao) {


echo "processando $solicitacao com " . self::class . "!";
}
}

class ManipuladorConcretoB extends ManipuladorBase {

public function podeManipular($solicitacao): bool {


if ($solicitacao == "B") {
return true;
}
return false;
}

protected function processar($solicitacao) {


echo "processando $solicitacao com " . self::class . "!!";
}
}
Chain of Responsability (conceitual - parte 3)
class ManipuladorConcretoC extends ManipuladorBase {

public function podeManipular($solicitacao): bool {


if ($solicitacao == "C" && (new \DateTime())->format('d') < 30) {
return true;
}
return false;
}

protected function processar($solicitacao) {


echo "processando $solicitacao com " . self::class . "!!!";
}

$chain = new ManipuladorConcretoA();

//montando cadeia manualmente


$chain->setProximoManipulador(new ManipuladorConcretoB())
->setProximoManipulador(new ManipuladorConcretoC());
Chain of Responsability (conceitual - parte 4)
//montando cadeia com reflection
function getChainAuto($superClass = ManipuladorBase::class, $metodo =
'setProximoManipulador'): object {

$manipuladores = array_filter(
array_map(
fn($c) => is_subclass_of($c, $superClass) ? new $c : null,
get_declared_classes()
));

foreach ($manipuladores as $k => $v) {


if ($k !== array_key_last($manipuladores)) {
$v->{$metodo}($manipuladores[$k + 1]);
}
}

return array_shift($manipuladores);
}
$chainAuto = getChainAuto();

//enviando requisição
echo $chain->manipular("C");
echo $chainAuto->manipular("C");
Chain of Responsability (exemplo - parte 1)
<?php
class Pessoa {
protected string $nome;
protected \DateTimeInterface $dataNascimento;
public function __construct(string $nome, \DateTimeInterface
$dataNascimento) {
$this->nome = $nome;
$this->dataNascimento = $dataNascimento;
}
public function getNome(): string {
return $this->nome;
}
public function getDataNascimento(): \DateTimeInterface {
return $this->dataNascimento;
}
public function getIdade(): int {
return (new \DateTime())->diff($this->dataNascimento)->y;
}
}
Chain of Responsability (exemplo - parte 2)
class Cliente extends Pessoa {

private ?Pessoa $dependente;


private string $matricula;

public function __construct(string $nome, \DateTimeInterface $dataNascimento, string $matricula,


Pessoa $dependente = null) {
parent::__construct($nome, $dataNascimento);
$this->matricula = $matricula;
$this->dependente = $dependente;
}

public function setDependente(Pessoa $dependente): void {


$this->dependente = $dependente;
}

public function getDependente(): ?Pessoa {


return $this->dependente;
}

public function getMatricula(): string {


return $this->matricula;
}

}
Chain of Responsability (exemplo - parte 3)
class Requisicao {

private Pessoa $requisitor;


private string $tipo;

public function __construct(Pessoa $requisitor, string $tipo) {


$this->requisitor = $requisitor;
$this->tipo = $tipo;
}

public function getRequisitor(): Pessoa {


return $this->requisitor;
}

public function getTipo(): string {


return $this->tipo;
}

}
Chain of Responsability (exemplo - parte 4)
interface Manipulador {
public function setProximoManipulador(Manipulador $proximoManipulador): Manipulador;
public function podeManipular(Requisicao $requisicao): bool;
}

abstract class ManipuladorBase implements Manipulador {

private ?Manipulador $proximoManipulador = null;

public final function manipular(Requisicao $requisicao) : ?string{


if ($this->podeManipular($requisicao)) {
return $this->processar($requisicao);
}
if ($this->proximoManipulador != null) {
return $this->proximoManipulador->manipular($requisicao);
}
return null;
}
public final function getproximoManipulador(): ?Manipulador {
return $this->proximoManipulador;
}
public final function setProximoManipulador(Manipulador $proximoManipulador): Manipulador {
$this->proximoManipulador = $proximoManipulador;
return $proximoManipulador; //fluent interface
}
protected abstract function processar(Requisicao $requisicao) : string;
Chain of Responsability (exemplo - parte 5)
class AtendimentoNovoCliente extends ManipuladorBase {

public function podeManipular(Requisicao $requisicao): bool {


return get_class($requisicao->getRequisitor()) == Pessoa::class;
}

protected function processar(Requisicao $requisicao) : string{


return "atendendo novo cliente solitando: {$requisicao->getTipo()}";
}
}

class AtendimentoClienteSemDependente extends ManipuladorBase {

public function podeManipular(Requisicao $requisicao): bool {


return get_class($requisicao->getRequisitor()) == Cliente::class && !($requisicao->getRequisitor()->getDependente());
}
protected function processar(Requisicao $requisicao) : string {
return "atendendo cliente sem dependente solitando: {$requisicao->getTipo()}";
}
}
class AtendimentoClienteComDependente extends ManipuladorBase {
public function podeManipular(Requisicao $requisicao): bool {
return get_class($requisicao->getRequisitor()) == Cliente::class && $requisicao->getRequisitor()->getDependente();
}

protected function processar(Requisicao $requisicao) : string {


return "atendendo cliente {$requisicao->getRequisitor()->getNome()} com o dependente {$requisicao->getRequisitor()->getDependente()->getNome()}";
}
}
Chain of Responsability (exemplo - parte 6)
<?php
$pessoa1 = new Pessoa("João", \DateTime::createFromFormat('d/m/Y', '20/01/1988'));
$cliente1 = new Cliente("Maria", \DateTime::createFromFormat('d/m/Y', '01/10/1977'), '001');
$cliente2 = new Cliente("José", \DateTime::createFromFormat('d/m/Y', '01/10/1977'), '002',
new Pessoa("Enzo", \DateTime::createFromFormat('d/m/Y', '14/09/2018')));
$chain = new AtendimentoNovoCliente();
$chain->setProximoManipulador(new AtendimentoClienteSemDependente())
->setProximoManipulador(new AtendimentoClienteComDependente());

echo $chain->manipular(new Requisicao($pessoa1, "informações"));


echo "<br>" . PHP_EOL;
echo $chain->manipular(new Requisicao($cliente1, "dúvida"));
echo "<br>" . PHP_EOL;
echo $chain->manipular(new Requisicao($cliente2, "reclamação"));
Command
Command é um pattern comportamental que transforma
uma solicitação em um objeto independente que contém
todas as informações sobre a solicitação. Essa
transformação permite parametrizar métodos com
diferentes solicitações, atrasar ou enfileirar a execução
de uma solicitação e oferecer suporte a operações que
podem ser desfeitas (SHVETS, 2019).

Participantes:

1. Command: declara a interface para executar uma


operação
2. Command Concreto: vincula a ação ao Receptor
3. Receptor: recebe as ações do Command
4. Invocador: lida com uma coleção de comandos e
determina quando os comandos são executados.
5. Cliente: gerencia interações entre Receptor /
Comando e Comando / Invocador.
Command (conceitual - parte 1)
<?php
interface Command {
public function executar();
}
interface Receptor {
public function fazAlgo();
}
class ConcreteCommand1 implements Command {
private Receptor $receptor;
public function __construct(Receptor $receptor) {
$this->receptor = $receptor;
}
public function executar() {
$this->receptor->fazAlgo();
}
}
class Invocador {
private ?Command $command;
public function __construct(Command $command) {
$this->command = $command;
}
public function setCommand(Command $command): void {
$this->command = $command;
}
public function executarCommand(){
$this->command->executar();
}
}
Command (conceitual - parte 2)
class Receptor1 implements Receptor {

public function fazAlgo(){


echo "fazendo algo !";
}

$receptor = new Receptor1();


$command = new ConcreteCommand1($receptor);
$invocador = new Invocador($command);
$invocador->executarCommand();
Command (exemplo - parte 1)
class Configuracao {//similar ao utilizado no singleton}
class Conexao {//similar ao utilizado no singleton}
//commands
abstract class Command {
protected Receptor $receptor;
public function __construct(Receptor $receptor) {
$this->receptor = $receptor;
}
abstract function executar();
}

class CommandLogar extends Command {


public string $texto;
public function __construct(Receptor $receptor, $texto = "") {
parent::__construct($receptor);
$this->texto = $texto;
}
public function executar() {
$this->receptor->logar($this->texto);
}
}

class CommandLimpar extends Command {


public function executar() {
$this->receptor->limparTudo();
}
}
Command (exemplo - parte 2)
abstract class Receptor {
protected function getData(): string {
return (new \DateTime())->format('Y-m-d H:i:s');
}
abstract function logar(string $texto);
abstract function limparTudo();
}

class Banco extends Receptor {


private ?\PDO $pdo = null;
public function __construct() {
$this->pdo = Conexao::getInstance()->getPdo();
}
public function limparTudo() {
$this->pdo->prepare("DELETE FROM log")->execute();
}
public function logar(string $texto) {
$this->pdo
->prepare("INSERT INTO log (data,texto) VALUES (:data, :texto)")
->execute([
'data' => $this->getData(),
'texto' => $texto
]);
}
}
Command (exemplo - parte 3)
class Txt extends Receptor {
private string $arquivo = "log.txt";
public function __construct() {
touch($this->arquivo);
}
public function limparTudo() {
file_put_contents($this->arquivo, "");
}
public function logar(string $texto) {
file_put_contents($this->arquivo, $this->getData() . " " . $texto . PHP_EOL, FILE_APPEND | LOCK_EX);
}
}
class Invocador {
private \SplQueue $commands;
public function __construct() {
$this->commands = new \SplQueue();
}
public function armazenarExecutar(Command $command) {
$this->commands->push($command);
$command->executar();
}
public function getHistorico(): \SplQueue {
return $this->commands;
}
}
$receptor = new Banco(); //Txt();
$command = new CommandLogar($receptor, "testando"); //CommandLimpar
$invocador = new Invocador();
$invocador->armazenarExecutar($command);
Interpreter
O Intepreter é um pattern comportamental que
descreve como definir uma gramática para uma
“linguagem simples”, representando sentenças desta
linguagem e interpretando estas sentenças (GAMMA
et al, 1994);

O Interpreter é um design pattern que especifica como


analisar sentenças de uma linguagem. A ideia básica é
ter uma classe para cada símbolo (terminal ou não
terminal) em uma linguagem de computador
especializada.

Permite representar uma sentença de uma linguagem


por uma Árvore Binária de Expressão (Binary
Expression Tree);
Interpreter
Os participantes de uma implementação típica do pattern Interpreter são:

AbstractExpression: define a interface do intérprete e estipula a operação de interpretação do intérprete. A interface Interpret, como o próprio
nome sugere, é usada especificamente para explicar as funções a serem implementadas pelo interpretador. (Por exemplo, a interface Interpret no
interpretador de adição é para completar a função de adição de dois operandos).

TerminalExpression: interpretador de terminador, usado para implementar as operações relacionadas ao terminador nas regras gramaticais, não
contém mais outros interpretadores, se você usar o modo de combinação para construir uma árvore de sintaxe abstrata, é equivalente ao objeto
folha no modo de combinação, você pode Existem vários intérpretes de símbolo de terminal.

NonterminalExpression: um intérprete não terminal, usado para implementar operações não relacionadas ao terminal em regras gramaticais,
geralmente um intérprete corresponde a uma regra gramatical e pode conter outros intérpretes. Se você usar o modo de combinação para construir
uma árvore de sintaxe abstrata, é equivalente a uma combinação de objetos combinados no padrão. Pode haver vários intérpretes não terminais.

Contexto: Contexto, geralmente contém dados ou funções comuns exigidas por cada intérprete. Este contexto desempenha um papel muito
importante no modo de intérprete. Geralmente é usado para transferir dados compartilhados por todos os intérpretes, e intérpretes posteriores
podem obter esses valores aqui.

Cliente: O cliente refere-se ao cliente que usa o intérprete. Normalmente, as expressões feitas de acordo com a sintaxe da linguagem são
convertidas em uma árvore sintática abstrata descrita pelo objeto intérprete, e então a operação de interpretação é chamada.
Interpreter (conceitual - parte 1)
<?php
class Contexto {
public string $valor = "";
public function __construct(string $valor){
$this->valor = $valor;
}
}

interface Expression {
public function interpretar(Contexto $contexto);
}

class ExpressaoTerminal implements Expression {


public function interpretar(Contexto $contexto) {
echo "interpretando {$contexto->valor}";
}
}

class ExpressaoNaoTerminal implements Expression {


public function interpretar(Contexto $contexto) {
echo "interpretando {$contexto->valor}";
}
}
Interpreter (conceitual - parte 2)

//*******cliente
$contexto = new Contexto("1234");

// populando 'abstract syntax tree'


$list = [];
$list[] = new ExpressaoTerminal();
$list[] = new ExpressaoNaoTerminal();
$list[] = new ExpressaoTerminal();
$list[] = new ExpressaoTerminal();

// Interpretar
foreach ($list as $key => $expressao) {
$expressao->interpretar($contexto);
}
Interpreter (exemplo - parte 1)
interface Expressao {
public function interpretar(array $contexto): int;
}

class ExpressaoTerminal implements Expressao {

private string $nome;

public function __construct(string $nome) {


$this->nome = $nome;
}
public function interpretar(array $contexto): int {
return $contexto[$this->nome] ?? throw new \DomainException(" o valor de {$this->nome} não existe
nos dados");
}
}

abstract class OperacaoAritimetica implements Expressao { //não-terminal

protected Expressao $expressaoEsquerda;


protected Expressao $expressaoDireita;
public function __construct(Expressao $expressaoEsquerda, Expressao $expressaoDireita) {
$this->expressaoEsquerda = $expressaoEsquerda;
$this->expressaoDireita = $expressaoDireita;
}
public static abstract function getSimbolo(): string;
}
Interpreter (exemplo - parte 2)
class Adicao extends OperacaoAritimetica {

public function interpretar(array $contexto): int {


return $this->expressaoEsquerda->interpretar($contexto) +
$this->expressaoDireita->interpretar($contexto);
}

public static function getSimbolo(): string {


return "+";
}
}

class Subtracao extends OperacaoAritimetica {

public function interpretar(array $contexto): int {


return $this->expressaoEsquerda->interpretar($contexto) -
$this->expressaoDireita->interpretar($contexto);
}

public static function getSimbolo(): string {


return "-";
}
}
Interpreter (exemplo - parte 3)
class Calculadora { //cliente
private Expressao $expressao;
public function __construct(string $texto) {
$stack = new \SplStack();
$expressaoEsquerda = null;
$expressaoDireita = null;
$classesOperacoes = array_filter(get_declared_classes(),
fn($class) => is_subclass_of($class, OperacaoAritimetica::class));

for ($i = 0; $i < strlen($texto); $i++) {


$operacoes = array_filter($classesOperacoes, fn($s) =>
$s::getSimbolo() == $texto[$i]);

$classeNaoTerminal = array_shift($operacoes);

if (!$classeNaoTerminal) {
$stack->push(new ExpressaoTerminal($texto[$i]));
} else {
$expressaoEsquerda = $stack->pop();
$expressaoDireita = new ExpressaoTerminal($texto[++$i]);
$stack->push(new $classeNaoTerminal($expressaoEsquerda, $expressaoDireita));
}
}
$this->expressao = $stack->pop();
}
public function calcular(array $dados) {
return $this->expressao->interpretar($dados);
}
}
Interpreter (exemplo - parte 4)

$calculator = new Calculadora("a+b+a");


$dados = [];
$dados["a"] = 8;
$dados["b"] = 22;
echo "O resultado é {$calculator->calcular($dados)}";
Iterator
Iterator é um pattern comportamental
que permite atravessar elementos de
uma coleção sem expor sua
representação subjacente (SHVETS,
2019).
Iterator (conceitual - parte 1)
class IteratorConcreto implements \Iterator {

private int $posicaoAtual = 0;


private AgregadorConcreto $agregadorConcreto;

public function __construct(AgregadorConcreto $agregadorConcreto) {


$this->agregadorConcreto = $agregadorConcreto;
}
public function current() {
return $this->agregadorConcreto->getItem($this->posicaoAtual);
}
public function next() {
$this->posicaoAtual++;
}
public function rewind() {
$this->posicaoAtual = 0;
}
public function valid(): bool {
return !is_null($this->agregadorConcreto->getItem($this->posicaoAtual));
}
public function key(): int {
return $this->posicaoAtual;
}
}
Iterator (conceitual - parte 2)
class AgregadorConcreto implements \IteratorAggregate {

private array $itens = [];


public function getIterator() {
return new IteratorConcreto($this);
}
public function addItem(string $string): void {
$this->itens[] = $string;
}
public function getItem(int $key) : ?string {
if (isset($this->itens[$key])) {
return $this->itens[$key];
}
return null;
}
}

//cliente
$agregadorConcreto = new AgregadorConcreto();
$agregadorConcreto->addItem('Brincadeira em excesso');
$agregadorConcreto->addItem('Mosantos de Villar');
$agregadorConcreto->addItem('Bobo da Corte');
$agregadorConcreto->addItem('Meus benefícios');

foreach ($agregadorConcreto as $texto) {


var_dump($texto);
}
Iterator (exemplo - parte 1)
<?php
class Pessoa {
private int $id;
private string $nome;
private string $email;
public function __construct(int $id, string $nome, string $email) {
$this->id = $id;
$this->nome = $nome;
$this->email = $email;
}
public function getId(): int {
return $this->id;
}
public function getNome(): string {
return $this->nome;
}
public function getEmail(): string {
return $this->email;
}
public function __toString(): string {
return sprintf('%d, %s, %s', $this->id, $this->nome, $this->email);
}

}
Iterator (exemplo - parte 2)
class PessoaCsvIterator implements \Iterator {
private PessoaCSVAggregate $pessoaAggregate;
protected int $posicaoAtual = -1;
public function __construct(PessoaCSVAggregate $pessoaAggregate) {
$this->pessoaAggregate = $pessoaAggregate;
}
public function rewind(): void {
$this->posicaoAtual = -1;
rewind($this->pessoaAggregate->getArquivoResource());
fgets($this->pessoaAggregate->getArquivoResource()); //pular a primeira linha
}
public function current(): Pessoa {
$pessoa = $this->pessoaAggregate->getPessoa(
fgetcsv($this->pessoaAggregate->getArquivoResource()
));
$this->posicaoAtual++;
return $pessoa;
}
public function key(): int {
return $this->posicaoAtual;
}
public function next(): bool {
return !feof($this->pessoaAggregate->getArquivoResource());
}
public function valid(): bool {
if (!$this->next()) {
fclose($this->pessoaAggregate->getArquivoResource());
return false;
}
return true;
}
}
Iterator (exemplo - parte 3)
class PessoaCSVAggregate implements \IteratorAggregate {

private $arquivoResource;

public function __construct(string $file) {


$this->arquivoResource = fopen($file, 'rb');
}
public function getIterator(): PessoaCsvIterator {
return new PessoaCsvIterator($this);
}
public function getArquivoResource() {
return $this->arquivoResource;
}
public function getPessoa(array $array): Pessoa {
return new Pessoa($array[0], $array[1], $array[2]);
}
}
//cliente
$csv = new PessoaCSVAggregate('emails.csv');
foreach ($csv as $key => $pessoa) {
echo "key : $key <br/>";
echo "$pessoa <br/>";
}
Mediator
O Mediator (mediador) é um design pattern
comportamental que visa reduzir
dependências caóticas entre objetos. O
pattern restringe as comunicações diretas
entre os objetos e os força a colaborar
apenas através de um objeto mediador
(SHVETS, 2019).

Objetos fortemente acoplados são difíceis de


implementar, alterar, testar e reutilizar porque
se referem e conhecem muitos objetos
diferentes (WIKIPEDIA, 2020).
Mediator (conceitual - parte 1)
interface Mediador {
public function intermediar(ComponenteAssociado $remetente, string $evento): void;
}

class MediadorConcreto implements Mediador {


private ComponenteAssociadoA $componenteA;
private ComponenteAssociadoB $componenteB;
public function __construct(ComponenteAssociadoA $c1, ComponenteAssociadoB $c2) {
$this->componenteA = $c1;
$this->componenteA->setMediador($this);
$this->componenteB = $c2;
$this->componenteB->setMediador($this);
}
public function intermediar(ComponenteAssociado $remetente, string $evento): void {
if ($evento == "A") {
echo "Mediador reage ao evento A e executa: <br>".PHP_EOL;
$remetente->setEstado("A");
$this->componenteB->fazAlgoZ();
}
if ($evento == "D") {
echo "Mediador reage ao evento D e executa:".PHP_EOL;
$remetente->setEstado("B");
$this->componenteA->fazAlgoY();
$this->componenteB->fazAlgoZ();
}
}
}
Mediator (conceitual - parte 2)
//colegas
abstract class ComponenteAssociado {

protected ?Mediador $mediador;


protected ?string $estado;

public function __construct(Mediador $mediador = null) {


$this->mediador = $mediador;
}

public function setMediador(Mediador $mediador): void {


$this->mediador = $mediador;
}

public function setEstado($estado){


$this->estado = $estado;
}

public function getEstado($estado){


return $this->estado;
}
}
Mediator (conceitual - parte 3)
class ComponenteAssociadoA extends ComponenteAssociado {
public function fazAlgoX(): void {
echo "executando ".__FUNCTION__."<br>".PHP_EOL;
$this->mediador->intermediar($this, "A");
}
public function fazAlgoY(): void {
echo "executando ".__FUNCTION__."<br>".PHP_EOL;
$this->mediador->intermediar($this, "B");
}
}
class ComponenteAssociadoB extends ComponenteAssociado {
public function fazAlgoZ(): void {
echo "executando ".__FUNCTION__."<br>".PHP_EOL;
$this->mediador->intermediar($this, "C");
}
public function fazAlgoXY(): void {
echo "executando ".__FUNCTION__."<br>".PHP_EOL;
$this->mediador->intermediar($this, "D");
}
}
//cliente
$c1 = new ComponenteAssociadoA();
$c2 = new ComponenteAssociadoB();
$mediador = new MediadorConcreto($c1, $c2);
$c1->fazAlgoX();
echo "<br/>";
$c2->fazAlgoXY();
Mediator (exemplo - parte 1)
<?php
Trait CliColorido {

abstract function getCor(): int;

function printar(string $texto) {


echo "\033[{$this->getCor()}m$texto" . PHP_EOL;
fgets(STDIN);
}

abstract class ControleTrafegoAereoMediator {

protected array $aeronaves = [];

abstract function existeOutraAeronaveComPrioridadeParaPouso(Aeronave $aeronave): bool;


abstract function receberLocalizacao(Aeronave $aeronave);
abstract function registrar(Aeronave $aeronave);
abstract function notificarPouso(Aeronave $aeronave);
abstract function notificarPousoConcluido(Aeronave $aeronave);

public final function getOutrasAeronaves(Aeronave $aeronave): array {


return array_filter($this->aeronaves, fn($a) => $a != $aeronave);
}

}
Mediator (exemplo - parte 2)
class ControleTrafegoAereoGaleao extends ControleTrafegoAereoMediator {
use CliColorido;
public function registrar(Aeronave $aeronave) {
$this->printar(">Trafego Aéreo: Olá {$aeronave->numeroVoo}! Estou com você no radar ! Diga-me se você quer pousar ou mudar sua localização!");
$this->aeronaves[] = $aeronave; //
}
public function notificarPouso(Aeronave $aeronave) {
$this->printar(">Trafego Aéreo: Recebido {$aeronave->numeroVoo}! Vou avisar que você está pousando para os outros pilotos!");
foreach ($this->getOutrasAeronaves($aeronave) as $outraAeronave) {
$outraAeronave->notificarAeronavesSobrePouso($aeronave->numeroVoo);
}
}
public function notificarPousoConcluido(Aeronave $aeronave) {
$this->printar(">Trafego Aéreo: Recebido {$aeronave->numeroVoo}! Vou avisar que você já pousou os outros pilotos!");
foreach ($this->getOutrasAeronaves($aeronave) as $outraAeronave) { //usar_map
$outraAeronave->notificarSobrePistaDisponivel();
}
}
public function existeOutraAeronaveComPrioridadeParaPouso(Aeronave $remetente): bool {
$this->printar(">Trafego Aéreo: Eu recebi o seu pedido {$remetente->numeroVoo}, estou verificando as prioridades para pousar...");
foreach ($this->getOutrasAeronaves($remetente) as $outraAeronave) {
if ($outraAeronave->altitude <= $remetente->altitude) {
$this->printar(">Trafego Aéreo: Você precisa aguardar! O voô {$outraAeronave->numeroVoo} tem prioridade para pousar !");
return true;
}
}
return false;
}
public function receberLocalizacao(Aeronave $aeronave) {
$this->printar(">Trafego Aéreo: Recebido {$aeronave->numeroVoo}! Vou notificar sua nova localização para os outros pilotos!");
foreach ($this->getOutrasAeronaves($aeronave) as $outraAeronave) {
$outraAeronave->veficarDistancia($aeronave->altitude);
}
}
public function getCor(): int {
return 37;
}
}
Mediator (exemplo - parte 3)
abstract class Aeronave {
use CliColorido;
private ControleTrafegoAereoMediator $mediator;
public int $altitude;
public string $numeroVoo;

public function __construct(ControleTrafegoAereoMediator $mediator, string $numeroVoo, int $altitude) {


$this->mediator = $mediator;
$this->numeroVoo = $numeroVoo;
$this->altitude = $altitude;
$this->mediator->registrar($this);
}
public final function aterrisar() {
$this->printar(">{$this->numeroVoo}: solicito autorização para pouso.");
if ($this->mediator->existeOutraAeronaveComPrioridadeParaPouso($this)) {
$this->printar(">{$this->numeroVoo}: recebido! Eu irei aguardar");
return;
}
$this->printar(">{$this->numeroVoo}: ok! Vou iniciar os procedimentos para pouso");
$this->mediator->notificarPouso($this);
$this->printar(">{$this->numeroVoo}: estou aterrisando !");
$this->printar(">{$this->numeroVoo}: acabei de pousar ! ");
$this->mediator->notificarPousoConcluido($this);
}
public function notificarAeronavesSobrePouso(string $numeroVoo) { //trocar para serNotificado….
$this->printar(">{$this->numeroVoo}: Recebido! Favor me infomar quando o voô {$numeroVoo} pousar");
}
public function notificarSobrePistaDisponivel() {
$this->printar(">{$this->numeroVoo}: Recebido! Estou próximo ao aeroporto! Eu tentarei pousar em 20 minutos!");
}
public function veficarDistancia(int $altitudeOutraAeronave) {
$this->printar(">{$this->numeroVoo}: Recebido! Verificando Distância de Segurança");
if (abs($altitudeOutraAeronave - $this->altitude) < 100) {
$this->printar(">{$this->numeroVoo}: Estou próximo de outra aeronave, vou diminuir a velocidade");
}
}
public final function setAltitude(int $altitude) {
$this->altitude += $altitude;
$this->printar(">{$this->numeroVoo}: Subindo mais $altitude pés para a altitude de {$this->altitude} pés!");
$this->mediator->receberLocalizacao($this);
}
}
Mediator (exemplo - parte 4)
//"colleagues concretos"
class Boeing747 extends Aeronave {
public function getCor(): int {
return 32;
}
}

class Boeing787 extends Aeronave {


public function getCor(): int {
return 33;
}

class Boeing777 extends Aeronave {


public function getCor(): int {
return 34;
}
}

class AirbusA320 extends Aeronave {


public function getCor(): int {
return 31;
}
}
Mediator (exemplo - parte 5)

echo "\033[37mIniciando...." . PHP_EOL;


echo <<<ASCII
_
-=\`\
|\ ____\_\__
-=\c`""""""" "`) Sᴇᴊᴀ ʙᴇᴍ ᴠɪɴᴅᴏ !
`~~~~~/ /~~`
-==/ /
'-'
ASCII.PHP_EOL;
$mediator = new ControleTrafegoAereoGaleao();
$vooA = new AirbusA320($mediator, "A37685", 2000);
$vooB = new Boeing787($mediator, "B67329", 1500);
$vooC = new Boeing747($mediator, "C234545", 1460);
$vooD = new Boeing777($mediator, "D23488", 1300);
$vooA->aterrisar();
$vooB->aterrisar();
$vooC->setAltitude(100);
$vooD->aterrisar();
echo "\033[37m" . PHP_EOL;
Memento
“Memento: um objeto que você guarda para se lembrar de uma
pessoa, local ou evento” (Dicionário de Cambridge).

Memento é um pattern comportamental que permite salvar e


restaurar o estado anterior de um objeto sem revelar os detalhes
de sua implementação (SHVETS, 2019).

Este Pattern possui três participantes:

1. Originator: objeto capaz produzir instantâneos de seu


próprio estado, bem como restaurar seu estado de
instantâneos quando necessário.
2. Memento: objeto de valor que como um instantâneo do
estado do originador. É uma prática comum tornar a
lembrança imutável e transmitir os dados apenas uma
vez, por meio do construtor.
3. Caretaker (zelador): sabe não apenas "quando" e "por
que" capturar o estado do originador, mas também
quando o estado deve ser restaurado (SHVETS, 2019).
Memento (conceitual - parte 1)
interface Originator {
public function salvar(): Memento;
public function restaurar(Memento $memento): void;
public function __toString(): string;
}

class OriginatorConcreto implements Originator {

private array $estado = [];


public function __construct(string $texto) {
$this->estado[] = $texto;
}
public function setEstado($texto): void {
$this->estado[] = $texto;
}
public function salvar(): Memento { //snapshot
return new Memento($this->estado);
}
public function restaurar(Memento $memento): void {
$this->estado = $memento->getEstado();
}
public function __toString(): string {
return "printando: " . implode(" ", $this->estado) . PHP_EOL;
}
}
Memento (conceitual - parte 2)
class Memento {
private $estado;
public function __construct($estado) {
$this->estado = $estado;
}
public function getEstado() {
return $this->estado;
}
}
class Caretaker {
private \SplStack $mementos;
private Originator $originator;
public function __construct(Originator $originator) {
$this->mementos = new \SplStack();
$this->originator = $originator;
}
public function persistirEstadoAtual(): void {
$this->mementos->push($this->originator->salvar());
}
public function print() {
echo $this->originator;
}
public function desfazer(): void {
if (!$this->mementos->count()) {
return;
}
$memento = $this->mementos->pop();
$this->originator->restaurar($memento);
}
}
Memento (conceitual - parte 3)
//cliente
$originator = new OriginatorConcreto("ABC123");
$caretaker = new Caretaker($originator);
$caretaker->persistirEstadoAtual();
$originator->setEstado("XYZ456");
$caretaker->persistirEstadoAtual();
$originator->setEstado("WSD999");
$caretaker->persistirEstadoAtual();
$originator->setEstado("POO111");
//$caretaker->persistirEstadoAtual();
$caretaker->print();
$caretaker->desfazer();
$caretaker->desfazer();
$caretaker->print();
Memento (exemplo - parte 1)
<?php
class Ingrediente {
private string $nome;
private float $valor;
public function __construct(string $nome, float $valor) {
$this->nome = $nome;
$this->valor = $valor;
}
public function getValor() {
return $this->valor;
}
public function getNome() {
return $this->nome;
}
public function __toString(): string {
return $this->nome;
}
}
class Destilado extends Ingrediente {
private string $teorAlcoolico;
public function __construct(string $nome, float $valor, float $teorAlcoolico) {
parent::__construct($nome, $valor);
$this->teorAlcoolico = $teorAlcoolico;
}
public function getTeorAlcoolico(): float {
return $this->teorAlcoolico;
}
}
Memento (exemplo - parte 2)
interface Originator {
public function salvar(): Memento;
public function restaurar(Memento $memento): void;
public function __toString(): string;
}

class Coquetel implements Originator {


private array $estado = [];
public function __construct(Destilado $destilado) {
$this->estado[] = $destilado;
}
public function addIngrediente(Ingrediente $ingrediente): void {
$this->estado[] = $ingrediente;
}
public function __toString(): string {
return "Coquetel R$ {$this->getCustoTotal()} feito de: " . implode(", ", $this->estado) . PHP_EOL;
}
private function getCustoTotal(): float {
return array_reduce($this->estado,
fn($total, Ingrediente $r) => $total + $r->getValor(), 0);
}
public function salvar(): Memento { //snapshot
return new Memento($this->estado);
}
public function restaurar(Memento $memento): void {
$this->estado = $memento->getEstado();
}
}
Memento (exemplo - parte 3)
class Memento {
private $estado;
public function __construct($estado) {
$this->estado = $estado;
}
public function getEstado() {
return $this->estado;
}
}
class Caretaker {
private \SplStack $mementos;
private Originator $coquetel;
public function __construct(Originator $coquetel) {
$this->mementos = new \SplStack();
$this->originator = $coquetel;
}
public function persistirEstadoAtual(): void {
$this->mementos->push($this->originator->salvar());
}
public function print() {
echo $this->originator;
}
public function desfazer(): void {
if (!$this->mementos->count()) {
return;
}
$memento = $this->mementos->pop();
$this->originator->restaurar($memento);
}
}
Memento (exemplo - parte 4)
//cliente
$rum = new Destilado('rum', 5.00, 70);
$cachaca = new Destilado('cachaca', 1.00, 48);
$vodka = new Destilado('vodka', 1.99, 40);
$acucar = new Ingrediente('açucar', 0);
$limao = new Ingrediente('limão', 1.00);
$creme1 = new Ingrediente('creme de coco', 1.00);

$coquetel = new Coquetel($cachaca);


$caretaker = new Caretaker($coquetel);
$caretaker->persistirEstadoAtual();
$coquetel->addIngrediente($acucar);
$caretaker->persistirEstadoAtual();
$coquetel->addIngrediente($limao);
$caretaker->persistirEstadoAtual();
$coquetel->addIngrediente($creme1);
//$caretaker->persistirEstadoAtual();
$caretaker->print();
$caretaker->desfazer();
$caretaker->desfazer();
$caretaker->print();
Observer
Observer é um pattern comportamental que
permite definir um mecanismo de subscrição
para notificar vários objetos sobre quaisquer
eventos que ocorram no objeto que estão
observando (SHVETS, 2019).

O Observer é um pattern no qual um objeto,


chamado de sujeito (subject), mantém uma
lista de seus dependentes, chamados
observadores, e os notifica automaticamente
sobre qualquer alteração de estado, geralmente
chamando um de seus métodos (WIKIPEDIA,
2020).
Observer (conceitual - parte 1)
abstract class ItemInteresse implements \SplSubject {

protected $estado;
protected \SplObjectStorage $observers;

public function __construct() {


$this->observers = new \SplObjectStorage;
}

public function attach(\SplObserver $observer): void {


$this->observers->attach($observer);
}

public function detach(\SplObserver $observer): void {


$this->observers->detach($observer);
}

public function getEstado() {


return $this->estado;
}

}
Observer (conceitual - parte 2)
class ItemInteresseConcreto extends ItemInteresse {

const INCIALIZADO = "INICIALIZADO";


const FINALIZADO = "FINALIZADO";

public function notify(): void {


echo "Subject: Notificando observers...<br/>" . PHP_EOL;
foreach ($this->observers as $observer) {
$observer->update($this);
}
}

public function inicializar(): void {


$this->estado = self::INCIALIZADO;
$this->notify();
}

public function finalizar(): void {


$this->estado = self::FINALIZADO;
$this->notify();
}

}
Observer (conceitual - parte 3)
class ObservadorConcretoA implements \SplObserver {

public function update(\SplSubject $subject): void {


echo self::class . ": reagindo ao evento ! <br/>" . PHP_EOL;
}

}
class ObservadorConcretoB implements \SplObserver {

public function update(\SplSubject $subject): void {


if ($subject instanceof ItemInteresseConcreto && $subject->getEstado() ==
ItemInteresseConcreto::INCIALIZADO) {
echo self::class . ": reagindo ao evento ! <br/>" . PHP_EOL;
}
}

}
//cliente
$subject = new ItemInteresseConcreto();
$o1 = new ObservadorConcretoA();
$subject->attach($o1);
$o2 = new ObservadorConcretoB;
$subject->attach($o2);
$subject->inicializar();
$subject->finalizar();
Observer (exemplo - parte 1)
class ExceptionHandler implements \SplSubject {

private \SplObjectStorage $observers;


private ?\Throwable $throwable = null;

function __construct() {
$this->observers = new \SplObjectStorage;
}
public function attach(\SplObserver $observer): void {
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer): void {
$this->observers->detach($observer);
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function handle(Throwable $t) {
$this->throwable = $t;
$this->notify();
}
public function getMessage(): ?string {
if ($this->throwable) {
return $this->throwable->getMessage();
}
return null;
}

public function getType(): ?string {


if ($this->throwable) {
return get_class($this->throwable);
}
return null;
}
}
Observer (exemplo - parte 2)
class Logger implements \SplObserver {

const ARQUIVO_LOG = 'log.txt';

public function __construct() {


touch(self::ARQUIVO_LOG);
}

public function update(\SplSubject $subject) {


file_put_contents(self::ARQUIVO_LOG,
$subject->getMessage() . PHP_EOL, FILE_APPEND | LOCK_EX);
}
}

class Mailer implements \SplObserver {

const TIPOS_PARA_EMAIL = [\RuntimeException::class, \SoapFault::class];

public function update(\SplSubject $subject) {

$tipo = $subject->getType();

if (array_filter(self::TIPOS_PARA_EMAIL, fn($t) => is_a($tipo, $t, true))) {


echo "mandando email sobre {$subject->getType()} : {$subject->getMessage()}";
//mail() //implementação real
}
}

}
Observer (exemplo - parte 3)
//cliente (montando)
$handler = new ExceptionHandler();
$handler->attach(new Logger());
$handler->attach(new Mailer());
set_exception_handler([$handler, 'handle']);

//forçando
funcaoNaoExiste();
//new \SoapClient("naoexiste.com/wsdl");
//new \PDO("mysql:errado","login","senha");
State
State é um pattern comportamental que
permite que um objeto altere seu
comportamento quando seu estado
interno for alterado. Como se o objeto
tivesse trocado de classe (SHEVETS,
2019).

Este pattern é próximo do conceito de


máquinas de estado finita (WIKIPEDIA,
2020).
Problema da Catraca (Máquina de Estado Finito)
class Catraca {
private string $estado = 'TRAVADA';
passarCartao()
empurrar() passarCartao()
public function empurrar(){

if($this->estado == 'TRAVADA'){
echo "passe o cartão !".PHP_EOL;
}
TRAVADA DESTRAVADA
if($this->estado == 'DESTRAVADA'){
echo "girando".PHP_EOL;
$this->estado = 'TRAVADA';
}
empurrar()
}
public function passarCartao(){

if($this->estado == 'TRAVADA'){
$this->estado = 'DESTRAVADA';
echo "destravada!".PHP_EOL;
}
}
} Novos estados irão demandar mais desvios
$catraca = new Catraca(); condicionais (ex.: if) aumentando a complexidade e
$catraca->empurrar();
$catraca->empurrar();
responsabilidade da classe.
$catraca->passarCartao();
$catraca->empurrar();
$catraca->empurrar();
Exemplo Catraca com State Pattern
interface EstadoCatraca { class Catraca {
public function empurrar(Catraca $catraca);
public function passarCartao(Catraca $catraca); public EstadoCatraca $estado;
}
public function __construct(){
class EstadoTravado implements EstadoCatraca { $this->estado = new EstadoTravado();
}
public function empurrar(Catraca $catraca){ public function setEstado(EstadoCatraca $estado){
echo "passe o cartão !".PHP_EOL; $this->estado = $estado;
} }
public function passarCartao(Catraca $catraca){ public function empurrar(){
$catraca->setEstado(new EstadoDestravado()); $this->estado->empurrar($this);
echo "destravada!".PHP_EOL; }
} public function passarCartao(){
} $this->estado->passarCartao($this);
}
}
class EstadoDestravado implements EstadoCatraca {
$catraca = new Catraca();
public function empurrar(Catraca $catraca){ $catraca->empurrar();
echo "girando".PHP_EOL; $catraca->empurrar();
$catraca->setEstado(new EstadoTravado()); $catraca->passarCartao();
} $catraca->empurrar();
public function passarCartao(Catraca $catraca){} $catraca->empurrar();
}
State (conceitual - parte 1)
interface State {
function fazAlgoA(Contexto $contexto);
function fazAlgoB(Contexto $contexto);
}
class EstadoConcretoA implements State {

public function fazAlgoA(Contexto $contexto) {


echo self::class.": fazendo algo A".PHP_EOL;
}
public function fazAlgoB(Contexto $contexto) {
echo self::class.": fazendo algo B e passando para o estado B ".PHP_EOL;
$contexto->setState(new EstadoConcretoB());
}
}

class EstadoConcretoB implements State {

public function fazAlgoA(Contexto $contexto) {


if($contexto->getValor() > 10){
echo "passando para o estado de A".PHP_EOL;
$contexto->setState(new EstadoConcretoA());
}else{
echo "fazendo algoA ainda em ".self::class.PHP_EOL;
}
}
public function fazAlgoB(Contexto $contexto) {
echo "fazendo algoB de ".self::class.PHP_EOL;
}
}
State (conceitual - parte 2)
class Contexto {
public State $state;
private int $valor = 0;
public function __construct(){
$this->state = new EstadoConcretoA();
}
public function setState(State $state){
$this->state = $state;
}
public function fazAlgoA(){
$this->state->fazAlgoA($this);
}
public function fazAlgoB(){
$this->state->fazAlgoB($this);
}
public function getValor() : int {
return $this->valor;
}
public function setValor(int $valor){
$this->valor = $valor;
}
}
$contexto = new Contexto();
$contexto->fazAlgoA();
$contexto->fazAlgoA();
$contexto->fazAlgoB();
$contexto->fazAlgoA();
$contexto->fazAlgoB();
$contexto->setValor(11);
$contexto->fazAlgoA();
$contexto->fazAlgoB();
State (exemplo - parte 1)
abstract class CaixaEletronicoState {

protected CaixaEletronico $caixaEletronico;


public function __construct(CaixaEletronico $caixaEletronico) {
$this->caixaEletronico = $caixaEletronico;
}
abstract function sacar(float $valor);
abstract function abastecer(float $valor);

class EmFuncionamento extends CaixaEletronicoState {

public function sacar(float $valor) {


$valorDisponivel = $this->caixaEletronico->getValorDisponivel();
if ($valor > $valorDisponivel) {
$valor = $valorDisponivel;
echo("Quantia parcial !" . PHP_EOL);
}
echo("R$ $valor será dispensado" . PHP_EOL);
$novoValorDisponivel = $valorDisponivel - $valor;
$this->caixaEletronico->setValorDisponivel($novoValorDisponivel);
if ($novoValorDisponivel == 0) {
$this->caixaEletronico->setState(new SemDinheiro($this->caixaEletronico));
}
}
public function abastecer(float $valor) {
echo("R$ $valor foi carregado !" . PHP_EOL);
$this->caixaEletronico->setValorDisponivel($this->caixaEletronico->getValorDisponivel() + $valor);
}
}
State (exemplo - parte 2)
class SemDinheiro extends CaixaEletronicoState {

public function sacar(float $valor) {


echo("Caixa sem dinheiro" . PHP_EOL);
}

public function abastecer(float $valor) {


echo("R$ $valor foi carregado !" . PHP_EOL);
$this->caixaEletronico->setState(new EmFuncionamento($this->caixaEletronico));
$this->caixaEletronico->setValorDisponivel(
$this->caixaEletronico->getValorDisponivel() + $valor);
}

}
State (exemplo - parte 3)
class CaixaEletronico {

private float $valorDisponivel = 0;


private CaixaEletronicoState $estadoCorrente;

public function __construct() {


$this->estadoCorrente = new SemDinheiro($this);
}
public function getValorDisponivel(): float {
return $this->valorDisponivel;
}
public function setValorDisponivel(float $valorDisponivel) {
$this->valorDisponivel = $valorDisponivel;
}
public function setState(CaixaEletronicoState $state) {
$this->estadoCorrente = $state;
}
public function getState(): CaixaEletronicoState {
return $this->estadoCorrente;
}
public function sacar(float $valor) {
$this->estadoCorrente->sacar($valor);
}
public function abastecer(float $valor) {
$this->estadoCorrente->abastecer($valor);
}
}
State (exemplo - parte 4)

$caixaEletronico = new CaixaEletronico();


$caixaEletronico->abastecer(100);
$caixaEletronico->sacar(50);
$caixaEletronico->sacar(30);
$caixaEletronico->sacar(30);
$caixaEletronico->sacar(20);
$caixaEletronico->abastecer(50);
$caixaEletronico->sacar(50);
$caixaEletronico->sacar(50);
$caixaEletronico->sacar(50);
$caixaEletronico->sacar(50);
Strategy
É um pattern comportamental que
permite definir uma família de
algoritmos, colocar cada um deles em
uma classe separada e tornar seus
objetos intercambiáveis.

Strategy permite que o algoritmo varie


independentemente dos clientes que o
utilizam (GAMMA et al, 1994).
Strategy (conceitual - parte 1)
<?php

interface Strategy {

function executarAlgoritmo(array $entrada): array;

class StrategyConcretoA implements Strategy {

public function executarAlgoritmo(array $entrada): array {


return array_map('strtoupper', $entrada);

}
}

class StrategyConcretoB implements Strategy {

public function executarAlgoritmo(array $entrada): array {


return array_map('strrev', $entrada);

}
}
Strategy (conceitual - parte 2)
class Contexto {

private Strategy $strategy;


private array $dados = ['maria', 'lucas', 'thiago'];

function __construct(Strategy $strategy) {


$this->strategy = $strategy;
}

public function setStrategy(Strategy $strategy) {


$this->strategy = $strategy;
}

public function fazAlgo(): string {

$resultado = $this->strategy->executarAlgoritmo($this->dados);
return implode(",", $resultado) . PHP_EOL;
}

$contexto = new Contexto(new StrategyConcretoA());


echo $contexto->fazAlgo();
$contexto->setStrategy(new StrategyConcretoB());
echo $contexto->fazAlgo();
Strategy (exemplo - parte 1)
<?php
interface StrategyVenda {
public function gerarValor(Produto $produto): float;
}

class StrategyVendaOrdinaria implements StrategyVenda {

public function gerarValor(Produto $produto): float {


return $produto->getValorCusto() + ($produto->getValorCusto() * 0.2);
}

class StrategyVendaQueimaEstoque implements StrategyVenda {

public function gerarValor(Produto $produto): float {


$tempoEmEstoque = $produto->getData()->diff(new \DateTime());
if ($tempoEmEstoque->m > 2) {
return $produto->getValorCusto() + ($produto->getValorCusto() * 0.1);
}
return $produto->getValorCusto() + ($produto->getValorCusto() * 0.2);
}

}
Strategy (exemplo - parte 2)
class Produto {
private string $nome;
private float $valorCusto;
private \DateTimeInterface $data;
private float $valor;

public function __construct(string $nome, float $valorCusto, \DateTimeInterface $data) {


$this->nome = $nome;
$this->valorCusto = $valorCusto;
$this->valor = $valorCusto;
$this->data = $data;
}
public function getNome(): string {
return $this->nome;
}
public function getValorCusto(): float {
return $this->valorCusto;
}
public function getData(): \DateTimeInterface {
return $this->data;
}
public function getValor(): float {
return $this->valor;
}
public function setValor(float $valor) {
$this->valor = $valor;
}

}
Strategy (exemplo - parte 3)
class Venda {

private array $produtos = [];


private \DateTimeInterface $data;
private StrategyVenda $strategy;
public function __construct(StrategyVenda $strategy) {
$this->strategy = $strategy;
$this->data = new \DateTimeImmutable();
}
public function getProdutos(): array {
return $this->produtos;
}
public function getData(): \DateTimeInterface {
return $this->data;
}
public function setStrategy(StrategyVenda $strategy) {
$this->strategy = $strategy;
}
public function addProduto(Produto $produto): self {
$produto->setValor($this->strategy->gerarValor($produto));
$this->produtos[] = $produto;
return $this;
}
public function getValorTotal(): float {
return array_reduce($this->getProdutos(),
fn($total, Produto $p) => $total + $p->getValor(), 0);
}
}
Strategy (exemplo - parte 4)
//cliente
$p1 = new Produto('bala', 1.99, new \DateTime());
$p2 = new Produto('sapato', 60.88, new \DateTime('-3 months'));
$p3 = new Produto('brinquedo', 100.00, new \DateTime('-1 week'));

$v1 = new Venda(new StrategyVendaOrdinaria());


$v1->addProduto($p1)
->addProduto($p2)
->addProduto($p3);
echo $v1->getValorTotal();
Template Method
Template Method é um pattern comportamental que define o
esqueleto de um algoritmo na superclasse, mas permite
que as subclasses possam substituir etapas específicas do
algoritmo sem alterar sua estrutura (SHVETS, 2019).

Existem três tipos de “etapas/passos” que podem ser


definidos em forma de métodos no Template Method:

1. Abstratos(as): precisam ser implementados pelas


subclasses;
2. Opcionais: possuem uma implementação default,
porém, podem ser sobrescritas(os) se necessário;
3. Hooks: similar ao opcional, porém normalmente com
implementação nula; Os hooks podem ser utilizados
em momentos importantes do algoritmo
possibilitando uma extensão customizada nas
subclasses que os(as) sobrescrevem;
Template Method (conceitual - parte 1)
abstract class ClasseBase {

final public function templateMethod(): void {


$this->hook1();
$this->operacaoDefault1();
if($this->etapaRequerida1()){
$this->operacaoDefault2();
}
$this->etapaRequerida2();
$this->hook2();
}

protected function operacaoDefault1(): bool {


return (bool) rand(0,1);
}
protected function operacaoDefault2(): void {
echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
abstract protected function etapaRequerida1();
abstract protected function etapaRequerida2(): void;

protected function hook1(): void {}


protected function hook2(): void {}

}
Template Method (conceitual - parte 2)
class ClassConcreta1 extends ClasseBase {

protected function etapaRequerida1(): void {


echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
protected function etapaRequerida2(): void {
echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
protected function operacaoDefault1(): bool {
return true;
}
}
class ClassConcreta2 extends ClasseBase {

protected function etapaRequerida1(): void {


echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
protected function etapaRequerida2(): void {
echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
protected function hook1(): void {
echo "fazendo ".__FUNCTION__." na classe ".self::class.PHP_EOL;
}
}
Template Method (conceitual - parte 3)
//***cliente
echo "Utilizando a ".ClassConcreta1::class.PHP_EOL;
$o1 = new ClassConcreta1();
$o1->templateMethod();

echo "Utilizando a ".ClassConcreta2::class.PHP_EOL;


$o2 = new ClassConcreta2();
$o2->templateMethod();
Template Method (exemplo - 1)
include 'conexao.php'; //mesmo do Singleton
abstract class Paginator {
private \PDO $pdo;
private int $pagina;
private int $limit;
public function __construct(\PDO $pdo, int $pagina = 1, $limit = 5) {
$this->pdo = $pdo;
$this->pagina = $pagina;
$this->limit = $limit;
}
public final function getPagina(): int {
return $this->pagina;
}
public final function getLimit(): int {
return $this->limit;
}
public final function getOffSet() {
return ($this->pagina - 1) * $this->limit;
}
protected abstract function getPaginateQuery(string $query, string $order = 'id'): string;
public final function getPaginateStatement(string $query): \PDOStatement {
$query = $this->setSQLBeforeQuery()." ".$this->getPaginateQuery($query);
$statement = $this->pdo->prepare($query);
$statement->bindValue(':offset', (int) $this->getOffSet(), \PDO::PARAM_INT);
$statement->bindValue(':limit', (int) $this->limit, \PDO::PARAM_INT);
return $statement;
}
public function getTotal(string $query) {
$query_total = $this->pdo->query("SELECT COUNT(*) FROM ($query) q");
return (int) $query_total->fetchColumn();
}
public function setSQLBeforeQuery() : ?string {
return null;
}
}
Template Method (exemplo - parte 2)
class AnsiPaginator extends Paginator {

protected function getPaginateQuery(string $query, string $order = 'id'): string {


return "$query ORDER BY $order OFFSET :offset ROWS FETCH NEXT :limit
ROWS ONLY";
}

class MySQLPaginator extends Paginator {

protected function getPaginateQuery(string $query, string $order = 'id'): string {


return "$query ORDER BY $order LIMIT :limit OFFSET :offset";
}

class SQLServerCustomPaginator extends AnsiPaginator {

public function setSQLBeforeQuery(): ?string {


return "SET DATEFORMAT dmy;";
}

}
Template Method (exemplo - parte 3)
class HtmlUtil {

private static function montaLinha(array $row, $tag = 'td') {


return "<tr>" . implode('', array_map(function($row) use ($tag) {
return "<$tag>" . $row . "</$tag>";
}, $row)) . "</tr>";
}

public static function getTable(\PDOStatement $statement, array $header = []) {


$statement->execute();
$table = "<table border>";
$table .= (!empty($header)) ? self::montaLinha($header, 'th') : '';
while ($row = $statement->fetch()) {
$table .= self::montaLinha($row);
}
return "$table </table>";
}

public static function getPaginationLinks(int $pagina, int $limit, int $total): string {
$links = (($pagina - 1) > 0) ? "<a href='?pagina=" . ($pagina - 1) . "'>Anterior</a>" : "Anterior";
$links .= "&nbsp";
return $links.= (($pagina) * $limit < $total) ? "<a href='?pagina=" . ($pagina + 1) . "'>Próximo</a>" : "Próximo";
}

}
Template Method (exemplo - parte 4)
<?php
$pagina = (isset($_REQUEST['pagina'])) ? $_REQUEST['pagina'] : 1;
$mySql = new \MySQLPaginator(Conexao::getInstance()->getPdo(), $pagina);
$query = "SELECT livro.id, livro.titulo, livro.preco, livro.isbn FROM livro";
$statement = $mySql->getPaginateStatement($query);
echo HtmlUtil::getTable($statement, ['ID','Título','Preço', 'ISBN']);
echo HtmlUtil::getPaginationLinks($pagina, $mySql->getLimit(),
$mySql->getTotal($query));
Visitor
Visitor é pattern comportamental que provê uma maneira de
separar um algoritmo de uma estrutura de objetos na qual
ele opera. Um resultado prático dessa separação é a
capacidade de adicionar novas operações às estruturas de
objetos existentes sem modificar as estruturas. É uma
maneira de seguir o princípio aberto/fechado (WIKIPEDIA,
2020).

Em essência, o visitante permite adicionar novas funções


virtuais a uma família de classes, sem modificar as classes.
Em vez disso, é criada uma classe de visitante que
implementa todas as especializações apropriadas da função
virtual. O visitante toma a referência da instância como
entrada e implementa o objetivo por meio de double dispatch
(WIKIPEDIA, 2020).
Visitor (problema do despacho)
Na ciência da computação, o despacho dinâmico é o processo de selecionar qual implementação
de uma operação polimórfica (método ou função) chamar no em de execução. É comumente
empregado em linguagens de programação orientada a objetos (OOP).

Polimorfismo por tipo de parâmetro (ex.: Java) => https://ideone.com/PDthf0


Utilizando Visitor (um tipo de despacho múltiplo): https://ideone.com/JChjkM

Na engenharia de software, o despacho duplo é uma forma especial de despacho múltiplo e um


mecanismo que despacha uma chamada de função para diferentes funções concretas,
dependendo dos tipos de tempo de execução de dois objetos envolvidos na chamada. Na maioria
dos sistemas orientados a objetos, a função concreta chamada de uma chamada de função no
código depende do tipo dinâmico de um único objeto e, portanto, são conhecidas como chamadas
de despacho único ou simplesmente chamadas de função virtual.
Visitor (conceitual - parte 1)
<?php
interface ComponenteOuElemento {
public function aceitar(Visitor $visitor): void;
}

class ComponenteOuElementoA implements ComponenteOuElemento {

public function aceitar(Visitor $visitor): void {


$visitor->visitarComponenteOuElementoA($this);
}

public function fazAlgoX(): string {


return "A";
}

class ComponenteOuElementoB implements ComponenteOuElemento {

public function aceitar(Visitor $visitor): void {


$visitor->visitarComponenteOuElementoB($this);
}

public function fazAlgoY(): string {


return "B";
}

}
Visitor (conceitual - parte 2)
interface Visitor {
public function visitarComponenteOuElementoA(ComponenteOuElementoA $element): void;
public function visitarComponenteOuElementoB(ComponenteOuElementoB $element): void;
}

class Visitor1 implements Visitor {

public function visitarComponenteOuElementoA(ComponenteOuElementoA $element): void {


echo $element->fazAlgoX() ." + ".self::class.PHP_EOL;
}
public function visitarComponenteOuElementoB(ComponenteOuElementoB $element): void {
echo $element->fazAlgoY() ." - ".self::class.PHP_EOL;
}

class Visitor2 implements Visitor {

public function visitarComponenteOuElementoA(ComponenteOuElementoA $element): void {


echo $element->fazAlgoX() ." / ".self::class.PHP_EOL;
}
public function visitarComponenteOuElementoB(ComponenteOuElementoB $element): void {
echo $element->fazAlgoY() ." * ".self::class.PHP_EOL;
}

}
Visitor (conceitual - parte 3)
//Cliente
$components = [
new ComponenteOuElementoA(),
new ComponenteOuElementoB(),
];

$visitor = new Visitor1();


array_map(fn(ComponenteOuElemento $c) =>
$c->aceitar($visitor) ,$components);
echo PHP_EOL;
Visitor (exemplo - parte 1)

interface Elemento {

public function aceitar(Visitor $visitor);


}

interface Visitor {

public function visitarUniversidade(Universidade $universidade): array;

public function visitarAluno(Aluno $aluno): string;


}
Visitor (exemplo - parte 2)
class Aluno implements Elemento {

private string $nome;


private array $faltasJustificadas = [];

public function __construct(string $nome) {


$this->nome = $nome;
}

public function addFaltaJustificada(\DateTimeInterface $inicio, \DateTimeInterface $fim): self {


$this->faltasJustificadas[] = new FaltaJustificada($inicio, $fim);
return $this;
}

public function getNome(): string {


return $this->nome;
}

public function getFaltaJustificadas(): array {


return $this->faltasJustificadas;
}

public function aceitar(Visitor $visitor): string {


return $visitor->visitarAluno($this);
}

}
Visitor (exemplo - parte 3)
class FaltaJustificada {
private \DateTimeInterface $inicio;
private \DateTimeInterface $fim;
public function __construct(\DateTimeInterface $inicio, \DateTimeInterface $fim) {
$this->inicio = $inicio;
$this->fim = $fim;
}
public function getInicio(): \DateTimeInterface {
return $this->inicio;
}
public function getFim(): \DateTimeInterface {
return $this->fim;
}
}
class Universidade implements Elemento {
private string $nome;
private array $alunos;
public function __construct(string $nome, array $alunos) {
$this->nome = $nome;
$this->alunos = $alunos;
}
public function getNome(): string {
return $this->nome;
}
public function getAlunos(): array {
return $this->alunos;
}
public function aceitar(Visitor $visitor): array {
return $visitor->visitarUniversidade($this);
}
}
Visitor (exemplo - parte 4)
class RelatorioFaltas implements Visitor {

public function visitarAluno(Aluno $aluno): string {

$diasPerdidos = 0;

foreach ($aluno->getFaltaJustificadas() as $faltaJustificada) {


$diasPerdidos += $faltaJustificada->getInicio()->diff($faltaJustificada->getFim())->days + 1;
}

return "Aluno: {$aluno->getNome()} perdeu {$diasPerdidos} dias";


}

public function visitarUniversidade(Universidade $universidade): array {

$resultado = [];
$resultado[] = "Gerando relatório para: \"{$universidade->getNome()}\"";

foreach ($universidade->getAlunos() as $aluno) {


$resultado[] = $this->visitarAluno($aluno);
}

return $resultado;
}

}
Visitor (exemplo - parte 5)

$aluno1 = new Aluno("João");


$aluno1->addFaltaJustificada(new \DateTime("2019-10-01"), new \DateTime("2019-10-21"));
$aluno1->addFaltaJustificada(new \DateTime("2019-11-02"), new \DateTime("2019-11-10"));
$aluno2 = new Aluno("Maria");
$aluno2->addFaltaJustificada(new \DateTime("2019-11-01"), new \DateTime("2019-11-15"));
$aluno3 = new Aluno("Carlos Picanha");

$universidade = new Universidade("Universidade do Churrasco", [$aluno1, $aluno2, $aluno3]);


$resultado = $universidade->aceitar(new RelatorioFaltas());

foreach ($resultado as $dado) {


echo $dado . PHP_EOL;
}
Fluent Interface
<?php
class Cafe {
private $acucar = 0;
private $leite = 0; $cafe = new Cafe();
echo $cafe->addAcucar(2)
public function addAcucar(int $colheres){ ->addLeite(20)
$this->acucar += $colheres; ->addAcucar(1)
return $this;
}
->preparar();

public function addLeite(int $mililitros){


$this->leite += $mililitros;
return $this;
}

public function preparar(){


return "Café com {$this->acucar} colheres de
açucar e {$this->leite} ml de leite";
}
}
Fiber (PHP 8.1 >=)
● Fibra (fibra) é um bloco de código que mantém sua própria pilha (variáveis e estado), que
pode ser iniciada, suspensa ou encerrada cooperativamente pelo código principal e
pela Fiber;

● Fiber é uma “Thread leve” que usa multitarefa cooperativa em vez de multitarefa
preemptiva;

● Uma Fiber em execução deve "ceder" (yield) explicitamente para permitir que outra Fiber
seja executada, o que torna sua implementação muito mais fácil do que o kernel ou
threads de usuário;

● Assim como as threads, as Fiber compartilham o espaço de endereçamento;

● É importante ressaltar que a execução concorrente não significa execução simultânea. A


Fibra e o fluxo de execução principal não acontecem ao mesmo tempo. Cabe ao fluxo de
execução principal iniciar uma Fiber, e ao iniciar, a Fiber é executada exclusivamente. A
Thread principal não pode observar, encerrar ou suspender uma Fibra enquanto a Fiber
estiver sendo executada. A Fibra pode suspender a si mesma e não pode ser retomada
por si mesma — o thread principal deve retomar a Fibra;

● A Fiber por si só não permite a execução simultânea de várias fibras ou da thread


principal e uma Fiber.
Fiber (métodos)
<?php

$callable = fn(string $mensagem) => print "Olá $mensagem !";


$fiber = new Fiber($callable);
$fiber->start('João');
//$fiber->start('João'); //Uncaught FiberError: Cannot start a fiber that has already been started
$callable2 = function(string $mensagem){
print "Olá $mensagem !";
Fiber::suspend(10);
};

//Fiber::suspend(); //Uncaught FiberError: Cannot suspend outside of a fiber

$fiber2 = new Fiber($callable2);


$retorno = $fiber2->start("Maria");
echo $retorno;
$fiber2->resume();

$fiber3 = new Fiber(function() {


$ultimo_valor = Fiber::suspend(16);
echo "Retomando com o valor {$ultimo_valor}\n";
});
$ultimo_valor = $fiber3->start();
echo "Suspenso com último valor {$ultimo_valor}\n";
$fiber3->resume(42);
//$fiber3->resume(42); //Cannot resume a fiber that is not suspended
Fiber (métodos - parte 2)
<?php

$fiber = new Fiber(fn()=> print 'oi fiber');


var_dump($fiber->isStarted()); // false
$fiber->start();
echo PHP_EOL;
var_dump($fiber->isStarted()); // true

<?php

$fiber = new Fiber(function(): void {


Fiber::suspend();
});

var_dump($fiber->isTerminated()); // false
var_dump($fiber->isSuspended()); // false
$fiber->start();
var_dump($fiber->isSuspended()); // true
$fiber->resume();
var_dump($fiber->isSuspended()); // false
var_dump($fiber->isTerminated()); // true
Fiber (exemplo assíncrono)
<?php

function getFiberFromStream($stream, string $url): \Fiber {


return new \Fiber(function ($stream) use ($url): void {
while (!feof($stream)) {
echo "Lendo 100 bytes da url: $url" . PHP_EOL;
$conteudo = fread($stream, 100);
Fiber::suspend($conteudo);
}
});
}

function getConteudos(array $urls): array {

$conteudos = [];
$fibers = [];

foreach ($urls as $key => $url) {


$stream = fopen($url, 'r');
stream_set_blocking($stream, false);
$fiber = getFiberFromStream($stream, $url);
$conteudo = $fiber->start($stream);

$fibers[$key] = ['fiber' => $fiber, 'conteudo' => $conteudo, 'stream' => $stream];
}

$existe_fiber_nao_terminada = true;

while ($existe_fiber_nao_terminada) {

$existe_fiber_nao_terminada = false;

foreach ($fibers as $key => $item) {

['fiber' => $fiber, 'conteudo' => $conteudo, 'stream' => $stream] = $item;
CURL
REST
M.V.C (Model View Controller)
Model–view–controller (MVC) é um padrão
de projeto de software comumente usado
para desenvolver interfaces de usuário que
dividem a lógica de programa relacionada
View
em três elementos interconectados.

Isso é feito para separar as representações Controller


internas dos dados das formas como as
informações são apresentadas e aceitas pelo Model
usuário (WIKIPEDIA, 2022);
Composer
Composer init e dump-autoload
PSR4 - Autoloader
Apache .htaccess
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]


Referências
https://biratkirat.medium.com/step-23-domain-specific-language-jon-jagger-2ae8c
391ce63
https://docstore.mik.ua/orelly/webprog/pcook/ch13_05
.htm
https://springframework.guru/gang-of-four-design-patterns/bridge-pattern/

https://www.javatpoint.com/structural-design-patterns

http://pt.slideshare.net/patrick.allaert/php-data-structures-and-the-impact-of-php-7-
on-them-php-days-2015

https://medium.com/@rafacdelnero/design-patterns-saga-18-real-world-situations-
https://refactoring.guru/design-patte
with-flyweight-8a6a2d94e32f
rns/flyweight/php/example

https://docs.microsoft.com/pt-br/dotnet/standard/base-types/anchors-in-regular-ex
Referências Bibliográficas
MARTIN, Robert Cecil. Agile Software Development: Principles, Patterns, and Practices. NJ, EUA: Prentice Hall, 2003. ISBN
0135974445.

GAMMA, Erich et al. Design Patterns: Elements of Reusable Object-Oriented Software. Westford: Addison-wesley, 1994.

MA, Burton. http://www.cse.yorku.ca/~burton/teaching/2009W/April_22.ppt&prev=search

http://www.kathrynpieplow.pwrfaculty.org/wp-content/uploads/2010/01/Ralph-Wand-definition-design.pdf

ISO/IEC/IEEE., 24765:2017 Systems and Software Engineering—Vocabulary. Second Edition. 2017

DAVIS, Alan Mark. 201 Principles of software development. 1995. ISBN 0070158401

NODET, Xavier. What are invariants, how can they be used, and have you ever used it in your program?. 2010. Disponível em:
https://softwareengineering.stackexchange.com/questions/32727/what-are-invariants-how-can-they-be-used-and-have-you-ever-used-i
t-in-your-pro

Mili, Hafedh & El-Boussaidi, Ghizlane. (2005). Representing and Applying Design Patterns: What Is the Problem?. 3713. 186-200.
10.1007/11557432_14.

Você também pode gostar