Escolar Documentos
Profissional Documentos
Cultura Documentos
Esse é um livro Leanpub. A Leanpub dá poderes aos autores e editores a partir do processo de
Publicação Lean. Publicação Lean é a ação de publicar um ebook em desenvolvimento com
ferramentas leves e muitas iterações para conseguir feedbacks dos leitores, pivotar até que você
tenha o livro ideal e então conseguir tração.
Autoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Revisor técnico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Revisor adjunto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Capa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Sobre o guia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Histórico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Vantagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Por quê fazer o preparatório? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Estatísticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Formato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Acessibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Navegação e revisão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Compra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Agendamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Reagendamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Dia da prova . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
O que NÃO cai no teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Tecnologias relacionadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Questões de simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Armadilhas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Polêmicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Outras certificações PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Término do exame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Se falhar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Aprovado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Objetivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Atualizando a certificação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Configurações de ambiente consideradas no exame . . . . . . . . . . . . . . . . . . . . . 45
Perguntas com enunciados ou alternativas iguais . . . . . . . . . . . . . . . . . . . . . . 46
Notações utilizadas neste guia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
CONTEÚDO
Quiz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Sugestões, críticas, ideias e erratas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Fundamentos do PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Operadores binários (bitwise operators) . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Variáveis e “variáveis variáveis” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Constantes mágicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Expressões em constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Visibilidade em constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Tipos de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Type Juggling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Declaração de tipos de dados de retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Indução de tipos escalares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Tipos nulos (Nullable types) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Tipos vazios (void types) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Delimitadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Estruturas de controle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Construções da linguagem e funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Extensões . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Performance com bytecode caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Unicode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Respostas do simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Funções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Função variádica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Escopo das variáveis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Passagem por referência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Funções anônimas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Respostas do simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Generators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Classes anônimas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Respostas do simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Segurança . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Segurança de Sessão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Cross-Site Scripting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Cross-Site Request Forgeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
SQL Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Remote Code Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Senhas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
E-mail Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Filtragem de dados de entrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Escapar saída de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
Criptografia e Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Upload de arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Armazenamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
SSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Respostas do simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Arrays associativos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Arrays enumerados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
Iteração de arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Array multi-dimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Funções para tratamento de array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Desconstrução simétrica de array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
SPL e Objetos como arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
Array constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Respostas do simulado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
• Facebook: http://facebook.com/arijunior/
• Twitter: @stopassola³
• LinkedIn: http://br.linkedin.com/in/stopassola/
• GitHub: https://github.com/stopassola
¹http://www.rumoacertificacaophp.com
²http://www.phprs.com.br
³https://twitter.com/stopassola
Autoria 2
Revisor técnico
• Facebook: http://facebook.com/renatomefi/
• Twitter: @renatomefi⁸
• LinkedIn: https://linkedin.com/in/renatomefidf/en/
• GitHub: https://github.com/renatomefi
• joindin: https://joind.in/user/renatomefidf
⁴https://www.meetup.com/PHPAmersfoort
⁵http://www.rumoacertificacaophp.com
⁶https://github.com/php-vcr/php-vcr
⁷https://github.com/overblog/GraphQLBundle
⁸https://twitter.com/renatomefi
Autoria 3
Revisor adjunto
• Linkedin: https://linkedin.com/in/thiagoluismo
• Github: https://github.com/thiagoluis
Autoria 4
Capa
• Facebook: https://www.facebook.com/wagner.ribeiro.
guitar
• Vimeo: https://vimeo.com/wagnerribeiro
• Twitter: https://twitter.com/wrguitarrista
• Instagram: https://www.instagram.com/wagner.
guitar
Wagner Ribeiro
Introdução
Com a popularidade do PHP em todo o mundo, cada vez mais empresas procuram formas de
identificar indivíduos com conhecimento comprovado. A certificação oferecida pela Rogue Wave
Zend Technologies Inc. reconhece os profissionais de TI com as habilidades e o conhecimento técnico
necessários para projetar e implementar aplicações web na linguagem PHP.
Esse guia propõe estratégias de como responder as questões do exame para tornar-se um Rogue
Wave Zend Certified PHP Engineer, exame 200-710 baseado no PHP 7.1. Faremos análises de código
em questões de exemplo e abordaremos conceitos que vão lhe ajudar a melhorar seus conhecimentos
na linguagem e, consequentemente, sua pontuação no teste.
É uma compilação dos estudos que realizei para obtenção do título como Zend Certified Engineer
(PHP 5.3)⁹ em 2012 e Zend Certified PHP Engineer (PHP 5.5) em 2014 e Rogue Wave Zend Certified
PHP Engineer (PHP 7.1) em 2017, com anotações que me foram cruciais para o êxito na prova,
inspirado no material oficial da Zend.
A jornada rumo à certificação PHP trata-se de um desafio pessoal, portanto meu papel será de
coaching nesse caminho.
O questionário apresentado no teste aborda 11 grandes áreas e seus respectivos sub-tópicos:
Fundamentos do PHP
• Sintaxe
• Operadores
• Variáveis
• Constantes
• Estruturas de controle
• Construções da linguagem e funções
• Namespaces
• Extensões
• Configuração
• Performance/bytecode caching
Funções
• Fundamentos do XML
• SimpleXML
• Extensão XML
• WebServices
• SOAP
• JSON
• DateTime
• DOMDocument
Recursos Web
• Sessões
• Formulários
• Métodos GET e POST
• Cookies
• Cabeçalhos HTTP
• Autenticação HTTP
• Códigos de status HTTP
• Definição de classe
• Atributos
• Métodos
• Visibilidade
• Modificadores
• Instanciação
• Herança
• Interfaces
• Exceções
• Autocarga (Autoload)
• Reflexão
Introdução 7
Segurança
• Configuração
• Segurança de Sessão
• Cross-Site Scripting
• Cross-Site Request Forgeries
• SQL Injection
• Remote Code Injection
• Email Injection
• Filter Input
• Escape Output
• Criptografia e algoritmos Hashing
• Upload de arquivos
• Configuração PHP
• API para hashing de senha
• Arquivos
• Leitura
• Escrita
• Funções de sistema de arquivos
• Streams
• Contextos
Strings e Padrões
• Aspas (quoting)
• Comparação
• Extração
• Busca
Introdução 8
• Substituição
• Formatação
• PCRE
• HEREDOC e NOWDOC
• Codificação
Banco de Dados
• SQL
• Joins
• Declarações preparadas
• Transações
• PDO
Arrays
• Arrays associativos
• Iteração de arrays
• Funções para tratamento de array
• SPL e Objetos como arrays
• Conversões
Manipulação de erros
• Tratamento de exceções
• Erros
• Throwables
As questões são distribuídas nas onze áreas, tentando contemplar o máximo possível de sub-tópicos.
A filosofia da certificação consiste em não apenas passar no exame, mas é o reconhecimento das
habilidades técnicas que demonstra a capacidade em resolver problemas.
A aprovação no teste facilita a função de recrutadores/empregadores, pois atesta que o profissional
tem proficiência na linguagem e experiência em desenvolvimento web como um todo.
Alguns assuntos tem mais peso que outros:
Ênfase maior
Fundamentos do PHP, Segurança e Programação Orientada à Objetos
Ênfase média
Funções, Recursos Web, Arrays e Strings e Padrões
Ênfase baixa
Banco de Dados, Manipulação de erros, Formato de dados e tipos e I/O (Entrada e Saída)
Introdução 9
Sobre o guia
O guia traz anotações gerais sobre o teste, portanto não é um curso completo de PHP – embora
sejam abordados assuntos do básico ao avançado.
Este livro é um roteiro de estudos, um guia preparatório.
A proposta do livro é traçar estratégias para melhorar a realização da prova e aprofundar tópicos
que se apresentam frequentemente no exame.
Daremos ênfase em questões complexas e como lidar com elas.
Como não seria viável cobrir cada detalhe da linguagem (das aproximadamente 5 mil funções e
respectivos parâmetros) iremos trabalhar questões que combinam diversos conceitos, analisar os
detalhes e armadilhas. O importante é compreender a lógica empregada.
Não há como garantir 100% de cobertura, pois o conjunto de funções é gigantesco. Consciente disso,
a Zend dá ênfase nas situações mais significativas.
Pressuponho que você já conheça a linguagem, que possua preferencialmente conhecimentos em
nível intermediário e avançado. Pois considero fundamental conciliar seus estudos com 1 a 3 anos
de experiência em programação PHP (dependendo da intensidade).
Considero esse livro como um curso preparatório para o exame que vai lhe auxiliar a obter um
melhor score no teste, portanto iremos rever conceitos do básico ao avançado, abordando também
situações peculiares e inusitadas.
Minha pretensão não é ensinar a linguagem em seus pormenores, partindo de conceitos pedago-
gicamente simples e evoluindo. Iremos tratar de inúmeros conceitos de forma pontual (a partir de
exemplos) com um leve encadeamento entre um tópico e outro.
É um livro independente, sem vínculo com a Zend (portanto não oficial). Lembrando que a Rogue
Wave Zend Technologies Inc. é a empresa mantenedora da linguagem PHP: http://www.zend.com
Outra característica do livro é que procuro utilizar exemplos do cotidiano, evitando expressões como
“foo bar apples oranges” ou algoritmos envolvendo zumbis.
Caso você queira aprender a linguagem PHP em toda sua magnitude, recomendo outros títulos como,
por exemplo, o excelente livro “PHP Programando com Orientação a Objetos - 3ª edição¹⁰” do autor
Pablo Dall’Oglio.
Uma dica crucial: não subestime a prova! O exame é um teste sério e merece toda a atenção. Ler
esse livro irá aumentar significativamente as suas chances de ser aprovado. Não posso garantir que
irá inexoravelmente ocorrer, mas você certamente vai melhorar drasticamente o seu resultado no
teste absorvendo todas as dicas abordadas aqui.
Você pode pular os capítulos e partir para os assuntos de maior interesse ou daqueles que você possua
um certo “débito técnico” (como dizem os adeptos das metodologias ágeis de desenvolvimento de
¹⁰http://www.novatec.com.br/livros/php-orientacao-objetos-3ed/
Introdução 10
software). Honestamente recomendo que você siga a ordem apresentada aqui e faça sistematica-
mente os simulados das lições no final de cada capítulo.
Espero, sinceramente, que este documento seja o seu passaporte como Engenheiro Certificado Zend
e torço para que você conquiste essa consagração muito em breve.
Você adquiriu este documento com exclusividade antes da versão final, portanto ela é suscetível a
bugs. Certamente irão ocorrer melhorias no texto no decorrer das próximas semanas, sendo que a
cada nova compilação faremos o devido anúncio nas redes sociais. O próprio LeanPub alerta sobre
novas releases.
Versão: 0.9.
Histórico
Abaixo faremos um resgate histórico com as principais mudanças ocorridas nos exames ZCE/ZCPE
(desde a sua criação) e algumas características:
• Baseada no PHP 4;
• Essa certificação extinguiu-se no fim de 2006, embora é importante frisar que o título “Zend
Certified Engineer” NÃO expira;
• Gerou mais de mil ZCEs em dois anos.
Introdução 11
Lançada em 2010 e baseada na versão 5.3 do PHP, ficou disponível até o fim de 2013:
• 5.4: Servidor web embutido, timer de alta precisão, Traits, sintaxe curta em arrays e novos
acessos à objetos;
• 5.5: geradores (palavra-chave yield), finally (try-catch) e outras melhorias.
Introdução 14
Esse painel foi inspirado na apresentação introdutória do curso “Zend PHP 5.3 Certification
Training”¹¹ ministrado pelo instrutor Doug Bierer.
Os logotipos foram obtidos no site da Zend¹².
Embora o anúncio do exame “PHP 5” tenha ocorrido em 2005, ele efetivou-se definitivamente só em
2007 – consequentemente o gráfico apresenta ciclos de três anos entre as certificações (com exceção
da mais recente, que levou quatro anos).
Obs.: grandes releases, como é o caso da versão 7 do PHP, levam entre 6 à 9 meses para a Zend
atualize para um novo teste - devidamente testado e homologado.
Houve grande debate no salto do número de versão 5 para 7 (pulando a versão 6). Mais informações
em https://wiki.php.net/rfc/php6
Vantagens
A filosofia por trás da certificação vai além da aprovação no exame – é o reconhecimento das
habilidades como desenvolvedor web proficiente.
Certificação atribui credibilidade, além de autoridade legal e moral.
O assunto “certificação” é polêmico, pois sempre vem à tona dois extremos: excelentes progra-
madores que não são certificados versus ZCPE’s que apresentam fragilidades técnicas (embora,
honestamente, eu desconheça profissionais titulados e que sejam medíocres).
Nesse contexto é importante salientar que a prova certifica competência técnica e não comporta-
mentais, e nesse sentido alguns desenvolvedores podem ser estigmatizados.
Abstraindo qualquer ideologia, há vários pontos a favor de submeter-se ao exame que são compro-
vadamente verdadeiros e trazem vantagens. Dentre eles:
A atitude de submeter-se não deixa de ser um desafio pessoal, que lhe trará ainda mais confiança
como profissional e destaque junto a comunidade.
Sem falar nas licenças de software que certamente extrapolam o valor pago pelo voucher, atualmente
ao investimento de US$ 195.
A Zend Certified Engineer Directory, antigamente conhecida como Yellow Pages, é um canal
de contato direto para com os profissionais certificados Zend (com proficiência em PHP e Zend
Framework) e possibilita que empresas busquem especialistas nestas áreas.
É bastante comum, após certificado, o ZCPE começar a receber propostas de trabalho, ao invés dele
sair em busca de oportunidades.
A obtenção de certificação ajuda você a ganhar visibilidade e lhe dá credibilidade, com experiência
comprovada na linguagem. Além de contribuir, a nível de excelência, com sua organização no
desenvolvimento de aplicações web baseadas em PHP.
A certificação ZCPE auxilia os empregadores a predizer se o profissional conseguirá aplicar a
tecnologia PHP 7.1 em problemas reais no desenvolvimento de software e de negócio. É uma garantia
de que o desenvolvedor possui padrões mínimos de experiência e conhecimento.
Numa disputa de vaga para desenvolvedor PHP você fica em evidência ao olhar do recrutador e
certamente pula uma etapa no processo seletivo.
Muitas empresas já utilizam o logotipo oficial, disponibilizado pela Zend, no intuito de atrair
profissionais PHP certificados:
¹³http://www.zend.com/en/services/certification/zend-certified-engineer-directory
¹⁴ApenasdaversãocorrentedoZendStudio.Osupgradessãocompradosseparadamente,casosejadeinteresse
Introdução 17
A certificação conta como qualificação técnica para a empresa, principalmente nas licitações, onde
o título ZCPE é pré-requisito e o número de profissionais certificados é utilizado em caráter de
desempate.
O programa de certificação transmite credibilidade ao mercado, tirando o PHP da “marginalidade”
como alguns insistem em categorizá-lo.
Contratar profissionais certificados diminui riscos para a empresa contratante.
Um dos maiores dramas do setor de Recursos Humanos (RH) é saber se o profissional está qualificado
para a execução do projeto, se possui competências para assumir um determinado cargo e se
apresenta os requisitos técnicos mínimos.
Muitas empresas, mediante suscetíveis equívocos de admissão, começaram a adotar seus próprios
métodos de contratação: análise de currículo, bateria de entrevistas, dinâmicas de grupo, testes,
programas de trainee e outros. A IBM, por exemplo, realiza UM ano de processo seletivo – para ter
certeza que a contratação será bem sucedida.
Mas a maioria das empresas não possui serviços de RH, tão pouco recursos financeiros que banquem
esse processo. Então o exame de certificação torna-se uma ferramenta importantíssima como
Introdução 18
1 $funcoes = get_defined_functions();
2 echo count($funcoes['internal']);
Esse livro procura abordar uma quantidade significativa de funções, associando com problemas
reais, de modo a facilitar a assimilação. Memorizar quase 5 mil funções, seus parâmetros e a ordem
em que eles devem ser utilizados, é uma tarefa humanamente impossível.
A combinação deles num determinado contexto é a proposta dos exercícios que faremos aqui,
totalmente alinhados com o teste real.
Sem falar nas inconsistências que a linguagem apresenta, como por exemplo, a ordem dos argu-
mentos da função array_map(callback, array) enquanto que array_filter(array, callback)
recebe os mesmos argumentos porém invertidos.
Outro exemplo de problemas na nomenclatura são as funções da biblioteca gettext: Ex.:
1 `bindtextdomain(domínio, diretório);`
2 `bind_textdomain_codeset(domínio, codeset);`
Estatísticas
Atualmente há aproximadamente 450 profissionais certificados ZCE/ZCPE no Brasil, segundo a
http://www.zend.com/en/services/certification/zend-certified-engineer-directory
Considerando a quantidades de programadores PHP existente, pode-se perceber o quão ínfimo é
esse número – consequentemente, mais raro e prestigiado é o título.
Formato
O exame atualmente possui 75 perguntas randômicas (a partir de um repositório contendo várias
centenas de questões), criadas por um grupo chamado Zend PHP Education Advisory Board¹⁵
formado por personalidades conhecidas na comunidade PHP, altamente gabaritados. Alguns são
funcionários da Zend, outros são autores reconhecidos e desenvolvedores expoentes – com diferentes
pontos de vista.
¹⁵http://www.zend.com/services/certification/php-5-certification/education-advisory-board
Introdução 20
Obs.: até a versão atual do exame (baseado na versão 7.1 do PHP), o teste apresentava 70 questões -
cinco a menos que hoje, portanto o exame atual ficou ainda mais exigente.
As perguntas tem um caráter neutro, não fazem referência aos produtos Zend, são abrangentes (não
tendem à uma área específica), jamais se repetem no percurso do teste e se distribuem igualmente
entre as onze áreas.
O tamanho do repositório da Zend é desconhecido, bem como a lógica que orquestra a ordem das
perguntas.
Os trechos de código (tanto no enunciado quanto nas alternativas) não possuem syntax highlight
– na qual estamos acostumados em nossos editores favoritos, e sabemos o quanto ajuda na
interpretação de código. O snippet PHP é chapado, para que o candidato compreenda o código,
sem artifícios.
Obs.: em alguns simulados oficiais da Zend na qual tive acesso, alguns códigos aparecem com
a sintaxe em destaque (colorida). Mas na prova não haverá tal recurso, então não confie nessa
possibilidade.
Os enunciados não exibem pseudocódigos. O algoritmo é sempre na linguagem PHP, mas não
necessariamente seguem os padrões PSR1 e PSR2¹⁶.
Não há penalidades para respostas incorretas. De qualquer forma jamais deixe uma questão em
branco, pois não há punição. Arrisque!
¹⁶http://www.php-fig.org
Introdução 21
Curiosidade: em outros testes como o de Magento (por exemplo), eles apresentam 71 perguntas
sendo 65 com e 6 sem pontuação. No exame para PHP da Rogue Wave Zend Technologies, todas as
perguntas impactam no resultado.
Vão do nível intermediário ao avançado e devem ser respondidas em 90 minutos. Isso significa
que você tem, em média, 1 minuto e 12 segundos para cada questão. A administração do tempo no
decorrer do exame é crucial pois impacta diretamente em sua performance, mesmo que tecnicamente
você seja qualificado e preparado.
Após o tempo estipulado, o teste finaliza automaticamente.
Minha sugestão é que priorize as questões mais breves, ao invés dos algoritmos que exigem testes
de mesa¹⁷ e que tendem a consumir mais tempo.
O teste de mesa é imprescindível para compreender o comportamento da linguagem diante do script
exposto na pergunta.
A percentual mínimo de acertos para a aprovação não é divulgado. O score se dá pelo número de
perguntas versus peso, portanto especula-se que seja 70% de acerto. Esse percentual representa 53
questões (das 75 no total), o quê representa um belo desafio.
Não há penalização por respostas erradas. Apenas crédito pelas corretas.
Algumas bibliografias afirmam que uma questão pode ajudar a resolver outra, mas pela minha
experiência e de colegas certificados, não consegui comprovar essa teoria. Cada questão é totalmente
independente das demais e autocontida – ou seja, tudo o que você precisará estará disponível no
enunciado dela própria.
A Zend tem parceria com a Pearson VUE Test Center¹⁸ que é o centro autorizado para prestação de
certificações, com salas credenciadas em todo o mundo.
Cada pergunta disponibiliza respostas em três formatos diferentes:
Os campos abertos, originalmente chamados de freetext são lacunas vazias que esperam a inserção
de texto livre, que pode ser nome de função/método, classe, saída de execução de código, nome de
constante, conteúdo de variável, cláusula do php.ini, tipos de erro (Notice, Warning e Fatal error)
e outros.
São tipos de perguntas menos frequentes e nesse caso não há uma estratégia para a resolução da
questão – é necessário conhecer realmente o assunto que está sendo perguntado. Ex.:
¹⁷Teste de mesa é a interpretação mental do programa analisando-o linha após linha, percebendo as atribuições de variáveis, laços, testes,
chamadas de função etc.
¹⁸http://www.pearsonvue.com
Introdução 22
A resposta aqui é __autoload(), lembrando que nomes de funções devem ser escritas em caixa baixa
(lowercase), sem espaços em branco, nem explicações ou comentários. Fica a seu critério utilizar
parênteses ou não, quando solicitado nome de função ou método. Portanto a resposta digitada no
campo também poderia ser __autoload (nesse exemplo).
A propósito: a função __autoload é invocada toda vez que algum objeto é criado, onde os
nomes de classes requisitadas são convertidos em nomes de arquivos (que serão incorporados via
include/require), seguindo uma convenção que o usuário estabelece.
Dificilmente haverá um enunciado dizendo Read carefully (leia cuidadosamente), pois todas são
igualmente capciosas.
Você não precisará codificar longas porções de código, mas apenas nomes de variáveis e pequenos
trechos (snippets) de PHP.
Também não é exigido que mentalize todo o PHP Manual[^PHPManual], mas apenas as funções
mais significativas e a ordem correta dos parâmetros dessas funções – que iremos abordar aqui nesse
guia. O foco do teste é na análise de algoritmos ou invés de memorização.
A certificação independe de Sistema Operacional, Sistema Gerenciador de Banco de Dados (SGBD)
e servidor web. Os requisitos que transcendem o PHP são o protocolo HTTP, SQL (Structured Query
Language), HTML, XML, CSS e JavaScript. Mais informações no tópico “Tecnologias relacionadas”
nesse capítulo.
Acessibilidade
Aos colegas que necessitem de alguma tecnologia assistiva, recomenda-se que indique assistência
especial no momento do agendamento, no site http://www.pearsonvue.com/zend/ .
Assim que a Pearson VUE autorizar, o centro homologado na qual você gostaria de realizar a prova
providenciará os recursos necessários: software para leitura de tela, fones de ouvido etc.
Mas é imprescindível certificar-se antes da aquisição do voucher, pois não é o padrão amplamente
utilizado e nem todos os centros estão preparados com tais tecnologias: http://www.pearsonvue.
com/contact/americas/customerservice/
Navegação e revisão
Para que você esteja familiarizado com o ambiente do teste, recomendo navegar pelo endereço http:
//www.pearsonvue.com/demo
Introdução 23
É um design “ancião”, mas cumpre perfeitamente o seu propósito – de não desviar a atenção do
candidato.
Você tem a possibilidade de marcar as questões para revisão (Flag for Review), quantas perguntas
achar necessário.
http://www.pearsonvue.com/athena/static/review/flag_for_review.asp
Isso significa que, ao término da sequência das 75 questões, o sistema lhe remete automaticamente
para uma tela de revisão - que pode ser acessada a qualquer momento.
Essa tela especial de análise apresenta a lista de todas as questões, dentre elas as que foram
assinaladas para retificação e também as perguntas que estejam que ainda não foram respondidas
totalmente (que faltam marcar alternativas, por exemplo). Assim você poderá verificar novamente
suas repostas.
Introdução 24
http://www.pearsonvue.com/athena/static/review/using_the_review_screen.asp
O sistema também exibe questões sem resposta (ainda em branco), que serão contabilizadas
diretamente como incorretas, prejudicando o score final, portanto jamais deixe de respondê-las –
mesmo que você não tenha certeza ou que desconheça completamente o assunto.
Compra
O voucher do exame custa US$ 195 (no site americano) ou € 170 (na Europa). Antigamente havia
um bug no site que mostrava a mesma unidade de valor, porém em moedas distintas. Nesse caso o
câmbio mais favorável aos brasileiros ainda é o dólar, portanto observe com atenção qual versão do
site da Zend você está acessando: http://shop.zend.com/en/php-certification.html (preço em dólar)
OU http://shop.zend.com/eu/php-certification.html (preço em euro).
Caso queira adquirir um volume maior de vouchers (acima de 10 unidades) para certificar toda a
equipe, pode-se obter desconto. Experimente contatar o departamento comercial da Zend: sales@
zend.com.
Várias empresas estimulam seus colaboradores a obter o título a ponto de patrocinarem o custo dos
vouchers. Fale com a chefia ou o responsável dos recursos humanos.
2. Após a escolha do produto (voucher), você paga através de cartão de crédito internacional:
Então primeiramente você compra o voucher no site da Zend (será necessário criar uma conta) e, ao
término do processo, receberá por e-mail um identificador semelhante a esse: ZTPB379B231A.
Obs.: o voucher é válido por UM ano a partir da data da compra.
O pagamento é feito com cartão de crédito internacional e pode ser usado um cartão de outra pessoa
ou de uma organização (não necessariamente em seu nome). O importante é verificar se seus dados
estão corretos nos cadastros da Pearson VUE, que serão inspecionados no dia do exame para atestar
que “você é você mesmo”, e na Zend que será utilizado na impressão do certificado físico e remetido
via correio para o endereço fornecido.
O processo de certificação é uma combinação de esforços da Zend, Pearson VUE e dos centros
autorizados. O valor de US$ 195 é rateado entre todos, descontando os impostos. Portanto, embora o
valor seja oneroso para a realidade brasileira, há um consenso de que a certificação existe sem fins
lucrativos. O propósito fundamental é qualificar e aperfeiçoar a massa crítica de profissionais PHP.
Introdução 26
Agendamento
O próximo passo será agendar a prova no site da Pearson VUE Test Center¹⁹ http://www.pearsonvue.
com/zend/:
1. Logo após a criação da conta no site²⁰, certifique-se de que todas as suas informações estão
perfeitamente corretas, para evitar algum imbróglio quando apresentar-se presencialmente
para a realização do teste. Após autenticar-se, escolha o exame apropriado no catálogo - em
nosso caso o “200-710: Zend Certified Engineer”:
2. Na tela de confirmação (Exam Details) pressione o botão Schedule this Exam (Agendar este
Exame) no canto inferior direito da tela:
¹⁹A Pearson VUE tem afiliados em 175 países e parceria com mais de 3.500 centros, líder em testes de proficiência nas mais diversas áreas do
conhecimento.
²⁰http://www.pearsonvue.com/zend/
Introdução 27
3. Na tela de localização do Centro de Testes (Test Center Search) o sistema realiza uma busca
automática baseada em seu endereço, previamente cadastrado, e exibe os centros credenciados
mais próximos geograficamente.
Eles possuem afiliados em todo o mundo, inclusive em várias cidades brasileiras - principal-
mente nas capitais.
Obs.: você pode verificar com antecedência um centro homologado Pearson VUE, acessando
o endereço: http://www.pearsonvue.com/zend/locate/.
Introdução 28
Quanto ao idioma, na versão mais recente do teste (ZCPE PHP 7.1), existe apenas em inglês.
Somente nas versões anteriores do teste (ZCE 4, 5 e 5.3) era possível escolher entre: alemão,
francês, inglês e japonês.
Até o momento não há previsão de uma versão traduzida para o português (e é pouco provável
que haja), portanto o inglês acaba sendo a única opção, então é fundamental ter uma boa base
do idioma – nem que seja apenas como leitura técnica.
Finalizando essa questão do idioma, atente para expressões como what, where, when, how,
result, output e follow. Respectivamente: o quê, onde, quando, que resultado, saída e siga.
4. Após escolher o centro autorizado Pearson VUE mais conveniente, é o momento de agendar
efetivamente. O sistema traz o calendário com as datas e horários disponíveis daquele centro
específico.
Dependendo da sua urgência, talvez seja interessante optar por outro local que porventura
possa ter mais disponibilidade de datas e horários.
Não é necessário agendar também com o centro parceiro, pois os sistemas de ambos são
integrados.
Introdução 29
5. A etapa seguinte será a tela de confirmação My Order (“Meu Pedido”). Basta verificar o tipo
de exame que você deseja submeter-se (200-710), local mais conveniente, data e horário:
Introdução 30
A respeito do idioma, nesse guia, optei por confeccionar os simulados em português justamente por
ser nosso idioma nativo e podermos nos concentrar no conhecimento técnico envolvido, ao invés
de desviarmos nossa atenção em traduções. Questões de idioma são secundárias e dificilmente você
terá problemas ao interpretar as questões.
Tomei o cuidado de formular perguntas muito próximas do formato original (com a diferença que
aqui estarão em português) para que seja o mais coerente possível e totalmente alinhado com o
exame real.
Lembrando que as perguntas são criadas pelo Zend Education Advisory Board originalmente em
língua inglesa. Como as perguntas são bastante técnicas o inglês não interfere tanto.
Introdução 31
É possível reagendar o exame ou até solicitar a devolução total do valor pago a qualquer momento
(a partir do site da Pearson VUE). Desde que o faça, no mínimo, com dois dias de antecedência.
O centro examinador local mantém uma agenda integrada com a Pearson VUE. Significa que não
requer confirmar o agendamento, a menos que você esteja muito aflito e prefere confirmar.
Obs.: você pode realizar a compra do voucher através do telefone ou no próprio centro autorizado
Pearson VUE. Nesses casos utilize o código 200-710, que faz referência ao exame vigente.
Reagendamento
Após a compra do voucher no site da Zend, ele é válido por 1 (um) ano.
O segundo passo é criar uma conta na VUE, escolher o local mais apropriado para a realização do
teste, data e horário. Na tela de pagamento, você informa o código do voucher, enviado previamente
pela Zend.
No período de 1 ano você pode reagendar o teste várias vezes, sem custo. Desde que seja feito com,
no mínimo, 48h (quarenta e oito horas) de antecedência.
É uma possibilidade tentadora mas, na prática, não resolve. Por experiência própria, use com
moderação, somente em casos de urgência. Pois o adiamento só aumenta a ansiedade, gerando ainda
mais expectativa e prejudicando a sua performance global.
Se postergar demais a prova, acaba esquecendo o que viu no começo dos estudos.
Dia da prova
Você precisa chegar ao local do exame com 1h (uma hora) antes do agendamento portando algum
documento oficial atualizado com foto: CPF, RG ou carteira de motorista. A recomendação é que
você traga dois identificadores contendo foto e assinatura.
Os dados que constam nesses documentos serão verificados com àqueles que foram previamente
cadastrados na VUE, então certifique-se de ter digitado seus dados corretamente.
Seus itens serão devidamente guardados em um armário chaveado e serás impedido de circular na
sala durante o exame – os centros onde o exame ZCPE é aplicado seguem normas rígidas para evitar
todo tipo de fraude. A intenção e evitar casos onde outra pessoa presta o exame em nome de outrem,
por isso há todo um rigor para com o candidato.
Não é permitido levar material para a sala do exame, tão pouco smartphones, laptop, tablet, livros
etc.
Em caso de necessidades especiais, solicite com antecedência a disponibilidade dos recursos
assistivos junto ao próprio centro.
Introdução 32
No dia do teste o candidato recebe uma quadro apagável (eraseable board) e uma caneta especial
(para uso nesse quadro) que o ajudarão nos testes de mesa e cálculos, pois há um índice de
aproximadamente 40% de questões envolvendo trechos de código (as vezes bastante extensos) que
requer um certo esforço cerebral em emular a execução.
A Pearson VUE oferece também um protetor auricular. Opcional, mas que ajuda na concentração,
protegendo o candidato de ruídos externos para que enfoque única e exclusivamente no teste.
A pessoa responsável pela condução do teste fará alguns procedimentos na estação onde será
realizada a prova.
Lembre-se que a aplicação e processamento do exame é gerenciado pela Pearson VUE, com anos
de experiência e que atendem centenas de outras companhias. O centro examinador autorizado que
você escolheu oferece exames em várias áreas, portanto certifique-se de ler atentamente se o seu
teste é mesmo o oferecido pela Rogue Wave Zend Technologies Inc.
Tecnologias relacionadas
O desenvolvimento web é um ofício que demanda conhecimentos também em outras áreas, que
transcendem a linguagem PHP (server side).
Faz-se necessário conhecimentos também de marcações HTML e CSS, formato na qual as telas
geradas serão renderizadas nos navegadores web.
O protocolo HTTP também é requerido, pois trata da “conversa” entre o navegador e servidor.
²¹Object-Relational Mapping
²²http://madhavvyas.blogspot.com.br/2011/03/php-easter-eggs.html
²³http://php.net/filter
Introdução 33
Questões de simulado
O livro apresenta cerca de 150 questões inéditas, de minha autoria, abordando os temas que mais
geraram polêmica dentre as centenas de perguntas publicadas em nossa lista de discussão Rumo à
Certificação PHP²⁴.
Tive o cuidado de selecionar minuciosamente os assuntos que provocaram mais dúvidas, de maior
repercussão, e trouxe para o livro uma compilação de tudo que foi discutido lá.
Importante ressaltar que as questões aqui apresentadas se assemelham muito com as perguntas do
exame, mas elas não são idênticas. Casualmente é possível que apareçam questões iguais, mas é
importante estar ciente de que iremos focar nos conceitos, ao invés de mentalizarmos perguntas e
respostas.
Armadilhas
A falta de padronização nos nomes das funções provoca bastante confusão. Essa inconsistência
reflete também no exame, portanto necessita ser bem analisada.
Funções de conversão: to ou 2, com ou sem _ (underscore):
Uso de to Uso de 2
cal_to_jd bin2hex
ftok deg2rad
idn_to_ascii image2wbmp
idn_to_unicode nl2br
idn_to_utf8 rad2deg
strtok ip2long
strtolower jpeg2wbmp
strtotime long2ip
strtoupper px_timestamp2string
²⁴http://www.rumoacertificacaophp.com
Introdução 34
Obs.: as funções acima ficaram dispostas numa tabela por questões de diagramação, pois não há
correlação entre as linhas.
Outro detalhe são as funções case insensitive (tolerante a letras maiúsculas e minúsculas), utilizando
i versus case:
Objeto/verbo Verbo/objeto
socket_read readdir
ldap_read readfile
date_parse parse_str
xml_parse parse_url
imap_check checkdnsrr
pspell_check checkdate
timezone_version_get getheaders
realpath_cache_get getcwd
ini_get getenv
O underscore é outra armadilha que deve ser observada com muitíssima atenção:
Como escrever uma classe onde seus atributos NÃO podem ser acessados externament\
e aos métodos?
Nesse caso a alternativa correta é a E, pois declarando a propriedade da classe como private elas
serão manipuláveis somente a partir dos seus respectivos métodos – jamais externamente.
Dica: espere questões que utilizem bastante a forma negada, como visto no exemplo acima.
As alternativas apresentadas podem ofuscar a sua resposta, apresentando opções invertidas. A
tendência é focarmos em afirmações.
Veja outro exemplo que pode induzir uma resposta equivocada:
Quais operações abaixo não podem ser implementadas usando o recurso ftp://stream?
(escolha duas)
A: Leitura de arquivo
B: Escrita de arquivo
C: Estabelecendo uma conexão permanente e alterar diretórios interativamente
D: Criando um novo diretório
E: Nenhuma das alternativas anteriores
Inferindo o comportamento de um wrapper de FTP (File Transfer Protocol), fica nítido que as
respostas seriam as alternativas A e B, porém atente-se ao “não podem” que modifica totalmente
a resposta. Nesse caso as alternativas corretas seriam as letras C e D.
Analise atentamente esse trecho de código maroto:
1 $this->range1=($this->limit*$this->page)-$this->limit;
Perceba que estamos operando dentro de um método e referenciando atributos como range1, page
e limit.
Observe o fragmento page)-$this e veja que não há o arrow operator -> que age no contexto de
orientação à objetos, invocando métodos ou manipulando propriedades.
Então seria um erro de sintaxe? Não: é sinal de subtração!
Atente-se as expressões:
Introdução 36
Expressão Descrição
-> Operador que atua sob atributos e chama métodos de uma classe
=> Operador utilizado em arrays para associar um índice ao seu
respectivo valor
>= Operador de maior igual utilizado em testes como if
Polêmicas
Eventualmente surgem questões de análise de código que demandam grande raciocínio e tempo
(que é escasso) na realização dos testes de mesa. Esses scripts trazem sintaxes que formam grandes
labirintos, muitas vezes até taxadas de irreais, ofuscando o candidato.
De fato existem questões com uma lógica mirabolante (sem que haja erro de sintaxe) e você precisa
estar preparado para enfrentá-las, inevitavelmente.
Outro perfil de questão que gera muita discussão na comunidade são as verificações de ordem dos
parâmetros nas funções. É muito fácil confundir-se na sequência em que os argumentos devem ser
inseridos, portanto esteja atento as “pegadinhas” nesse sentido – pois elas existem e vão aparecer no
seu teste.
Há também discussões nas questões envolvendo conhecimento da sintaxe ao invés da habilidade
analítica, pois se argumenta que tais dúvidas poderiam ser facilmente consultadas no manual da
linguagem ao invés de memorizá-las. Muitos entendem que as perguntas deveriam abordar desafios
no contexto do desenvolvimento de um código limpo, escalável e seguro.
Alguns argumentam que é necessário mentalizar mais de 5 mil funções e seus respectivos parâme-
tros, que seria uma tarefa desumana. Na realidade o exame foca em questões complexas utilizando
as principais funções da linguagem, portanto procure se tranquilizar.
Há uma corrente de desenvolvedores que põem em dúvida a validade do teste, argumentando que
ele não atesta plenamente um programador qualificado.
Concordo que grandes desenvolvedores da atualidade de fato não são certificados. Mas se você
domina o PHP, por que não certificar-se? Tenha a certeza que será um desafio e tanto.
Término do exame
Obrigatoriamente você precisa concluir o exame antes dos 90 minutos, mesmo que hajam perguntas
pendentes – em branco ou a espera de revisão.
Caso o tempo se esgote e você ainda não tenha respondido o exame na íntegra, independentemente
dos acertos nas demais questões, você será automaticamente reprovado. Portanto o controle do
tempo é FUNDAMENTAL.
Ao confirmar o término do teste, dentro do período limite, o resultado aparece de imediato. Pode
ser um tanto perturbador mas minimiza a angústia da espera.
Aparecerão os dizeres: Congratulations ou Failed.
Em caso de aprovação o score não é informado. Mas se falhar, o sistema oferece um sucinto feedback
de como foi seu desempenho em cada área de conhecimento, com o respectivo percentual de acerto
- mais detalhes no tópico seguinte.
Se falhar
A Zend oferece um desconto caso reprove no exame e pretenda submeter-se novamente. Esse proce-
dimento chama-se Certification Exam Retakes. Para mais informações escreva para: certification@
zend.com.
Geralmente eles solicitam o seu identificador junto a Zend (ID), seu score e data/hora da reprovação.
Em poucos dias você receberá um cupom de desconto para realizar o sua nova tentativa. Obs.: até o
momento o percentual de abatimento está em 20%.
²⁵http://www.ncsacademy.com/certification/php.cfm
²⁶http://www.w3schools.com/cert/cert_php.asp
²⁷http://www.brainbench.com
²⁸http://certificacao.imasters.com.br/prova/php-boas-praticas
²⁹http://www.phpexam.jp/about/english/
Introdução 38
Se falhar, o sistema permite que você refaça o teste somente após 15 dias a partir da data de
reprovação. A cada reprovação, aumenta em quinze dias o período de espera, portanto na primeira
vez serão 15 dias, na segunda serão 30 dias, depois 45 e assim sucessivamente.
O próprio mecanismo da Pearson VUE impede que o usuário faça o agendamento antes desse
período.
Independente do resultado, o centro de certificação autorizado lhe fornece um documento ao final
do exame - que vai nortear seus estudos para a próxima tentativa.
No caso de Fail, o sistema gera um relatório do nível de conhecimento apresentado nas diferentes
áreas de estudo. É um tanto vago, pois mostra apenas o percentual de acertos obtidos e não informa
exatamente erros e acertos.
Introdução 39
Lembre-se que mesmo reprovando é possível extrair muita informação do exame e da experiência
de todo o processo de certificação. Com certeza lhe trará algum tipo de benefício, se positivamente
Introdução 40
explorado.
Saiba que essa sensação é amarga, mas efêmera. Então supere e tente outra vez!
Aprovado
Assim que você tiver a felicidade de receber a mensagem de aprovação Congratulations - a sessão
do sistema é encerrada e você será encaminhado para uma estação que controla a realização dos
testes do centro autorizado Pearson VUE.
Imediatamente é emitido um documento atestando a aprovação no exame.
Introdução 41
Introdução 42
A partir desse momento você já começa a desfrutar das vantagens de ser um profissional certificado.
Recomendo atualizar seus dados no site da Zend (principalmente o endereço de correspondência) e
aguardar a chegada do certificado físico, uma carta de boas-vindas com a assinatura em punho do
Training Program Manager da Zend e um adesivo exclusivo.
Verifique atentamente o endereço que consta na sua conta (rua, número, bairro, cidade, código postal,
estado e país), pois o certificado será despachado para essa localidade.
O título de Zend Certified PHP Engineer não expira. Significa que você será um ZCPE vitalício.
Mesmo assim é válido, ao surgimento de certificações PHP sob novas versões, que você se submeta
ao teste novamente em caráter de atualização pessoal – mas não é obrigatório. Até porque você
estará apto a utilizar o logotipo de acordo com a versão do teste na qual se submeteu e atualizar-se
é sempre positivo.
Se já você já é certificado, a Zend oferece um desconto de US$ 70 na atualização. Preço final fica em
US$ 125.
Objetivo
O tão almejado título de Zend Certified PHP Engineer se consolida quando recebemos fisicamente
o certificado, após algumas semanas, enviado diretamente da sede da Zend na Califórnia (EUA).
Introdução 43
E junto com ele você receberá um documento lhe dando o respaldo sobre o título recém conquistado
e também um adesivo exclusivo, apenas para profissionais certificados Zend.
Introdução 44
Introdução 45
Atualizando a certificação
Mesmo sabendo que a certificação ZCE/ZCPE é perpétua, ao lançamento de novas releases do PHP,
é provável que o profissional PHP tenha interesse em submeter-se novamente em uma versão do
exame mais atualizada.
Seja impulsionado pelas oportunidades do mercado ou por desafio pessoal, é sempre válido atestar
seus conhecimentos numa versão recente da linguagem.
Você pode submeter-se novamente e atualizar o seu título, pois o upgrade não é automático.
A versão 7.1 do exame (versão vigente) lhe dará um ganho no currículo, pois o teste aborda, por
exemplo, Orientação a Objetos como jamais se viu anteriormente - principalmente aos que se
certificaram na época do PHP 4.
A Zend oferece desconto de US$ 70 (setenta dólares) para quem deseja obter a nova certificação.
Basta contactar sales@zend.com e solicitar um código promocional exclusivo.
error_reporting = E_ALL
display_errors = On
Jamais haverão questões abordando nuances do PHP sob Windows ou salientando alguma versão
específica de plataforma Linux. É dito que as perguntas são agnósticas com relação a plataforma.
Introdução 46
Qual dos seguintes meta-caracteres (sinalizados por ??) pode ser usado para pesq\
uisar qualquer caractere que não seja dígito decimal?
A: \s
B: \S
C: \d
D: \D
Agora observe essa outra questão que, num primeiro olhar, parece idêntica a anterior. Mas não são!
Preste atenção na pergunta logo após o código PHP:
Qual dos seguintes meta-caracteres (sinalizados por ??) pode ser usado para pe\
squisar qualquer caractere que não seja espaço em branco?
A: \s
B: \S
C: \d
D: \D
Introdução 47
Perceba que as duas questões têm a mesma introdução, alternativas iguais e a perguntas são muito
parecidas – mas evidentemente o detalhe dígito decimal versus espaço em branco faz toda a
diferença.
Nesse caso, trata-se de expressões regulares (que será visto em mais detalhes posteriormente no
capítulo “Strings e Padrões”).
Tabela simplificada de meta-caracteres PCRE (Perl-compatible Regular Expressions), importante na
resolução das questões anteriores:
Meta-caracter Equivalência
\d Número de 0-9
\D Tudo exceto número de 0-9
\s Qualquer espaço, tabulação e nova linha
\S Tudo exceto espaço, tabulação e nova linha
\w Qualquer alfanumérico e _ (underscore)
\W Tudo exceto alfanumérico e _ (underscore)
Uma dica rápida para mentalizar: d significa digit (números, de 0 a 9), w significa word (palavras,
alfanumérico) e s significa space (espaço, tabulações e nova linha).
Sendo que MAIÚSCULO indica negação. Portanto a resposta correta da penúltima questão apre-
sentada é: D: \D (“qualquer caractere que não seja dígito decimal”) e a última questão apresentada
tem como resposta B: \S (“qualquer caractere que não seja espaço em branco”).
Alguns dos snippets de código e de output foram reformatados automaticamente pelo parser do
LeanPub. Nesses casos as linhas foram divididas por uma barra invertida (\) seguida por um caractere
de nova linha. Ao transcrever os exemplos, remova estes dois caracteres e una as linhas novamente,
obtendo os resultados corretos.
A tag de fechamento não é obrigatória – inclusive aconselha-se manter o bloco PHP aberto quando
não há HTML alternado, para evitar erros de cabeçalho.
Quiz
No fim de cada capítulo teremos uma conjunto de 5 perguntas para testar seus conhecimentos. São
simulados que procuram contemplar 100% do conteúdo, no intuito de deixá-lo mais preparado para
o exame.
O enfoque desse guia é na realização de simulados para lhe dar ótimas condições em busca da
aprovação e, consequentemente, uma melhor colocação no mercado de trabalho.
1 if("um"==0){
2 echo "Verdadeiro";
3 }
4 else {
5 echo "Falso";
6 }
A primeira vista, você deve achar que o resultado da execução deste singelo script será Falso, afinal
a string um é nitidamente diferente de 0 numérico – mas é aí onde ocorrem os equívocos, que podem
comprometer uma questão inteira.
O PHP, por natureza, é uma linguagem fracamente tipada - abstraindo as mudanças trazidas no
PHP 7.0 que discutiremos adiante. Significa que não é exigido a declaração dos tipos de dados que
as variáveis irão receber e retornos de função. Ex.: inteiro, alfanumérico, booleano etc.
Os tipos de dados das variáveis podem sofrer mudanças no decorrer da execução de acordo com o
valor que lhes são atribuídas. Essa característica chama-se tipagem dinâmica e trata-se de uma
característica fundamental da linguagem. Ou seja: uma string converter-se em número que, no
momento seguinte, converte em booleano (true ou false).
Implicitamente o PHP realiza casting automático no intuito de facilitar a vida do programador, mas
também gera problemas e um potencial enorme de questões nessa linha.
O PHP realmente se esforça para que o seu script funcione, mesmo que o código esteja problemático
– a tipagem dinâmica é uma característica da linguagem e não é necessariamente ruim, desde que
utilizada com parcimônia.
Tal comportamento pode ser perigoso e acontece pois o PHP converte a string um em integer,
resultando em inteiro 0. Consequentemente o teste 0 == 0 é verdadeiro.
Experimente:
Fundamentos do PHP 50
1 var_dump(intval("um")); //int(0)
2 var_dump(intval(0)); //int(0)
A propósito: a função intval() converte o conteúdo passado como parâmetro em inteiro. Ex.:
Outro fator a ser mencionado é que o == não irá comparar os tipos de dados, diferentemente do ===
(idêntico). Veremos adiante mais detalhes a respeito.
<?php
function ExibeFilme($filme = ""){
if(8 == "8 Mile"){
$resultado = "Verdadeiro";
}
else {
$resultado = "Falso";
}
echo $filme;
}
echo ExibeFilme();
?>
A: Verdadeiro
B: Falso
C: Parse error
D: (vazio)
E: 8 Mile
Pelo princípio da conversão automática que vimos anteriormente, o filme 8 Mile é convertido para
8 inteiro, resultando num teste verdadeiro. Porém esse código está encapsulado numa função. Por
sua vez, essa função escreve na tela o conteúdo da variável $filme que é vazia (pois a função foi
invocada sem a passagem de parâmetros).
Por fim de nada adiantou o teste, pois a exibição de $filme foi inicializada como vazia por default,
caso não houvesse argumento. Observe a atribuição feita na definição da função ($filme = "").
Portanto a resposta é D: (vazio). Mais informações em http://php.net/manual/en/language.operators.
comparison.php.
Fundamentos do PHP 51
Esse tipo de situação ocorre com frequência, ofuscando e direcionando a atenção para um ponto
do código irrelevante, que evidentemente altera o resultado. Eles apresentam um código que sequer
realiza tal operação.
A transformação automática de tipos de acordo com o contexto é chamada de Type Juggling³¹:
http://php.net/manual/en/language.types.type-juggling.php, que veremos adiante.
Sintaxe
Após essa introdução, veremos como incorporar código PHP em páginas web:
Blocos Descrição
<?php ... ?> Canônica
<? ... ?> Curta
<?= ... ?> Curta com echo implícito
Na versão 7.0 do PHP foram removidas as tags <% ... %> (formato ASP) e <script
language="php"> ... </script> prevista na especificação do HTML.
Os blocos de código PHP definido pelas tags em formato ASP estão desabilitadas na instalação
padrão da linguagem. Mesmo no php.ini do PHP 7.1 consta a diretiva: asp_tags = Off.
As tags curtas são configuradas através da declaração short_open_tag no php.ini.
Importante lembrar que a tag reduzida juntamente com a construção echo aumenta a velocidade de
codificação do desenvolvedor no momento de exibir dados na página. Ex.: <?="valor do campo"?>
ao invés de <?php echo "valor do campo"; ?>.
Em geral, ao codificar PHP, a tag tradicional <?php é a opção mais utilizada - recomendada pelas
boas práticas.
<?='ZCPE'?>
³¹Juggling significa malabarismo em inglês e as transformações automáticas representam justamente essa ideia, também conhecido como
“moldagem implícita” (implicit casting).
Fundamentos do PHP 52
C: As "short tags" <? ... ?> devem estar especificadas no php.ini para funcionar
D: Essa construção é particularmente possível e exibe "ZCPE"
E: Gera um mensagem do tipo Notice pois considera "ZCPE" como uma constante
A resposta para essa pergunta é a alternativa D, pois essa construção é possível mesmo que a diretiva
short_open_tag esteja desligada (Off). Lembre-se que <?= é uma short tag com echo implícito e
está SEMPRE habilitada.
Podemos alternar inúmeros blocos PHP dentro de um mesmo documento HTML, inclusive abrindo
chaves num trecho de código PHP (pertencente a um if, declaração de função, classe etc), apresentar
uma porção HTML e fechar chave em outro bloco, na sequência.
Todas as declarações PHP terminam com ponto-e-vírgula (;) desconsiderando espaços e quebras de
linha. Significa que auditoria($data,$acao,$usuario); é o mesmo que:
auditoria(
$data ,
$acao ,
$usuario
);
Essa característica é vantajosa quando formos identar nosso código, de modo que fique mais legível.
As variáveis iniciam por cifrão $ seguido de letras e underscore, assim como os nomes de
funções/métodos. Não é permitido que os nomes de variáveis e funções iniciem por números.
A: $1 = "Ari";
B: $_1 = "Ari";
C: function object() {}
D: function 6Pack() {}
E: function _6Pack() {}
A primeira alternativa $1 não é permitida pela variável começar por número, tão pouco a alternativa
D: function 6Pack() {}. Todas as demais são corretas, são elas as alternativas: B, C e E. Importante
lembrar que object não é uma palavra reservada e pode ser utilizado livremente.
Comentários de código são feitos com // para uma única linha e /* e */ para blocos, quando
possuem mais de uma linha.
Fundamentos do PHP 53
Nos comentários do arquivo de configuração php.ini eles são feitos com ponto-e-vírgula ; e desde
a versão 7.0 não se aceita mais sustenido #.
Ao exibir dados, podemos utilizar as construções da linguagem echo ou print. Existem diferenças
sutis entre elas como, por exemplo, echo pode ter mais de um parâmetro (desde que estejam
sem parênteses) enquanto que print aceita somente um argumento. Outro detalhe é que print
assemelha-se a uma função, pois retorna sempre o valor 1. Atenção que esse retorno fixado em 1
geralmente aparece no exame.
<?php
echo '5'.(print '2')+3;
?>
Operadores
Como qualquer linguagem, vamos rever as operações matemáticas:
Essa questão pode gerar dúvida, já que a divisão é feita por -8 (negativo oito). O módulo considera
o valor absoluto de um número, isso quer dizer que o sinal é simplesmente desconsiderado. Então a
resposta é B: 4.
A atribuição é feita através do sinal de igualdade =, com atenção especial a cópia por referência
utilizando o &:
1 $a = 1;
2 $b = $a;
3 $b = 2;
4 //$a == 1
5
6 $a = 1;
7 $b =& $a;
8 $b = 2;
9 //$a == 2
Dentro da forma curta, temos duas maneiras de incrementar por uma unidade: $a++ e ++$a. Ou
decrementar por uma unidade, através da construção $a-- e --$a.
Fundamentos do PHP 55
1 //Divisão
2 var_dump(6/3); //int(2)
3 var_dump(5/2); //float(2.5)
4
5 //Divisão de inteiros negativos
6 echo floor(-5/2); //floor(-2.5) que resulta -3
7 echo (int)(-5/2); //int(-2.5) que resulta -2
8
9 //Nova função (PHP 7.0)
10 echo intdiv(5,2); //2
A versão 7.0 do PHP implementou a função intdiv() que retorna o quociente inteiro da
divisão do dividendo pelo divisor.
Como operadores de igualdade temos == e diferente determinado por != (também representado por
<>). Válido ressaltar que o PHP assume a conversão para um tipo de dados em comum. Ex.: "123"
== 123.
E o idêntico escrito por === e seu oposto !==, onde o PHP verifica também o tipo de dado. Ex.: "123"
!== 123.
A: Sim
B: Não
Nessa questão observe que 1230E-1 é uma notação científica, que representa 123.0. Internamente
um float(123) que é diferente de um integer(123). Como ele compara com !== significa que $a
é true, portanto letra A: é a opção correta.
Aproveitando o contexto de notação científica, podemos nos deparar em algo como echo 1230E+1
que significa 12300.
Fundamentos do PHP 56
Operador Significado
> Maior que
< Menor que
>= Maior ou igual
<= Menor ou igual
<=> Nave espacial (spaceship)
?? Coalescência nula
O operador spaceship foi incorporado no PHP 7.0. Se o operando da esquerda é menor que o da
direita, o operador <=> devolve -1. Se o operando menor for o da direita, então retorna 1 e se forem
iguais retorna 0:
1 $a = 5;
2 $b = $c = 9;
3
4 echo ($a <=> $b); // -1
5 echo ($b <=> $c); // 0
6 echo ($c <=> $a); // 1
Ao testarmos a entrada de dados via GET, por exemplo, geralmente utilizamos uma expressão
ternária (que veremos adiante):
A função isset() retorna true se a variável passada como parâmetro existir e false caso não exista.
Essa expressão alimenta a variável $usuario com o conteúdo vindo de $_GET['usuario'] caso ele
exista, se não $usuario guarda a string ninguem.
De forma que o código fique mais elegante, sem mensagens de Notice: Undefined index: usuario
in, frequentemente utilizamos esse tipo de expressão para testar os dados vindos de outras fontes.
Então a versão 7.0 trouxe o operador ?? conhecido como coalescência nula que simplifica essa
construção.
O operador ?? retornará o primeiro operando se este existir (e não for null), se não retornará o
segundo operando.
Este operador permite aninhamento. Significa que se o primeiro não existir, ele passa para a segunda
tentativa e assim sucessivamente:
Outra construção que pode entrar no teste seria:
Fundamentos do PHP 57
1 echo isset($a) ? 'A existe' : ($a = 'a').' agora existe'; //a agora existe
O isset($a) retorna false, pois $a não existe. Então o operador ternário executa o código após os
dois pontos (:), atribuindo um valor para $a, imediatamente exibindo-o em decorrência do echo
inicial.
A: 0
B: 1
C: 2
D: 3
Essa pergunta é estranha pois realiza várias comparações de strings, o que é aceitável no PHP. Mas a
conversão é realizada considerando o código ASCII dos caracteres, mais especificamente do primeiro
(que é o mais significativo).
Atente-se que a variável minúscula $php é quem recebe o valor em maiúsculo e vice-versa, sendo
que os caracteres maiúsculos vem antes na tabela ASCII e consequentemente tem um número menor
associado, o que também confunde.
Analisando de trás para frente, teremos o teste ($p == $P) que é false e multiplicado por 3 que
resulta em 0. O mesmo acontece com ($p > $P) que também é false e multiplicado por 2 retorna
0, daí sobra ($p < $P) que é true e exibe 1 por interferência do type juggling. Portanto a alternativa
correta é B: 1.
Abaixo explico de forma visual a resolução dessa pergunta:
Fundamentos do PHP 58
• conversão de números inteiros para o modo binário – que é um sistema numérico composto
por dois dígitos, que compreende 0 e 1;
• operação envolvendo números binários.
Primeiramente vamos entender como converter um número decimal em binário sem o uso de
calculadora. No momento da prova, esse tipo de questão requer um scratch paper (papel de rascunho)
ou eraseable board (quadro apagável).
Para convertermos um número qualquer (base 10), basta dividi-lo por 2 (base binária) até chegarmos
em 1 (um). Depois coletamos o resto das divisões na ordem contrária.
Veja passo-a-passo o exemplo onde transformamos o número 13 decimal em binário:
Para realizarmos a operação inversa, que seria a transformação de binário para decimal, primeira-
mente multiplique o dígito binário (0 ou 1) por seu valor de posição. Cada coluna aumenta em
múltiplos de 2, começando (da direita para esquerda) em 2⁰, 2¹, 2², 2³, 2⁴, 2⁵ e assim por diante:
Passo Cálculo
binário 1 1 0 1
passo opcional (para efeito didático) 2³ 2² 2¹ 2⁰
multiplica o binário pelo “valor de posição” 8 4 2 1
soma os resultados da multiplicação 8+4+0+1
decimal 13
Fundamentos do PHP 60
O meio mais fácil de converter números binários pequenos (com até dez dígitos) em seus equivalentes
decimais é imaginar que o valor de posição de cada coluna binária esteja escrito nos dedos da mão.
Contanto que o número binário não tenha mais de cinco dígitos, tudo o que você precisa fazer é
erguer o dedo correspondente ao respectivo dígito binário 1 e abaixar o dedo quando for um 0.
Ao erguer os dedos correspondentes para formar 11001 da segunda ilustração, você obterá o 16
(polegar), 8 (indicador) e 1 (dedo mínimo); o resultado de sua soma é o número decimal 25.
Fundamentos do PHP 61
A terceira figura mostra como decodificar o 01110: que gera 8+4+2 = 14.
O método pode ainda ser ampliado usando-se ambas as mãos para ilustrar números binários mais
longos.
Após aprendermos a conversão, faremos então as operações binárias AND, OR e XOR.
Mas antes precisamos resgatar alguns conceitos básicos da ciência da computação, como a “tabela
verdade”³²:
A B AND (&)
0 0 0
0 1 0
1 0 0
1 1 1
A B OR (|)
0 0 0
0 1 1
1 0 1
1 1 1
³²https://pt.wikipedia.org/wiki/Tabela_verdade
Fundamentos do PHP 62
A B XOR (^)
0 0 0
0 1 1
1 0 1
1 1 0
Quando sugir uma questão com os operadores binários & (AND), | (OR) e ^ (XOR) é necessário
primeiramente converter o número em binário, dispôr dos dígitos em sequência (um embaixo do
outro) e comparar cada algarismo:
Fundamentos do PHP 63
Fundamentos do PHP 64
A: 5^3
B: 5 * 5 * 5
C: pow(5,3)
D: pow(3,5)
E: 5 ** 3
A alternativa A pode confundir os usuários de planilhas eletrônicas, tipo Excel, onde o elevado ao
expoente 3 tem exatamente essa sintaxe. Lembre-se que ^ no PHP é o operador bit-a-bit XOR, que
nada tem haver com exponenciação.
A alternativa B funciona, mas não é elegante (como propõe o enunciado) pois não é genérica o
suficiente, caso quiséssemos trocar a base.
A opção D sugere a função pow (específica para esse propósito), mas os argumentos são ($base,
$expoente), enquanto que a alternativa mostra os parâmetros invertidos.
Como vimos no exercício anterior, o operador ** eleva a potência da direita o número que está a
sua esquerda. Ele pode ser expressado na sua respectiva forma curta **=. Ex.:
1 $a = 2;
2 $a **= 3;
3 echo $a; //8
Nas operações bit-a-bit (bitwise) temos a possibilidade de deslocamento de bits a esquerda e a direita.
Se deslocarmos a esquerda, conhecida como left shift, utilizamos o operador <<.
No exemplo a seguir iremos realizar um left shift do número 3 em binário deslocando 4 bits à
esquerda. O dígito 3 é representado como 11 em binário, que será incrementado de zeros quantas
vezes for o deslocamento (no caso 4), transformando em 110000 binário que representa 48 decimal.
Você também pode associar o operador << como uma operação de multiplicação, onde o desloca-
mento é o expoente de 2. Ex.: 3 * 2^4^ = 3 * 16 = 48.
No próximo exemplo iremos demonstrar o right shift através do operador >>, ao realizar o cálculo
do número 8 em binário deslocado 2 bits à direita.
Como sabemos, o número 8 decimal é representado pelo binário 1000. Se deslocarmos 2 bits a direita,
ele transforma-se em 10 binário que representa o 2 decimal.
Fundamentos do PHP 66
A operação bit-a-bit de deslocamento a direita é também conhecida como uma operação de divisão.
Ex.: //8 ÷ 2² = 8 ÷ 4 = 2.
A: 3
B: 0
C: 10
D: 2
Nessa questão temos o & lógico (operação bitwise) entre 10 decimal (1010 binário) e 2 decimal (0010
binário). Ao realizarmos as operações de AND, resultará em 0010 que é o número 2 decimal.
Fundamentos do PHP 67
Outra operação binária interessante é a negação (NOT), representada por ∼, onde os zeros se
transformam em uns e os uns em zeros.
A: 32
B: 1
C: 0
D: 0.5
Usando a técnica alternativa para resolução de operação binárias, detectamos uma operação de
deslocamento a direita, portanto uma divisão. Então ficaria 4 ÷ 2³ = 4 ÷ 8 = 0.5. A tendência seria
marcarmos a alternativa D, mas o manual do PHP³³ é bastante claro, informando que o resultado
será convertidos para inteiro, então integer(0.5) resulta em 0. Isso significa que a alternativa
correta é a opção C.
1. sinal de cifrão
2. uma letra ou underscore
3. [ opcional ] letra, dígito ou underscore
Ex.:
1 $n = "ari";
2 $ari = "xyz";
3 echo $$n;
A saída será xyz, pois o parser primeiramente avalia $n (de $$n) que contém ari, formando uma
nova variável ($ari) que, por sua vez, deve ser novamente tratada. Esse modo indireto é chamado
de variável variável.
Veja mais um exemplo:
1 $pais = "Brasil";
2 $$pais = "Portugal"; //seria o mesmo que ${"Brasil"}
3 echo $Brasil; //exibe "Portugal"
Você pode acessar o conteúdo da variável através de uma construção alternativa: ${"ari"}.
A: $_a1
B: $_1
C: $1
D: $$a
Nesse caso as alternativas corretas são A, B e D. A alternativa C possui cifrão seguido de um número
e essa construção não é permitida.
Obs.: a alternativa D é uma variável variável e perfeitamente aceita.
Lembrando que os nomes devem iniciar com o sinal de dólar $, seguido de letra ou sublinhado
_ (como segundo caractere) e letra/dígito/sublinhado no terceiro caractere em diante.
Variáveis válidas seriam: $titulo, $__profissao, $Cidade e $iDaDe.
Variáveis inválidas seriam: $#lote, $5porcento, $(observacao) e $recur$o.
As variáveis (bem como as constantes, que serão vistas no tópico seguinte) são case sensitive. Ou seja:
diferenciam maiúsculas e minúsculas. Portanto $ari aponta para uma região de memória diferente
de $Ari.
Fundamentos do PHP 69
Constantes
Definidas através da função nativa define(), também são case sensitive e convencionou-se utilizá-
las sempre em maiúsculas. Pelo fato do conteúdo ser fixo, não permitem que se alterem no decorrer
da execução do script.
1 define("HELLO", "Oi");
2 echo HELLO;
3 define("HELLO", "Beleza"); //Mostra um aviso, mas não interrompe a execução do s\
4 cript
5 echo HELLO;
Todas as constantes tem o escopo global. Isso significa que elas serão visíveis em toda a parte do
código, inclusive dentro de funções, classes e métodos.
1 define("IDADEMINIMA", 5);
2 function ObtemCortesia(){
3 echo IDADEMINIMA;
4 }
5 ObtemCortesia(); //5
Por natureza as variáveis definidas dentro de funções tem escopo local, a menos que se force a
visibilidade com a palavra reservada global $variavel;
Também posso definir a constante dentro da função/classe/método e recorrer à ela externamente.
Ex.:
Ao definir a constante dentro da função também a faz presente externamente. Veja esta questão:
?>
A: 8
B: <vazio>
C: "8"
D: Notice: Use of undefined constant EXPEDIENTE_INICIAL...
Constantes mágicas
O PHP nos oferece uma série de constantes mágicas. A maioria segue a convenção, com dois
underscores no ínicio e dois no fim. Ex.:
Constantes mágicas Descrição
__LINE__ Número da linha corrente, do arquivo em questão
__FILE__ Caminho completo e nome do arquivo com links simbólicos resolvidos
__DIR__ Diretório do arquivo
__FUNCTION__ Nome da função
__CLASS__ Nome da classe incluindo o namespace em que foi declarado
__TRAIT__ Nome do trait incluindo o namespace em que foi declarado
__METHOD__ Nome do método
__NAMESPACE__ Nome do namespace
NomeDaClasse::class Nome completo da classe
Digamos que você tenha um script.php com o conteúdo abaixo rodando na raiz do se\
rvidor /var/www:
<?php
include "constantesmagicas.php";
A: script.php
B: constantesmagicas.php
C: /var/www/script.php
D: /var/www/constantesmagicas.php
E: Nenhuma das alternativas
Pode haver alguma confusão nessa pergunta, pois o código PHP que está rodando é script.php,
porém a constante mágica __FILE__ foi exibida em constantesmagicas.php. Portanto ele retorna o
caminho completo do arquivo em que ele está fisicamente, nesse caso /var/www/constantesmagi-
cas.php.
Expressões em constantes
1 define("IDADE_MIN", 5);
2 const IDATE_MAX = IDADE_MIN * 4;
3 const QUILOMETRAGEM = 7;
4 define("DISTANCIA_US", QUILOMETRAGEM * 1.61);
5
6 class C {
7 const CENTROCUSTO = "Matriz";
8 const MENSAGEM = "Sua unidade é: ".self::CENTROCUSTO;
9 }
10
11 echo IDATE_MAX;
12 echo DISTANCIA_US;
13 $obj = new C;
14 echo $obj::MENSAGEM;
Outra possibilidade que temos disponível a partir da versão 5.6 do PHP, são as constantes originadas
de arrays, definidas através da palavra-chave const ou pela função define():
Fundamentos do PHP 72
Visibilidade em constantes
Um recurso interessante disponibilizado na versão 7.1 é a possibilidade de definir visibilidade para
as constantes, assim como fazemos habitualmente em atributos.
1 class Cerveja
2 {
3 public const IMPOSTO = 27;
4 protected const PRODUCAO = 2000;
5 }
6
7 $produto = new Cerveja;
8 echo $produto::IMPOSTO."<br>";
9 echo $produto::PRODUCAO."<br>";
O resultado será:
27
Fatal error: Uncaught Error: Cannot access protected const Cerveja::PRODUCAO in \
...
Nas definições de constantes observe que a constante IMPOSTO é pública (como sempre foi, na era pré-
PHP 7.1) e resulta em 27. Porém a constante PRODUCAO é protected e não permite que seja acessada
externamente a classe, gerando um Fatal error.
Tipos de dados
O PHP suporta os mais variados tipos de dados (veja tabela abaixo), inclusive com total flexibilidade
na atribuição de valores, sem a necessidade de tipificá-las anteriormente – basta carregar o dado na
respectiva variável, deliberadamente.
Existem três categorias de variáveis: escalar, composta e de recurso.
A variável escalar permite armazenar um dado por vez e podem ser divididas em quatro sub-tipos:
Fundamentos do PHP 73
Inteiro 36
-250
Booleanos true
false
Variáveis compostas podem conter diversos valores ao mesmo tempo e podem ser de dois sub-tipos:
Já um recurso aponta para algo não nativo do PHP, tal como um manipulador de arquivo provido
pelo sistema operacional ou um conector de banco de dados. Este tipo de variável não pode ser
convertido para outro tipo:
Por fim temos o tipo nulo (null), que são variáveis sem valor. Veremos exemplos mais adiante no
sub-tópico Tipos Nulos (Nullable types).
Tamanha flexibilidade pode tornar-se um tormento quando o programador tem pouca experiência
e não faz uma atribuição consciente do tipo de dado que uma determinada estrutura deveria conter
- em decorrência do type juggling (que veremos no próximo tópico).
Uma função correlacionada que sempre cai no teste é a boolval() que obtém o valor booleano de
uma variável.
Tomei a liberdade de extrair da documentação oficial http://php.net/manual/pt_BR/function.boolval.
³⁴http://php.net/manual/en/resource.php
Fundamentos do PHP 74
php um trecho de código que exemplifica os principais cenários de uso, que provavelmente vão cair
no teste. Ex.:
O resultado será:
0: false
42: true
0.0: false
4.2: true
"": false
"string": true
"0": false
"1": true
[1, 2]: true
[]: false
stdClass: true
Type Juggling
Sabemos que o PHP tornou-se amplamente difundido em decorrência de sua flexibilidade, da
possibilidade de podermos atribuir uma variável diretamente sem a necessidade de a definirmos
anteriormente.
Outra habilidade interessante, e que foi pensada para ser dessa forma, é a capacidade de atribuirmos
qualquer tipo de dado em uma variável. É uma característica conhecida como fracamente tipada.
Se você testar operandos com == o operador não compara os tipos de dados, diferentemente do
operador === (que significa idêntico).
Daí entra a “magia” onde, implicitamente, o PHP realiza uma conversão automática, conhecida na
literatura como type juggling ou implicit casting.
Fundamentos do PHP 75
Veja esse exemplo de conversão, onde $x que possui o valor 39 inteiro é concatenado com 11 em
formato string:
1 $x = 39;
2 $x .= "11"; // $x = "3911";
Evidentemente que há um custo para a realização das conversões implícitas, algo que o PHP 7 tira
proveito. Se o desenvolvedor opta por restringir os tipos de dados que as variáveis terão, o código
PHP fica consideravelmente mais rápido.
No type juggling o PHP examina a string da esquerda para a direita até encontrar um caractere
inválido. Veja alguns exemplos de conversão:
1 $x = "12x" * 1; // (int)12
2 $x = "1.2x" * 1; // (float)1.2
3 $x = "12E-1x" * 1; // (float)1.2
4 $x = "08x" * 1; // (int)8
5 $x = "E1x" * 1; // (int)0
Agora sabemos que o PHP realiza um casting automático para equiparar os operandos. Ex.:
Pois se você converter (int)'1a' resultará em 1 inteiro, diferente de (int)'a1' que resultará em
inteiro 0.
Na versão 7.1 esse tipo de operação aritmética gera um Notice: A non well formed numeric
value encountered in ..., o que é bastante útil para o desenvolvedor.
Se você somar um número com uma string (que é uma situação inusitada), por exemplo echo 1
+ 'a';, daí o PHP 7.1 gera um Warning: A non-numeric value encountered in .... Veja que o
nível de criticidade, nesses casos, aumentou de Notice para Warning.
Fundamentos do PHP 76
A: Parse error
B: 3
C: 13
D: 310
E: Notice e o valor 3
A primeira instrução que chama a atenção é "0x02". Sabemos que valores iniciados por 0x são
identificados como hexadecimais - mesmo assim o 2 hexadecimal equivale a 2 decimal.
Mas o crítico nesse caso são as aspas duplas externas que a identificam como uma string. Ao realizar a
operação de multiplicação, o PHP faz a conversão automática transformando "0x02" em (integer)0,
sendo que a multiplicação por 5 resulta em 0.
A resposta nesse caso é um Notice em decorrência da multiplicação por string (Notice: A non well
formed numeric value encountered), então a resposta é E: Notice e o valor 3.
Obs.: na versão 7.0 do PHP essa resposta seria diretamente 3, pois ele já converteria os valores
implicitamente.
Outra habilidade do casting é transformar array em objeto e vice-versa.
$a = array('a', 'b'=>'c');
//Atribuição de 'a' será realizada na chave '0'
//Ao converter o array em objeto, os índices transformam-se em atributos
//portanto teremos [0] => 'a', ['b'] => 'c'
Analisando a primeira linha vemos que a atribuição de a será realizada na chave 0 e o valor c será
armazenado no índice b. Ao converter o array [0] => 'a', ['b'] => 'c' em objeto, os índices do
array transformam-se em atributos do objeto. Significa que o primeiro teste property_exists()
será falso e o segundo verdadeiro. Resultado: falsoverdadeiro.
Fundamentos do PHP 77
1 function contaLetrasDasPalavras($texto){
2 //Se o parâmetro for vazio, retorna vazio
3 if(empty($texto)){
4 return "";
5 }
6 $string = explode(" ", $texto);
7 foreach($string as $item){
8 //Alimenta o vetor com o tamanho de caracteres de cada palavra
9 $vetor[] = strlen($item);
10 }
11 return $vetor;
12 }
13
14 echo "<pre>";
15 print_r(contaLetrasDasPalavras("Ari Stopassola Junior"));
16 echo "</pre>";
Array
(
[0] => 3
[1] => 10
[2] => 6
)
Agora experimente determinar que obrigatoriamente o retorno da função deve ser um array.
Adicione : array na declaração de função de modo que ela fique:
Fundamentos do PHP 78
A partir de agora, se você passar uma string vazia para a função contaLetrasDasPalavras() a
condição do if será satisfeita e devolverá um retorno vazio provocando:
Esta mesma construção pode ser utilizada no contexto de orientação à objetos. Veja um exemplo
com interfaces (que será explorada mais adiante), onde pré-determinamos que o retorno do método
obtemData() será um objeto DateTime. Ex.:
1 interface Auditoria
2 {
3 public function obtemData() : DateTime;
4 public function obtemGrafico();
5 }
Então provavelmente vais ter uma classe que implementará a interface Auditoria. Lembrando que
esse conceito será desenvolvido de forma aprofundada no seu respectivo capítulo (em “Programação
Orientada à Objetos”):
1 interface Auditoria
2 {
3 public function obtemData() : DateTime;
4 public function obtemGrafico();
5 }
6
7 class Vendas implements Auditoria {
8 public function obtemData() : DateTime {
9 return new DateTime("2017-02-23");
10 }
11 public function obtemGrafico(){
12 //...
13 }
14 }
15
16 $obj = new Vendas;
17 var_dump($obj->obtemData());
A sintaxe do Scalar Type Hinting é que venha(m) antes da(s) variável(eis) que compõe os parâmetros.
Ex.:
No contexto de indução de tipos booleanos e inteiros, o PHP não suporta alias, então jamais utilize,
respectivamente, nomes como boolean e integer - somente as palavras-chave bool e int.
A única exceção nas “dicas de tipos” de parâmetros é quando o usuário determina um argumento
como float, entretanto o PHP aceita um valor inteiro.
Lembrando que originalmente o PHP é uma linguagem fracamente tipada, característica que
contribuiu em sua popularização. Em modo fraco (weak mode) os tipos de dados são convertidos
automaticamente seguindo as regras de conversão das versões anteriores do PHP. Em modo rigoroso
(strict mode) os tipos de dados devem ser obedecidos e requer que seja habilitado arquivo por
arquivo.
Para alterar a execução do script em modo rigoroso:
• declare(strict_types=1);
• deve ser a primeira declaração do arquivo
• a declaração será aplicada para todo o arquivo
• não afeta scripts via include ou que incorporam esse arquivo
Veja um exemplo utilizando o modo flexível, onde o PHP converte o tipo de dado automaticamente
através do type juggling:
Fundamentos do PHP 80
Lembre-se que usar o modo strict gera um ganho significativo de performance, mas requer maior
disciplina do programador para passar e receber adequadamente os tipos de dados.
1 class Usuario
2 {
3 public $nome;
4
5 public function getNome() : string
6 {
7 return $this->nome;
8 }
9 }
10
11 $usr = new Usuario;
12 //$usr->nome = "Ari";
13 echo $usr->getNome();
O erro fatal ocorreu pois $nome permanece nulo, já que sequer lhe foi atribuido alguma string.
Para evitar esse tipo de erro crítico, o PHP 7.1 trouxe a possibilidade de colocarmos um ponto
de interrogação na frente da especificação de tipo, permitindo então que o valor retornado seja
eventualmente null. Ficaria assim:
1 class Servico
2 {
3 public function __construct()
4 {
5 $this->registraServico();
6 }
7
8 public function registraServico() : void
9 {
10 //Executa, mas não necessariamente retorna valor ou devolve nulo
11 }
12 }
13
14 $srv = new Servico;
Fundamentos do PHP 82
Nesse caso, que simula um registro de serviço qualquer, convém especificar que não há retorno
para o método registraServico(). É uma funcionalidade do PHP 7.1 que determina um retorno
absolutamente vazio, ou seja, sem retorno.
Delimitadores
Delimitadores de string podem ser aspas duplas ou simples. Usar aspas duplas oferece vantagens,
já que o conteúdo delimitado é interpretado e podemos trabalhar com:
Nesse caso o parser irá substituir o $n2 que contém a string Stopassola, e não apenas o $n (Ari
Stopassola Junior) concatenado com a string 2, que formaria Ari Stopassola Junior2. Portanto
a resposta é D: Olá Stopassola.
Observe essa pequena modificação:
Daí o resultado seria totalmente diferente. Nesse caso A: Ari Stopassola Junior2 decorrente das
chaves que delimitam o nome da variável.
Fundamentos do PHP 83
Observe atentamente e perceba que o primeiro conjunto de caracteres está demarcado sob aspas
simples, enquanto que o segundo está envolto por aspas duplas. Aspas simples mantém os caracteres
exatamente como estão, portanto o tamanho (strlen()) da string dessa primeira expressão é 4. Já
a segunda porção resulta em 3 pois \n é convertido em quebra de linha e conta como um caractere,
ao invés de dois.
A resposta então é: 12.
Lembre-se de preencher a resposta sem espaço, ponto, tabulação ou comentário. Simplesmente o
número!
Estruturas de controle
O PHP suporta diversas estruturas de controle categorizadas em condicionais e de iteração. Na
condicional temos o tradicional if-then-else. Ex.:
1 if (expressao){
2 //Faça algo
3 } else if (outraExpressao) {
4 //Faça outra coisa
5 } else {
6 //Faça algo diferente
7 }
1 if (expressao):
2 //Faça algo
3 elseif (outraExpressao) :
4 //Faça outra coisa
5 else :
6 //Faça algo diferente
7 endif;
Podemos também omitir as chaves de ínicio e fim dos blocos, desde que a instrução não ultrapasse
uma linha. Ex.:
1 if (true)
2 echo 'verdadeiro';
3 else
4 echo 'continua o fluxo';
1 if (true)
2 echo 'verdadeiro';
3 echo 'continua o fluxo';
Vai exibir verdadeirocontinua o fluxo, dando a impressão de que a segunda linha também faz
parte do bloco if enquanto true. O teste faz exibir somente a primeira linha e as demais pertencem
ao fluxo normal de execução do código. Melhor identado, ficaria assim:
1 if (true)
2 echo 'verdadeiro';
3 echo 'continua o fluxo';
1 $a = ValorSeVerdadeiro ?: ValorSeFalso
A: 'verdadeiro'
B: true
C: 'v'
D: 'f'
E: false
Perceba que nesse exemplo estamos avaliando operadores ternários aninhados. O desenvolvedor é
induzido a resolver a expressão à direita (false ? 'v' : 'f') que resultaria em 'f' já que a con-
dição é falsa) e posteriormente executaria o teste restante mais a esquerda (true ? 'verdadeiro'
: 'f'), resultando em 'verdadeiro'.
Mas os operadores ternários tem um comportamento diferenciado e são avaliados da esquerda para
a direita.
A ordem correta é:
O site oficial php.net³⁵ desaconselha o uso de operadores ternários empilhados, justamente por esse
comportamento não ser tão óbvio.
³⁵http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary
Fundamentos do PHP 86
A stdClass (standard class) é uma classe genérica vazia nativa da linguagem. Ao forçarmos
uma conversão desse objeto para booleano, a expressão new stdClass será transformada para
bool(true), portanto o teste será verdadeiro e consequentemente irá mostrar 1 na tela - que é a
conversão do (bool)true da operação ternária. Portanto você deve digitar 1 no campo.
Você pode se deparar com outra variante do operador ternário: a sua forma curta de uso. Observe:
1 $resultado = "Feito";
2 $a = $resultado ?: "Incompleto";
3 echo $a;
1 $total = 0;
2 $i = 1;
3
4 while($i < 10){
5 $total += 7;
6 $i++;
7 }
8 echo $total; //63
Enquanto a expressão $i < 10 for verdadeira, o loop continua executando as declarações do bloco.
O while também possui uma sintaxe alternativa. Ex.:
1 $total = 0;
2 $i = 1;
3
4 while($i < 10):
5 $total += 7;
6 $i++;
7 endwhile;
8 echo $total; //63
1 $i=0;
2 while ($i < 10) {
3 echo "$i ";
4 if ($i == 4) {
5 echo "encerrando o if.";
6 break;
7 echo "isso jamais será executado";
8 }
9 $i++;
10 }
No código acima, o loop iria de 0 até 9, exceto pelo teste if que sai da iteração quando $i valor 4. A
saída será:
0 1 2 3 4 encerrando o if.
Da mesma forma que você foge da iteração com break, é possível passar para o próximo teste da
condição através da palavra-chave continue.
1 $i=0;
2 while ($i < 10) {
3 if ( ($i >= 4) AND ($i <= 8) ) {
4 $i++;
5 continue;
6 }
7 echo "O valor de \$i é $i<br>";
8 $i++;
9 }
Ao identificar que ($i >= 4) AND ($i <= 8) o $i é incrementado e o continue executado. Significa
que a linha echo "O valor de \$i é $i<br>"; e o outro incremento de $i não serão executados
naquelas circunstâncias, já que o continue força o laço a ir para o ciclo seguinte.
A saída, no exemplo acima, será:
O valor de $i é 0
O valor de $i é 1
O valor de $i é 2
O valor de $i é 3
O valor de $i é 9
Podemos também ter laços aninhados, e a capacidade de dispararmos o break e continue para que
operem em mais níveis de iteração. Ex.:
Fundamentos do PHP 88
1 $i = 0;
2 $j = 0;
3
4 while($i < 10){
5 while($j < 10){
6 if($j == 5){
7 break 2; //interrompe os DOIS laços
8 }
9 $j++;
10 }
11 $i++;
12 }
13
14 echo "{$i} {$j}";
1 $cpu = 3000;
2 do {
3 echo "Cpu: $cpu Mhz";
4 $cpu = $cpu + 200;
5 }
6 while ($cpu <= 1000);
O resultado acima será: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, .
Fundamentos do PHP 89
A: Erro
B: 1,1,1,1,1,1,1,1,1,1,
C: 1,1,2,3,5,8,13,21,34,55,
D: Vazio
Nessa pergunta, iremos chamar dez vezes a função fibonacci(). Mas perceba que não passamos
argumentos, portanto $x1 e $x2 assumem valores default, respectivamente 0 e 1.
A malícia da questão é a passagem de parâmetros por referência (&) insinuando que os valores de $x1
e $x2 se perpetuariam - o que não é verdade. Já que a função é sempre invocada sem parâmetros,
consequentemente devolve sempre o valor 1 atribuído em $result.
Portanto a resposta correta é B: 1,1,1,1,1,1,1,1,1,1,.
Mas voltando ao assunto das estruturas de repetição, outra sintaxe do for é utilizando : (dois pontos)
e endfor. Ex.:
No exemplo acima, observe apenas que a terceira expressão do for é um decremento. Sendo que o
laço ocorre normalmente.
Podemos especificar múltiplas expressões em cada sentença do for, separando-as por vírgulas. Ex.:
A saída desse código, rodando sob o PHP 5.6.30 (por exemplo) será:
Versão: 5.6.30
O valor de $b é: 2
Significa que ele assumiu o último default especificado, caso o switch não se enquadrasse em
nenhum case anterior.
No PHP 7.0 essa execução foi penalizada, gerando um Fatal error: Switch statements may only
contain one default clause in....
Versão: 7.1.1
O valor de $b é: 1
Namespaces
Espaço de nomes é uma forma de agrupar código PHP correlacionado, que sejam pertinentes ao
mesmo contexto como uma biblioteca ou módulo da aplicação.
Fundamentos do PHP 91
É extremamente útil para evitar colisão de nomes de funções, classes, interfaces e constantes –
na medida em que o software vai crescendo e novas bibliotecas vão sendo incorporadas. Seu uso
é importante pois subdivide o namespace global, servindo como uma espécie de “prefixo” para um
conjunto de classes.
Definições de classes e funções são globais, então o desenvolvedor precisava ter cuidado ao nomear
suas estruturas, correndo o risco de sobrescrever outros recursos disponíveis (inclusive bibliotecas
de terceiros).
Antes do PHP 5.3 a estratégia de nomenclatura era criar nomes com prefixos, algo como: Perito_-
Biblioteca_TrataImagem. A medida que a funcionalidade aumenta de tamanho, mais prefixos são
adicionados, deixando os nomes extremamente longos e consequentemente difícil de manipular e
refatorar.
A vantagem do namespace é encapsular classes, interfaces, funções e constantes abreviando nomes
longos e facilitando o reuso de código.
A: 3
B: Contando
C: Fatal error: Cannot redeclare count() in ...
D: Nenhuma das alternativas
Lembre-se que count() é uma função nativa da linguagem e provocaria erro em condições normais
EXCETO pelo fato de estarmos usando namespaces. Isso significa que ela existirá no contexto de
MinhaBiblioteca, podendo ser redeclarada - já que a \count() está no espaço de nomes global.
Portanto a alternativa correta é B: Contando.
Citando outro exemplo, podemos ter uma classe chamada Cliente no contexto de Atendimento e
outra classe também chamada Cliente no âmbito Operacional - coexistindo em harmonia, pois
estarão em namespaces distintos.
No script onde consta a definição e implementação da(s) classe(s), definimos um espaço de nomes
através da palavra reservada namespace. Ex.:
Fundamentos do PHP 92
1 namespace Atendimento\Balcao;
2 //abaixo segue o código que implementa a classe Cliente, dentre outras
Para fazermos uso da classe apropriada, de acordo com o contexto desejado, utilizo a palavra
reservada use <namespace>. Ex.: use Atendimento\Balcao;
Significa que estou operando a classe Cliente no âmbito Atendimento\Balcao, diferente da imple-
mentação da classe Cliente do espaço de nomes \Operacional (a partir da raiz da aplicação), por
exemplo.
Ao referenciar as classes, funções e constantes globais, pode-se simplesmente ignorar a instrução
use ou simplesmente apontar para a raiz: use \
Como boas práticas convencionou-se definir um namespace por arquivo de código, sendo que
declare é a única construção que precede a declaração de namespace, e nenhuma outra.
Você pode ter implementações de uma classe (de mesmo nome) sob diferentes namespaces num
mesmo arquivo – mas não usá-los. Ex.:
1 <?php
2 namespace lojafisica;
3 const EXPEDIENTE = 8;
4 class Cliente {
5 function Informacoes()
6 {
7 return __METHOD__;
8 }
9 }
10
11 namespace site;
12 const EXPEDIENTE = 24;
13 class Cliente {
14 function Informacoes()
15 {
16 return __METHOD__;
17 }
18 }
19
20 $ari = new Cliente;
21 echo $ari->Informacoes();
Nesse exemplo o objeto $ari é a instância da classe Cliente do namespace site, pois foi o último
espaço de nomes declarado e portanto segue sendo o namespace corrente. A saída do script acima
é: site\Cliente::Informacoes.
Fundamentos do PHP 93
<?php
namespace ABC\XYZ;
class A {
function __construct (){
echo __METHOD__;
}
}
$obj = new A;
?>
A: __construct
B: A::__construct
C: ABC\XYZ\__construct
D: ABC\XYZ\A::__construct
Abrindo um parênteses, outra constante mágica interessante é a __METHOD__ que informa qual
método encontra-se o código em que esse comando foi invocado, bastante importante na depuração
de erros.
Obs.: você pode utilizar __FUNCTION__ ou __METHOD__, independentemente se estiver usando funções
ou métodos. Em ambos os contextos (procedural x OO) os dois métodos mágicos funcionam.
Mas voltando ao escopo da pergunta, ao explorar o recurso de namespace a constante __METHOD__-
também os considera no processo de debug. Então a resposta correta é D: ABC\XYZ\A::__construct
Fully-qualified names não tem ambiguidade. Começam com a barra invertida \ indicando a raiz da
biblioteca, mapeando toda a ramificação, semelhante ao caminho do filesystem. Ex.:
Pode-se usar essa abordagem quando a aplicação exige poucas chamadas a essa classe. Mas é
impraticável quando requer muitas chamadas, já que requer mais digitação e suscetibilidade a erros.
Nesses casos é interessante o uso de um alias:
Fundamentos do PHP 94
1 use \Agencia\Receptivo as R;
2 use \Agencia\Receptivo\Traslado as T;
alias é uma estratégia para redução da nomenclatura de modo a facilitar o uso do namespace. Ex.:
use Applicacao\Receptivo\Balcao as BalcaoIn;.
Como novidade incorporada na versão 5.6 do PHP foi o operador use, que antes era utilizado
apenas para importar classes de um determinado espaço de nomes, e agora foi extendido e importa
constantes e funções.
1 namespace Vendas\Operadora {
2 const QUILOMETRAGEM = 7;
3 function f() { echo __FUNCTION__."\n"; }
4 }
5
6 namespace {
7 use const Vendas\Operadora\QUILOMETRAGEM;
8 use function Vendas\Operadora\f;
9
10 echo QUILOMETRAGEM."<br>";
11 f();
12 }
Eventualmente o desenvolvedor pode ter constantes e funções úteis para o seu projeto, portanto use
const e use function acessam esses recursos.
Outra novidade trazida pelo PHP 7.0 foi o agrupamento de declarações use:
1 use certificamp\php70\fundamentos\EspacoDeNomes;
2 use certificamp\php70\fundamentos\Constantes;
Ao invés de termos várias linhas para carregar diferentes namespaces, agora podemos agrupá-los:
Fundamentos do PHP 95
1 use certificamp\php70\fundamentos\{EspacoDeNomes,Constantes};
1 use certificamp\php70\fundamentos\EspacoDeNomes;
2 use certificamp\php70\fundamentos\Constantes;
3 use certificamp\php70\bancodedados\Juncoes;
4 //OU
5 use certificamp\php70\{
6 fundamentos\EspacoDeNomes,
7 fundamentos\Constantes,
8 bancodedados\Juncoes
9 };
1 use certificamp\php70\{
2 fundamentos\EspacoDeNomes,
3 fundamentos\Constantes as Const,
4 bancodedados\Juncoes
5 };
Constantes e funções também podem ser encadeadas nesse agrupamento. Veja esse exemplo:
1 use certificamp\php70\fundamentos{
2 EspacoDeNomes,
3 const CAPITULO,
4 const QUESTOES,
5 function AplicaSimulado,
6 };
Extensões
Existem muitos add-ons disponíveis para a realização de tarefas específicas.
As extensões são adicionadas no arquivo de configuração php.ini (informando o seu caminho) e
podem ser habilitadas ou desabilitadas, bem como ter parametrizações específicas.
Quando disponíveis, tornam-se parte do namespace global da aplicação e podem oferecer funções,
classes, interfaces, constantes e variáveis.
Fundamentos do PHP 96
A gama de extensões é enorme e não há como abordarmos todas nesse guia. Importante salientar
que podem ser instaladas via PECL (PHP Extension Community Library) que é um repositório
de extensões desenvolvidas na linguagem C, similar ao repositório PEAR (PHP Extension and
Application Repository) que são funcionalidades escritas em PHP em forma de bibliotecas.
Atente-se para o termo userland, comumente utilizado, que significa espaço do usuário, que é o
código que roda fora do núcleo da linguagem.
Configuração
As configurações do PHP se concentram no arquivo php.ini. Todas as opções podem ser visualizadas
através da função phpinfo().
Então é possível editar o php.ini, localizar a linha que contém a cláusula e realizar os ajustes. Ex.:
memory_limit = 128M
Mas nem sempre essa opção está disponível, dependendo do tipo de hospedagem que se utiliza. Nos
casos de hospedagem compartilhada, talvez seja necessário realizarmos ajustes através da função
ini_set() Ex.:
1 ini_set("memory_limit", "128M");
Caso queira que essa configuração afete um diretório inteiro ou a raiz do site/sistema, é possível
adicionar uma diretiva no arquivo .htaccess (que provê configurações do servidor Apache), mas
desde que o PHP esteja rodando como módulo do Apache (ao invés de CGI):
Para evitar que o PHP exceda os limites máximos pré-estabelecidos no consumo de recursos
computacionais, podemos ajustar algumas cláusulas do php.ini, dentre elas:
Quais dos seguintes métodos estão disponíveis para limitar a quantidade de recur\
sos disponíveis ao PHP, configurados através do php.ini?
[ escolha 2 ]
No contexto de threads, o PHP usa uma extensão não nativa chamada pthreads³⁶, portanto podemos
desconsiderar a alternativa E:. As alternativas B e D dizem respeito às configurações do servidor de
aplicação (Apache ou NGINX), bem como do sistema operacional. Portanto as opções corretas são:
A (memory_limit) e C (max_execution_time).
³⁶http://pthreads.org
Fundamentos do PHP 98
zend_extension=opcache.so
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1
Caso queira experimentar o ganho de performance, realize um teste de carga (stress test)
com Apache Bench antes e depois de habilitar o OPcache. Ex.:
ab -n 100 -c 5 http://www.site.com/
A: É um mecanismo de cache comumente usado em funções para que rodem mais rapida\
mente
B: Faz cache de dados vindos de recursos externos, como bancos de dados e Web Se\
rvices, para que eles possam ser acessados mais rapidamente
C: É um cache que gera código legível por máquina, criado depois que é feito o p\
arser do código PHP
D: Realiza cache do HTML gerado pela saída do código PHP
A resposta correta é C:. O opcode cache gera um bytecode após o primeiro acesso, acelerando
as requisições subsequentes. Os resultados são melhores em scripts que não dependam de dados
externos, como banco de dados e serviços web.
Unicode
O padrão Unicode³⁷ superou o conjunto ASCII, pois oferece atualmente mais de 100 mil caracteres
- muito além dos 128 símbolos tradicionais do ASCII.
Os caracteres unicode também são conhecidos como code points.
O PHP 7.0 escapa caracteres unicode e os interpreta. Ex.:
³⁷http://www.unicode.org
Fundamentos do PHP 99
1 echo "\u{1F618}<br>";
2 echo "\u{260E} (54) 3282 1826<br>";
3 echo "Oferecemos \u{1F374}";
Para ver a lista completa de emojis, por exemplo, acesse Emoji Unicode Tables³⁸ ou Unicode.org³⁹.
As versões mais recentes do PHP trouxeram uma classe nativa chamada IntlChar que fornece uma
vasta quantidade de recursos com informação sobre os caracteres unicode, expondo funcionalidades
adicionais extraídas da International Components for Unicode - ICU⁴⁰.
Obs.: desde que a extensão Intl⁴¹ esteja instalada.
A função rand() convencional utiliza a libc que tem problemas de performance e nem
sempre gera números verdadeiramente randômicos. A partir da versão 7.1 o rand() tornou-
se um alias para mt_rand() que utiliza o algoritmo Mersenne Twister (MT)⁴², considerado
mais rápido e com melhor aleatoriedade.
A propósito: não confunda rand() com round() que é uma função utilizada para arredondamentos
de valores.
Simulado
1.
³⁸http://apps.timwhitlock.info/emoji/tables/unicode
³⁹http://unicode.org/emoji/charts/full-emoji-list.html
⁴⁰http://www.icu-project.org
⁴¹http://php.net/manual/pt_BR/book.intl.php
⁴²http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
Fundamentos do PHP 100
A:
foreach ($vetor as $conteudo => $indice){
echo $conteudo." ".$indice."<br>";
}
B:
foreach ($vetor as $indice => $conteudo){
echo $conteudo." ".$indice."<br>";
}
C:
foreach ($vetor as $indice => $conteudo){
echo $vetor[$indice]." ".$vetor['profissao']."<br>";
}
D:
for($i=0; $i<3; $i++){
//echo $i;
echo $vetor[$i]." ".$vetor['profissao']."<br>";
}
2.
Que operador deve ser usado ao invés do ??? onde a variável $resultado obtém o $\
valor1 se $valor1 for avaliado como verdadeiro e $resultado recebe $valor2 quand\
o falso? Apenas escreva o operador necessário no código.
Fundamentos do PHP 101
3.
A: bool(true);
B: float(1);
C: integer(1);
D: float(1.0);
4.
Qual das seguintes opções deve ser usada em conjunto com CASE dentro de uma decl\
aração SWITCH?
A: Scalar
B: Uma expressão
C: Boolean
D: Todas anteriores
5.
Respostas do simulado
1.
Na alternativa A os nomes das variáveis $indice e $conteudo induzem ao erro. A ordem correta dos
elementos na sintaxe do foreach prevê o seguinte formato:
Fundamentos do PHP 102
O $indice vem antes do $conteudo (na construção do foreach), então a alternativa B teria alguma
chance – assim como as alternativas C e D, exceto pelo fato de que a atribuição de $vetor acaba
sobrescrevendo a profissão, permanecendo só a última inserção. No caso Artista plástica.
Atribuir em $vetor[] gera um índice automático começando em 0 (caso não exista um índice
numérico no vetor), depois cria o 1 e assim sucessivamente. Mas a chave $vetor['profissao']
é fixa, fazendo com que possamos resgatar somente a profissão da última pessoa adicionada.
Portanto a resposta é E: Nenhuma das alternativas.
2.
Segundo a especificação requerida no enunciado, o operador adequado para essa situação é ?: que
é um operador ternário em sua forma curta.
3.
A resposta correta é B: float(1);.
O retorno da função round() arredonda e devolve um número de ponto flutuante (float), o que
pode causar confusão. Atente-se a esse detalhe.
4.
Variáveis escalares são as que contém integer, float, string ou boolean - que funcionam
perfeitamente com as instruções SWITCH/CASE. Expressões também são permitidas como, por
exemplo: switch(++$a). Portanto todas as alternativas listadas são permitidas. Resposta é D: Todas
anteriores.
5.
Primeiramente observe 0x02 que indica um número hexadecimal, por iniciar com 0x ou apenas x.
A conversão hexadecimal para decimal foge do nosso enfoque, mas basta lembrar-se da seguinte
tabela:
Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Hexadecimal 0 1 2 3 4 5 6 7 8 9 A B C D E F
Dificilmente o número decimal apresentado no exame será maior que 15. Caso, extraordinariamente,
apareça valores acima - basta seguir a lógica de conversão mostrada para o octal e binário. Da direita
para a esquerda, o primeiro dígito deve ser multiplicado por 16^0^ (que é 1), o segundo por 16^1^
(16), o terceiro por 16^2^ (256), 16^3^ (4096) e assim por diante. Basta somar e teremos a conversão
de hexadecimal para decimal. Ex.: 0x80 = 8 * 16^1 + 8 * 16^0 = 128.
Mas sabemos que 02 decimal é 2 em hexadecimal, multiplicado por 2 e somado com 1 (inteiro,
decorrente da conversão automática para inteiro) teremos 5. Resposta C.
Funções
Funções são blocos de código que executam isoladamente alguma ação, com seu próprio escopo de
variáveis.
Os nomes de funções não obedecem maiúsculas e minúsculas. Diz-se que são case-insensitive.
Significa que você pode chamar o procedimento pelo nome FUNCAO(), funcao() ou até mesmo
FuNcAo().
Podem ser referenciadas depois de utilizadas, já que o PHP pré-compila o script antes de executá-lo
de fato.
As funções são definidas através da palavra-chave function seguido do nome (que é opcional, no
caso das funções anônimas que veremos adiante), da lista de parâmetros (opcional) e do retorno
(opcional também):
Ao declararmos argumentos opcionais, como é o caso de $x1, precisamos prover um valor literal
que não precise ser avaliado - como é o caso da expressão $x1 = $x2 da alternativa D:. O array()
vazio e o valor null são aceitáveis, bem como o type hinting A. Portanto a resposta é D:.
Argumentos
A lista de argumentos é separada por vírgulas (,), sendo que o PHP dispõe de algumas funções para
manipulação de parâmetros:
Funções 104
Funções Descrição
func_num_args() Devolve o número de argumentos passados para a função
func_get_arg(x) Devolve o argumento passado da posição x (começando em zero)
func_get_args() Devolve todos os argumentos passados para a função
1 function exibeCodigoPostalDeOrigem($cep)
2 {
3 return $cep;
4 }
5 echo exibeCodigoPostalDeOrigem(); //ocasiona Fatal error
Mas as funções podem receber um argumento padrão, caso o desenvolvedor não informe. Ex.:
1 function exibeCodigoPostalDeOrigem($cep="95670-000")
2 {
3 return $cep;
4 }
5 echo exibeCodigoPostalDeOrigem(); //mostra 95670-000
Mas se o programador chamar a função passando parâmetro, a função assume o valor informado:
<?php
function codigoPostalDeOrigem($cep="95670-000", $cidade="Gramado", $uf="RS")
{
echo $cep . " - " . $cidade . " - " . $uf;
}
Para que a função codigoPostalDeOrigem() assuma valores padrão, como ela NÃO dev\
e ser invocada?
A alternativa D resulta em Parse error pois não é permitido ignorar dessa forma um parâmetro que
é esperado pela função.
Função variádica
Funções variádicas (variadic functions) foram incorporadas na versão 5.6 do PHP e permitem que
se passe qualquer número de parâmetros para a função, ao invés de usar internamente a função
func_get_args. O operador ... (três pontos) deve ser utilizado na definição de função:
1 function SomaTrajeto(...$paradas)
2 {
3 $total = 0;
4 foreach($paradas as $valor){
5 $total += $valor;
6 }
7 return $total;
8 }
9
10 echo SomaTrajeto(7, 40, 32, 78);
O resultado do código acima é a soma dos argumentos 7, 40, 32, 78, itens que podem aumentar
ou diminuir de quantidade sem afetar a execução do script.
Outro recurso interessante é o desempacotamento de argumentos (argument unpacking) realizado
também pelos três pontos ..., porém no contexto de chamada de função. Esse operador é conhecido
em outras linguagens como splat ou rest parameter:
A função adiciona() recebe quatro argumentos, mas ao invocar a função passou-se normalmente
o primeiro e os demais foram agrupados no array $elementos, que depois será rearranjado nos
respectivos parâmetros $b, $c e $d.
Funções 106
1 function exibe()
2 {
3 global $nome;
4 $nome = "Ari";
5
6 $GLOBALS["cidade"] = "Gramado";
7 }
8
9 exibe();
10 echo $nome; //exibe Ari
11 echo $cidade; //exibe Gramado
Há de se tomar cautela no uso de globais, pois a tendência é sempre encapsularmos para que não
haja substituição de valores e comportamentos anormais em nosso código.
A: ZCE 7.1
B: vazio
C: ZCPE 5.5ZCE 7.1
D: ZCPE 5.5
Essa questão serve para testar sua atenção. Ela sugestiona no enunciado às variáveis globais e
depois mostra uma implementação. Mas observe que a função exibe() sequer foi chamada, portanto
o conteúdo de $titulo mantém-se intacto com a atribuição inicial. Portanto a alternativa correta é
D: ZCPE 5.5.
Funções 107
Ao colocar o & (e comercial) antes da variável, significa que a função vai passar o valor como
referência. Ou seja: toda e qualquer atribuição/manipulação dessa variável que houver internamente
a função, será refletida fora dela.
Lembre-se que não é mais possível usar o & na chamada da função. Nas versões recentes do PHP só
é permitido usar o & na definição da função.
Nesse exemplo de questão observe que apenas a função antes() passa a variável $a como referência.
Inicialmente $a é vazia, mas após a execução de antes() ela terá o valo " antes". A confusão
acontece ao executar depois() que induz o aspirante em achar que o valor de $a será novamente
modificado, o que não é verdade. Portanto a resposta que deve ser digitada é " antes" (sem aspas e
com espaço no começo).
Retorno
A declaração return estabelece o fim da execução da função e pode devolver arrays, objetos, funções
anônimas, variáveis, booleanos e nulo.
Neste código, a função tragaUma(x) é como um alias para $certificacoes[x], ao invés de copiar
seu valor simplesmente - porque associamos por referência.
A variável $titulo é um apontamento para $certificacoes[1], portanto ao modificar $titulo
estaremos mexendo no índice 1 do array $certificacoes.
Esta técnica consiste em devolver uma referência ao invés de um valor propriamente, tendo uma
execução mais lenta.
Analisando o código a seguir, qual linha deve ser alterada para que a saída seja\
2:
<?php
class A {
protected $x = array(); /* Linha A */
public function getX() { /* Linha B */
return $this->x; /* Linha C */
}
Funções 109
A: Não será necessário substituir linha alguma. O código como está já mostra o v\
alor 2
B: Linha A, para: protected &$x = array();
C: Linha B, para: public function &getX() {
D: Linha C, para: return &$this->x;
E: Linha D, para: $a =& new A();
Funções anônimas
Funções anônimas, eventualmente referenciadas como closures ou lambdas, são funções sem um
nome especificado - geralmente criadas como callbacks, também em atribuições de variáveis ou
como retorno de outras funções/métodos.
Pela sua natureza transitória, faz mais sentido criar uma anonymous function para tal tarefa. Assim
a função fica junto de seu uso, gerando códigos mais concisos e legíveis. Ex.:
1 $closure = function($colega) {
2 echo "Olá ".$colega;
3 };
4
5 $closure("Ari Junior");
Se você inspecionar a fundo, na realidade a variável $closure é uma instância da classe Closure.
Funções 110
A: Certificamp
B: string
C: function
D: object
E: resource
A função gettype() retorna o tipo da variável indicada, que pode ser: boolean, integer, double (ao
invés de float), string, array, object, resource, NULL e unknown type.
Nesse caso $s é uma função anônima que essencialmente é uma instância da classe Closure. Portanto
a saída desse código será object (alternativa D). Experimente:
A vantagem de usar uma função anônima como callback é que a sua implementação fica junto do
seu uso, tornando-a mais concisa e legível, diferentemente das funções convencionais nomeadas
(que geralmente se desmembram em locais separados).
O escopo da função anônima se restringe ao âmbito interno da função, como é o comportamento
padrão de qualquer função convencional nomeada. Mas é possível resgatar variáveis do escopo
externo utilizando a palavra reservada use. Ex.:
No exemplo acima teremos a variável $mensagem que é por natureza “desconhecida” internamente,
mas torna-se disponível assim que utilizamos use ($mensagem). Você pode passar inúmeras variá-
veis separando-as por vírgula.
Outra função interessante é call_user_func() que chama funções de usuário, passa parâmetros,
invoca métodos de objetos específicos e métodos estáticos. Veja o exemplo abaixo:
Funções 111
1 function certificamp($versao)
2 {
3 echo "Zend Certified Engineer - PHP ".$versao;
4 }
5
6 //callback simples que chama a função certificamp() e passa '7.1' como parâmetro
7 call_user_func('certificamp', '7.1');
8
9 //chama o método estático meuMetodo() de MinhaClasse
10 call_user_func(array('MinhaClasse', 'meuMetodo'));
11
12 //outra forma de chamar método estático (a partir do PHP 5.2.3)
13 call_user_func('MinhaClasse::meuMetodo');
14
15 //chamada metodo meuMetodo() do objeto $obj
16 $obj = new MinhaClasse;
17 call_user_func(array($obj, 'meuMetodo'));
Nesse contexto, vale a pena mencionar uma variação interessante que é a função call_user_-
func_array(). Essa função aceita os parâmetros em forma de array http://php.net/manual/pt_BR/
function.call-user-func-array.php.
Veremos ainda mais sobre closures no capítulo “Programação Orientada à Objetos”.
Simulado
1.
2.
A: Usando algo como uma function test() com a função get_variable_args() no corp\
Funções 112
o da função
B: Usando algo como function test($parametros[])
C: Usando algo como function test(... $parameters)
D: Usando algo como uma functiontest() com a função func_get_args() dentro, no c\
orpo da função
E: Isto não é possível em PHP
3.
4.
<?php
function ordena_meu_array($array)
{
return sort($array);
Funções 113
$a = array(3, 2, 1);
var_dump(ordena_meu_array(&$a));
?>
A: NULL
B: 0 => 1, 1 => 2, 2 => 3
C: An invalid reference error
D: 2 => 1, 1 => 2, 0 => 3
E: bool(true)
F: Parse error
5.
Quais declarações a seguir, a respeito de funções anônimas em PHP, NÃO são verda\
deiras?
[ Escolha 2 ]
Respostas do simulado
1.
Esta pergunta trata da forma como os valores são passados para a função.
Na alternativa A se espera a passagem de um objeto $x1, instância da classe A. Caso não aconteça, o
script assume o valor null. Funciona perfeitamente, portanto ainda não é a resposta correta - já que
o enunciado solicita qual a declaração inválida.
Pulando para a alternativa C, o princípio é o mesmo da opção A (usando Type Hinting) e também
correta.
Funções 114
A resposta D pode gerar uma certa dúvida, mas a especificação aceita que se passe um array
como argumento padrão. Mais informações em https://secure.php.net/manual/pt_BR/functions.
arguments.php#functions.arguments.default.
Portanto a declaração NÃO válida é alternativa B.
2.
A alternativa A é a solução que usávamos antes da versão PHP 5.6, quando trouxeram o recurso
chamado funções variádicas. Como a prova baseia-se na release 7.1, a alternativa correta é C:
Usando algo como function test(... $parameters).
3.
A malícia dessa questão está na tentativa de confundir com a definição de função encolhe(&$tamanho),
passando $tamanho por referência.
Na prática o resultado não mudaria, já que estamos sobrescrevendo a variável $tamanho no contexto
interno relativo a função cresce(). Fazendo o teste de mesa, teremos:
$tamanho (externo) $tamanho (interno) Explicação
normal Atribuição inicial
normal bigger Chama a função cresce() e concatena
normal bigger smaller Chama a função encolhe() e altera por referência
normal bigger smaller Retorna a execução para cresce() e sobrescreve - mantend
normal normal bigger smaller Retorna para o fluxo normal do código, concatenando
Assim sendo, nessa questão a alternativa correta é A: normal normal bigger smaller.
4.
Essa é outra questão que ludibria o aspirante ao título ZCPE. O código insinua que visualizaremos
um array ordenado, o que é falso.
Com alguma perspicácia sabemos que a passagem de valores por referência requer que seja
previamente especificado na definição da função e não na chamada da função - como mostra o
código.
Desde a versão 5.4 do PHP não é mais permitido passar argumentos por referência na chamada de
funções. Para que o código funcionasse a contento seria necessário alterar a definição da função,
mudando para function ordena_meu_array(&$array) e assim evitar um Parse error.
Portanto a alternativa correta é F: Parse error, pois o & não é esperado ali.
Aproveitando a pergunta, lembre-se que a saída da função sort() é um booleano que indica se a
ordenação teve sucesso ou não, por isso a alternativa E: bool(true) seria uma armadilha.
5.
As respostas corretas são: B e C, lembrando que o enunciado pede quais são as respostas falsas.
Funções 115
As funções anônimas criadas no contexto do objeto podem ser vinculadas a outro objeto através do
método bindTo(). A mesma possibilidade ocorre quando atribuímos closure a uma propriedade de
um objeto, podendo vincular a outro.
Formatos e tipos de dados
Neste capítulo vamos rever os conceitos básicos de XML, bem como extensões para manipulação
deste formato. Na manipulação de XML utilizaremos as classes SimpleXML e DOM.
Vamos adentrar ao mundo dos Web Services com SOAP e REST, com exemplos de serviços que
disponibilizam e consomem dados.
Iremos abordar também a classe DateTime e o processo de manipulação de datas.
Fundamentos do XML
XML é acrônimo de eXtensible Markup Language e foi desenvolvido para estruturar, manter e
compartilhar dados - como formato de arquivo universal.
É uma linguagem de marcação onde o próprio usuário define as tags, que são metadados e que
podem seguir um dialeto específico como, por exemplo, SOAP e RSS.
1 <?xml version="1.0"?>
2 <biblioteca>
3 <livro isbn="9781449392772">
4 <titulo>Programming PHP</titulo>
5 <autor>Rasmus Lerdorf</autor>
6 <editora>O'Reilly</editora>
7 </livro>
8 <livro isbn="9781491905012">
9 <titulo>Modern PHP</titulo>
10 <autor>Josh Lockhart</autor>
11 <editora>O'Reilly</editora>
12 </livro>
13 </biblioteca>
O XML usa formatos auxiliares como Document Type Declaration (DTD) e XML Schema, para
descrever os dados.
Provê intercâmbio de dados entre sistemas heterogêneos, viabilizando o compartilhamento de dados
independente de plataforma - promovendo interoperabilidade.
Funciona em diferentes plataformas (de hardware, software ou linguagem), é formal, conciso,
extensível e baseado em texto puro.
Formatos e tipos de dados 117
O XML pode ser bem formado (Well-formed) quando contém um único elemento raiz, todas as tags
abertas estão devidamente fechadas e caracteres especiais são tratados: (<, >, &, '," etc).
Diz-se que ele é válido quando tem sua estrutura coerente com o DTD (que descreve a estrutura do
documento) e é bem formado.
Declarar um documento XML como standalone (independente) significa que ele não p\
ossui associação com informações externas de marcação.
Qual a alternativa abaixo sinaliza um documento XML como standalone?
SimpleXML
É uma biblioteca que provê acesso simplificado aos dados em formato XML. Era uma extensão que
tornou-se parte do core.
SimpleXML fornece uma classe que proporciona acesso orientado à objetos as estruturas XML.
Elementos XML tornam-se propriedades de objetos e atributos desses elementos podem ser acessados
através de arrays associativos.
Para abrir um XML via modo procedural a partir do conteúdo ou acessando o arquivo que contém
os dados. Ex.:
1 $xmlstr = file_get_contents('arquivo.xml');
2 $xml = new SimpleXMLElement($xmlstr);
3 //ou diretamente…
4 $xml = new SimpleXMLElement('arquivo.xml', NULL, true);
1 $xml = simplexml_load_string("<topicos>
2 <topico id='1'>JSON</topico>
3 <topico id='2'>XML</topico>
4 </topicos>");
5
6 foreach($xml->topico as $topico){
7 $topico['id'] +=5;
8 echo $topico['id'] . ": ". $topico."<br>";
9 }
1 $biblioteca = simplexml_load_file('arquivo.xml');
2 foreach ($biblioteca->livro as $livro) {
3 echo $livro['isbn']."<br>";
4 echo $livro->titulo."<br>";
5 echo $livro->autor."<br>";
6 echo $livro->editora."<br><br>";
7 }
A: simplexml_save_string($xml, "saida.xml");
B: simplexml_save_file($xml, "saida.xml");
C: $xml->saveAs("saida.xml");
D: file_put_contents($xml->asXML(), "saida.xml");
Formatos e tipos de dados 119
$xmlObj = simplexml_load_string($xmlstr);
??????????????????????
echo $xmlObj->asXML();
?>
⁴³http://php.net/manual/pt_BR/simplexmlelement.addchild.php
Formatos e tipos de dados 120
O método xpath() de SimpleXMLElement que aparece na alternativa D, recebe a string que representa
a consulta XPath (que veremos mais adiante), portanto não tem o propósito de adicionar elementos
ao XML.
As alternativas A e C fazem referência ao elemento cerveja que não foi explicitamente criado,
lembrando que o nodo cerveja nesse contexto é a raiz do documento XML. Então a resposta correta
é E: $xmlObj->addChild("caracteristica", "Coloração âmbar");.
Extensão XML
Seguindo os preceitos do World Wide Web Consortium (W3C)⁴⁴, desde a versão 5.1 o PHP utiliza a
Gnome XML library (libxml2) que suporta inúmeras outras bibliotecas, dentre elas: DOM, libxml,
SimpleXML, SOAP, WDDX, XSL, XML, XMLReader, XMLRPC e XMLWriter.
É a extensão que permite realizar parser de documentos XML identificando entidades, elementos,
DTD e analisando e validando seu conteúdo.
1 $parser = xml_parser_create();
2 //faz a varredura
3 xml_parser_free($parser);
Não entraremos nos detalhes da extensão XML, pois não há relatos de perguntas utilizando funções
de tão baixo nível como, por exemplo: xml_parser_create_ns() que inicia o parser com suporte a
namespace ou xml_set_element_handler() que configura as funções responsáveis por manipular
elementos de início e fim.
XPath
XPath é uma sofisticada tecnologia para buscas em documentos XML.
A biblioteca SimpleXML implementa o método xpath() para que possamos disparar uma pesquisa
XPath perante um código XML, devolvendo um array.
A consulta $xml->xpath('//'); significa qualquer lugar a partir da raiz. O * é um curinga que
representa qualquer coisa e @ indica um atributo específico. Ex.:
⁴⁴https://www.w3.org
Formatos e tipos de dados 121
As alternativas A e B são similares e assumem que há algo errado com a expressão XPath ou com a
estrutura do documentos XML - que não é o caso.
Analisando a expressão XPath //cerveja, significa que pretendemos obter todos os nodos <cerveja>
independente da posição na hierarquia do XML. A instrução seguinte [@amargor > 35] nos possi-
bilita filtrar pelos elementos que possuem o atributo amargor maior que 35, nesse caso devolvendo
os nodos Amber Ale e IPA. Portanto alternativa correta será a letra D:.
Web Services
Web Services é a tecnologia que permite o intercâmbio de dados entre diferentes plataformas,
utilizando a internet como meio de transporte.
Possibilita que sistemas heterogêneos compartilhem dados através de padrões abertos:
• XML-RPC
• SOAP
• REST
Formatos e tipos de dados 122
Ilustração que demonstra um cenário heterogêneo de plataformas de sistemas operacionais, SGBD e linguagens
- onde todos se comunicam
SOAP
É o acrônimo de Simple Object Access Protocol, extensão utilizada para disponibilizar serviços web
(através da classe SoapServer) e criar recursos que consumam esses serviços (classe SoapClient),
que utiliza o protocolo HTTP como camada de transporte.
Formatos e tipos de dados 123
1 class MeuServidorSoap {
2 public function obterMensagem()
3 {
4 return "Zend Certified PHP Engineer";
5 }
6 public function adicionarNumeros($num1, $num2)
7 {
8 return $num1 + $num2;
9 }
10 }
11
12 $opcoes = array("uri" => "http://localhost");
13 $server = new SoapServer(NULL, $opcoes);
14 $server->setClass("MeuServidorSoap");
15 $server->handle();
O array $opcoes pode conter tipo de encode, forma de autenticação, proxy, versão do SOAP,
certificado .pem, método de compressão etc.
Obs.: a opção 'uri' é obrigatória em modo sem WSDL associado.
1 $opcoes = array(
2 "location" => "http://localhost/livro_zcpe/soapserver.php",
3 "uri" => "http://localhost"
4 );
5 $client = new SoapClient(NULL, $opcoes);
6 echo $client->obterMensagem()."\n";
7 echo $client->adicionarNumeros(2, 7)."\n";
Sem o descritor WSDL faz-se necessário conhecer a API do serviço web que se pretende consumir
como, por exemplo, os métodos obterMensagem() e adicionarNumeros() com seus respectivos
argumentos, valores de retorno e tipos de dados.
Caso disponha o descritor WSDL, utilize a sintaxe SoapClient('endereco.wsdl');.
Formatos e tipos de dados 124
Quais das alternativa abaixo são válidas ao implementar um cliente SOAP para con\
sumo de WebServices:
[ Escolha 2 ]
Caso o serviço web disponibilize um descritor WSDL, basta informá-lo como primeiro parâmetro
- que é o caso da alternativa D:. Se não houver descritor (quando o primeiro argumento é null),
estamos operando em modo non-WSDL e obrigatoriamente precisamos passar um array de opções
como segundo argumento. Então B: também é válida.
A alternativa A exibe a mensagem 'uri' option is required in nonWSDL mode e C mostra Inva-
lid parameters: ambas ocasionam Fatal error.
1 try {
2 $soap = new SoapClient('arquivo.wsdl');
3 $res = $soap->metodo('arg');
4 } catch (SoapFault $erro) {
5 ...
6 }
Já na abordagem procedural, teremos a função is_soap_fault() que realiza a captura do erro e nos
dá mais informações:
1 if (is_soap_fault($res)) { ... }
REST
Acrônimo de REpresentational State Transfer (Transferência de Estado Representacional), o REST é
um conjunto de princípios que tira vantagem dos recursos HTTP, dentre eles os verbos: POST, GET,
PUT e DELETE - que coincidem com um conjunto de ações típicas de CRUD (Create, Read, Update,
Delete). Há também o verbo PATCH que atualiza um registro, de forma parcial.
Formatos e tipos de dados 125
Uma representação de recursos pode ser JSON, XML ou ambos. Pense no recurso como um registro
de banco de dados, por exemplo, ou uma postagem de blog, autor etc. Sendo que cada recurso tem
uma URI associada, que corresponde a um identificador único.
As URLs geralmente são bem descritivas e permitem aos desenvolvedores inferir onde encontrar os
recursos. Ex.:
• https://api.github.com/users/stopassola
• https://api.github.com/users/stopassola/repos
Perceba que as URLs são intuitivas a pontos de adivinharmos o que será encontrado, além da
possibilidade de adicionarmos parâmetros para alterar a forma como a coleção é visualizada, sejam
filtros e formas de ordenação. Ex.:
• http://api.joind.in/v2.1/events?format=json
O REST utiliza recursos intrínsecos do HTTP como, por exemplo, os próprios cabeçalhos das
requisições. Ex.:
Realizar operações de debug é particularmente mais difícil, pois não há controle sobre o servidor
que provê o serviço web. Mas o PHP dispõe de alguns
JSON
Acrônimo de JavaScript Object Notation⁴⁵, JSON é um formato aberto criado em 2001 por Douglas
Crockford e tornou-se padrão para intercâmbio de dados, pois é independente de linguagem ou
plataforma.
É mais rápido e compacto que XML e baseia-se em pares nome -> valor separados por vírgulas, entre
chaves.
⁴⁵http://www.json.org
Formatos e tipos de dados 126
Todas as linguagens modernas de programação possuem parsers para consumir e produzir dados no
formato JSON, que tornou-se uma alternativa perante o XML.
JSON é mais fácil de ler por humanos e computadores e sua estrutura mapeia conceitos comuns na
programação, como objetos e arrays.
É seguramente mais eficiente (através do parser e na transmissão via rede) por ser um formato mais
compacto, pois suprime volumes enormes de tags de início e fim.
1 {
2 "nome": "Ari Stopassola Junior",
3 "idade": 36
4 }
• Objeto
• Array
Ex.: "interesse": [ "PHP", "Mac", "HomeBrew", "Volei" ]
• Alfanumérico (entre aspas)
• Número (inteiro ou ponto flutuante, sem aspas)
• Booleano (true ou false sem aspas)
• null (sem aspas)
1 {
2 "nome": "Ari Stopassola Junior",
3 "idade": 40,
4 "estado_civil": "casado",
5 "curriculo":
6 [
7 "BSc",
8 "ZCE",
9 "ZCPE",
10 "ZFCA",
11 "CSM"
12 ],
13 "ativo": true,
14 "reincidente": null,
15 "saldo": 1032.6,
16 "telefones":
Formatos e tipos de dados 127
17 [
18 {
19 "tipo": "residencial",
20 "numero": "(54) 3282 0118"
21 },
22 {
23 "tipo": "pessoal",
24 "numero": "(54) 99912 1620"
25 }
26 ]
27 }
A função json_decode() cria um objeto baseado na classe padrão do PHP (chamada stdClass)
contendo os atributos do objeto com seus respectivos valores.
A sintaxe é: json_decode($json, $assoc = false, $profundidade, $opcoes) onde $assoc, quando
true, o objeto retornado será convertido em array associativo. O terceiro parâmetro $profundidade
é o limite máximo de aninhamento permitido para o documento JSON e $opcoes são constantes⁴⁶
que alteram a forma como o JSON será processado.
1 $arq = file_get_contents("index.json");
2 $json = json_decode($arq);
3 echo "<pre>";
4 print_r($json);
5 echo "</pre>";
Constantes Descrição
JSON_ERROR_NONE Confirma que nenhum erro ocorreu
SON_ERROR_DEPTH Confirma que ultrapassou o limite máximo de
profundidade da estrutura JSON
JSON_ERROR_SYNTAX Confirma que ocorreu um erro de sintaxe do
JSON
JSON_ERROR_UTF8 Confirma que encontrou caracteres mal
formados, possivelmente com encode incorreto
JSON_PRESERVE_ZERO_FRACTION Mantém o ponto flutuante do valor mesmo que
ele possa ser convertido para inteiro. Ex.:
continua 5.0 ao invés de transformar em 5.
⁴⁶http://php.net/manual/pt_BR/json.constants.php
Formatos e tipos de dados 128
Caso o seu script esteja tendo problemas ao realizar o parser do JSON, experimente validar o
conteúdo através do serviço http://jsonlint.com.
Uma função importante no processo de debug é json_last_error() que devolve o último erro
encontrado na varredura do documento JSON.
DateTime
São funções que resgatam a data e horário do servidor, considerando a localização geográfica, horário
de verão (daylight saving time), ano bissexto (leap year) e outras características.
Existem quatro classes inter-relacionadas para manipulação de data e horários. A principal delas é
a classe DateTime que trabalha com o tempo propriamente dito, DateTimeZone para lidar com fuso
horário, DateInterval que trata intervalos de tempo e DatePeriod que permite iterar entre um
conjunto recorrente de datas/horários em intervalos regulares.
Há também configurações em tempo de execução (diretivas do php.ini) que afetam as funções de
data/hora, como por exemplo: date.timezone e date.default_latitude.
A arquitetura do servidor também influencia no cálculo de datas, principalmente ao tratar times-
tamps⁴⁷ quando mantidos em sistemas operacionais 32-bit, que chegariam ao limite no ano de 2038⁴⁸.
1 //Especifica um timezone
2 date_default_timezone_set("America/Sao_Paulo");
3
4 //Mostra no formato: Friday, 19-Aug-2016 15:21:11 BRT
5 echo date(DateTime::COOKIE);
6
7 //Mostra no formato: Fri, 19 Aug 2016 15:21:11 -0300
8 echo date(DateTime::RSS);
9
10 //Cria um objeto $data com o dia e horário atual
11 $data = new DateTime();
12
13 //Incrementa um período de 7 dias ao objeto $data
14 $data->add(new DateInterval("P7D"));
15
16 //Especifica um formato onde:
17 //`d` representa o dia, `m` o mês, `Y` ano, `H` hora, `i` minuto e `s` segundo
18 echo $data->format("d-m-Y H:i:s");
⁴⁷Número de segundos desde 1 de janeiro de 1970, também conhecido como Unix epoch.
⁴⁸Unix Millennium Bug https://en.wikipedia.org/wiki/Year_2038_problem
Formatos e tipos de dados 129
Um detalhe no formato especificado pela constante DateTime::RSS do exemplo acima, que mostra
-0300. Esse é o timezone que representa a diferença (offset) baseado no horário de Greenwich -
Greenwich Mean Time (GMT).
O construtor da classe DateInterval recebe uma especificação de intervalo iniciando com a letra
P (“período”) seguida de designadores de período. São eles: Y para anos, M representa meses, D são
dias e W para semanas.
A especificação de intervalo também pode conter elementos de tempo e deve ser precedida pela
letra T. Os designadores para tempo são: H para horas, M minutos e S para segundos.
Métodos Descrição
DateTime::add(DateInterval $intervalo) Adiciona uma quantidade de tempo ao objeto
DateTime
DateTime::__construct([string $tempo = Instancia a classe DateTime
"now" [, DateTimeZone $tz = NULL ]])
DateTime::createFromFormat(string $formato, Retorna um objeto DateTime baseado num formato
string $tempo [, DateTimeZone $tz]) específico de data/hora
DateTime::format(string $formato) Devolve a data formatada de acordo com o formato
desejado
Eventualmente talvez você não lembre de todas as notações possíveis, como é o caso do Y que
representa o ano com quatro algarismos ou H que extrai a hora com dois dígitos. As alternativas A, B
e C conseguem transformar naquele formato que o enunciado pede, mas atente-se que a pergunta se
refere a classe DateTime e portanto nós precisamos operar sob objetos, portanto a alternativa correta
é B: $data->format('Y-m-d H:i:s');.
Uma forma fácil de comparar datas é testando diretamente os objetos da classe DateTime. Ex.:
Formatos e tipos de dados 130
Função Descrição
simplexml_import_dom() Converte nodo DOM em objeto SimpleXML
dom_import_simplexml() Converte objeto SimpleXML em nodo DOM
Simulado
1.
</body>
</html>
Considerando que a variável $xml foi atribuída com o conteúdo acima, através da \
função simplexml_load_file(), como exibir o conteúdo "Autor B":
A: echo $xml->body->address->cite[1];
B: echo $xml->body->address[1]->cite;
C: echo $xml->xpath("/html/body/address[1]/cite");
D: echo $xml->xpath("/html/body/address[cite='Autor B']");
E: Não é possível pois trata-se de um documento XHTML (montado para visualização\
em navegadores) e não um XML puro.
2.
3.
4.
Quais regras a seguir devem ser cumpridas para considerar correto um documento X\
ML?
[ escolha 2 ]
Formatos e tipos de dados 132
5.
A: echo $xml->livros->livro[2];
B: echo $xml->livros->livro[1];
C: echo $xml->livro[1];
D: echo $xml->xpath("/livros/livro[@id=2]");
E: $c = $xml->children(); echo $c[1];
Respostas do simulado
1.
Analisando a alternativa A ao percorrermos diretamente o nodo $xml->body->address->cite, a
classe SimpleXMLElement aponta para o primeiro nodo <cite> (que diz respeito ao ‘Autor A’), ainda
assim armazenado no índice zero ao invés do 1 como apresenta a alternativa. Significa que a opção
A é incorreta.
A alternativa C é capciosa. O índice 1 refere-se ao primeiro elemento, que diz respeito ao Autor A,
portanto incorreta.
A opção D realiza uma busca XPath no nodo address filtrando pelo nodo filho <cite> que contenha
Formatos e tipos de dados 133
o valor Autor B. Porém ele traz todo o elemento, ao invés de apenas o conteúdo Autor B como diz
o enunciado.
Resposta correta é B: echo $xml->body->address[1]->cite;.
2.
A biblioteca SimpleXML para manipulação de XML é muito versátil e permite todas as opções
listadas, portanto a resposta é E: Nenhuma das anteriores.
3.
A função dom_import_simplexml() transforma nodos da classe SimpleXML em um objeto nativo de
nodos DOMElement. Portanto resposta é a alternativa A.
4.
Um documento XML aderente aos padrões deve ser bem formado (well-formed). Ou seja: todas as
tags abertas são devidamente fechadas, sem sobreposição de tags e somente um elemento raiz. Então
a resposta A: Ser bem formado é uma das alternativas corretas.
Quanto a ser válido, geralmente é feito através de um documento DTD associado ao XML que define
a estrutura do documento, seus elementos, relacionamentos e regras. Nesse caso a alternativa C seria
indicada, mas a validação também pode ser feita através de XML Schema e outras técnicas. Nesse
caso a resposta mais acertada (e genérica) é B: Ser válido.
5.
A variável $xml contém uma instância da classe SimpleXMLElement. Através dela podemos acessar,
diretamente, o nodo livro - desprezando o elemento raiz livros (no plural).
O primeiro nodo é magicamente alocado no índice 0, enquanto que o segundo fica armazenado em
1, portanto a alternativa C: echo $xml->livro[1]; é uma das opções corretas.
Analisando a alternativa E, sabemos que o método children() encontra nodos filhos de um
determinado nó. Se exibirmos $c teremos também:
SimpleXMLElement Object
(
[book] => Array
(
[0] => Descomplicando a certificação PHP
[1] => Preparatório para a certificação PHP
)
Significa que visualizar o conteúdo $c[1] vai mostrar, assim como a echo $xml->livro[1], o con-
teúdo Preparatório para a certificação PHP, portanto alternativa E: $c = $xml->children();
echo $c[1]; também é correta.
Recursos Web
O desenvolvimento web transcende a linguagem PHP em muitos aspectos. Sabemos que o PHP
é uma linguagem server-side, ou seja, ele roda no lado do servidor de aplicação. O servidor de
aplicação pode ser um Apache, NGINX, IIS e outros, o que implica em conhecermos minimamente
essas plataformas, bem como o sistema operacional que o suporta.
Ao executar, o PHP envia uma saída HTTP para o navegador do cliente - geralmente em formato
HTML, com JavaScript e CSS associado. Essa comunicação é feita através de transações HTTP, utiliza
eventualmente métodos de autenticação, uso de cookies, formulários, consumo e disponibilização de
web services (em formato SOAP ou REST), intercâmbio de dados com JSON etc.
A relação do PHP para com outras tecnologias de internet serão vistas nesse capítulo.
Sessões
Afirma-se que o protocolo HTTP é stateless. Significa que ele trata as requisições isoladamente.
Pela natureza da especificação, não há vínculo entre cliente/servidor.
Para identificar tal usuário (registrando informações a seu respeito) criou-se a técnica de sessões.
Sessões é uma forma de preservar dados através de requisições web subsequentes.
Lembrando que o suporte a sessões é habilitado por padrão no PHP, onde o Session ID (SID) é o
identificador único da sessão específico para cada usuário, gravado em cookie ou passado via URL.
A $_SESSION fica disponível como uma superglobal, pela qual o desenvolvedor consegue extrair
dados previamente gravados lá.
Existem algumas configurações específicas no php.ini referentes a sessões, dentre elas:
Diretiva Descrição
session.auto_start = 0 Inicializa a sessão automaticamente, sem a necessidade de usar
explicitamente a função session_start(). Útil mas pode causar
overhead, caso hajam requisições que não necessitem desse recurso.
Por padrão ela vem desabilitada: 0 (zero) ou Off.
session.name = PHPSESSID Nome padrão da sessão, utilizada como nome do cookie. Session ID é
um identificador único associado ao usuário.
session.use_only_cookies = 1 Caso os cookies estejam desabilitados, é necessário passar o PHPSESSID
como parâmetro via GET na URL de modo a resgatar dados de
usuário na requisição seguinte. Para minimizar ataques de roubo de
sessão é possível forçar o PHP a somente aceitar um identificador de
sessão que esteja armazenado em cookie – que melhora a segurança.
Recursos Web 135
Diretiva Descrição
session.save_handler = files Por medida de segurança e melhoria de performance, muitos
administradores de sistemas optam por armazenar os dados de sessão
em Banco de Dados. Por padrão, as sessões ficam armazenadas em
arquivos temporários no filesystem do servidor. A nível de usuário
temos a função session_set_save_handler() que customiza a forma
de armazenamento de sessão.
Função Descrição
session_start Inicializa a sessão
session_destroy Destrói todos os dados gravados na sessão
session_id Obtém ou configura o ID corrente da sessão. Ex.:
da233554931027fb371b9e74569c69e3
session_cache_expire Retorna o tempo (em minutos) que a sessão expira.
Pode-se configurar (php.ini) tal propriedade através da
cláusula session.cache_expire = 180
session_regenerate_id Atualiza o ID da sessão por um SID completamente novo
session_status Obtém a situação atual da sessão
A: session_encode()
B: session_commit()
C: session_id()
D: session_cache_limiter()
Nessa questão, o aspirante ao exame pode ter se arrependido de não ler o php.net na íntegra (o que
até não seria má ideia). Mas iremos descartando as alternativas mais improváveis:
Recursos Web 136
1. session_encode() é utilizado para codificar todo o conteúdo da sessão numa grande string,
para ser remontada posteriormente;
2. session_id() obtém ou define o id de sessão para a sessão corrente;
3. session_cache_limiter() você resgata ou modifica o modo de limitação de cache utilizado,
que pode ser: public, private, nocache e private_no_expire. Mais informações em http:
//php.net/manual/pt_BR/function.session-cache-limiter.php.
Como garantir que outros usuários não leiam os arquivos de sua sessão numa hospe\
dagem compartilhada?
Nessa questão, relacionada a aspectos de segurança, todas as alternativas são válidas portanto
alternativa E é a opção correta.
Formulários
Formulários são usados para coletar dados on-line, preenchidos pelos usuários.
Elementos de formulário (nomes e identificadores) que contém pontos e espaços são convertidos em
underscore. Campo de formulário com o nome campo.x será transformado em $_GET['campo_x']
ou $_POST['campo_x'].
Campos podem ser automaticamente convertidos em array utilizando colquetes [] concatenado ao
nome do elemento, como na seguinte sintaxe: <input type="text" name="observacao[]" />.
Ao colocar [] (abre e fecha colchetes) após o nome do campo, no formulário HTML, esse dado se
transforma em array ao ser submetido.
Elementos de formulário tornam-se automaticamente disponíveis em scripts PHP a partir das
superglobais $_GET, $_POST e $_REQUEST.
Recursos Web 137
O que falta no formulário HTML abaixo para garantir que o arquivo seja devidamen\
te submetido como upload para um script PHP?
O encode indicado na tag <form> para que o arquivo binário chegue apropriadamente ao script
upload.php é multipart/form-data, portanto a resposta correta é C: adicionar enctype="multipart/form-
data" como atributo da tag <form>.
Quando há dois ou mais botões do tipo submit no mesmo formulário, podemos indentificar qual foi
disparado através dos atributos name e value dos respectivos elementos HTML. Ex.:
Porém existem dados críticos que não podem ser simplesmente passados via GET, por serem sigilosos
e jamais devem aparecer explícitos no endereço web. Nesses casos recomenda-se o método POST,
até porque ele suporta mais dados de transferência comparado ao GET.
Recursos Web 138
A superglobal $_REQUEST independe da origem dos dados e mescla informações de GET, POST e
Cookies - mas seu uso é desaconselhado.
Observe a questão a seguir, abordando formulários HTML:
A:
Array
(
[objetivo] => Array
(
[0] => compras
[1] => passeios
[2] => gastronomia
)
)
B:
Array
(
[objetivo] => Array
(
[0] => compras
[1] => passeios
[2] => gastronomia
)
[x] => 15
[y] => 18
)
Recursos Web 139
C:
Array
(
[objetivo] => gastronomia
[x] => 15
[y] => 18
)
D:
Array
(
[objetivo] => Array
(
[0] => compras
[1] => passeios
[2] => gastronomia
)
[cadastrar_x] => 15
[cadastrar_y] => 18
)
Para acessar o conteúdo dos dados preenchidos no formulário, utilize o esquema abaixo:
Cookies
Os cookies (do inglês “biscoitos”) é um forma de armazenar pequenas porções de dados textuas no
navegador, com espaço restrito que varia de 4 a 6Kb (dependendo do browser).
Recursos Web 140
Gravar dados no navegador é inseguro pois fica no lado cliente e devemos seguir o mantra: “Jamais
confie na entrada de dados pelo usuário”.
Lembrando que nem sempre o navegador está habilitado para receber esse tipo de gravação, por ter
sido deliberadamente desativado pelo usuário.
Dica: para visualizar mais facilmente os cookies gravados no navegador instale o addon Web
Developer⁴⁹ ou habilite o menu de desenvolvedor presente nos navegadores modernos.
A função que realiza o processo de gravação chama-se setcookie(). O primeiro argumento da fun-
ção é o nome, que pode ser resgatado posteriormente através da superglobal $_COOKIE['nome']. O
segundo é o valor que será guardado no computador do usuário, portanto jamais salve informações
críticas.
Os demais parâmetros definam as condições em que esse cookie ficará disponível: o terceiro
argumento é o tempo (no formato Unix timestamp) em que o cookie ficará gravado no browser.
Se omitido ou configurado em 0 (zero), ele vai expirar no fim da sessão, ao fechar o navegador. Esse
tempo é definido em conjunto com a função time() que retorna o instante atual (em timestamp)
somado ao tempo desejado. Ex.: setcookie("ocultar_menu", "sim", time() + 86400);.
Lembrando que 86400 equivalem a um dia: 60 segundos x 60 minutos x 24 horas.
O quarto parâmetro é o caminho. Ou seja, se omitido fica sendo a raiz do site (/).
Quinto parâmetro é o domínio na qual ele pertence. Ex.: ao informar, por exemplo, .site.com torna
o cookie disponível para todos os subdomínios.
O sexto argumento aceita um booleano e assegura que o cookie somente será enviado através de
uma conexão segura.
Já o último parâmetro chama-se httponly e configura o cookie para que ele esteja disponível apenas
sob protocolo HTTP, portanto não estaria disponível para código JavaScript, evitando ataques XSS⁵⁰.
É uma funcionalidade polêmica, pois não funciona em todos os navegadores web.
Abaixo apresento uma imagem extraída do plugin Live HTTP headers⁵¹ que visualiza os cabeçalhos
trocados entre cliente e servidor, sendo que podemos enxergar os pares nome=valor.
⁴⁹http://chrispederick.com/work/web-developer
⁵⁰Cross-siteScripting
⁵¹http://livehttpheaders.mozdev.org
Recursos Web 141
Existe também a função setrawcookie() (com os mesmos parâmetros acima), mas com uma
diferença fundamental: ela guarda o valor do cookie em format “cru”, sem encode.
A função setcookie("usuario", "Ari Stopassola Junior\nZCPE"); transforma o valor em:
Para “apagar” um cookie, não há uma forma explícita de o fazê-lo e sim atribuir valor nulo (ou vazio)
ao dado e atribuir timestamp negativo no parâmetro de tempo. Ex.: setcookie("ocultar_menu",
NULL, -3600);
Recursos Web 142
É possível um website armazenar na máquina cliente dois cookies com nomes idênti\
cos mas valores diferentes?
A resposta é sim, pois se consegue salvar dois cookies desde que estejam apontados em diferentes
caminhos. Nesse caso a alternativa C é a correta.
Veja um printscreen do plugin Firebug⁵², na aba “Cookies”, onde demonstra dois cookies de mesmo
nome com valores distintos: um no contexto de /a e outro em /b.
Upload
O upload acontece quando o usuário preenche o formulário com um campo especial do tipo file,
apontando para um arquivo escolhido em seu próprio filesystem.
O elemento HTML do formulário fica algo como <input type='file'> e requer obrigatoriamente
o atributo enctype='multipart/form-data' na tag <form>.
No momento seguinte todos os dados do arquivos são acessados através da superglobal $_FILES⁵³,
permitindo que o PHP o manipule de todas as formas. Abaixo seguem os índices do array $_FILES
contendo informações sobre o(s) arquivo(s) que está(ão) sendo recebido(s) no servidor:
Função Descrição
$_FILES['name'] Nome original do arquivo
$_FILES['type'] Tipo MIME
$_FILES['size'] Tamanho do arquivo (em bytes⁵⁴)
$_FILES['tmp_name'] Nome temporário que o arquivo receberá no servidor
$_FILES['error'] Mensagem de erro do processo de upload (caso ocorra)
⁵²http://getfirebug.com
⁵³http://php.net/manual/pt_BR/features.file-upload.post-method.php
⁵⁴Para converter em Megabytes, multiplique por 1048576 (1024 * 1024).
Recursos Web 143
Após identificar o arquivo, você precisa executar a função PHP que materializa o arquivo no seu
devido local físico junto ao sistema de arquivos, pois o arquivo é automaticamente excluído (do
diretório temporário) após a execução do script.
Função Descrição
move_uploaded_file() Move um arquivo enviado para uma nova localização
is_uploaded_file() Retorna um boolean identificando se o arquivo foi
enviado pelo método POST HTTP, útil para evitar ataques
maliciosos onde o usuário pode deturpar o arquivo em
questão, fazendo o script operar em arquivos críticos
(como por exemplo /etc/passwd). Potencial pergunta
do exame para preenchimento em campo aberto!
Nessa questão usaremos a estratégia da eliminação. Primeiramente observe que o enunciado quer
saber qual a alternativa NÃO é fundamental para que o upload de arquivos funcione no PHP.
A alternativa A é imprescindível: a cláusula file_uploads no php.ini deve estar habilitada
(configurada como On ou 1).
A opção B enfatiza que o respectivo formulário deve utilizar o método POST de envio de dados,
configuração que é requisito básico também.
Na resposta D refere-se ao atributo enctype que obrigatoriamente deve estar ajustado como
multipart/form-data, caso contrário o arquivo chega corrompido no lado do servidor.
Então sobra a alternativa C, que menciona um campo hidden com o nome MAX_FILE_SIZE, que na
prática não tem impacto na execução do script.
Um recurso interessante (e com grandes chances de cair no exame) que serve para restringir
o tamanho do upload de arquivos, é definir a cláusula upload_max_filesize no php.ini. Ex.:
upload_max_filesize = 32M.
Após o advento da versão 5.6 do PHP, a linguagem suporta arquivos maiores que 2Gb
(gigabytes).
Recursos Web 144
Progresso de upload
Novas versões do PHP incorporaram recursos que nos permitem acompanhar o progresso de upload
de arquivos.
Esse controle do tráfego oferece instrumentos para customizar a transferência do arquivo para o
servidor e aumentar a interatividade desse processo para com o usuário.
A: session.upload_progress.file
B: session.upload_progress.key
C: session.upload_progress.prefix
D: session.upload_progress.freq
Recursos Web 145
E: session.upload_progress.name
Cabeçalhos HTTP
Toda requisição ao servidor gera um retorno contendo cabeçalhos, que transmite informações sobre
o recurso que está sendo solicitado.
O header é sempre enviado e pode-se manipular a forma como o navegador interpreta um
determinado conteúdo.
Como requisito deve ser especificado antes da saída de qualquer elemento de tela.
A função utilizada para configurar os cabeçalhos HTTP é a header(), muito utilizada para
redirecionamento, autenticação, personalizar erros, interpretar corretamente o conteúdo transferido,
controle de cache, compressão e encoding.
O primeiro parâmetro é a string de cabeçalho. Ex.: header('Location: http://www.certificamp.com');.
Obs.: a string Location redireciona a requisição para outra URL.
O segundo argumento opcional indica se o cabeçalho deve substituir um header similar anterior ou
adicionar um segundo cabeçalho de mesmo tipo. Por padrão, ele irá substituir, mas se você passar
false como o segundo argumento, podes forçar vários cabeçalhos de mesmo tipo.
Já o terceiro parâmetro, também opcional, indica o status HTTP (sendo que o default é 200,
informando que a requisição foi bem sucedida). Ex.: header('Location: /index2.php', true,
301);.
Obs.: o status HTTP⁵⁵ de código 301 indica que o site foi movido permanentemente.
Outra função bastante interessante chama-se header_list(). Ela retorna todos os cabeçalhos que
foram enviados ao navegador. Veja esse exemplo:
E o resultado foi:
⁵⁵https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
Recursos Web 146
array(4) {
[0]=>
string(24) "X-Powered-By: PHP/7.1.1"
[1]=>
string(83) "Set-Cookie: menu=horizontal; expires=Thu, 10-Jun-2017 19:47:20 GMT\
; Max-Age=2592000"
[2]=>
string(20) "Content-language: en"
[3]=>
string(38) "Content-type: text/plain;charset=UTF-8"
}
A função header_sent() retorna um booleano caso os cabeçalhos já tenham sido enviados (true)
ou não (false).
Funções interessantes Descrição
http_response_code(404); Altera o código HTTP para 404
header_register_callback('funcao'); Chama uma função de usuário que mexe nos cabeçalhos,
geralmente com header_remove() e header().
Observe o código PHP abaixo, onde realizamos algumas manipulações de cabeçalhos HTTP:
1 function AlteraCabecalhos()
2 {
3 header_remove("X-Powered-By"); //remove "X-Powered-By: PHP/7.1.1"
4 header("X-Author: Ari Junior"); //adiciona um novo cabeçalho
5 }
6
7 //Chama uma função que altera os cabeçalhos
8 header_register_callback("AlteraCabecalhos");
9
10 //Altera o código HTTP para 404
11 http_response_code(404);
12
13 //Retorna o código HTTP 404, anteriormente configurado
14 echo http_response_code();
15
16 //Exibe todos os cabeçalhos transmitidos
17 print_r(headers_list());
Outra possibilidade interessante é usar a função headers_sent() para saber se algum cookie, por
exemplo, foi enviado ao navegador do usuário - já que a função setcookie() adiciona um cabeçalho
HTTP Set-Cookie.
Quais classes de status HTTP são utilizadas para manifestar condições de erro no\
servidor e redirecionamento, respectivamente?
A: 4XX e 3XX
B: 4XX e 2XX
C: 3XX e 5XX
D: 5XX e 3XX
Essa questão tem duas alternativas contento a classe de erro 4XX, que também indicam erros
provenientes do usuário. A classe de erros 5XX informam que existem problemas para lidar com
a requisição, do ponto de vista do servidor - como por exemplo o 503 (Service Unavailable)
indicando que o servidor está inoperante ou com excessos de solicitações.
A classe de erros 3XX realizam redirecionamentos automáticos, quando um website foi movido para
outro endereço etc. Portanto a alternativa correta é D: 5XX e 3XX. Mais informações em https://
www.w3.org/Protocols/HTTP/HTRESP.html.
Outro erro comum é tentar enviar cabeçalhos HTTP, através da função header(), após algo ter sido
escrito em tela - provocando Warning: Cannot modify header information - headers already
sent by....
Mas há situações em que simplesmente não há como mexer no fluxo de execução do script de modo
que os headers sejam disparados antes (para evitar erros). Então utilizamos uma técnica para reter
o conteúdo de saída (buffer) e realizar o envio quando for mais apropriado - geralmente após a
transmissão dos cabeçalhos. Ex.:
Recursos Web 148
Autenticação HTTP
Embora o exame seja independente de servidor web, algumas funcionalidades interessantes são
usadas com frequência e pertencem ao módulo PHP para o Apache. Geralmente os demais servidores
acabam implementando tais funcionalidades, por tratar-se de recursos realmente úteis e se tornaram
padrão de mercado. Dentre eles se destacam o mod_rewrite (sobrescrita de URL) e a autenticação
clássica do Apache.
A autenticação é um hook do Apache que invoca uma janela de login e senha. Após o preenchimento
das credenciais, tornam-se disponíveis (na superglobal $_SERVER) nos índices: PHP_AUTH_USER
(usuário), PHP_AUTH_PW (senha) e AUTH_TYPE (tipo de autenticação).
1 <?php
2 $autenticado = false;
3 if(isset($_SERVER['PHP_AUTH_USER'])) $usr = $_SERVER['PHP_AUTH_USER'];
4 if(isset($_SERVER['PHP_AUTH_PW'])) $senha = $_SERVER['PHP_AUTH_PW'];
5 if (isset($usr) && isset($senha) && $usr == "admin" && $senha == "ninja")
6 {
7 $autenticado = true;
8 }
9
10 if (!$autenticado)
11 {
12 header('WWW-Authenticate: Basic realm="Area protegida"');
13 header('HTTP/1.0 401 Unauthorized');
14 echo "Lamento, mas você não foi autenticado. Tente novamente...";
15 exit;
16 }
17 ?>
18 Parabéns! Você está numa área protegida.
Recursos Web 149
Quais dos seguintes itens da superglobal $_SERVER são importantes para autentica\
ção do cliente ao utilizar o tipo básico (Basic) de autenticação HTTP?
[ escolha 2 ]
A: PHP_AUTH_TYPE
B: PHP_AUTH_PASSWORD
C: PHP_AUTH_DIGEST
D: PHP_AUTH_PW
E: PHP_AUTH_USER
Como visto no início desse tópico, as duas superglobais que armazenam as credenciais do usuário
para o esquema de autenticação HTTP Basic são $_SERVER['PHP_AUTH_USER'] e $_SERVER['PHP_-
AUTH_PW'], portanto alternativas D e E.
Simulado
1.
Quando o script PHP realiza upload de arquivo com o método PUT do HTTP, onde os \
dados de arquivo se encontram exatamente?
A: Na superglobal $_FILES
B: No stream de entrada php://input
C: Na superglobal $_POST
D: No escopo de variáveis globais
2.
Da forma como o script PHP (no caso o "aceito.php") lida com os dados vindos do \
formulário, qual seria o valor de $_POST['aceito']?
Recursos Web 150
A: aceito
B: ok
C: true
D: on
3.
Em sua aplicação PHP, como você pode enviar o mesmo cabeçalho HTTP duas vezes, m\
as com valores diferentes?
4.
Qual alternativa a seguir NÃO pode ser utilizada para enviar cookies a partir da\
aplicação PHP?
A: header()
B: $_COOKIE
C: setcookie()
D: setrawcookie()
5.
A: Basic
B: Advanced
C: Strict
D: Digest
E: Realm
Recursos Web 151
Respostas do simulado
1.
Comumente obtemos os dados de upload através da superglobal $_FILES, porém as requisições
PUT são mais simples que o upload de arquivos usando requisições POST. Portanto a resposta é B:
No stream de entrada php://input. Mais informações em https://secure.php.net/manual/pt_BR/
features.file-upload.put-method.php.
2.
Reposta D: on.
3.
Ao enviar o mesmo tipo de cabeçalho duas vezes, a segunda sobrescreve a primeira. Exceto se
você passar false como segundo argumento da função header() - daí ele preserva ambos. Portanto
letra A é a resposta correta.
4.
Pergunta capciosa e tende a confundir. O importante é lembrar que a superglobal $_COOKIE é
utilizada para ler cookies e não escrever (como diz o enunciado da questão).
Como a pergunta pede qual alternativa é incorreta, então justamente você deveria ter marcado a
opção B: $_COOKIE.
5.
Os tipos Advanced e Strict não existem na especificação da RFC 7235 https://tools.ietf.org/html/
rfc7235.
Realm é um atributo da autenticação que agrupa páginas semelhantes, permitindo acesso ou não
nesse escopo, também conhecido com protection Space. Entretanto não é um tipo de autenticação,
mas sim uma configuração.
Basic e Digest são esquemas de autenticação bastante difundidos, consequentemente são as
alternativas corretas: A e D.
Programação Orientada à Objetos
O paradigma orientado à objetos ou programação OO (POO, como comumente é chamada) traz
inúmeros benefícios ao desenvolvimento de software. Dentre eles:
• Modularizar o código;
• Organizar melhor grandes projetos;
• Estimular o reuso;
• Abstrair dados e comportamento, semelhante ao mundo real;
• Ocultar a implementação, focando na funcionalidade;
• Encapsulamento expõndo as funcionalidades, restringindo acesso aos dados internos;
• Desenvolvimento incremental através de herança.
Definição de classe
Classe é uma estrutura de dados que determina a forma dos objetos. Atua como um molde, atri-
buindo conhecimento (atributos) e comportamento (métodos). É uma unidade lógica que encapsula
dados e ações.
1 class Nome {
2 [atributos]
3 [métodos]
4 }
Deve começar com letra ou underscore seguido de letras, números e/ou underscore, sem limite de
tamanho. Ex.: class ServicosLocais { ... }.
A convenção utilizada chama-se upper camel case que estabelece a primeira letra de cada palavra
em maiúscula. Ex.: class PacotesPasseios { ... }.
Programação Orientada à Objetos 153
1 class Servico {
2 public $Nome;
3 public $distanciaEmKm; //Lower Camel Case
4 public $cidade_origem = "Gramado"; //Inicializando
5 public $_veiculo; //Foge as boas práticas
6
7 public $data_viagem = time(); //Erro! Inicialização ilegal
8 public $-guiaCredenciado; //NÃO é permitido
9 public $cidade_destino = $cidade_origem; //Inválido!
10 }
A criação de classes é feita através da palavra-chave class e os objetos são instâncias de classes e
para tal utilizam a palavra-chave new. Ex.:
1 class minhaClasse {
2 //...
3 }
4
5 $objeto = new minhaClasse();
Métodos e atributos
Os atributos são dados ou propriedades de objetos. É o conhecimento que tal objeto possui.
Lembrando que var é um resquício do PHP 4, mas ainda funcional como garantia de retro-
compatibilidade. Ex.: var $idade;.
1 class hospedagem {
2 public $diaria; //Inicializa como null
3 public $banheira = "sim";
4 $quarto; //ERRO!
5 }
Os métodos são funções e expressa o comportamento dos objetos. Manifesta o que os objetos são
capazes de fazer.
Para acessar propriedades e métodos da instância corrente, utilize $this que é variável especial
que aponta para a sua própria instância.
Programação Orientada à Objetos 154
1 class hospedagem {
2 var $tipo = "luxo";
3 function ObtemTipo() {
4 echo $this->tipo;
5 }
6 }
1 class restaurante {
2 function __construct(){
3 //...
4 }
5 }
Ou pode ser o método de mesmo nome da classe, que foi uma convenção implementada no PHP 4
e que se perpetuou por questões de retro-compatibilidade. Ex.:
1 class restaurante {
2 function restaurante(){
3 //...
4 }
5 }
1 class restaurante {
2 function __destruct() {
3 //...
4 }
5 }
6
7 $japones = new restaurante;
8 unset($japones);
9 //OU
10 $japones = null;
Visibilidade
A visibilidade serve para instruir os desenvolvedores em como utilizar os atributos adequadamente.
Escopo Visibilidade
public Acessível em toda parte (dentro e fora da classe)
protected Na classe que foi definida e seus descendentes
private Somente na classe que foi definida
Caso não seja especificado, o padrão é public tanto para atributos, quanto para métodos.
Modificador private é util, por exemplo, num atributo $salario – pois trata-se de um dado
crítico e que deve ser acessível apenas na própria classe que foi especificado, que é onde contém
a responsabilidade sobre àquele objeto.
Obs.: por convenção usa-se $_ antes do nome da variável quando a propriedade for protected. Ex.:
$_preco.
Instanciação
As instância da classe (objetos) são entidade com formato baseado numa estrutura pré-definida. É
um exemplar gerado a partir da classe. Ex.: $objeto = new classe;.
?>
A: $p = Pessoa("Ari");
B: $p = new Pessoa();
C: $p = new Pessoa("nome"=>"Ari");
D: $p = new Pessoa("Ari");
A alternativa A gera um "Fatal error: Call to undefined function Pessoa()" pois o PHP tenta
invocar uma função procedural chamada Pessoa (não a classe) que não existe.
A opção B possui uma sintaxe mais coerente, logo o construtor requer um argumento ($n) e ele foi
negligenciado - desse modo gera um Warning: Missing argument 1 for Pessoa::__construct().
A resposta C usa uma sintaxe inapropriada e gera um erro primário de Parse error: syntax error,
unexpected '=>' (T_DOUBLE_ARROW).
Por fim, a alternativa D é a correta, pois cria o objeto adequadamente passando o parâmetro Ari ao
construtor, que por sua vez atribui à propriedade $nome.
Embora o atributo $nome não exista na definição da classe, ele é criado em tempo de execução e
possui visibilidade pública.
Abaixo segue um trecho de código que comprova a explicação dessa pergunta:
1 class Pessoa {
2 function __construct($n){
3 $this->nome = $n;
4 }
5 }
6
7 //Fatal error: Uncaught Error: Call to undefined function Pessoa()
8 $p = Pessoa("Ari");
9
10 //Fatal error: Uncaught ArgumentCountError:
11 //Too few arguments to function Pessoa::__construct()
12 $p = new Pessoa();
13
14 //Parse error: syntax error, unexpected '=>' (T_DOUBLE_ARROW)
15 $p = new Pessoa("nome"=>"Ari");
16
17 //CORRETA
18 $p = new Pessoa("Ari");
A palavra reservada clone realiza um cópia do objeto, sendo que qualquer mudança no objeto
original NÃO reflete no clone. Ex.: $guia = clone $pessoa;.
Programação Orientada à Objetos 157
Pode-se customizar a forma como o clone é criado, definindo um método mágico chamado __-
clone(). Ex.:
1 function __clone()
2 {
3 $this->data_criacao = time();
4 }
Quando a palavra-chave clone for usada, lembre-se que o construtor da classe clonada não será
disparado.
É válido ressaltar que a cópia de objetos é diferente da clonagem. Na cópia de objetos (através de
atribuição) faz-se um apontamento para uma mesma área de memória.
Na cópia, os objetos são passados por referência, preservando o apontamento com o objeto original.
Ex.:
Na linha 1 do código acima, instanciamos stdClass (standard class) que é uma classe genérica vazia,
nativa da linguagem.
Programação Orientada à Objetos 158
Embora Classe seja vazia, continua sendo uma classe definida pelo usuário, consequentemente o
objeto $obj não é uma instância da classe padrão stdClass - testada através do operador instanceof.
Significa que o resultado é N.
A partir da versão 5.4 do PHP foi implementando o recurso Class member access on instantiation.
Significa que ao invés de carregar toda a instância da classe, o desenvolvedor apenas acessa o atributo
ou método necessário, com menos overhead e praticidade:
PHP < 5.4:
1 (...)()
2 (...)['propriedade']
3 (...)->propriedade
4 (...)->facaAlgo()
5 (...)::$propriedade
6 (...)::metodo()
Essa construção é familiar para quem trabalha com JavaScript e formalmente é chamada de IIFE:
Immediately Invoked Function Expression. Ex.:
Programação Orientada à Objetos 160
1 (function($nome = null){
2 $nome = $nome ?? "forasteiro";
3 $pessoa = new class($nome)
4 {
5 protected $nome;
6 public function __construct($nome)
7 {
8 $this->nome = $nome;
9 }
10 public function boasvindas()
11 {
12 echo "Olá ".$this->nome."<br>";
13 }
14 };
15 $pessoa->boasvindas();
16 })();
Útil para não poluir o escopo global com variáveis ou objetos temporários. Lembrando que
os parênteses no final () imediatamente executam a função, gerando a saída Olá forasteiro,
permitindo que seja passado algum parâmetro. Experimente alterar a última linha do código acima
para:
Herança
Sobrescrita de métodos
A sobrescrita de métodos (method overriding) é um técnica que consiste em redeclarar métodos,
propriedades e constantes, definindo um novo comportamento.
Nas constantes, basta redeclarar que o novo valor é assumido. Nos métodos, eles deve possuir a
mesma quantidade de argumentos (exceto o construtor).
Pode-se reusar uma implementação pronta chamando o relativo método da classe pai, através de
parent::método();. Ex.:
⁵⁶Abreviatura de Unified Modeling Language (Linguagem Unificade de Modelagem), amplamente utilizada na diagramação de sistemas
orientados à objetos.
Programação Orientada à Objetos 162
1 class Veiculo {
2 public $nome;
3 public $placa;
4 public $autonomia;
5 public $capacidade;
6
7 function ExibeVeiculo(){
8 $saida = "Nome: <strong>".$this->nome."</strong><br/>";
9 $saida .= "Placa: <strong>".$this->placa."</strong><br/>";
10 $saida .= "Autonomia: <strong>".$this->autonomia."</strong> Km/litro<br/>";
11 $saida .= "Capacidade: <strong>".$this->capacidade."</strong> pessoas";
12 return $saida;
13 }
14 }
15 /****** Sobrescreve o método ********/
16 class VeiculoProprio extends Veiculo {
17 function ExibeVeiculo(){
18 $saida = "<div style='background-color:palegreen;'>";
19 $saida .= parent::ExibeVeiculo();
20 $saida .= "</div>";
21 return $saida;
22 }
23 }
24 /************* Executa *************/
25 $van = new VeiculoProprio;
26 $van->nome = "Van";
27 $van->placa = "IRA 0312";
28 $van->autonomia = 8;
29 $van->capacidade = 6;
30 echo $van->ExibeVeiculo();
Modificadores
Para prevenir sobrescritas indesejadas, utilizamos a palavra reservada final. Ela evita que classes e
métodos sejam sobrescritos pelos descendentes.
O keyword final pode ser aplicada em classes e métodos, exceto atributos, funções, interfaces e
classes abstratas. Se for utilizada em classe, a mesma não poderá ser estendida.
Programação Orientada à Objetos 163
1 class pai{
2 final function facaAlgo()
3 {
4 echo "Faça algo da clase pai";
5 }
6 }
7 class filha extends pai
8 {
9 //Fatal error: Cannot override final method
10 function facaAlgo()
11 {
12 echo "Faça algo da clase filha";
13 }
14 }
15 $obj = new filha;
Classes abstratas
A classe definida com a palavra-chave abstract provê um esqueleto para as classes “concretas”
(que é onde residem as implementações de fato). Elas servem como uma assinatura para as classes
que serão efetivamente instanciadas, de modo que o arquiteto/desenvolvedor possa primeiramente
mapear a estrutura dos recursos que serão implementados posteriormente nas classes ditas “concre-
tas”.
As classes abstratas até podem contar implementação, mas jamais poderão ser instanciadas direta-
mente. A criação dos objetos é sempre feita a partir das classes que herda.
Programação Orientada à Objetos 164
Nesse exemplo, observe que temos uma classe e um método definidos como abstratos. No caso do
método fazAlgo() da ClasseAbstrata lembre-se que não podem haver implementações nele, assim
sendo você o declara normalmente e termina a declaração com ponto-e-vírgula (;)– sem trabalhar
o corpo do método.
Seguindo o exemplo, em compensação, existe o método Implementa() que possui código.
Ao colocar a palavra-chave abstract em frente ao método, obrigatoriamente ele deve ser imple-
mentado nas classes derivadas.
A classe que estende a classe abstrata é responsável por implementar o método abstrato e deve
reproduzir a assinatura do método, tal como foi estipulada. Significa que o controle de acesso do
método concreto é menos rigoroso do que o método abstrato.
Recapitulando:
Abaixo segue uma síntese de como funciona a alteração de visibilidade de entidades abstratas para
concretas:
Abstrata Concreta
private protected e public
protected public
public não pode ser redeclarada
As classes abstratas são um misto de interfaces (que veremos adiante) com classes, podendo
especificar o formato e também oferecendo implementação.
Interfaces
Assim como as classes abstratas, as interfaces nos apresentam exclusivamente as assinaturas de
classes e métodos. A interface especifica o modelo puro, sem implementação.
Por conseguinte nos força a seguir um padrão rígido na definição de classes e métodos.
Basta iniciar a construção da classe com a palavra-chave interface e escrever os métodos sem corpo
– apenas fechando a instrução com ponto-e-vírgula.
1 interface classeBase1 {
2 public function fazAlgo();
3 public function funcaoEspecial();
4 }
Qualquer classe que implemente uma interface compromete-se a codificar todos os seus métodos.
Para isso usa-se a palavra reservada implements, com a possibilidade de implementar mais de uma
interface simultaneamente (separando por vírgula).
1 interface classeBase1
2 {
3 public function fazAlgo();
4 public function funcaoEspecial();
5 }
6 interface classeBase2
7 {
8 public function funcaoGenial();
9 }
10 class minhaClasse implements classeBase1,classebase2
11 {
12 function fazAlgo()
Programação Orientada à Objetos 166
13 {
14 echo "Fiz algo";
15 }
16 function funcaoEspecial()
17 {
18 echo "Especial";
19 }
20 function funcaoGenial()
21 {
22 echo "Genial";
23 }
24 }
25
26 $obj = new minhaClasse;
27 $obj->fazAlgo();
A interface é similar a classe, contudo não possui implementação. Importante lembrar que uma
interface pode estender outras interfaces (desde que não haja código).
1 interface classeBaseUm {
2 function fazAlgo();
3 function funcaoEspecial();
4 }
5 interface classeBaseDois extends classeBaseUm
6 {
7 function funcaoGenial();
8 }
Observe no exemplo acima que a visibilidade dos métodos fazAlgo() e funcaoEspecial() foi
omitida, desse modo o PHP assume como public implicitamente – que é um pré-requisito na
construção de interfaces. Visibilidade private e protected não são permitidas nesse contexto.
Outro ponto importante é evitar a colisão de nomes de métodos que foram definidos em interfaces
diferentes, mas implementados pela mesma classe.
Programação Orientada à Objetos 167
1 interface A
2 {
3 function f();
4 }
5
6 interface B
7 {
8 function f();
9 }
10
11 class C implements A, B
12 {
13 function f()
14 {
15 echo "Implementação do método f";
16 }
17 }
Embora não provoque erro, é uma prática totalmente desaconselhável. Caso a assinatura do método
fosse diferente, daí sim geraria um Fatal error: Declaration of C::f() must be compatible
with A::f($arg) in: ....
Veja outro exemplo:
1 interface A
2 {
3 function f($arg);
4 }
5
6 interface B
7 {
8 function f();
9 }
10
11 class C implements A, B
12 {
13 function f()
14 {
15 echo "Implementação do método f";
16 }
17 }
Outro problema é tentar usar um método que não tenha sido implementado na classe concreta,
Programação Orientada à Objetos 168
embora esteja especificado na interface. Ao invocar o método (que apenas existe na sua descrição),
evidentemente, gerará um Fatal error.
A: Todos os itens
B: Classes que implementem Design Patterns
C: Qualquer classe que implementa uma interface
D: Qualquer classe que estende a classe pai
E: Uma super classe genérica com implementações específicas nas classes filho
Que tipo de definição de classe pode ser usada para emular herança múltipla?
A: Class
B: Abstract
C: Interface
D: Final
A resposta aqui é C: Interface. Como já foi dito, uma classe pode implementar várias interfaces.
Programação Orientada à Objetos 169
Caso haja algum referência em interface que não esteja implementada na classe, o PHP pro-
voca um: Fatal error: Class nome contains 1 abstract method and must therefore be de-
clared abstract or implement the remaining methods (Classe::metodo) in.
Na linha 1 a classe Colecao implementa IteratorAggregate que é uma interface para criar um
iterator externo. Na linha 8 o único método que obrigatoriamente deve ser implementado é
getIterator() e devolve um iterator, que pode ser um array ou uma coleção de objetos.
Além da interface IteratorAggregate, temos também Iterator. Lembrando que o “contrato” prevê
que sejam obrigatoriamente implementados os métodos: current(), next(), key(), rewind() e
valid(). Mais informações em: http://php.net/manual/pt_BR/class.iterator.php.
Programação Orientada à Objetos 170
Outra possibilidade contemplada na versão 7.1 do PHP foi a declaração do tipo de dados iterable
que permite array, objetos da classe ArrayIterator, generator e coleções de objetos. Ex.: public
function disparaEmail(iterable $contas) : void.
Retro-compatibilidade
O método construtor do PHP 4 era uma função com o mesmo nome da classe, invocado automati-
camente quando um objeto fosse instanciado.
Por questões de compatibilidade, esse recurso ainda funciona - embora em modo obsoleto e com
alerta de que será removido muito em breve.
Programação Orientada à Objetos 171
1 class C {
2 function c(){
3 echo "ARI";
4 }
5 function __construct(){
6 echo "arijunior";
7 }
8 }
9 $obj = new C;
Extraordinariamente quando houverem DOIS construtores (nesse caso o método c() e __cons-
truct()), o que teria prioridade na execução seria o __construct(), consequentemente o código
acima exibirá arijunior. Na falta deste, daí sim o c() seria disparado e exibiria ARI.
<?php
class A {
protected $a = 1;
function a() { echo $this->a++; }
}
class B extends A {
protected $a = 10;
function b() { echo $this->a++; $this->a(); }
}
$b = new B;
$b->b();
?>
A: 10111213
B: 1011
C: 101
D: 12
1 echo $this->a++;
Lembre-se que o PHP escreve o conteúdo da propriedade $a e só depois faz o incremento de uma
unidade, chamado de pós-incremento ($a++).
Programação Orientada à Objetos 172
Exceções
A classe Exception é um mecanismo para controle de erros mais minucioso que a forma convenci-
onal através de error_reporting(), die() ou flags booleanas.
Oferece diversos níveis de depuração com alto grau de detalhamento, pois qualquer exceção não
tratada é fatal.
O PHP 5+ disponibiliza a classe Exception que pode ser estendida, customizando-a para a sua
aplicação.
Objetos baseados na classe Exception geram thrown que traduzindo significa erros “lançados” e
devidamente capturados - evitando um comportamento inexperado na execução.
Exceções é uma forma elegante de responder a problemas e proporcionando mudanças no fluxo
de execução quando estes ocorrerem. Esse mecanismo dá uma chance de recuperação em casos
anormais.
Então os trechos de código considerados críticos devem ser delimitados por try e catch. Se um
try for disparado, deve haver um catch associado, caso contrário gera Fatal error, interrompendo
abruptamente a execução do script.
1 try{
2 $address_db = Address::load(0);
3 echo '<tt><pre>' . var_export($address_db, TRUE) . '</pre></tt>';
4 }catch (Exception $e){
5 //Obtem a mensagem de "throw new Exception ('mensagem');"
6 echo $e->getMessage();
7 }
A versão 5.5 do PHP trouxe uma nova estrutura para controle de exceções: finally.
O finally executa DEPOIS dos blocos try/catch e ANTES da retomada normal do fluxo. Veja o
pseudo-código abaixo onde a coluna da esquerda possui código redundante (desbloqueia tabela),
enquanto que a coluna da direita utiliza o recurso finally tornando a execução mais elegante:
Observe que a instrução desbloqueia tabela aparece duas vezes, tanto no try quanto no catch,
justamente pela necessidade de realizar essa operação quando a execução for bem ou mal sucedida.
Ao utilizar a instrução finally, podemos especificar que essa porção de código executará de
qualquer maneira.
Veja um exemplo de código real, sem a redundância do comando $conexao->exec("UNLOCK TA-
BLES"), na era pré-PHP 5.5:
1 class Classe
2 {
3 static $bar = "Variável";
4 const bar = "Constante";
5 static public function bar()
6 {
7 echo "Método";
8 }
9 }
10 echo Classe::$bar;
11 echo Classe::bar;
12 echo Classe::bar();
Programação Orientada à Objetos 175
Essa opção existe especialmente para disponibilizar atributos e métodos que independem do estado
do objeto, facilitando a leitura do código.
Os dois-pontos dois-pontos ou double colon é muitas vezes referenciado como Paamayim Nekudo-
tayim⁵⁷.
No uso de recursos static utilize self para chamar, internamente a classe, uma propriedade e
método estático - ao invés de $this.
<?php
class myClass {
public $member = "ABC";
static function showMember() {
var_dump($this->member);
}
}
myClass::showMember();
?>
A: Null
B: string(3)"ABC"
C: string(0)""
D: Nenhuma das alternativas
A resposta correta dessa questão é D: Nenhuma das alternativas, pois no contexto static é preciso
utilizar a palavra-chave self::$member (e não $this->member) – desde que $member seja estático
também.
1 class ExemploEstatico {
2 static public $idademinima = 5;
3 static public function Exibe(){
4 return "Bem-vindo";
5 }
6 }
7
8 echo ExemploEstatico::$idademinima;
9 echo ExemploEstatico::Exibe();
⁵⁷Paamayim Nekudotayim significa dois pontos duplos (double colon) em hebraico, idioma falado na terra natal de Andi Gutmans e Zeev Suraski
- fundadores da Zend Technologies.
Programação Orientada à Objetos 176
Nesse exemplo o script mostra 5 (valor contido no atributo $idademinima) e Bem-vindo que é a saída
do método Exibe().
Para acessar a propriedade idademinima e o método Exibe, utilizamos o operador de resolução de
escopo :: também conhecido como Paamayim Nekudotayim. Enfatizo esse nome em hebraico, pois
geralmente cai no exame.
Tal construção é útil quando certas operações estão auto-contidas ou exigem única e exclusivamente
de seus argumentos.
Observe a sutileza desse próximo exemplo. Ex.:
1 class Classe {
2 static $elemento = "Variável";
3 const elemento = "Constante";
4 static public function elemento()
5 {
6 return "Método";
7 }
8 }
9 echo Classe::$elemento;
10 echo Classe::elemento;
11 echo Classe::elemento();
Perceba que as constantes de classe também se utilizam de :: (double colon). Embora o atributo
(variável), constante e método (função) tenham exatamente o mesmo nome, eles são estruturas de
dados e propósitos totalmente distintos.
Nesse caso o resultado seria: VariávelConstanteMétodo.
Jamais tente acessar propriedades estáticas por meio do operador de resolução de escopo ->. Ex.:
Essa atitude resultará em: Strict Standards: Accessing static property Classe::$elemento
as non static in...
1 class ExemploEstatico{
2 static public $idademinima = 5;
3 static public $passeio = "Tour Uva e Vinho";
4 static public function Exibe(){
5 if(self::$passeio=="Tour Uva e Vinho")
6 {
7 self::$idademinima++;
8 echo "Idade mínima é ".self::$idademinima." anos.";
9 }
10 }
11 }
12
13 echo ExemploEstatico::Exibe();
Lembre-se de utilizar o $ (cifrão) no nome do atributo, caso contrário diz respeito a constantes.
A vantagem de utilizarmos static é na facilidade de leitura do código e menor consumo de poder
computacional.
Ao utilizar a palavra reservada self há um resultado estranho, logo o código vai exibir Atividade
que é o retorno do método exibeNome() da classe pai - sendo que self deveria invocar o método da
própria classe que o está chamando - nesse caso a classe filha Passeio.
Para evitar esse efeito colateral, substitua no código a palavra self por static, daí ele vai se
comportar adequadamente, exibindo o retorno de exibeNome() da classe Passeio.
Autocarga (Autoload)
Autoload é a autocarga de classes feita sob-demanda. Este processo é também conhecido como lazy
loading.
Com ela é possível fazermos a carga das classes a medida em que as instanciamos. Dessa forma não
precisamos carregar todas as classes assim que o script PHP é executado, economizando recurso
computacional e acelerando a execução do código.
Programação Orientada à Objetos 179
1 function __autoload($c)
2 {
3 include_once "./_classes/".$c.".class.php";
4 }
5 $obj = new minhaClasse(); //carrega o arquivo ./_classes/minhaClasse.class.php
1 Qual das seguintes linhas de código NÃO vai disparar o auto-carregamento da clas\
2 se Nemo se ela não estiver definida?
3
4 A: $a = new Nemo();
5 B: echo Nemo::$a;
6 C: class Captain extends Nemo {}
7 D: $b = $a instanceof Nemo;
A alternativa A é a mais evidente e invoca a classe Nemo caso ela já não esteja disponível. A opção B
solicita um atributo estático $a da classe Nemo e também faz a auto-carga. Na alternativa C a classe
Captain é filha de Nemo que, por sua vez, requisita ao __autolad() a respectiva classe.
Por fim, o operador instanceof retorna true ou false caso os operandos sejam instâncias de mesma
classe ou de classes filhas com mesmo pai. Mas o operador não é suficiente para realizar a carga
automática por autoload - é necessário primeiramente instanciar.
Programação Orientada à Objetos 180
Reflexão
Reflection é uma API que expõe a introspecção de código.
É uma coleção de funções e objetos para examinar o conteúdo do script, como uma “engenharia
reversa” de classes, interfaces, funções, métodos e extensões.
É a habilidade de um programa observar e modificar sua própria estrutura.
A partir da versão 5.3 do PHP a extensão reflection é habilitada por padrão.
Com ela é possível também resgatar comentários/documentação de funções, classes e métodos.
Considere a definição de classes a seguir:
1 interface Atividade
2 {
3 public function obtemDesconto($cd);
4 }
5
6 class Passeio implements Atividade
7 {
8 public $nome;
9 public $preco;
10 public $desconto;
11 public function __construct($n)
12 {
13 $this->nome = $n;
14 $this->desconto = 10; //percentual
15 }
16
17 /**
18 * Método que abate ou não um desconto percentual
19 *
20 * @param integer $com_desconto Valor percentual para cálculo do desconto
21 *
Programação Orientada à Objetos 181
22 * @return number
23 */
24 public function obtemDesconto($com_desconto)
25 {
26 $valor = 0;
27 if($com_desconto){
28 $valor = $this->preco * $this->desconto / 100;
29 $this->preco -= $valor;
30 }
31 return $valor;
32 }
33 }
E agora iremos inspecionar a nossa classe através da ReflectionClass e de seus diversos métodos:
Que código pode ser usado para criar um objeto da classe MyClass em $a?
A: $a = new ReflectionObject("MyClass");
B: $c = new ReflectionClass("MyClass"); $a = $c->newInstance();
C: $a = ReflectionMethod::invoke(array("MyClass", "__construct"));
D: $m = new ReflectionMethod("MyClass", "__construct"); $a = $m->invoke(null);
A alternativa correta é B, pois que o método newInstance(), como o nome já explicita, instancia a
respectiva classe passando os argumentos ao construtor dela. Ex.:
1 $c = new ReflectionClass("MyClass");
2 $a = $c->newInstance();
1 $vetor = array(
2 'nome' => 'Ari Junior',
3 'email' => 'arijunior@gmail.com'
4 );
5 $objeto1 = (object)$vetor; //Cria um objeto stdClass
6 echo var_export($objeto1, true);
Lembre-se na prova que podemos facilmente converter o array $vetor em um objeto padrão, sendo
que os índices serão propriedades contendo seus valores (atribuídos previamente no vetor).
Mas voltando ao Type Hinting, assunto principal desse capítulo, podemos induzir os tipos de dados
passados como argumentos de métodos e funções.
Atualmente a indução de tipos suporta objetos, arrays e callable (que representam callbacks⁵⁸).
Se o tipo de dado não for compatível com a “dica de tipo” sugerida, gera um erro fatal.
⁵⁸Funções anônimas, também conhecidas como closures, que permitem a criação de funções sem nome - particularmente úteis como valor de
parâmetros e atribuição de variáveis.
Programação Orientada à Objetos 183
1 class MinhaClasse1 {}
2 class MinhaClasse2 {}
3 function facaAlgo(MinhaClasse1 $c) {}
4
5 $c1 = new MinhaClasse1;
6 $c2 = new MinhaClasse2;
7
8 facaAlgo($c1); //ok
9 facaAlgo($c2); //Fatal error
Ao lidar com objetos, é permitido que o objeto passado como parâmetro seja uma instância da classe
herdada de outra.
1 class MinhaClasse1 {}
2 class MinhaClasse2 extends MinhaClasse1 {}
3 function facaAlgo(MinhaClasse1 $c) {}
4
5 $c1 = new MinhaClasse1;
6 $c2 = new MinhaClasse2;
7
8 facaAlgo($c1); //ok
9 facaAlgo($c2); //ok
Constantes de Classe
As constantes são propriedades que nunca mudam o seu conteúdo. Ex.: códigos de erro interno.
São representadas, segundo as melhores práticas, geralmente por todas as letras em maiúscula. Ex.:
MAIUSCULAS_SEP_POR_UNDERSCORE.
Devem conter valores simples como string, booleano e inteiro e atribuídas na especificação da
classe (ex.: const IDADE_MAX_NAO_PAGANTE = 6;), acessíveis posteriormente (quando instanciadas)
através da sintaxe NomeDaClasse::IDADE_MAX_NAO_PAGANTE.
As constantes que operam sob o paradigma OO são consideradas mais rápidas que as constantes
convencionais, criadas ao estilo define(CONSTANTE, 'val'). Veja um exemplo:
1 class Classe
2 {
3 const BAR = "Constante";
4 }
Lembrando que a função define() (na programação imperativa) pode receber ainda um terceiro
parâmetro boolean onde true habilita o case-insensitive. Ou seja: NOMECONSTANTE se iguala a
nomeconstante ou NomeConstante.
Programação Orientada à Objetos 184
<?php
class Eu {
const NOME = "Ari";
}
class MiniEu extends Eu {}
echo MiniEu::NOME;
A: Ari
B: MiniEu::NOME
C: Nada
D: NOME
E: Fatal error
Veja que a classe MiniEu herda recursos de Eu, sendo que Eu (classe pai) possui uma constante
chamada NOME contendo Ari. Ao requisitar NOME de MiniEu, naturalmente o resultado será Ari, pois
é uma característica da herança. Então a opção correta é A: Ari.
Métodos mágicos
Os métodos mágicos disparam quando ocorrer uma ação específica no objeto. Iniciam por __ (dois
underscores) seguidos de letras e tem escopo público. Mediante um evento, certo método mágico
dispara automaticamente (quando existir).
São úteis, por exemplo, para inicializar ou finalizar dados de objetos, proceder acesso à métodos e
atributos não definidos ou converter objetos para uma representação textual.
A: print $c;
B: echo "$c";
C: print(string)$c;
D: echo '(',$c,')';
E: print '('.$c.')';
Programação Orientada à Objetos 185
Nesse questão TODAS são corretas, pois tanto print quanto echo disparam o método __toString().
Para ilustrar, ao tentar exibir um objeto diretamente (através de echo ou print), instantaneamente
o método mágico __toString() é disparado. Na implementação do método é importante que a
mensagem seja formatada com return. Ex.:
1 class Passeio {
2 public function __toString()
3 {
4 return "Método mágico toString";
5 //se utilizar echo ao invés de return gera um Recoverable fatal error
6 }
7 }
8 $obj = new Passeio;
9 echo $obj;
Outro método mágico interessante é __invoke(), invocado quando o objeto é chamado em forma
de função. Ex.:
1 class Passeio {
2 public function __invoke()
3 {
4 echo "Passeando";
5 }
6 }
7
8 $obj = new Passeio();
9 $obj(); // Passeando
Ao acessar propriedades não existentes de uma classe, seja para ler ou atribuir valor, os métodos
mágicos invocados serão respectivamente __get() e __set().
Uma implementação possível seria:
Programação Orientada à Objetos 186
1 class Passeio {
2 private $preco = "89";
3 private $desconto_idoso = "20";
4
5 public function __get($x)
6 {
7 return (isset($this->$x) ? $this->$x : null);
8 }
9 public function __set($x, $valor)
10 {
11 $this->$x = $valor;
12 }
13 }
14
15 $obj = new Passeio();
16 $obj->idade_min = 6; //$idade_min NÃO EXISTE, então será incorporado ao objeto
17 echo $obj->idade_min;
Ao tentar acessar um método inexistente, o PHP executa um método especial __call (se houver).
Ex.:
1 class Passeio {
2 private $roteiro = "Gramado";
3
4 public function __call($nome, $params)
5 {
6 if($nome == "MostrarRoteiro"){
7 echo $this->roteiro;
8 }
9 }
10 }
11
12 $obj = new Passeio();
13 $obj->MostrarRoteiro(); //MostrarRoteiro NÃO EXISTE, então invoca __call
O PHP reserva quinze funções especiais (métodos sempre começando por dois sublinhados __) que
podem ser explicitamente implementados na respectiva classe, de modo a realizar alguma tarefa
especial idealizada pelo desenvolvedor.
Ao executar ações em objetos, podem haver métodos associados à eles que serão automaticamente
disparados.
Programação Orientada à Objetos 187
1 $s = serialize(array(1, 2, "Ari"));
2 //$s = "a:3:{i:0;i:1;i:1;i:2;i:2;s:3:"Ari";}"
Nesse formato, podemos transmitir tal elemento para outra página (via GET, por exemplo) e
remontar o objeto através da função inversa chamada unserialize(). Ex.: $objeto = unseria-
lize($string);.
Não é recomendado que se utilize esse tipo de transformação para persistir objetos em banco de
dados relacional. Para tanto, utilize um mecanismo de ORM (Object Relational Mapping) que foge
do escopo desse guia.
Continuando no âmbito da programação orientada à objetos, temos dezenas de funções que nos
auxiliam no desenvolvimento. Dentre elas:
Função Descrição
get_object_vars($obj) Obtém os atributos públicos de um
objeto
get_class_vars($class) Obtém os atributos públicos de uma
classe
get_class_methods(get_class($obj)) Obtém o nome dos métodos da
respectiva classe, obtida através de
get_class
is_subclass_of($obj, $class) Verifica se o objeto tem esta classe como
uma de suas classes pai
get_parent_class($obj) Recupera o nome da classe pai do objeto
interface_exists($intfc) Verifica se uma interface foi definida
is_a($obj, $class) Verifica se o objeto é de uma classe ou de
sua classe pai
method_exists($obj,$method) Checa se o método da classe existe
property_exists($class,$property) Checa se o objeto ou a classe tem tal
propriedade
Programação Orientada à Objetos 189
Padrões de Projetos
Os Padrões de Projetos, internacionalmente conhecidos como Design Patterns, são soluções reusáveis
para problemas comuns na área de engenharia de software.
Consiste na estratégia para resolução de problemas usuais no contexto da programação orientada
à objetos e oferece um conjunto de diagramas (baseados na UML) selecionados pela renomada
“Gangue dos quatro”⁵⁹, que exploram as vantagens da POO e seus desafios, solucionando problemas
do cotidiano.
Padrões de projetos não enfatizam código, mas tratam conceitualmente do problema dando-lhe um
guia de como resolvê-lo, por conseguinte o programador implementa a solução na linguagem que
lhe for conveniente.
Singleton
Imagine que você esteja criando um aplicativo que se conecta repetidamente a um SGBD, para
recuperar dados de pedidos de faturas, sendo que todos os dados vêm sempre do mesmo banco.
Uma implementação provável seria:
Para preservar recursos, você deveria garantir que apenas UMA conexão de banco de dados seja
estabelecida durante a requisição. O código também precisa abrir o mínimo de conexões possível.
Refatorando teríamos:
Nesse cenário o Singleton restringe um determinado objeto a ser único, como uma instância global
para toda a aplicação.
No exemplo abaixo utilizamos o método getInstance() para identificar se houve uma nova
instanciação da classe Conexao e impedir que novos objetos subsequentes sejam criados a partir
dela.
⁵⁹Gang of Four (GoF) é um grupo formado por Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides - expoentes na engenharia de software
- que escreveram um livro clássico contendo vários padrões de projetos. Mais informações em https://en.wikipedia.org/wiki/Design_Patterns.
Programação Orientada à Objetos 190
1 class Conexao
2 {
3 private $_conexao;
4
5 private function __construct()
6 {
7 //O DSN deve vir de um arquivo de configuração ou variáveis de ambiente
8 $this->_conexao = new PDO("mysql:host=localhost;dbname=zcpe","usr","pass");
9 echo "NOVA instancia<br>";
10 }
11
12 public static function getInstance()
13 {
14 static $instance = null;
15 if (is_null($instance)){
16 $instance = new Conexao();
17 }
18 else {
19 echo "Instancia já existe<br>";
20 }
21 return $instance;
22 }
23 }
24
25 $objeto1 = Conexao::getInstance();
26 $objeto2 = Conexao::getInstance();
27 $objeto3 = Conexao::getInstance();
1 NOVA instancia
2 Instancia já existe
3 Instancia já existe
Factory
Imagine que você queira carregar diferentes formatos de arquivos de configuração, que eventual-
mente podem ser arquivos .ini ou no formato XML, JSON e outros.
Seria interessante que o sistema nos permitisse criar um objeto sem a necessidade de conhecer os
detalhes de cada implementação.
Programação Orientada à Objetos 191
Nesse caso usaríamos o padrão Factory, pois é sabido que a subclasse terá especificidades de acordo
com cada formato desejado (.ini, .json, .xml, .yaml etc). Por outro lado devemos padronizar as
interfaces.
Observe o seguinte exemplo utlizado pelo Zend Framework, que ilustra o cenário ilustrado acima:
1 $config = Zend\Config\Factory::fromFile("config.json");
2 $config = Zend\Config\Factory::fromFile("config.xml");
Observe que o método factoryMethod() da classe Creator é um método abstrato que será
implementado na classe concreta ConcreteCreator. Significa que o método factoryMethod()
abstrato possui apenas a “assinatura”.
No caso do problema mencionado acima, teremos diferentes formas de tratar os arquivos de
configuração (seja YAML, XML, JSON e outros) e utilizaríamos um diagrama de classes derivado do
padrão, semelhante a esse:
Programação Orientada à Objetos 192
MVC (Modelo-Visão-Controlador)
O padrão Model-View-Controller (comumente referenciado por MVC) provê uma estrutura que
separa a lógica de negócio (modelo) dos aspectos de interface (visão), mediados pelo controlador.
É uma arquitetura onde o model tem o propósito de atualizar o estado do sistema e a view altera a
apresentação das informações para o usuário, diante das ações realizadas pelo controlador. A idéia
fundamental do MVC tem enfoque na reusabilidade de código e separação de conceitos.
Método Descrição
rewind() Move o iterador de volta para o primeiro elemento
key() Devolve o índice do elemento atual
current() Retorna o valor do elemento atual
next() Move o ponteiro para o próximo elemento
valid() Verifica se há outro elemento para que o iterador possa se mover
Para usá-los utilize a palavra-chave new e configure as opções com as constantes disponíveis. Ex.:
FilesystemIterator::SKIP_DOTS. Essa constante, particularmente, serve para escapar do diretório
corrente (.) e do diretório pai (..).
Para aplicar essas configurações faz-se na instanciação ou utilizando o método setFlags() - veremos
exemplos mais adiante:
Cada elemento dentro do laço é um objeto, com a possibilidade de convertê-lo para array através
da função iterator_to_array().
Essa observação é particularmente importante para tentar aplicar testes sobre $arq, como por
exemplo if(is_file($arq)), e o resultado não ser o esperado. Como estamos lidando com objetos,
o correto seria utilizar o método isFile(), nesse caso: if($arq->isFile()).
Há outros métodos interessantes como, por exemplo, getPathname() que devolve o caminho
completo do objeto arquivo.
Veja esse exemplo do uso de iterators aninhados, onde a saída de um alimenta a entrada de outro
promovendo um encadeamento:
Para usuários de Windows o resultado pode ser algo parecido com: biblioteca/imagens\5_-
mini.jpg. Observe a mistura de barras (normal e invertida) no caminho. Esse resultado pode
atrapalhar a execução do código em ambientes Linux e OSX, então melhor seria adaptarmos para
que seja mais portável, passando ao construtor a constante FilesystemIterator::UNIX_PATHS. Ex.:
Como foi dito no início desse capítulo, outra forma de passar opções à classe é através do método
setFlags(). Ex.:
DirectoryIterator FilesystemIterator
Observe que $certificacoes agora tornou-se um objeto, instância da classe SplFixedArray e com
diversos métodos disponíveis: http://php.net/manual/en/class.splfixedarray.php.
Uma sugestão interessante e otimizada para armazenamento de objetos únicos em memória, seria
o uso da classe SplObjectStorage. Ex.:
Generators
Como bem disse Josh Lockhart no seu livro “Modern PHP”⁶⁰, os generators são iteradores
simplificados.
Para você percorrer uma coleção de objetos, a forma elegante de fazê-lo é desenvolvendo uma classe
que implemente a interface Iterator⁶¹ - que demanda obrigatoriamente os métodos: rewind(),
current(), key(), next() e valid().
Nitidamente seria uma classe pesada e mais complexa, que carrega todos os elementos em memória
- como faz o iterador padrão PHP.
Já os generators computam e produzem iterações sob demanda, com reflexo direto no ganho de
performance. Utilizam foreach() para percorrer os dados sem a necessidade de construir um array
em memória.
Geradores permitem criar iterações de forma simples sem a necessidade de código extra desneces-
sário, que serviria apenas para cumprir uma assinatura (conhecido pelo termo boilerplate code).
O generator é uma função ou método que utiliza a palavra reservada yield para devolver o elemento
corrente (ao invés de return), diminuindo significativamente o uso de recurso computacional.
1 function familia() {
2 //Retorna um objeto da classe Generator
3 yield 'Ari Junior';
4 yield 'Ariane';
5 yield 'Ariel';
6 yield 'Laci';
7 yield 'Ari';
8 }
9
10 foreach (familia() as $pessoa) {
11 echo $pessoa."<br>";
12 }
Devolve:
⁶⁰http://shop.oreilly.com/product/0636920033868.do
⁶¹http://php.net/manual/pt_BR/class.iterator.php
Programação Orientada à Objetos 197
Ari Junior
Ariane
Ariel
Laci
Ari
No site da documentação oficial⁶² diz-se que os generators fornecem uma maneira fácil de imple-
mentar iteradores simples, sem a sobrecarga ou complexidade de criar uma classe que implemente
a interface Iterator.
Os geradores saberão quem é o próximo elemento da cadeia quando perguntados, mas não há como
retroceder, nem avançar até o final ao estilo fast forward. Mas nada impede de iterar várias vezes o
mesmo generator ou cloná-lo se necessário.
Para criar um generator basta criar uma função PHP que utiliza a palavra-chave yield uma ou mais
vezes.
Ao identificar a palavra-chave yield dentro da função/método nehum código interno é executado,
exceto quando essa função sofre iteração.
1 function gen() {
2 echo "Laci";
3 yield "Ari Junior";
4 echo "Paloma";
5 yield "Ari Neto";
6 echo "Ariane";
7 }
8 gen();
Ao chamar a função gen(), estranhamente nada é exibido, mesmo com várias instruções echo
incorporadas lá.
Mas ao percorrer o gerador, daí sim teremos a execução de gen(). Experimente substituir gen() por
foreach(gen() as $item){}.
Entenda o yield como um return especial que devolve a chave/valor corrente. Quando solicitado
o próximo elemento da iteração (através de foreach, por exemplo), o generator continua de onde
parou o último yield. Ele se lembra da série até ser chamado novamente.
Esse comportamento minimiza o overhead de manter diversos objetos em memória.
Por simplicidade, evita ter de implementar os cinco métodos da interface Iterator (promovida pela
SPL) que são: current, key, next, rewind e valid.
Veja um exemplo para leitura de arquivos:
⁶²https://secure.php.net/manual/pt_BR/language.generators.overview.php
Programação Orientada à Objetos 198
1 function obtemLinhas($arquivo) {
2 //Abre o arquivo em modo leitura
3 $arq = fopen($arquivo, 'r');
4 while(!feof($arq)) {
5 $linha = fgets($arq);
6 yield $linha;
7 }
8 //Fecha o manipulador de arquivo
9 fclose($arq);
10 }
11
12 foreach(obtemLinhas("familia.txt") as $linha){
13 echo $linha."<br>";
14 }
Antes da versão 7.0 do PHP a única forma de usar return dentro de um generator seria devolvendo
vazio (null), fazendo com que a série chegue ao final ignorando os demais yield. Ex.:
1 function colecao() {
2 yield 'a'; // iterador retorna "a"
3 yield null; // iterador retorna null
4 yield 'b'; // iterador retorna "b"
5 return null; // iterador encerrado
6 yield 'c';
7 }
8
9 //Exibe "ab" e ignora o "c", que seria o elemento da sequência
10 foreach(colecao() as $item){
11 echo $item;
12 }
Com o advento do PHP 7.0, tornou-se possível retornar um valor que não fosse nulo, sem gerar um
erro fatal como nas versões anteriores.
Programação Orientada à Objetos 199
1 function colecao() {
2 $quantidade = 0;
3 $vetor = array("Ari Junior", "Ariel", "Ariane", "Ari Neto");
4 while(list($indice, $valor) = each($vetor)) {
5 yield $valor;
6 $quantidade++;
7 }
8 return $quantidade;
9 }
10
11 //Cria uma instância do generator
12 $colecao = colecao();
13
14 foreach($colecao as $elemento){
15 echo $elemento."<br>";
16 }
A saída será:
Ari Junior
Ariel
Ariane
Ari Neto
Mas para resgatar o valor de retorno de um generator (nesse caso o conteúdo de $quantidade) você
precisa utilizar o método getReturn(). Ex.: $colecao->getReturn(); que mostrará 4.
Obs.: o método getReturn() só devolve seu valor quando a série acessada pelo generator for per-
corrida até o final. Caso haja um break no meio do laço, o getReturn() gera um Fatal error: Un-
caught Exception: Cannot get return value of a generator that hasn't returned in....
function gen() {
for ($i=0; $i<=3; $i++)
yield $i;
}
$generator = gen();
if(is_array($generator))
echo "Array";
elseif(is_object($generator))
echo "Objeto";
else
Programação Orientada à Objetos 200
A palavra-chave yield deflagra que trata-se de uma instância da classe generator, portanto o teste
is_object() é verdadeiro. Então deve-se escrever no campo freetext exatamente a resposta: Objeto.
Uma nova capacidade implementada na versão 7.0 foi a delegação de generators. Veja esse exemplo:
1 function generatorA(){
2 yield "a";
3 $gB = yield from generatorB();
4 yield "g";
5 return $gB;
6 }
7
8 function generatorB(){
9 yield "b";
10 $gB = yield from ["c", "d", "e"];
11 yield "f";
12 return "Gerado a partir de generatorB";
13 }
14
15 $gen = generatorA();
16
17 foreach($gen as $elemento){
18 echo $elemento."<br>";
19 }
20
21 echo $gen->getReturn();
a
b
c
d
e
f
g
Gerado a partir de generatorB
Traits
Herdar é quando uma classe filha reutiliza atributos e métodos de uma classe ancestral. Como “efeito
colateral” a sub-classe incorpora todos os recursos disponíveis na classe pai, ou seja, toda a herança.
Podemos ter um pseudo-controle do que será herdado através do escopo de visibilidade, mas sabe-se
que na herança simples o reuso não é pleno, já que (conceitualmente falando) deveria ser possível
fazer uso de recursos contidos também em OUTRAS classes - caso PHP implementasse herança
múltipla, que não é o caso.
Alguns arquitetos de software argumentam que se houver necessidade de herança múltipla, talvez
seja indício de falha na modelagem.
PHP não implementa herança múltipla, consequentemente evita o Deadly diamond of death⁶³
(“Problema do Diamante”), que é o conflito quando há redundância de métodos herdados de classes
distintas. A classe filha não saberia que método usar se duas super-classes possuem um recurso de
mesmo nome.
⁶³https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem
Programação Orientada à Objetos 202
Agora imagine o seguinte cenário, totalmente legítimo: temos classes Transfer e Compras que
necessitam exatamente do mesmo método chamado CargaDescarga().
Programação Orientada à Objetos 203
Antes do advento de traits (traduzindo do inglês seria algo como “traços” ou “características”),
seria obrigatório copiar tal método, duplicando código, o que seria péssimo. Com traits conseguimos
injetar o método em uma ou mais classes.
Como laboratório, experimente instalar o PHP Copy & Paste Detector⁶⁴ (comando phpcpd) e execute
o comando no ZendFramework 1, por exemplo: phpcpd vendor/zendframework/zendframework. O
resultado é um percentual de código replicado, que na época fazia-se necessário, numa era pré PHP
5.3 (sem traits).
É dito que os traits promovem “herança horizontal” e que são usados ao invés de instanciados,
sendo que classes podem usar vários traits ao mesmo tempo (assim como as interfaces), sabendo
que traits não podem herdar de outro traits.
Traits podem usar outros traits e possuem atributos, caso seja conveniente.
Voltando ao cenário do método CargaDescarga(), nós poderíamos desmembrar a classe Compras e
colocar esse recurso num trait chamado Recursos. Veja o exemplo:
⁶⁴https://github.com/sebastianbergmann/phpcpd
Programação Orientada à Objetos 204
1 class Passeios {
2 function PagaComissao(){
3 return "Paga comissao...";
4 }
5 }
6
7 trait Recursos {
8 function CargaDescarga(){
9 return "Realiza carga e descarga";
10 }
11 }
12
13 class Transfer extends Passeios {
14 use Recursos;
15 }
16
17 $obj = new Transfer;
18 echo $obj->CargaDescarga();
Em grandes projetos, porventura pode acontecer de existirem métodos de mesmo nome definidos
em diferentes traits. Veja esse exemplo:
1 trait Colaborador {
2 function preencherOpinario()
3 {
4 return "Opinario dos colaboradores";
5 }
6 }
7 trait Parceiro {
8 function preencherOpinario()
9 {
10 return "Opinario dos parceiros";
11 }
12 }
13 trait Cliente {
14 function preencherOpinario()
15 {
16 return "Opinario dos clientes";
17 }
18 }
19 class Estagiario {
20 use Colaborador, Parceiro, Cliente;
Programação Orientada à Objetos 205
21 }
22
23 $obj = new Estagiario;
24 echo $obj->preencherOpinario();
Perceba que preencherOpinario() consta nos traits Colaborador, Parceiro e Cliente. O erro fatal
ocorre antes mesmo de instanciar a classe Estagiario e invocar o método preencherOpinario(),
ainda no momento da definição da classe Estagiario:
Fatal error: Trait method PreencherOpinario has not been applied, because there \
are collisions with other trait methods on Estagiario in…
Nesse caso, para resolver a situação, devemos usar uma técnica de resolução de conflitos, através
do operador insteadof:
1 class Estagiario {
2 use Colaborador, Parceiro, Cliente {
3 Colaborador::PreencherOpinario insteadof Parceiro, Cliente;
4 }
5 }
Traduzindo instead of do inglês teremos “ao invés de”. Segundo o trecho de código acima, a classe
vai priorizar o método PreencherOpinario() do trait Colaborador diante dos recursos contidos em
Parceiro e Cliente.
<?php
trait Colaborador {
function PreencherOpinario(){
return "colaboradores";
}
}
class Pessoa {
function PreencherOpinario(){
return "generico";
}
}
function PreencherOpinario(){
return "estagiarios";
}
}
Lembre-se que a ordem de uso dos métodos segue a seguinte sequência de fallback:
1º) da própria classe;
2º) do trait;
3º) da classe pai.
Mesmo que o método PreencherOpinario() esteja contida nessas três estruturas, será priorizado
inicialmente a classe local, depois o trait em uso e por fim a super-classe. Portanto a resposta correta
é estagiarios.
Outra técnica para distinção entre métodos é atribuir álias, que os diferencie:
1 class Estagiario {
2 use Colaborador, Parceiro, Cliente {
3 Colaborador::PreencherOpinario insteadof Parceiro, Cliente;
4 Parceiro::PreencherOpinario as OpinarioSimplificado;
5 }
6 }
O álias em traits é ainda mais versátil: ele permite alterar a visibilidade, já que um trait definido
como protected ou private, na excência, impedem seu uso. Veja esse exemplo:
Programação Orientada à Objetos 207
Vamos observar a alternativa E: Traits automaticamente resolvem conflitos com base na or-
dem de definição, lembrando que há uma ordem no uso de métodos conflitantes que primeira-
mente inspeciona a própria classe, depois recorre ao trait e, por fim, vasculha a classe pai. Mas se
a classe usa diversos traits que contenham um mesmo método, é imprescindível usar a palavra-
chave insteadof na resolução do conflito. Portanto o PHP não o faz automaticamente como sugere
a alternativa, sendo assim a opção E é falsa.
A outra alternativa inválida é B: Um trait pode implementar uma interface pois o compilador
“copia” o código do trait na respectiva classe. Como um suposto implements AlgumaInterface
ficaria fora da classe, tal operação não é permitida.
Para verificar quais traits determinada classe está utilizando, chame a função class_uses():
Programação Orientada à Objetos 208
1 var_dump(class_uses("Financeiro"));
2 array(2) {
3 ["Cambio"]=>
4 string(6) "Cambio"
5 ["Sindicato"]=>
6 string(9) "Sindicato"
7 }
Digamos que seu código tenha a função __autoload() implementada ou mesmo outra f\
unção registrada com spl_autoload_register(). Ao tentar usar um trait que ainda \
não tenha sido carregado, o auto carregamento será invocado e o trait disponibil\
izado?
1 spl_autoload_register(function ($nome) {
2 include $nome."inc.php";
3 });
4
5 class MinhaClasse {
6 use MeuTrait;
7 }
8
9 $obj = new MinhaClasse();
Programação Orientada à Objetos 209
Closures
As closures são funções anônimas e, por conseguinte, as variáveis internas possuem escopo local -
com exceção da variável especial $this que faz referência ao próprio objeto. No exemplo abaixo,
observe que o retorno do método ConverteEmKm() é uma função e, assim sendo, deve ser chamado
da seguinte forma: echo $fn();.
Caso quiséssemos acessar variáveis definidas externamente ao closure (supostamente não acessíveis)
utilizaremos a palavra-chave use passando como parâmetro todas as variáveis desejadas. Caso não
utilize use, o PHP acusaria Notice: Undefined variable: km in....
Programação Orientada à Objetos 210
Você pode passar inúmeras variáveis como argumentos de use separando-os por vírgula. Por
exemplo: use ($var, &$total, $subtotal).
Outra forma de associar estados ao objeto Closure é através do método bindTo(). Esse método
permite associar o estado interno de uma clousure com um objeto diferente, possibilitando acesso
à variáveis membro protegidas e privadas do objeto na qual a closure está sendo vinculada.
<?php
class Number {
private $v = 0;
public function __construct($v) { $this->v = $v; }
public function mul() {
return function ($x) { return $this->v * $x; };
}
}
Porém utilizamos o método bindTo() passando o estado de outro objeto, que tem o valor 1 atribuido
em $v.
Assim sendo o resultado será 1 * 5 = 5. A resposta que deve ser digitada é 5.
Classes anônimas
Essa é uma funcionalidade incorporada na versão 7.0 do PHP, que permite implementar classes
sem um nome associado. Significa que o desenvolvedor pode implementar uma classe que será
atribuída à uma variável, que pode ser o retorno de uma função e até passar como argumento de
uma função/método.
A sintaxe é exatamente a mesma da classe convencional (sem o nome, evidentemente). Ex.:
1 Classe anônima
2 object(class@anonymous)#1 (0) { }
Abaixo elencamos algumas situações interessantes que justificam o uso das anonymous classes,
dentre eles:
Um inconveniente das classes anônimas é não poderem ser serializadas. É sabido que faz-se
necessário a definição de classe na realização do processo de unserialize(), processo inverso do
serialize().
Simulado
1.
Programação Orientada à Objetos 212
A: print $obj;
B: echo "$obj";
C: print (string)$obj;
D: echo '(',$obj,')';
E: print '('.$obj.')';
2.
Quais das seguintes linhas de código NÃO irão carregar a classe Servicos via __a\
utoload, se ela não estiver definida?
A: $a = new Servicos();
B: echo Servicos::$a;
C: class Passeios extends Servicos {}
D: $b = $a instanceof Servicos;
3.
<?php
class MinhaClasse {}
function facaAlgo(MinhaClasse $c = null) {
echo empty($c) ? "vazia" : "não vazia";
}
$c = new MinhaClasse;
facaAlgo();
4.
Programação Orientada à Objetos 213
define("PI", 3.14159);
class Arquimedes
{
const PI = PI;
}
class Matematica
{
const PI = Arquimedes::PI;
}
echo Matematica::PI;
A: Parse error
B: Matematica::PI
C: 3.14159
D: PI
E: Arquimedes::PI
5.
A: booleano
B: operador
C: função
D: um construtor da linguagem
E: uma classe mágica
Respostas do simulado
1.
Nessa pergunta, TODAS as alternativas estão corretas: A, B, C, D e E.
Analise o seguinte trecho de código:
Programação Orientada à Objetos 214
1 class minhaClasse{
2 function __toString(){
3 return "Exibe";
4 }
5 }
6
7 $obj = new minhaClasse;
8
9 print $obj; //Alternativa A:
10 echo "$obj"; //Alternativa B:
11 print (string)$obj; //Alternativa C:
12 echo '(',$obj,')'; //Alternativa D:
13 print '('.$obj.')'; //Alternativa E:
A confusão pode ocorrer na alternativa D: echo '(',$obj,')'; onde aparecem vírgulas ao invés
de pontos (concatenação). Embora muitos não saibam, o echo suporta vírgulas para cada argumento
passado, diferentemente de print que requer o ponto para concatenação.
Numa análise mais aprofundada, a título de curiosidade, percebe-se um ganho de performance no
uso de vírgulas.
Nesse exemplo o script resultante então seria: ExibeExibeExibe(Exibe)(Exibe).
Portanto o método mágico __toString() seria invocado em todas as vezes.
2.
Aqui o importante é observar o enunciado no negativo.
Estamos habituados em focar na(s) alternativa(s) correta(s). Nesse caso as alternativas estão todas
coerentes – exceto uma.
Tanto as opções A, B e C chamam a classe Servicos. Primeiramente instanciando respectivamente
via atributo estático e por herança.
A alternativa D não faz sentido pois o operador instanceof retorna verdadeiro se o objeto $a é
uma instância de Servicos e falso o contrário. Nessa instrução a classe Servicos não é requerida
e portanto o __autoload não se manifesta. Portanto a resposta certa é D: $b = $a instanceof
Servicos;.
3.
Exemplo de questão que usa a identação para confundir o aspirante ao título ZCPE. Olhando
rapidamente, dá impressão de que a função procedural facaAlgo() é um método, pois está
deslocada para a direita. Mas é apenas uma função convencional definida no namespace global.
Outro detalhe crucial é o Type Hint garantindo que o argumento será uma instância da classe
MinhaClasse.
Programação Orientada à Objetos 215
Sabe-se que o null é o valor default caso o parâmetro não seja informado e a sintaxe do PHP aceita
essa construção. Nesse caso o operador ternário mostra em tela o termo vazia, assim sendo a
alternativa C é a resposta correta.
4.
No código teremos a constante PI com o valor 3.14159. Depois a classe Arquimedes usa essa
constante “global” numa constante “interna” da classe, também chamada de PI. Sendo que a classe
Matematica também tem a sua constante PI que recebe o conteúdo de Arquimedes::PI, então o
resultado será a alternativa C: 3.14159.
5.
Vamos por eliminação: classe mágica não existe em PHP, logo descarte a alternativa E. Definiva-
mente ele não é um booleano, desta maneira a opção A também é descartada.
Sabemos que instanceof é um operador de tipo e avalia se um objeto é uma instância de determinada
classe. Objetos e classes são operandos de instanceof que devolve true ou false, em vista disso a
resposta é B: operador.
Segurança
Tenha sempre em mente o seguinte mantra: jamais confie em dados vindos do usuário. Essa é
uma regra básica que estigmatiza toda entrada de dados (de origem externa) como potencialmente
perigosa.
Por consequência lembre-se de filtrar e validar os dados de input como GET, POST, Cookie,
cabeçalhos HTTP (veremos adiante no tópico “Filtragem de dados de entrada”) e sempre tratar
a saída dos dados, pois eles podem deteriorar o sistema ao exibir código malicioso (veremos em
detalhes no tópico “Escapar saída de dados”). A regra básica é seguir o FIEO: Filter Input and Escape
Ouput.
Lembre-se que dados de entrada podem ser também cabeçalhos HTTP, Web Services, URLs, variáveis
de ambiente, banco de dados, APIs e outros.
Um conjunto de funções interessantes na filtragem de dados são as da família ctype_* (Character
type checking), dentre elas a função ctype_alnum() que verifica se os caracteres são alfanuméricos
e ctype_digit() que testa se os dados são numéricos.
Que função pode ser usada para validar se uma determinada cadeia consiste em ape\
nas letras e números?
A: ctype_alnum()
B: cytpe_alpha()
C: ctype_letterdigit()
D: ctype_xdigit()
Essa pergunta possui armadilhas nos nomes das funções. A função ctype_alpha() realmente existe
(vide alternativa B) e pode ser facilmente associada com alphanumeric, mas o nome “alpha” vem de
alphabetic – entretanto valida somente letras.
A função ctype_letterdigit() (da alternativa C) não existe e ctype_xdigit() (alternativa D) testa
se o argumento é hexadecimal: que são dados representados por 16 símbolos formados pela cadeia
de caracteres 0-9 e A-F.
Portanto a alternativa A é a opção correta: função ctype_alnum().
Retomando o tópico segurança, é importante que saibam da existência do projeto OWASP, sigla de
Open Web Application Security Project http://www.owasp.org.
OWASP é um projeto aberto para segurança de aplicações web sem fins lucrativos, orientada pelo
consenso, com o objetivo de disseminar conhecimento sobre segurança na internet.
Segurança 217
Não entraremos a fundo em cada ataque catalogado peloa OWASP, mas iremos explorar os mais
signficativos.
Configuração
Seguindo algumas orientações gerais, nas configurações no servidor de produção jamais mostre
os erros que porventura possam acontecer. Para tanto utilize a cláusula display_errors = off no
arquivo php.ini. Aconselha-se registrá-los em arquivos de log, habilitando a diretiva log_errors =
On e apontando para um arquivo textual através de error_log = /var/log/erros_do_php.log.
Importante mencionar que podemos alternar tais configurações em tempo de execução através da
função ini_set(). Ex.:
1 ini_set('error_reporting', E_ALL);
2 ini_set('log_errors', 'On');
3 ini_set('error_log', '/logs/erros.log');
Segurança de Sessão
O ataque Session Hijacking rouba uma sessão já estabelecida, mais atrativa quando um usuário já
estiver logado no sistema, por exemplo. As etapas do ataque são as seguintes:
Outro ataque interessante chama-se session fixation, variante do hijacking. Ele faz com que o usuário
utilize um ID de sessão fixo sugerido pelo atacante.
É recomendado sempre utilizar sessões baseadas em cookies, para evitar que elas fiquem trafegando
junto a URL. Para realizar tal medida, desabilite a instrução session.use_trans_sid (no php.ini).
Ação Instrução
Modificar o ID da sessão antes de operações críticas session_regenerate_id(true);
Tornar o cookie acessível somente por HTTP, evitando acesso via JavaScript session.cookie_httponly = On
Cross-Site Scripting
Também conhecido por XSS, é a inserção de códigos maliciosos baseados em linguagens cliente
(client-side) como JavaScript, HTML, CSS, VBScript, ActionScript etc.
Esse tipo de ataque captura informações do usuário que visualiza e interage com a página,
explorando blogs e fóruns de discussão.
São ataques geralmente implementados em JavaScript que podem, por exemplo, redirecionar o
Segurança 219
usuário para outra página, modificar o conteúdo da página (deface⁶⁵) ou ler cookies do usuário.
Imagine um sistema de blog contendo um mecanismo de comentários. Pressumponha que não haja
qualquer preocupação com o ataque Cross-Site Scripting e que possamos escrever, deliberadamente,
ao invés de um comentário real, um trecho de código JavaScript como sugerido abaixo. Quem for
acessar o site posteriormente, terá experiências desagradáveis:
Para evitar que o usuário adicione tais códigos nos campos HTML input ou textarea, o desenvol-
vedor deve filtrar os dados de entrada através de algumas funções especiais tais como:
Função Descrição
strip_tags() Elimita tags do conteúdo, exceto algumas tags
especificadas: strip_tags($item, '<b><i>').
htmlspecialchars() Converte caracteres especiais para o contexto HTML: &
torna-se &, " torna-se ", ' torna-se ' (desde
que passe a constante ENT_QUOTES como segundo
parâmetro), < torna-se < e > torna-se >.
htmlentities() Converte todos os caracteres em seus equivalentes HTML.
São os caracteres listados acima, bem como caracteres
acentuados, cedilha e outros. Ou seja: í torna-se í,
ç torna-se ç, ã torna-se ã e assim por diante.
A função htmlentities() gera mais código de saída, mas permite que se utilize tal conteúdo em
arquivos XML (sem corromper), além de evitar problemas ao manipular caracteres no padrão ISO-
8859-1.
⁶⁵https://en.wikipedia.org/wiki/Website_defacement
Segurança 220
1 <a href="http://milhas.com/transfere.php?beneficiado=hacker&quant=100000">Veja m\
2 inhas fotos!</a>
Perceba que, ao clicar no link em busca das “fotos”, na realidade o hacker está realizando uma
operação de transferência. Aqui nós utilizamos um exemplo bem elementar, para fins didáticos.
E se esse mesmo usuário acessasse uma página “contaminada” via XSS (Cross-Site Scripting), então
ele nem perceberia que está realizando uma transferência de milhas - caso a requisição estivesse
escondida em uma imagem de 0x0 pixels. Veja esse exemplo:
E se a transferência de milhas no site destino for via o método POST, é possível criar um formulário
“camuflado” submetido automaticamente:
1 <form name="FormOculto">
2 <input type="hidden" name="beneficiado" value="hacker">
3 <input type="hidden" name="quant" value="100000">
4 </form>
5 <script>document.forms['FormOculto'].submit();</script>
Como contra-medidas recomenda-se usar um token único nos formulários e verificar toda vez que o
formulário for submetido, além de exigir que o usuário se autentique novamente antes de qualquer
operação crítica.
Segurança 221
SQL Injection
O ataque de Injeção SQL é quando a consulta ao banco de dados é montada através de dados vindos
do usuário, sem nenhum tratamento. Ou seja, a montagem da cláusula SQL usa dados externos não
filtrados.
Imagine uma tela típica de autenticação:
Digamos que o desenvolvedor utilize essas credenciais (campo de e-mail e senha) e monte a consulta
SQL com tais dados, sem verificação. Ex.:
1 $email = $_POST['email'];
2 $senha = $_POST['senha'];
3 $sql = "SELECT * FROM usuarios WHERE email='$email' AND senha='$senha'";
Pressuponha que o hacker, ao invés de digitar uma senha no respectivo campo, coloque conteúdo
malicioso, como por exemplo: senha' OR '1'='1.
No lado do servidor, quando o script PHP substituir o conteúdo do campo senha na montagem da
query, teremos a seguinte cláusula SQL:
Independente do $email e $senha que o atacante informar, a consulta será executada com sucesso
pois 1 é obviamente igual a 1, portanto verdadeiro. Então é bem provável que o usuário consiga
acesso a áreas protegidas do sistema, desconhecendo completamente e-mail e senha de usuário.
Segurança 222
Outras injeções de SQL poderiam ser bem piores, como por exemplo: ‘; DROP TABLE usuarios;--
que faria apagar uma tabela inteira do banco de dados.
Como contra-medidas temos as prepared statements (quando suportadas pelo banco de dados),
algumas funções específicas do banco de dados em questão (ex.: mysqli_real_escape_string())
e SEMPRE validar todos os dados de entrada.
Não é suficiente apenas processar os dados com addslashes(), função que adiciona barra de escape
nas aspas (simples e duplas). Até porque essa mesma operação pode ser feita de forma automática
no php.ini pela diretiva magic_quotes_gpc = on.
Ao substituir $_POST['busca'] pelo código malicioso indicado no enunciado, a query será montada
da seguinte forma:
1 SELECT * FROM artigos WHERE conteudo LIKE '%%' UNION SELECT email,senha FROM usu\
2 arios;--%'
Significa que a consulta vai trazer todos os artigos, bem como a relação dos e-mails e senhas dos
usuários. Os dois traços -- indicam que tudo depois é comentário e os caracteres que “sobraram”
não devem afetar a query.
A única dificuldade será equiparar a quantidade de campos em ambas as consultas, o que é
relativamente fácil - por tentativa e erro.
Obs.: a literatura de banco de dados enfatiza que os campos das respectivas consultas necessitam
ter o mesmo formato, mas na prática o MySQL é bastante flexível e traz tais conteúdos mesmo que
sejam de tipos distintos.
Segurança 223
Partindo do princípio da eliminação, nessa questão, a alternativa B: Hashing não é capaz de fazer o
caminho de volta justamente por ela ser one-way.
A opção D: ROT13 transforma cada caracter em 13 posições a frente no alfabeto, então seria
facilmente decodificada. Quando ultrapassa a letra z ele rotaciona, voltando para o começo do
alfabeto.
A alternativa C: Caesar cipher[^Ceasarcipher] faz referência ao imperador romando que codifi-
cava a mensagem de acordo com o tamanho do cilindro que a embalava.
Então teremos a A: Criptografia de chave simétrica como técnica viável e segura para guardar
credenciais de banco de dados, permitindo que a aplicação consiga reverter a cifragem.
Nele identificamos um script PHP comum para todas as páginas, para compartilharmos a mesma
estrutura HTML (elementos visuais como bordas e menus), utilizando as mesmas chamadas
JavaScript, incorporando CSS, realizando conexões com banco de dados e apenas nos preocupando
com o conteúdo mais interno. É um modelo comumente conhecido como bootstrap.
Para implementarmos esse modelo de reaproveitamento de código, é necessário passar como
parâmetro que página nos interessa visualizar na área destinada ao conteúdo. Como exemplo,
vamos utilizar index.php?arquivo=xxxx.php onde xxxx poderia ser algo como editar, deletar,
visualizar e outras operações de CRUD⁶⁶.
Mediante essa estrutura, imagine que o desenvolvedor newbie⁶⁷ implementa o seguinte código PHP:
Pressupondo que o bootstrap faz o include do arquivo com o seu nome completo (ex.: http://site/noticias/index.
novo.inc.php) e que a cláusula do allow_url_fopen está On (php.ini), o atacante pode experimentar
um procedimento mais astuto:
1. Criando um script PHP e salve como texto puro (padrão ANSI⁶⁸) com a extensão jpg, por
exemplo. Veja o quadro abaixo para mais detalhes sobre a extensão do arquivo;
⁶⁶Acrônimo de Create, Read, Update e Delete.
⁶⁷https://pt.wikipedia.org/wiki/Newbie
⁶⁸https://pt.wikipedia.org/wiki/American_National_Standards_Institute
Segurança 225
Ou, ao invés de incorporar um script malicioso externo, você pode passar como parâmetro (via GET)
um arquivo de senhas do próprio servidor (ex.: ?opcao=../../../../passwd) e quebrar a senhas
por força bruta com algum utilitário do tipo John the Ripper⁶⁹.
Abaixo seguem algumas medidas para evitar Command Injection/Code Injection:
• Verifique os dados de entrada com uma whitelist, sendo assim somente dados previamente
autorizados poderão ser inseridos (via include/require);
• Remova caminhos de arquivos através da função basename(). Ex.: echo basename("../../etc/passwd");
retorna somente passwd, que não surte efeito;
• Desabilite, quando possível, a diretiva do php.ini que carrega conteúdo externo: allow_url_-
fopen = Off.
• Se a diretiva allow_url_fopen estiver habilitada (por algum requisito da aplicação), certifique-
se de configurar a cláusula allow_url_include para Off.
Com a diretiva de configuração allow_url_include ativada, permite-se a inclusão de dados
a partir de locais remotos (site ou servidor FTP).
Ao desabilitar allow_url_fopen, allow_url_include também é desativada.
A linguagem permite executar comandos shell com a possibilidade de visualização dos resultados
(através da STDOUT⁷⁰).
Existem diferentes formas de se executar comandos externos para executar comandos no servidor
ou software de terceiros:
• shell_exec
• exec
⁶⁹http://www.openwall.com/john
⁷⁰Standard output.
Segurança 226
• system
• \$comando“
• passthru
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,cu\
rl_multi_exec,parse_ini_file,show_source
A: &
B: "
C: '
D: ;
A função escapeshellarg() trata o argumento de modo a torná-lo seguro quando utilizado como
parâmetro de funções shell, tais como: exec(), system(), shell_exec(), passthru() e com o
operador backtick⁷¹ (ou operador de execução composto por dois acentos graves `, um no início e
outro no fim do comando).
Como exemplo de vulnerabilidade, observe o código PHP a seguir:
⁷¹Observe que o operador de execução não é aspas simples! Ex.: $output = `ls -al`;
Segurança 227
http://localhost/script.php?argumento=certificamp.com
Array
(
[0] => PING certificamp.com (186.202.153.42) 56(84) bytes of data.
[1] => 64 bytes from hm6843.locaweb.com.br (186.202.153.42): icmp_req=1 ttl=\
50 time=174 ms
[2] => 64 bytes from hm6843.locaweb.com.br (186.202.153.42): icmp_req=2 ttl=\
50 time=174 ms
[3] =>
[4] => --- certificamp.com ping statistics ---
[5] => 2 packets transmitted, 2 received, 0% packet loss, time 999ms
[6] => rtt min/avg/max/mdev = 174.259/174.427/174.596/0.450 ms
)
Mas se o usuário experimentar algo mais ousado como, por exemplo, um novo comando separado
por ponto-e-vírgula ;:
http://localhost/script.php?argumento=certificamp.com;whoami
O resultado seria o conteúdo abaixo, atentando-se para o índice 7 que contém acimainterac-
tive.com, saída do comando whoami daquele servidor:
Segurança 228
Array
(
[0] => PING certificamp.com (186.202.153.42) 56(84) bytes of data.
[1] => 64 bytes from hm6843.locaweb.com.br (186.202.153.42): icmp_req=1 ttl=\
50 time=174 ms
[2] => 64 bytes from hm6843.locaweb.com.br (186.202.153.42): icmp_req=2 ttl=\
50 time=174 ms
[3] =>
[4] => --- certificamp.com ping statistics ---
[5] => 2 packets transmitted, 2 received, 0% packet loss, time 1001ms
[6] => rtt min/avg/max/mdev = 174.779/174.791/174.804/0.418 ms
[7] => acimainteractive.com
)
http://localhost/script.php?argumento=certificamp.com;rm -rf *
Por isso que a função escapeshellarg() é tão importante, impedindo a execução de comandos
inline. Para tal, substitua a primeira linha de código PHP por:
Assim sendo, a função se encarrega de sanitizar o argumento e minimizar prejuízos. Quando houver
indício de comandos sucessívos, o resultado será:
Array
(
)
Mas voltando a resposta da questão, uma das características da função escapeshellarg() é adicionar
aspas simples no início e fim do argumento. Portanto o caracter escapado será a própria aspa
simples, no caso alternativa C: '.
Senhas
Jamais salve senhas de credenciais de usuário, por exemplo, em texto puro no banco de dados!
Utilize, ao menos, o cálculo de hash da senha em questão. A hash tem como características tamanho
fixo (independente dos dados de entrada) e ser unidirecional. Ou seja: impossibilita descobrir o
conteúdo original, exceto por força bruta.
Abaixo seguem duas funções que podem ajudar nesse processo. Ex.:
Segurança 229
A: Blowfish
B: DES
C: MD5
D: SHA1
O algoritmo SHA1 não está disponível sob circunstância alguma. Já o Blowfish e MD5 estão
disponíveis (para uso nessa função crypt()), mas não são os algoritmos default. O algoritmo padrão
é o DES, portanto alternativa B é a correta.
Para alterar o comportamento da função crypt(), utilize as constantes específicas, como por
exemplo: define(CRYPT_BLOWFISH, true);.
Desde a versão 5.5 do PHP temos disponível nativamente uma API bastante sofisticada para hashing
de senha:
A função password_hash() gera uma hash de 60 caracteres. Como argumento perceba a constante
PASSWORD_BCRYPT representa o algoritmo BlowFish.
Segurança 230
1 if(password_verify('pa$$word’,
2 '$2y$11$Junicao2k14AriNetoPalea7XU5I/HU2TBiIy1ISQPliFAawO8ptq')){
3 echo "Senha correta";
4 }
5 else {
6 echo "Senha incorreta";
7 }
Para fins didáticos, a hash da segunda linha está explicita no código. Mas, em condições normais,
evidentemente ela estará armazenada em banco de dados.
Outra função interessante disponível na API é password_get_info() que mostra informações a
respeito da senha gerada. Ex.:
1 print_r(password_get_info('$2y$11$Junicao2k14AriNetoPalea7XU5I/HU2TBiIy1ISQPliFA\
2 awO8ptq'));
O resultado é:
Array
(
[algo] => 1
[algoName] => bcrypt
[options] => Array
(
[cost] => 11
)
E-mail Injection
Primeiramente vamos entender como funciona a função nativa mail(), que realiza o envio de
mensagens eletrônicas. Essa função aceita uma série de parâmetros:
Segurança 231
To: endereco@vitima.com
Subject: Oi
From: remetente@anonimo.com
Tudo bem
Adorei seu site.
Tchau
Observe que a função mail() monta a requisição adicionando os cabeçalhos To:, Subject: e From:
baseado nos parâmetros passados para a função.
Isso significa que se adulterarmos os parâmetros e colocarmos mais cabeçalhos, ele fará a
concatenação desses dados e o subsequente disparo da mensagem. Veja um exemplo:
1 $remetente = "remetente@anonimo.com%0ACc:outro@anonimo.com%0ABcc:mais@anonimo.co\
2 m,aindaesse@anonimo.com";
3 mail("endereco@vitima.com", "Oi", "Tudo bem\nAdorei seu site.\nTchau", $remetent\
4 e);
O cabeçalho ficaria:
To: endereco@vitima.com
Subject: Oi
From: remetente@anonimo.com
Cc:outro@anonimo.com
Bcc:mais@anonimo.com,aindaesse@anonimo.com
Tudo bem
Adorei seu site.
Tchau
Segurança 232
Veja que agora o e-mail será enviado com cópia ‘Cc’ (Carbon Copy) para outro@anonimo.com e como
‘Bcc’ (Blind Carbon Copy) para mais@anonimo.com e aindaesse@anonimo.com.
Considerando que ainda muitos desenvolvedores usam o e-mail do usuário (definido como ‘From’) e
o obtém diretamente do formulário, então perceba o quão fácil seria hackear esse disparo de e-mail.
<?php
mail('ari@certificamp.com',
'Feedback",
'Aqui está o meu opinário.',
"From: {$_COOKIE['email']}");
?>
Que proteção você usaria para filtrar o segundo parâmetro da função `mail()` par\
a impedir o ataque de injeção de e-mail?
⁷²https://pt.wikipedia.org/wiki/Express%C3%A3o_regular
Segurança 233
Quem vem estudando a mais tempo pode vir a confundir-se com essa questão. Se estivéssemos
falando sob o contexto do PHP 5.3, a alternativa C seria a correta, pois na época o segundo parâmetro
não recebia tratamento adequado e abria essa brecha de segurança.
Analisando sob o ponto de vista do PHP 7.1 (versão vigente do exame), a alternativa correta é a A,
pois a função mail() já filtra automaticamente os dados de entrada.
1 $email = "certificamp.com";
2 if(filter_var($email, FILTER_VALIDATE_EMAIL)){
3 echo "E-mail VÁLIDO";
4 }
5 else {
6 echo "E-mail inválido";
7 }
1 class Logger {
2 public $logFile;
3 public $buffer;
4 public $fh;
5 public function __destruct() {
6 $this->WriteBuffer();
7 }
8 public function WriteBuffer() {
9 if (!$this->fh) {
10 $this->fh = fopen($this->logFile, 'w');
11 }
12 fwrite($this->fh, $this->buffer);
13 }
14 }
15
16 $cookieData = unserialize($_COOKIE['data']);
Sabemos que cookies são totalmente vulneráveis e poderíamos facilmente editar o conteúdo de data,
adicionando o seguinte código:
O:6:"Logger":3:{s:7:"logFile";s:8:"vish.php";s:6:"buffer";s:27:"<?php system($_G\
ET["mal"]);";s:2:"fh";N;}
Ao executar o script PHP, ele descerializa o conteúdo do cookie, gerando um arquivos chamado
vish.php com o conteúdo:
1 <?php system($_GET["mal"]);
Significa que poderemos invocar o script vish.php passando a variável mal via método GET e
executar deliberadamente qualquer comando no sistema operacional.
Pensando nessa vulnerabilidade, o PHP 7.0 implementou um segundo parâmetro na função unse-
rialize() que são nomes de classes permitidas:
1 $saida = "<h1><script>alert('Cuidado!');</script></h1>";
2 echo htmlentities($saida, ENT_QUOTES);
3 //Transforma em: <h1><script>alert('Cuidado!');</script\
4 ></h1>
Funções Descrição
htmlspecialchars() Converte caracteres para suas respectivas entidades HTML. Ex.: < converte em
<
htmlentities() Converte caracteres para suas respectivas entidades HTML, exceto aspas simples
(a menos que você especifique ENT_QUOTES como segundo parâmetro)
strip_tags() Remove tags HTML e PHP de uma string
filter_var() Filtra e sanifica de acordo com as constantes disponíveis
utf8_decode() Converte caracteres latinos (como cedilha, por exemplo) para a codificação UTF-8
Criptografia e Hashing
Criptografia é um algoritmo bidirecional, portanto tudo o que for criptografado poderá ser posteri-
ormente descriptografado. Diferentemente do cálculo de hashing que é um processo unidirecional e
não pode ser revertido para o formato original, sendo que o conteúdo transformado sempre resultará
na mesma hash.
Significa que para guardar credenciais de usuário em banco de dados, você pode armazenar a hash
resultante e comparar o conteúdo da hash. Caso hackers invadam o sistema, eles terão apenas uma
porção de caracteres embaralhados, sem sentido. Os algoritmos de hash mais famosos são MD5,
SHA1, bcrypt e Scrypt.
⁷³https://msdn.microsoft.com/en-us/library/windows/desktop/aa379942(v=vs.85).aspx
⁷⁴http://man7.org/linux/man-pages/man2/getrandom.2.html
Segurança 236
Upload de arquivos
Primeiramente lembre-se que a superglobal $_FILES vem do usuário, então desconfie já que o nome
do arquivo pode ser forjado. Algumas dicas são:
A extensão do arquivo não é o suficiente para determinar o tipo em questão. Tão pouco o MIME type
que vem no cabeçalho HTTP, que também pode ser modificado. Para ter certeza do conteúdo, utilize
a função mime_content_type() ou o método file() da classe finfo. Ex.:
Armazenamento
Quando falamos em gerenciador de banco de dados devemos tomar algumas providências elemen-
tares:
Medidas Descrição
Restrição de privilégios É preciso atribuir permissões (escrita e leitura) para
os usuários específicos que realmente necessitam de
acesso àquela base de dados
Não exponha o servidor de BD na internet Procure desmembrar o servidor de banco de dados
para outra máquina (mesmo que virtualizada),
acessível apenas internamente
Bases de dados críticas localizadas em outra rede Para dados muito sensíveis, é recomendado que essa
máquina esteja em segmentos de rede distintos
Troca periódica de senhas Altere periodicamente as senhas para complicar a
tarefa dos hackers
Leia os logs de erro Toda investida no servidor gera um registro. Os
atacantes farão milhares de tentativas, na maioria
frustradas. Mas esse movimento gera um volume
grande de logs e faz-se necessário averiguar sempre.
Segurança 237
SSL
Abreviatura de Secure Socket Layer (SSL) cifra os dados que trafegam entre cliente e servidor. Esse
mecanismo de encriptar/decriptar os dados, se houver alguma interceptação como, por exemplo,
feita por um sniffer de rede⁷⁵, dificilmente o hacker vai conseguir decifrar a mensagem original.
A conexão do servidor de aplicação para com o banco de dados deve utilizar SSH (Secure Shell
Protocol), que ainda pode ser melhorada através do uso das extensões como mcrypt⁷⁶ e mhash⁷⁷
cifrando dados antes da inserção e decriptando após a consulta.
Simulado
1.
Em transações HTTPS, como as URLs e query strings são passadas do browser para o\
servidor web?
A: Ambas são passadas em texto puro e toda a transação subsequente será encripta\
⁷⁵https://pt.wikipedia.org/wiki/Sniffing
⁷⁶http://php.net/mcrypt
⁷⁷http://php.net/mhash
⁷⁸https://pt.wikipedia.org/wiki/Hyper_Text_Transfer_Protocol_Secure
Segurança 238
da
B: Ambas são encriptadas
C: A URL mantém-se em texto puro, enquanto que a query strings é encriptada
D: A URL é encriptada, enquanto que a query strings é passada em texto puro
E: Para garantir a sua encriptação, a query string é convertida em um cabeçalho \
e passada junto com a informação via POST
2.
3.
A: html_display
B: error_reporting
C: display_errors
D: error_log
E: ignore_repeated_errors
4.
D: Habilitar magic_quotes_gpc
E: Nenhuma das alternativas anteriores
5.
Você está escrevendo uma aplicação PHP que será utilizada por milhares de pessoa\
s.
É preciso armazenar as credenciais do banco de dados de forma segura, mas também\
garantir que a aplicação seja de fácil deploy.
Qual é a melhor forma de fazê-lo?
Respostas do simulado
1.
Primeiramente vamos distinguir o que é uma URL de uma query string. Na requisição teremos o
endereço:
http://www.certificamp.com/atendimento/cronometro.php?id=19&acao=liga
Como URL teremos a porção http://www.certificamp.com/atendimento/cronometro.php, sendo
que id=19&acao=liga é a query string.
Quando uma transação HTTPS ocorre, o navegador e o servidor imediatamente negociam um
mecanismo de criptografia para que todos os dados subseqüentes não sejam transmitidos em
texto claro (como ocorre nas requisições HTTP convencionais), incluindo a URL e a sequência
de parâmetros. Portanto a resposta correta é B: Ambas são encriptadas.
2.
É sabido que a função md5() retorna sempre 32 caracteres hexadecimais, independente do valor de
entrada. Porém ao passar true como segundo parâmetro na função md5(), ela devolve a saída em
formato binário com um tamanho de 16. Portanto a resposta é D: 16.
3.
A cláusula html_display sequer existe no php.ini. A instrução que existe é html_errors. O error_-
reporting é utilizado para configurar o nível de severidade dos erros PHP. A diretiva error_log é
utilizada para direcionar as mensagens de erro para um arquivo.
Segurança 240
Arquivos
Há duas famílias de funções para manipulação de arquivos: as que iniciam por f*() e stream_*()
que trabalham com recursos enquanto que as funções iniciadas por file*() trabalham com nomes
de arquivos.
Na abordagem orientada a recursos (handler), temos como exemplos de função:
Acessando diretamente, passando o arquivo como argumento, teremos como exemplos de função:
file_get_contents(), file_put_contents(), file() e readfile().
De acordo com a explicação no começo desse tópico, sabemos que as funções que iniciam por f*()
e stream_*() trabalham com ponteiros para os arquivos. Na quarta linha observe que a função
stream_get_contents() passa um nome de arquivo como parâmetro, gerando erro. Portanto a
alternativa B é a resposta correta.
I/O (Entrada e Saída) 242
Leitura
A manipulação de arquivos utilizando recursos baseia-se na função fopen e recebe o nome do
arquivo como primeiro parâmetro e o modo de operação como segundo argumento. Abaixo segue
uma tabela com os possíveis modos para a função fopen:
1 $f = fopen("arquivo.txt", "r");
2 while (!feof($f)) {
3 echo htmlspecialchars(fread($f, 4096));
4 }
5 fclose($f);
A função fread() recebe o ponteiro do arquivo e o tamanho (em bytes) da leitura que será realizada.
Se o arquivo possuir mais que 4096 bytes, de acordo com o exemplo, o laço continuará executando
até que chegue o final do arquivo - mediante o teste da função feof().
Obs.: caso queira realizar a leitura integral do arquivo em apenas uma iteração de while, substitua o
tamanho (nesse exemplo especificado como 4096) pelo retorno da função filesize('arquivo.txt)'.
Escrita
O processo de escrita em arquivo, quando sob o contexto de recurso, é feito através das funções
fwrite() e fputs():
1 $f = fopen("arquivo.txt", "w");
2 fwrite($f, "ZCPE...");
3 fclose($f);
Outras funções são interessantes como, por exemplo, a fputcsv() que escreve um array (recebido
como parâmetro) para o formato CSV⁷⁹ e despeja dentro de um arquivo.
Ou também a função fprintf() que formata uma string e envia para um stream de arquivo.
⁷⁹https://pt.wikipedia.org/wiki/Comma-separated_values
I/O (Entrada e Saída) 243
Operações em arquivos
Embora o PHP permita invocar o shell para realizar operações em arquivos e diretórios, é
aconselhável utilizar as respectivas funções nativas para tal tarefa. Tais como:
Função Descrição
copy() copia arquivo
rename() move/renomeia arquivo
unlink() apaga arquivo
mkdir() cria diretório
rmdir() apaga diretório
A lista de operações no sistema de arquivos é enorme e convém dar uma conferida em http://php.
net/manual/en/ref.filesystem.php.
Streams
Em tempos longínquos, lá pelos idos do PHP 4.x, cada tipo de arquivo (seja local, comprimido ou
remoto) tinha uma implementação diferente. Mais recentemente, com o advento de streams, toda
interação com arquivo faz uso de uma camada que abstrai os detalhes de acesso.
Sob o contexto de streams os “arquivos” utilizam-se de wrappers (agrupadores) que oferecem ope-
rações atuando em fluxos de entrada/saída (I/O), operando em arquivos comprimidos, trabalhando
com FTP, realizando empacotamento via PHAR⁸⁰, atuando diretamente no filesystem e tantos outros.
Basta utilizar os respectivos prefixos, que vão na frente do caminho do arquivo. Ex.:
Wrappers Descrição
file:// Acessa um arquivo no filesystem local
http:// Acessa uma URL
https:// Acessa uma URL sob SSL (HTTPS)
ftp:// Acessa um endereço de FTP
ftps:// Acessa um endereço de FTP seguro
php:// Acessa stream de entra/saída (I/O)
compress.zlib:// Comprime um fluxo de dados em GZip http://www.gzip.org
compress.bzip2:// Comprime um fluxo de dados em BZip2 http://www.bzip.org
⁸⁰http://php.net/manual/pt_BR/intro.phar.php
I/O (Entrada e Saída) 244
A resposta correta nessa questão é a letra B, pois qualquer operação no handler $f será comprimida
e empacotada em arquivo.
O php://input é um stream de apenas leitura que permite obter dados “crus” (raw) do corpo de uma
requisição.
A partir da versão 5.6 do PHP podemos reaproveitar a conexão aberta php://input e realizar
operações de busca (seek), consumindo menos memória ao lidar com dados via POST.
O PHP permite que criemos o nosso próprio agrupador, personalizado através da função stream_-
wrapper_register(protocolo, classe) e utilizando a família de funções stream_*.
A API de streams provê uma gama de informações sobre o fluxo de dados, mas qual \
das informações a seguir é fornecido pela função stream_get_meta_data?
1 $f = fopen("compress.zlib://pacote.txt.gz", "wb");
2 fwrite($f, "Zend Certified PHP Engineer");
3 $metadados = stream_get_meta_data($f);
4 print_r($metadados);
5 fclose($f);
O resultado será:
Array
(
[wrapper_type] => ZLIB
[stream_type] => ZLIB
[mode] => wb
[unread_bytes] => 0
[seekable] => 1
[uri] => compress.zlib://pacote.txt.gz
[timed_out] =>
[blocked] => 1
[eof] =>
)
Contextos
Ao trabalhar com recursos da rede, podemos configurar uma série de parâmetros para o stream de
modo a operar adequadamente ou mudar seu comportamento.
Essas opções especiais são conhecidas como contextos e são criadas a partir da função stream_-
context_create(). Por exemplo, ao trabalhar com arquivos .zip pode ser necessário passar a senha
para descriptografá-lo:
I/O (Entrada e Saída) 246
1 $opcoes = array(
2 'zip' => array(
3 'password' => 'P@$$w0rd',
4 )
5 );
6
7 //Cria o contexto
8 $contexto = stream_context_create($opcoes);
9
10 //Utiliza o contexto
11 echo file_get_contents("zip://arquivo.zip#ZCPE.pdf", false, $contexto);
Lembre-se que a função stream_context(), mencionada na alternativa D:, não existe - o que existe é
a função stream_context_create(). Outra opção que não faz sentido é a alternativa B, que menciona
as sessões. Portanto as respostas corretas são: A e C.
Para inspecionar as opções vigentes em um determinado stream, utilize a função stream_context_-
get_options().
Uma função correlacionada é a stream_context_set_default() que configura um contexto especí-
fico independente da operação que venha a ser realizada (no âmbito de streams, obviamente) como,
por exemplo fopen() e file_get_contents(). A sintaxe é a mesma de stream_context_create().
Ex.:
Simulado
1.
I/O (Entrada e Saída) 247
Qual opção vai configurar o tempo de espera (timeout) de leitura da stream para \
10 segundos?
A: ini_set("default_socket_timeout", 10);
B: stream_read_timeout($stream, 10);
C: Especificar o timeout como 5º parâmetro na função fsockopen(), utilizada para\
abrir a stream
D: stream_set_timeout($stream, 10);
E: Nenhuma das alternativas
2.
Qual é o método recomendado para copiar dados entre dois arquivos abertos?
A: copy($arquivo_origem, $arquivo_destino);
B: copy($arquivo_destino, $arquivo_origem);
C: stream_copy_to_stream($arquivo_origem, $arquivo_destino);
D: stream_copy_to_stream($arquivo_destino, $arquivo_origem);
E: stream_bucket_prepend($arquivo_origem, $arquivo_destino);
3.
A: tcp
B: udp
C: udg
D: pdc
E: unix
4.
Qual dos seguintes trechos de código NÃO vai escrever o exato conteúdo do arquiv\
o "origem.txt" para "destino.txt"?
5.
A: feof()
B: fflush()
C: fseek()
D: fnmatch()
Respostas do simulado
1.
Resposta é D: stream_set_timeout($stream, 10);.
2.
Primeiramente observe que o enunciado menciona “arquivos abertos”. Logo, significa que estamos
lidando com recursos, então o mais indicado é utilizarmos funções de streams.
Sobram as alternativas C e D, mas a assinatura da função D está incorreta (a ordem dos parâmetros é
invertida). O correto é C: stream_copy_to_stream($arquivo_origem, $arquivo_destino).
3.
O PHP suporta inúmeros protocolos de transporte de redes como TCP, UDP, SSL, TLS, Unix e UDG.
Para averiguar a lista completa na sua instalação PHP, use stream_get_transports().
Assim sendo a opção não suportada é D: pdc. PDC significa Personal Digital Cellular Telecommu-
nication System⁸¹.
4.
⁸¹http://ieeexplore.ieee.org/document/497088/
I/O (Entrada e Saída) 249
Segundo o enunciado, alternativa correta é o snippet de código que não deve funcionar, portanto
alternativa B é a opção que deve ser marcada. A função readfile() devolve o número de bytes
contidos no arquivo origem.txt (e não o seu conteúdo).
5.
A opção correta é A: feof(), pois essa função testa o handler vasculhando pelo fim do arquivo.
Mais detalhes em: http://php.net/feof.
Strings e Padrões
Uma string é um conjunto de caracteres alfanuméricos e pode ser criada de 4 formas:
Aspas (quoting)
Quando encapsulamos strings em aspas simples representam alfanuméricos simples (sem substitui-
ção), de uso literal.
Ao encapsular em aspas duplas trata o conteúdo como strings complexas e permite escape de
caracteres e substituição de variáveis. Veja alguns exemplos:
1 $quem = "Mundo";
2 echo "Olá $quem\n"; //Exibe "Olá Mundo" seguido de nova linha
3 echo 'Olá $quem\n'; //Exibe "Olá $quem\n"
1 $familia = "Stopassola";
2 $nomes = array ("Ari", "Ari Junior", "Ari Neto");
3 echo "Dinastia dos {$familia}s!"; //Dinastia dos Stopassolas!
4 echo "Referência {$nomes[1]}[1976]"; //Referência Ari Junior[1976]
Comparação
Para compararmos strings, podemos utilizar operadores e funções:
Operador Descrição
== Comparação incluindo uma conversão prévia dos dados (Type Juggling)
=== Comparação incluindo checagem de tipo de dados
Strings e Padrões 251
Funções Descrição
strcmp() Comparação de strings considerando minúsculas e maiúsculas (case-sensitive)
strcasecmp() Comparação de strings desconsiderando minúsculas e maiúsculas (case-insensitive)
Os operadores já são nossos conhecidos, mas as funções str*cmp() merecem uma atenção especial:
se o retorno for 0 significa que as strings são iguais. Se o resultado for algo diferente de 0 então se
distinguem. Ex.: strcmp($a, $b) + strcmp($b, $a) == 0.
A: 0
B: 1
C: Não é possível determinar sem conhecer os valores de $a e $b
D: Parse error
Outra função peculiar é a levenshtein() que mostra o número mínimo de caracteres que você deve
substituir, inserir ou apagar para transformar uma string em outra:
1 $a = "censo";
2 $b = "senso";
3 echo levenshtein($a, $b); //1
E também o inverso, ou seja, a similaridade entre os dois textos. A função similar_text() retorna
o número de caracteres que se igualam:
⁸²http://www.asciitable.com
Strings e Padrões 252
O PHP dispõe de funções fonéticas para uso em processamento de linguagem natural e aplicações
de soletração.
A função metaphone() indica como soa uma string se pronunciada por uma interlocutor no idioma
inglês. Gera um índice de como se dá a fala daquela string. A função metaphone() cria a mesma
chave para palavras com sons semelhantes:
Podemos contar strings através de strlen(), diferentemente de count() que é uma função específica
para trabalhar com arrays.
Na realidade a função strlen() conta bytes. Então se você contar caracteres codifica-
dos como UTF-8, terá um resultado diferente do esperado. Ex.: echo strlen("řßů�");
//Resulta em 9.
Eventualmente necessitamos contar palavras, então o PHP nos oferece a função str_word_count(),
extremamente útil.
Extração
A função substr(), como o nome sugere, extrai parte da string. O primeiro argumento é a própria
string, o segundo parâmetro é a posição desejada que começa o pedaço (iniciando na posição zero)
e a terceiro parâmetro (opcional) é o tamanho que se pretende percorrer (length).
Strings e Padrões 253
Quando a posição de começo é identificada com um número negativo, a função conta a partir do
fim da string. Quando o tamanho for negativo, indica quantos caracteres serão suprimidos do final.
Se não delimitássemos a porção desejada, usando apenas substr("123456", -4), o resultado seria
3456. O -2 de length despreza os últimos dois caracteres (56) retornando apenas 34 - que é a resposta
que a questão espera.
No código a seguir, quando substr() recebe um número negativo como posição inici\
al, com um limite, qual a saída?
<?php
echo substr("Zend Certified PHP Engineer", -1, 20);
A: "Zend Certified PHP Engineer" porque o número limite (tamanho) é o mais signi\
ficativo
B: "r" porque o segundo parâmetro é negativo e inicia a contagem do fim
C: false, pois o índice da string inicia por 0 e não -1
D: false, pois substr() não aceita posição inicial como número negativo, tão pou\
co o tamanho deve ser maior que a própria string
A letra A está incorreta, pois a posição é o parâmetro mais importante na extração da sub-string.
Alternativas C e D estão numa mesma categoria de respostas e devolvem false, mas já podem
ser descartadas imediatamente, já que a função substr() é flexível com relação aos parâmetros
e trabalha mesmo que o tamanho exceda a própria string.
Opção B é a resposta correta, já que o argumento -1 indica uma posição partindo do fim da string,
sendo que o parâmetro 20 nem teria efeito diante da posição indicada.
Busca
A função strpos() é uma forte candidata a cair no exame. Ela retorna a posição da primeira
ocorrência da string que se pretende procurar, na documentação chamada comumente de needle
(agulha), num bloco de textos maior chamado de haystack (palheiro).
A sintaxe é strpos(palheiro, agulha) e caso não encontre a “agulha no palheiro”, a função retorna
false. A função permite informar um terceiro argumento (opcional) caso queira ignorar a checagem
de caracteres iniciais ou finais do haystack. Esse terceiro parâmetro é chamado de offset definido
por um número inteiro negativo - recurso adicionado na versão 7.1 do PHP.
?>
A: false
B: 0
C: "0"
D: array(0, 6)
A resposta é B: 0. Observe que é zero como número inteiro! Importante lembrar que a função
strpos() pode retornar 0 e significa que encontrou a string logo na primeira posição - que é
diferente de false.
Nas versões < 7.1 tínhamos problemas em utilizar o offset negativo, para restringir a busca e extração.
Ex.:
No primeiro exemplo, ele simplesmente não encontra a string linda, que nitidamente consta no
texto. A mensagem apresentada era: Warning: strpos(): Offset not contained in string.
1 $dinastia = "Junior";
2 echo $dinastia[-1];
O exemplo acima é outro caso onde o offset configurado como -1 também não funciona, gerando
a mensagem: Notice: Uninitialized string offset. Ambos os problemas fora devidamente
sanados na versão 7.1.
Saindo do contexto de substring, uma função para conversão de string em array, que aparece
regularmente no exame, é explode(string de divisão, string). Ex.:
E o processo inverso, de conversão de array em string, também aparece no teste e possui a seguinte
sintaxe: implode(string de junção, array). Ex.:
A resposta é dada num campo aberto, considerada muitas vezes como o tipo de resposta mais difícil.
A função explode() baseia-se numa string de divisão (nesse caso formada por . ponto) para formar
o novo array.
A função implode() tem uma característica curiosa: a mudança na ordem dos parâmetros não
influencia na execução da mesma. Significa que você pode executar, por exemplo, implode('
', array('Ari', 'Ariel', 'Ariane')) ou implode(array('Ari', 'Ariel', 'Ariane'), ' '),
teremos o mesmo resultado - sem gerar erro.
Voltando a questão, perceba que o . aparece várias vezes. Nesse caso o explode() extrai o conteúdo
que vem antes e depois, mesmo que seja vazio - portanto a resposta é 10. Ex.:
<?php
function count($a, &$b) {
if($a > $b) {
count($a--, ++$b);
return;
}
Strings e Padrões 256
}
$a = 5;
$b = 2;
count($a, $b);
?>
Uma construção “diferente”, em minha opinião, é o return vazio. Outro detalhe é a passagem de
valor por referência. Mas a malícia dessa pergunta é o próprio nome da função que colide com a
função nativa count() do PHP e, portanto, não permite que seja redeclarada. Nesse caso tens que
preencher no campo aberto: Fatal error.
Substituição
A função strstr() localiza a primeira ocorrência de uma string e devolve o restante a partir dela
(incluindo ela própria). Ex.:
1 $email = "ari@certificamp.com";
2 $dominio = strstr($email, '@');
3 echo $dominio; //Exibe "@certificamp.com"
Abaixo segue um exemplo da variação dessa mesma tarefa, porém sem distinção entre maiúsculas
e minúsculas:
1 echo str_replace(
2 array("Hello", "World"),
3 array("Olá", "Mundo"),
4 "Hello World"); //mostra Olá Mundo
5
6 echo str_ireplace(array("tchau", "arrivederci"), "Bye", "Tchau arrivederci");
7 //mostra Bye Bye
Obs.: a função str_replace() e str_ireplace() aceitam um quarto parâmetro que guarda o número
de trocas efetuadas.
O terceiro parâmetro pode ser substitído por um número inteiro que indica a posição onde começará
a troca. Nesse caso, substitui o conteúdo da posição 4 em diante (preservando o Cert):
Se o terceiro argumento for numérico, é possível passar um quarto parâmetro que delimita a
substituição. No exemplo abaixo inicia na posição 15 e avança 3 caracteres:
A transliteração troca caracteres por outros, letra por letra. A função que realiza essa tarefa é
strtr() .Ex.:
1 $palheiro = "abcdefg";
2 $agulha = "abc";
3 if(strpos($palheiro, $agulha) !== false){
4 echo "Encontrou";
5 }
Observe que abc aparece logo na primeira posição, portanto o índice é zero. Daí justifica o uso do
idêntico !== pois 0 seria interpretado como bool(false), via o type casting automático.
Essa função possui variantes. Uma para buscas case-insensitive e outra para percorrer na ordem
reversa. Ex.:
Formatação
Além da construção de linguagem echo, teremos várias outras funções com propósitos semelhantes,
que oferecem a formatação dos dados ao exibir.
Funções Descrição
print() Envia ao navegador um conteúdo que é o único argumento da função
printf() Escreve um conteúdo substituindo valores, baseados num modelo chamado string de formatação (que é o pr
sprintf() Retorna a string resultante após a interpolação da string de formatação com seus respectivos valores
vprintf() Mostra o conteúdo utilizando um array com os valores à serem substituídos (no segundo parâmetro), ao invés
vsprintf() Utiliza um array para realizar a interpolação e retorna a string, assim como faz sprintf()
fprintf() Direciona o conteúdo devidamente formatado para uma stream específica, que pode ser um arquivo ou qualq
É válido ressaltar que sprintf() retorna uma string formatada e que deve ser exibida através de
outra função, enquanto que printf() mostra o conteúdo diretamente:
Funções Descrição
% mostra o caracter %
%b binário
%d decimal
%nd número de dígitos
%e notação científica
%f ponto flutuante
%nf números decimais
%o octal
%s string
%x hexadecimal de a-f
%X hexadecimal de A-F
PCRE
Expressões regulares é uma técnica para descrever padrões em strings.
Permite examinar strings baseando-se numa estrutura genérica, altamente poderosa, em vista disso
com certo custo de performance.
A título de curiosidade, a linguagem PHP suportava dois tipos de expressões regulares:
• POSIX (que não cai na versão corrente do exame, pois foi totalmente removida na versão 7.0
do PHP)
• PCRE - Perl Compatible Regular Expression
• Literal que pode ser qualquer caracter (/abcde/ espera abcde) ou uma barra invertida para
escapar a barra. Ex.: /Gramado\/RS/;
• Caracteres de bordas que indicam exatamente os limites onde o padrão deve começar a
procura:
– ^ começo da linha;
– $ fim da linha;
– \A começo da string;
– \Z fim da string;
– \b começo ou fim da palavra;
– \B nem no começo e nem no fim da palavra.
Ao invés de utilizar colchetes e a lista de caracteres desejados, podemos utilizar classes de caracteres
pré-definidas internamente:
Quantificadores Descrição
* qualquer número de vezes
+ uma ou mais vezes
? zero ou uma vez
{n} n vezes
{n,} pelo menos n vezes
{,m} no máximo m vezes
{n,m} pelo menos n vezes e no máximo m vezes
Como disse o autor Aurélio Marinho Jargas ⁸³, o asterisco é um meta-caracter “guloso”. Ou seja: ele
compara o máximo de conteúdo que puder. Ex.:
1 $conteudo = "<p>Currículo:<ul><li>ZCPE</li><li>ZFCA</li><li>CSM</li></ul></p><p>\
2 Bio</p>";
3 $padrao = "/<.*>/";
4 $comparacoes = array();
5 $achou = preg_match_all($padrao, $conteudo, $comparacoes);
6 echo ($achou) ? "ACHOU" : "NÃO ACHOU";
7 echo "\n";
8 var_dump($comparacoes);
Ao confrontar o padrão /<.*>/ com $conteudo, a função preg_match_all() vai retornar absoluta-
mente tudo: <p>Currículo:<ul><li>ZCPE</li><li>ZFCA</li><li>CSM</li></ul></p><p>Bio</p>,
pois o padrão realmente se confirma.
Para aumentarmos a granularidade, basta adicionarmos o sinal de ? ao asterisco (ou ao sinal de
positivo), transformando o padrão em /<.*?>/. Nesse caso teremos como resultado:
1 (
2 [0] => <p>
3 [1] => <ul>
4 [2] => <li>
5 [3] => </li>
6 [4] => <li>
7 [5] => </li>
8 [6] => <li>
9 [7] => </li>
10 [8] => </ul>
11 [9] => </p>
12 [10] => <p>
13 [11] => </p>
14 )
⁸³http://aurelio.net
Strings e Padrões 262
Como utilizar um trecho de código HTML para ser usado como pattern numa Expressã\
o Regular?
A: preg_split
B: preg_quote
C: ereg_replace
D: strip_tags
Sabemos que determinados caracteres tem efeitos específicos na construção de uma expressão
regular, chamados de metacaracteres, como é o caso de: . \ + * ? [ ^ ] $ ( ) { } = ! < > |
:.
No exemplo acima, estamos usando como delimitador o / (barra) no início e fim da expressão regular,
mas nada impede de utilizarmos outros caracteres. Observe que estamos passando a / como segundo
parâmetro de preg_quote() para escapar inclusive esse caractere, caso o encontre. Pois se não o
fizéssemos, o /p do pattern seria entendido como fim da RegExp e provocaria erro, já que não existe
um modificador p.
A sintaxe utilizada para aprofundarmos a busca (de modo que ela seja menos “gulosa”) seria,
por exemplo, /expr1(expr2|expr3)expr4/ - sendo que o PHP permite posteriormente o acesso ao
conteúdo dos parênteses, mais internos.
Para confrontar um padrão, identificar e extrair apenas o desejado, utilizamos a família de funções
preg_*(). A principal delas é preg_match(padrão, string) que retorna 1 caso encontre o padrão,
0 caso não encontre ou false se a expressão regular estiver com erro:
Strings e Padrões 263
A alternativa A tenta localizar o caracteres H ou P. Ele até encontrará o termo Rumo à certificação
P, mas faltará o HP final. A alternativa C vai confrontar Rumo à certificação PPH, nessa ordem,
pois utiliza os quantificadores. A opção E utiliza o $ como delimitador, vasculhando a partir do
fim, que não condiz.
Portanto as alternativas corretas são B e D, lembrando que o metacaracter i indica case-insensitive,
sendo asim php (minúsculo) bate com o PHP (maiúsculo).
Outra função extremamente útil, da família da expressões regulares, é a preg_match_all(padrão,
texto, comparacoes_encontradas). Essa função retorna todas as comparações possíveis, embora
seja possível associar em um array enviado como terceiro parâmetro.
Para substituição temos a preg_replace(padrao_de_busca, padrao_de_substituicao, texto),
onde \0 faz referência a comparação completa, a expressão \1 é o primeira sub-correspondência,
\2 a segunda e assim por diante.
Há duas formas de substituição, usando a função preg_replace():
1 echo preg_replace("/PHP \d\.\d/", "PHP 7.1", "A versão vigente é PHP 5.5");
2 //exibe "A versão vigente é PHP 7.1"
A função preg_match() estanca assim que encontra a primeira equiparação, enquanto que a função
preg_match_all() continua vasculhando recursivamente e traz todas as combinações possíveis.
Strings e Padrões 264
A função callback chamada de funcao() recebe o array de combinações bem sucedidas e devolve a
string modificada. Nesse caso certificamp ari junior.
<?php
echo preg_replace(
array("∞", "º", "≠"),
array("infinito", " graus", "diferente"),
"A temperatura de 5º, de 0 a ∞ onde x é ≠ de y.");
A alternativa correta é uma mensagem de Warning (opção D), não relacionado aos caracteres
especiais, mas sim pela falta de delimitadores do padrão.
Se alterássemos a linha array("∞", "º", "≠") por array("/∞/", "/º/", "/≠/") funcionaria
perfeitamente, trazendo o resultado A temperatura de 5 graus, de 0 a infinito onde x é di-
ferente de y..
Vários conjuntos de caracteres podem ser representados através de codificação singlebyte, baseada
em valores de 8-bit. Ex.: idiomas latinos.
Consequentemente outros idiomas exigem mais bits para representá-los, como é o caso do chinês
por exemplo, por conter inúmeros ideogramas.
Dependendo do contexto, funções como substr() e strlen() não funcionam adequadamente.
Então faz-se necessário o uso de funções equivalentes para trabalhar em multibyte. Ex.: funções
mb_substr() e mb_strlen().
O módulo mbstring trabalha com unicode (UTF-8 e UCS-2), que não é padrão na instalação do PHP
e precisa ser habilitado no momento do config, quando compilado.
Uma função bastante util é mb_check_encoding() que verifica se a string é válida perante um
encoding específico.
Strings e Padrões 265
Observe o conteúdo UTF-8 que será igualado com uma expressão regular:
<?php
$conteudo = '6789, áéíóú äëïöü';
echo preg_match('/^\d{4},[\s\p{L}]+$/', $conteudo);
A: s
B: u
C: g
D: i
E: x
Desde a versão 5.1 do PHP, foi incorporado sequências pré-definidas de caracteres. São elas:
Onde {xx} é o tipo de sequência de caracteres como por exemplo: \p{Sm} para símbolos matemáticos,
\p{Lu} para letras maiúsculas (upper case) e várias outras possibilidades http://php.net/manual/pt_
BR/regexp.reference.unicode.php.
Ao executarmos o código, a expressão regular ^\d{4},[\s\p{L}]+$ não faz a match pois a sequência
\p{L} que deveria casar com qualquer caractere áéíóúäëïöü não surtirá efeito. Isso ocorre pois
os símbolos acentuados pertencem ao UTF-8, consequentemente necessitamos de um modificador
especial https://secure.php.net/manual/pt_BR/reference.pcre.pattern.modifiers.php.
O modificador u confere que strings sejam tratadas como charset UTF-8. Sendo assim a resposta
correta é B: u.
HEREDOC e NOWDOC
A sintaxe heredoc é uma forma de atribuir grandes porções de string contendo aspas duplas, sem a
necessidade de escapar tais caracteres.
Imagine que você necessite usar aspas duplas literalmente, tal como elas aparecem. Em condições
normais seria obrigatório concatenar a barra invertida para que as aspas duplas não fossem
consideradas delimitadores de string. Ex.:
Strings e Padrões 266
1 `$texto = "Como disse Steven Wright: "Roubar idéias de uma pessoa é plágio; roub\
2 ar de muitas é pesquisa."";`
Essa expressão gera uma violação de sintaxe e interrompe a execução do script, decorrente da citação
que consta no texto, pois nesse caso gostaria de usar as aspas duplas tal como estão dispostas.
Portanto:
1 `$texto = "Como disse Steven Wright: \"Roubar idéias de uma pessoa é plágio; rou\
2 bar de muitas é pesquisa.\"";`
Heredoc processa strings complexas utilizando tokens como delimitadores. Os tokens são criados
pelo próprio desenvolvedor e jamais podem aparecer no corpo do conteúdo, com penalidade de
quebra na execução (se isso ocorrer).
Inicia com o operador especial <<< seguido pelo token e quebra de linha - nada além disso.
Termina com o token novamente, seguido de ; (ponto-e-vírgula) e quebra de linha.
1 class Saudacao {
2 public $msg = <<<EOT
3 Seja bem-vindo
4 EOT;
5 }
1 $n = "Ari Junior";
2 $mensagem = <<<'FIM'
3 Caro colaborador $n, obrigado pela sua visita.
4 Lembre-se de levar nossa garrafa d'água personalizada.
5 FIM;
6 echo $mensagem;
O código acima exibe: Caro colaborador $n, obrigado pela sua visita. Lembre-se de levar
nossa garrafa d'água personalizada..
Você poder ser levado a crer que a resposta seria a alternativa A por tratar-se de uma sintaxe heredoc,
onde $n é substituído.
Porém há um erro de sintaxe no que diz respeito ao token de fechamento do delimitador EOT. Veja que
ele está afastado por tab ou espaço e isso impacta na interpretação do parser. Portanto a alternativa
correta é D: Parse error.
Codificação
Como visto anteriormente no tópicos sobre expressões regulares, alguns idiomas podem ser repre-
sentados por símbolos singlebyte, que são baseados em valores de 8-bit (limitados em 256 caracteres)
- como é o caso dos idiomas latinos⁸⁴.
⁸⁴https://pt.wikipedia.org/wiki/Lista_de_idiomas_que_adotam_o_alfabeto_latino.
Strings e Padrões 268
Outros alfabetos, porém, precisam de multibyte para serem representados, como é o caso russo,
grego, hebraico, chinês e outros.
Ao operar em strings contendo idiomas multibyte, necessitamos de uma extensão especial chamada
mbstring. Do contrário, os caracteres terão seu formato corrompido e as funções de strings não
funcionarão adequadamente.
Quando se fala em codificação, outro viés refere-se em como tratar caracteres que trafegam entre
conexões de banco de dados, URLs e páginas web.
Pelo fato do PHP interagir com páginas HTML, endereços web e banco de dados, existem funções
que nos ajudam a trabalhar com todos esses formatos.
Tanto o formato HTML, comandos SQL e URLs usam strings, mas diferem nos caracteres que
transitam em diferentes contextos e necessitam de tratamento. Um espaço, quando no contexto
de endereços web, deve ser transformado para %20, enquanto que o < (menor que) em documentos
HTML deve ser escrito em &alt;.
O PHP possui inúmeras funções para lidar com diferentes tipos de codificação.
Você quer permitir que seus usuários enviem código HTML em um formulário, sendo \
que ele será exibido como código, sem afetar o layout da página.
Quais funções você aplicaria ao texto, ao exibi-lo adequadamente?
[ Escolha 2 ]
A: strip_tags()
B: htmlentities()
C: htmltidy()
D: htmlspecialchars()
E: showhtml()
Tais funções serão amplamente debatidas no capítulo “Segurança”, por sua importante contribuição
em alguns ataques cibernéticos clássicos.
Simulado
1.
Strings e Padrões 269
O algoritmo pede que um número qualquer seja preenchido com zeros à esquerda enq\
uanto não alcançar 10 dígitos.
Ex.: 4 transforma-se em 0000000004, 6091 em 0000006091 e assim por diante.
Assinale a alternativa correta, identificando se as funções abaixo são verdadeir\
as ou falsas:
( ) str_pad($numero, 10, "0", STR_PAD_LEFT);
( ) sprintf("%010d", $numero);
A: V e V
B: V e F
C: F e V
D: F e F
2.
Em quantos elementos o array $pedacos irá conter depois que essa linha de código\
for executada?
$pedacos = explode("/", "///");
A: 3
B: 5
C: 0
D: 4
3.
A: 22
B: 22.00
C: 022.000000
D: 22.000000
4.
Strings e Padrões 270
5.
A: echo $teste(3);
B: echo $teste[2];
C: echo $teste(2);
D: echo $teste{2};
E: echo $teste{3};
Respostas do simulado
1.
Resposta correta é A, pois ambas são verdadeiras.
2.
Resposta é D: 4, pois a função explode() entende que há 4 porções entre as barras /, mesmo que
vazias. Importante lembrar que o conteúdo no início e fim da string também são contabilizados.
3.
Apresenta o número 22 através de 10 caracteres, com preenchimento de zeros a esquerda. Essa
notação é profundamente explicada no tópico “especificadores de string” na documentação oficial
da função sprintf() em: http://php.net/manual/pt_BR/function.sprintf.php.
Strings e Padrões 271
O especificador de tipo f diz que o argumento deve ser tratado como um float, nesse caso com 6
casas decimais.
Então a resposta é C: 022.000000.
4.
A expressão regular @p.n[tg].@ está devidamente delimitada por @ e utiliza apropriadamente os
meta-caracteres.
O padrão requer o caracter p seguido de qualquer coisa (definido pelo ponto .) sucedido por n.
Posteriormente podemo ter o caracter t OU g, seguido de qualquer coisa.
Assim sendo, a alternativa correta é C: pinto pinga, pois as demais palavras não seguem a mesma
combinação.
5.
As opções A e C tentam invocar uma função dinâmica e geram um Fatal error. Call to
undefined function.
SQL
Abaixo segue um exemplo de SQL genérico (ANSI) para criação de uma tabela chamada auditoria:
Ao executar esse código sob um banco de dados previamente criado, você irá construi uma tabela
chamada auditoria com o campo inteiro id que será auto incrementado (em uma unidade, que é o
comportamento padrão) a cada inserção, seguido do campo dia em formato DATE representado por
yyyy-mm-dd, campo nome que suporta 200 caracteres alfanuméricos, campo descricao que comporta
grandes porções de texto e o campo ativo que armazena um valor booleano.
Para ordenação de dados de uma consulta SQL, utilize a cláusula ORDER BY seguido do nome do
campo. A ordenação pode ser crescente ou decrescente:
Função Descrição
AVG() Valor da média
COUNT() Número de elementos
DISTINCT COUNT() Número de elementos distintos
MIN() Valor mínimo
MAX() Valor máximo
SUM() Soma dos valores
Junções (Joins)
Dados duplicados desperdiçam espaço, exigem manutenção, provocam perdas e geram inconsistên-
cias.
Dados podem (e devem) estar desmembrados entre diferentes tabelas, através do processo de
normalização de dados (que veremos sucintamente mais adiante), que é uma estratégia para reduzir
o tamanho do BD e facilitar as manutenções.
Observe a ilustração abaixo onde temos a tabela clientes, sendo que o campo hotel armazena o
nome do hotel explicitamente.
Digamos que, porventura, o nome do hotel venha a ser modificado ou mesmo tenha sofrido algum
erro de digitação: na modelagem anterior seria preciso alterar todos os registros, sem falar no
desperdício de espaço tendo que armazenar uma mesma string inúmeras vezes.
Evoluindo um pouco mais na modelagem, percebemos que podendo guardar apenas um código no
campo hoteis_id, desmembrando esse dado em outra tabela, que podemos chamar de Hoteis.
Banco de Dados 275
Na ilustração acima veja que agora temos ainda mais informações a respeito do hotel (como o campo
telefone, por exemplo), melhorando a organização através dessa separação.
O fato de “um hotel hospedar vários clientes” caracteriza uma relação 1:N (lê-se um-para-ene).
Outra relação bastante usual é quando uma entidade vincula outra entidade uma única vez, como
por exemplo “cada colaborador trabalha para uma empresa parceira”. Abaixo segue um exemplo de
como seria o diagrama da relação 1:1 (lê-se um-para-um):
Lembrando que fazer relacionamentos de tabelas para obter os dados corretamente pode ser custoso
computacionalmente, caso os dados estejam muito segmentados (espalhados).
Nos casos que vimos anteriormente, como as relações 1:1 e 1:N, é cenário típico que requer consultas
utilizando JOIN, que literalmente “juntam” dados normalizados (em diferentes tabelas), para formar
a entidade que se deseja obter.
Banco de Dados 276
Obs.: esse assunto, embora co-adjuvante, é corriqueiro no exame. Abaixo segue uma representação
visual das junções SQL, inspirado no artigo Visual Representation of SQL Joins de Christopher
Moffatt @clmoffatt⁸⁶.
Abaixo seguem os principais tipos de junção (join) entre duas ou mais tabelas, com o seus respectivos
diagramas inspirado na teoria dos conjuntos:
A junção LEFT JOIN obtém dados da tabela A mesmo que não haja correspondência em B. Ex.:
LEFT JOIN
A junção RIGHT JOIN obtém dados da tabela B mesmo que não haja correspondência em A. Ex.:
⁸⁶https://twitter.com/clmoffatt
Banco de Dados 277
RIGHT JOIN
Outra variação de LEFT JOIN é obter dados da tabela A somente quando houver correspondência
em B. Ex.:
1 SELECT campos FROM tabelaA as A LEFT JOIN tabelaB as B ON A.campo = B.campo WHER\
2 E B.campo IS NULL
Banco de Dados 278
A junção INNER JOIN obtém dados da tabela A e B somente quando houver correspondência
simultânea. Ex.:
INNER JOIN
Outra variação da junção RIGHT JOIN é obter dados da tabela B somente quando houver correspon-
dência em A e vice-versa. Ex.:
1 SELECT campos FROM tabelaA as A RIGHT JOIN tabelaB as B ON A.campo = B.campo WHE\
2 RE A.campo IS NULL
Banco de Dados 279
A: 3
B: 5
C: 9
D: 10
A consulta LEFT JOIN prioriza a tabela da “esquerda”, àquela que foi referenciada primeiro. Ou
seja: permite que a tabelas nomes retorne linhas mesmo que não haja equivalência na outra tabela
(emails).
O retorno da consulta será 5 tuplas, como visualizado abaixo, portanto a alternativa B é a resposta
correta:
+--------+------------------------+
| nome | email |
+--------+------------------------+
| Ari | ari@certificamp.com |
| Neto | NULL |
| Paloma | paloma@certificamp.com |
| Junior | NULL |
| Laci | laci@certificamp.com |
+--------+------------------------+
Outro tipo de relacionamento é o N:M (lê-se ene-para-eme). Como exemplo, no domínio do turismo,
“pacotes têm passeios e o mesmo passeio compõe vários pacotes”. Veja abaixo como seria o diagrama
Entidade Relacionamento – ER:
Banco de Dados 281
Normalização
Este é um tópico bônus, mas é um assunto que justifica o uso de JOINs e considero válido darmos
uma repassada.
O processo de normalização é:
Na Primeira Forma Normal (1FN) a 1ª regra declara que nenhuma tabela deve repetir colunas que
contenham os mesmos tipos de dados.
Banco de Dados 282
Ainda na 1FN, a 2ª regra diz que devemos desmembrar em mais campos para evitar dados repetidos
e misturados com outras informações:
A Terceira Forma Normal (3FN) elimina ainda mais dados redundantes ao custo de simplicidade e
performance.
No exemplo abaixo, se você espera que os campos endereço e cidade sofram alterações com
frequência, podem haver erros de soletração e comprometer a consistência dos dados – portanto
é preferível separá-los:
Declarações preparadas
As prepared statements são moldes de instruções SQL que subtituem valores de entrada do usuário.
É definido um espaço reservado (placeholders) para valores armazenados em variáveis, prevenindo
ataques SQL injection e mais eficiente quando a mesma query é reutilizada.
Banco de Dados 284
Os placeholders não podem ser usados para nomes de colunas ou operadores, sendo que valores
não-numéricos são automaticamente encapsulados entre aspas. Ex.:
Transações
É o conjunto de queries que devem ser executadas juntas, sendo que a operação se consolida apenas
se todas as queries do conjunto foram bem sucedidas.
Transações podem ser desfeitas se ocorrer algum erro. Essa operação é chamada de rollback.
Executar instruções SQL encapsuladas em transações é fundamental para operações críticas.
Previne que linhas sejam modificadas por outras conexões em aberto, que porventura estejam
manipulando as mesmas tabelas.
Banco de Dados 285
A: performance
B: disponibilidade
C: consistência
D: integridade
26 $recebe->execute();
27 if(!$recebe->rowCount()){
28 $db->rollBack();
29 $erro = "Falha na transação: não atualizamos a conta do ".$recebedor;
30 }
31 else {
32 $db->commit();
33 }
34 }
35 //Exibe o erro, caso exista
36 if(isset($erro)){
37 echo $erro;
38 }
39 }
• Encapsula operações no BD, iniciando por beginTransaction(), para que sejam obrigatoria-
mente executados na íntegra, até o método commit();
• Caso alguma cláusula SQL falhe, o banco mantém-se consistente justamente pelas operações
realizadas até então serão sumariamente desfeitas através do método rollBack();
• Provê isolamento quando aplicações acessam de forma concorrente;
• Executa “tudo ou nada” conhecido como o princípio da atomicidade;
• Certifique-se de que o BD suporte transações, pois o PDO não fornece esse recurso por si só;
• MyISAM ignora (silenciosamente) comandos de transações. Verifique se a estrutura do
MySQL baseia-se em InnoDB.
Atenção: MySQL faz commit automático para comandos DDL: CREATE, ALTER e DROP.
Considere a seguinte tabela contendo vários registros e assuma que o banco de da\
dos suporte transações. Qual o resultado do código PHP a seguir?
+----+--------+------------------------+
| id | nome | email |
+----+--------+------------------------+
| 1 | Ari | ari@certificamp.com |
| 2 | Neto | neto@certificamp.com |
| 3 | Paloma | paloma@certificamp.com |
| 5 | Laci | laci@certificamp.com |
+----+--------+------------------------+
(Assuma que a conexão PDO está devidamente estabelecida)
$dsn = 'mysql:host=localhost;dbname=bancodedados';
$usuario = 'login';
$senha = 'senha';
$pdo = new PDO($dsn, $usuario, $senha);
try {
$pdo->exec("INSERT INTO usuarios (id, nome, email) VALUES (6, 'Ariel', 'ariel@c\
ertificamp.com')");
$pdo->beginTransaction();
$pdo->exec("INSERT INTO usuarios (id, nome, email) VALUES (7, 'Laura', 'laura@c\
ertificamp.com')");
throw new Exception();
} catch (Exception $e) {
$pdo->rollBack();
}
PDO
PDO⁸⁷ é a abreviatura de PHP Data Object e provê uma camada de acesso unificada através de uma
interface simples e orientada a objetos.
Abstrai o acesso aos dados e não propriamente o banco de dados, pois não reescreve SQL, tão pouco
emula recursos ausentes em determinado BD. Ou seja: diretivas SQL específicas não são traduzidas.
A classe PDO vem instalada por padrão, mas as extensões e drivers de um BD específico devem ser
incorporadas de acordo com o tipo de banco que será utilizado.
Objetos PDO e MySQLi⁸⁸ conectam em SGBDs e seus métodos interagem com o banco de dados,
devolvendo outros objetos eventualmente. Ex.: $resultado = $conexao->query($sql);.
As constantes das classes PDO e MySQLi são geralmente usadas como argumentos de métodos. Por
exemplo: PDO::FETCH_ASSOC e MYSQLI_ASSOC.
Grande objetos⁸⁹, como volumes enormes de texto e arquivos binários, também podem ser armaze-
nados via PDO. Então a alternativa A é verdadeira e não condiz com o enunciado. Perceba que ele
pergunta qual é a alternativa falsa.
O controle de exceções em PDO pode ser tratado através de uma classe específica chamada
PDOException⁹⁰, então a alternativa C também é verdadeira.
Como já falamos no início desse tópico, a classe PDO não emula um recurso que porventura o banco
de dados em questão não possua, então a opção D também é verdadeira.
⁸⁷http://php.net/manual/pt_BR/book.pdo.php
⁸⁸MySQL Improved http://php.net/manual/pt_BR/book.mysqli.php.
⁸⁹http://php.net/manual/pt_BR/pdo.lobs.php
⁹⁰http://php.net/manual/pt_BR/class.pdoexception.php
Banco de Dados 289
E os placeholders, em declarações preparadas, não precisam ser nomeados quando usados via ? (sinal
de interrogação). Então a alternativa B é inválida, consequentemente será a alternativa “correta”
segundo o enunciado.
Abaixo seguem alguns exemplos de código no contexto de orientação à objetos:
A primeira ação é definirmos a conexão com o banco de dados. Ela é feita através da instrução
Database Source Name (DSN), que pode variar a sintaxe dependendo do tipo de BD em uso. Ex.:
Obs.: com exceção do DNS, todo o restante do código PDO apresentado nesse tópico é independente
de BD.
Recomenda-se utilizar o controle de exceções para encapsular código, evitando que o script exiba
informações em excesso ou falhe abruptamente.
Se houver erro de conexão ou execução de query inválida, será lançado (thrown) um objeto
PDOException (derivado da classe Exception, nativa do PHP). Dessa forma consegue-se manipular
o erro utilizando um bloco de código try/catch:
1 try {
2 $dsn = "mysql:host=localhost;dbname=oophp";
3 $db = new PDO($dsn, "usuario", "senha");
4 }
5 catch (PDOException $e){
6 $erro = $e->getMessage();
7 }
Assim que a conexão foi bem sucedida, podemos experimentar uma busca de resultados, conhecida
como fetching. Ex.:
Método Descrição
fetch() Obtém o próximo registro do resultado
fetchAll() Cria um array contendo todas as tuplas
fetchColumn() Obtém uma única coluna do registro seguinte
fetchObject() Pega a próxima linha como objeto
Banco de Dados 290
Exemplificando o método fetch(), abaixo segue uma consulta simples por nome e cidade da tabela
clientes - considerando que a conexão em $db foi previamente estabelecida:
Outro métodos interessante é o fetchAll() que monta o array resultante associando o nome do
campo e sua respectiva ordem de consulta, usados como índice:
Caso queira condensar o resultado e montar um array associativo apenas com o nome dos campos,
basta utilizar a constante PDO::FETCH_ASSOC como parâmetro do método fetchAll(). Ex.: $tudo =
$res->fetchAll(PDO::FETCH_ASSOC);. Veja o resultado da consulta:
Banco de Dados 292
Obs.: para utilizar somente índices numéricos, baseados na ordem de consulta dos campos, basta
utilizar como argumento a constante PDO::FETCH_NUM.
Outra forma de consulta é obter uma coluna específica através do método fetchColumn(). Ex.:
A primeira coluna da tabela é vinculada ao índice 0, a segunda é associada a chave 1 e assim por
diante. No exemplo acima, o método fetchColumn() foi invocado sem argumento, então ele assume
o índice 0:
Qual dos seguintes métodos são usados para buscar dados de uma declaração PDO?
[ escolha 3 ]
A: fetchColumn()
B: fetchObject()
C: fetch()
Banco de Dados 294
D: fetchClass()
E: fetchRow()
Antes de respondermos essa questão, é importante relembrar as várias formas de obter resultados
de uma consulta através da classe PDO, tais como os métodos fetch(), fetchAll(), fetchColumn()
e fetchObject().
Portanto as alternativas corretas são A, B e C, pois os métodos fetchClass() e fetchRow()
simplesmente não existem na classe PDO⁹¹.
Um modo interessante, essencialmente diferente dos outros, é com o uso de PDO::FETCH_BOUND que
não retorna dados por si só, mas faz com que a classe PDO atribua valores à variáveis que foram
previamente vinculadas através do método bindColumn(). Ex.:
1 $dsn = "mysql:host=localhost;dbname=zcpe";
2 $pdo = new PDO($dsn, 'login', 'senha');
3 $cmd = "SELECT * FROM usuarios WHERE id=:id";
4 $stmt = $pdo->prepare($cmd);
5 $id = 2;
6 $stmt->bindParam(':id', $id); //também aceita 'id', sem os dois pontos
7 $stmt->execute();
8 $stmt->bindColumn(3, $resultado);
9 $stmt->fetch(PDO::FETCH_BOUND);
10 //$resultado recebe o conteúdo da terceira coluna do registro com id = 2
Mas é fundamental dizer que o método rowCount() não é 100% portável. Ou seja: não funciona, por
exemplo, no banco de dados SQLite.
⁹¹http://php.net/manual/pt_BR/book.pdo.php
Banco de Dados 295
A conexão com o banco permite que sejam executadas cláusulas SQL a partir dos métodos query()
e exec(). A diferença é que o método query() devolve um objeto que contém um atributo
queryString, enquanto que o método exec() devolve a quantidade de linhas manipuladas pela
cláusula SQL em questão.
Para realizar o controle de erros e agilizar o debug, existem métodos da classe PDO e da classe
Exception que vão lhe auxiliar, respectivamente errorInfo() e getMessage():
Ao realizar consultas SQL eventualmente precisaremos tratar strings colocando aspas no início e
fim. Ex.: Ari transforma em 'Ari', d'água em 'd\'dágua'e assim por diante.
Para isso podemos utilizar o método quote(), mas ele é mais lento que as prepared statements e não
é suportado por todos os drivers PDO, como é o caso do ODBC.
Banco de Dados 296
1 $busca = "%Ari%";
2 $sql = "SELECT nome FROM usuarios WHERE nome LIKE ".$db->quote($busca);
Aprofundando um pouco mais as prepared statements, temos o método bindValue() que associa
(bind) um valor ao seu respectivo placeholder, utilizando o formato :nome:
1 $perfil = "advogado";
2 $sql = "SELECT nome FROM usuarios WHERE funcao = :perfil";
3 $stmt = $db->prepare($sql);
4 $stmt->bindValue(":perfil", $perfil);
5 $stmt->execute();
6 while($linha = $stmt->fetch()){
7 echo $linha['nome']."<br>";
8 }
Ao invés de passar valor por variável, como mostrado no exemplo anterior, você pode desejar passar
o valor diretamente. O método bindValue() aceita ambos os casos. Ex.:
1 $stmt->bindValue(":perfil", "advogado");
O uso das declarações preparadas nos traz uma série de benefícios, dentre eles o tratamento de
injeção de SQL - fundamentais para a segurança no BD. Essas instruções utilizam espaços reservados
para interpolação de valores. Uma forma de fazê-lo é através de anonymous placeholder que é o sinal
de interrogação ? para demarcar o local que deverá ser substituído.
O índice começa em 1 (um) e vai trocando na ordem em que os ? vão aparecendo na declaração.
Válido mencionar que prepared statement não permite substituições híbridas, que contenham
demarcações anônimas e nomeadas: ? e :nome, respectivamente.
1 $percentual = 10;
2 $sql = "SELECT nome FROM usuarios
3 WHERE percentual > ?
4 AND grupo = ? ORDER BY nome DESC";
5 $stmt = $db->prepare($sql);
6 $stmt->bindParam(1, $percentual);
7 $stmt->bindValue(2, "u");
8 $stmt->execute();
9 while($linha = $stmt->fetch()){
10 echo $linha['nome']."<br>";
11 }
Também é possível realizar interpolação em massa que modifica vários placeholders através de um
único comando. Basta passar o array como parâmetro do método execute(). Ex.:
1 $percentual = 10;
2 $sql = "SELECT nome FROM usuarios
3 WHERE percentual_sociedade > ?
4 AND grupo = ?
5 AND funcao = ?
6 ORDER BY nome DESC";
7 $stmt = $db->prepare($sql);
8 $valores = array($percentual, "a", "advogado");
9 $stmt->execute($valores);
Banco de Dados 298
A classe PDO não tem acesso a cláusula SQL final, devidamente combinada com os parâmetros
($stmt->queryString), pois eles são enviados separadamente para a camada de banco de dados.
A query transformada apenas pode ser visualizada através dos logs do BD.
Abaixo segue um exemplo de interpolação em massa utilizando espaço reservado nomeado (via
sintaxe :nome):
1 $percentual = 10;
2 $sql = "SELECT nome FROM usuarios
3 WHERE percentual_sociedade > :percentual
4 AND grupo = :grupo
5 AND funcao = :funcao
6 ORDER BY nome DESC";
7 $stmt = $db->prepare($sql);
8 $valores = array(":percentual" => $percentual, ":grupo" => "a", ":funcao" => "ad\
9 vogado");
10 $stmt->execute($valores);
Após a execução, é possível associar uma coluna a uma variável através do método bindColumn(),
desde que a coluna desejada conste na cláusula SQL.
A associação pode ser feita pela ordem da coluna ou pelo nome do campo:
1 $percentual = 10;
2 $sql = "SELECT nome,percentual,grupo FROM usuarios
3 WHERE percentual_sociedade > :percentual
4 AND grupo = :grupo
5 AND funcao = :funcao
6 ORDER BY nome DESC";
7 $stmt = $db->prepare($sql);
8 $valores = array(":percentual" => $percentual, ":grupo" => "u", ":funcao" => "ad\
9 vogado");
10 $stmt->execute($valores);
11
12 $stmt->bindColumn(1, $nome);
13 $stmt->bindColumn(2, $prct);
14 $stmt->bindColumn("grupo", $grp);
15
16 while($linha = $stmt->fetch()){
17 echo $nome." ";
18 echo $prct."% ";
19 echo $grp."<br>";
20 }
Banco de Dados 299
Método Descrição
fetch_assoc() Obtém a próxima linha e monta um array associativo, onde o índice é o nome do campo
fetch_row() Obtém a próxima linha e monta um array com índices numéricos (baseado na ordem dos campos)
fetch_array() Permite escolher que tipo de índice se deseja. Argumento: MYSQLI_BOTH, MYSQLI_ASSOC ou MYSQLI_NUM
fetch_all() Cria um array contendo todas as linhas
fetch_object() Obtém a próxima linha como objeto
Outros dois atributos importantíssimos da classe MySQLi são: num_rows que guarda os registros
obtidos e affected_rows que armazena os registros impactados pela consulta:
Reaproveitar uma consulta é de grande valia no intuito de alcançar melhor performance. O método
data_seek() permite retroceder o ponteiro numa respectiva posição e recomeçar a iteração. Ex.:
Banco de Dados 300
A: 12
B: 13
C: 14
D: 15
E: Fatal error
Talvez você tenha levado a crer que a resposta seria D: 15, mas ao invés de aparecer o valor 15
(que seria o último ID inserido), ele só guarda o primeiro inserido da série. No caso o 13, portanto
alternativa correta é B.
Para sanificar dados vindos do usuário, utilize o método real_escape_string() passando tal
conteúdo como argumento. É uma medida paliativa para escapar aspas e evitar SQL injection:
Banco de Dados 301
1 $busca = $db->real_escape_string($_GET['busca']);
2 $sql = "SELECT * FROM veiculos WHERE modelo LIKE '%".$busca."%'";
3 $resultado = $db->query($sql);
4 if($db->error){
5 echo $db->error;
6 }
7 while($linha = $resultado->fetch_assoc()){
8 echo $linha['modelo']."<br>";
9 }
10 }
Para realizar declarações preparadas com a classe MySQLi, é necessário observar alguns detalhes. O
primeiro é o método stmt_init() que cria um objeto statement, que possibilita a interpolação dos
placeholders.
Outra mudança, comparada a classe PDO, é a especificação do tipo de dado na substituição dos espaços
reservados - através do método bind_param(). Ex.:
Equivalente ao método bindParam() da classe PDO, que associa valores à variáveis, temos o método
bind_result(). Ex.:
Banco de Dados 302
Abaixo segue um exemplo de implementação de transações em MySQL Improved, desde que a engine
do banco seja InnoDB. Ex.:
Banco de Dados 303
As consultas através da classe MySQLi podem ser buffered x unbuffered. Isso significa que as queries
“bufferizadas” tem seu resultado armazenado em memória, consequentemente obtém o resultado da
consulta de forma imediata.
• Vantagens: como está em memória, consegue obter o número de linhas resultantes e mover o
ponteiro interno entre o conjunto resultante da consulta;
• Desvantagem: conjunto grande de resultado consome memória.
• Desvantagem: não acessa o número de resultados, nem move-se pelo result set e bloqueia
consultas enquanto outra está trazendo resultados (caso estejam na mesma conexão), gerando
sobrecarga no servidor.
• Queries subsequentes operando em buffer são mais fáceis de manipular pelo SGBD;
• Utilize o modo unbuffered quando a consulta trouxer um grande número de registros;
• Libere recursos quando não forem mais necessários;
• Não há como realizar novas consultas enquanto uma query unbuffered não for totalmente
consumida;
• Consultas “bufferizadas” permitem reuso dos resultados (data_seek).
Banco de Dados 305
Uma observação a respeito desse exemplo é que o método real_query() não devolve o resultado e
sim true ou false. Então é preciso o método use_result() em conjunto.
Para liberar memória (Garbage Collection) vinculada à consulta realizada pelo objeto $resultado
provido pela classe MySQLi, utilize os seguintes métodos. Ex.:
Método Descrição
$resultado->free(); Libera recurso de uma consulta
mysqli_free_result($resultado); Função (procedural)
Para liberar memória associada à consulta realizada (GC) do objeto $stmt provido pela classe
mysqli_stmt (MySQLi Statement), utilize os comandos:
Método Descrição
$stmt->close(); Destrói o objeto statement e cancela resultados pendentes
$stmt->free_result(); Limpa o resultado em memória, porém permite que a declaração seja reaproveitada
Liberar recursos evita a mensagem de erro: Commands out of sync; you can't run this command
now.
16 do {
17 $resultado = $db->store_result();
18 $linha = $resultado->fetch_assoc();
19 echo "<h2>".$linha['modelo']." faz ".$linha['kmporlitro']." Km/litro.</h2>";
20 $resultado->free();
21 } while($db->next_result());
Simulado
1.
+----+--------+------------------------+
| id | nome | email |
+----+--------+------------------------+
| 1 | Ari | ari@certificamp.com |
| 2 | Laci | laci@certificamp.com |
| 3 | Neto | neto@certificamp.com |
| 5 | Paloma | paloma@certificamp.com |
+----+--------+------------------------+
<?php
$dsn = 'mysql:host=localhost;dbname=zcpe';
$login = 'root';
$senha = 'senha';
$pdo = new PDO($dsn, $login, $senha);
2.
Há como ver a query SQL resultante de uma declaração preparada (prepared stateme\
nt)?
A: Sim
B: Não
3.
Considere os seguintes dados de tabela e o código PHP, além de que o banco de da\
dos suporte transações.
A tabela chama-se "usuarios" com chave primária "id".
Assuma que há uma conexão PDO devidamente estabelecida.
$dsn = 'mysql:host=localhost;dbname=zcpe';
$login = 'root';
$senha = 'senha';
$pdo = new PDO($dsn, $login, $senha);
try {
$pdo->exec("INSERT INTO usuarios (id, name, email) VALUES (6, 'Renato', 'renato\
mefidf@gmail.com')");
$pdo->begin();
$pdo->exec("INSERT INTO usuarios (id, name, email) VALUES (7, 'Thiago', 'thiago\
luismo@gmail.com')");
throw new Exception;
}
catch (Exception $e) {
$pdo->rollBack();
}
+----+--------+------------------------+
| id | nome | email |
+----+--------+------------------------+
| 1 | Ari | ari@certificamp.com |
| 2 | Laci | laci@certificamp.com |
| 3 | Neto | neto@certificamp.com |
| 5 | Paloma | paloma@certificamp.com |
Banco de Dados 308
+----+--------+------------------------+
Qual é o resultado?
4.
A função mysqli_affected_rows() pode ser usada para executar quais das seguintes\
ações?
[ escolha 2 ]
A: obter o número de linhas que são afetados após o COMMIT em uma transação
B: obter o número de linhas que são afetados pela declaração INSERT
C: obter o número de linhas que são afetados pela declaração SELECT
D: obter o número de linhas que são afetados pela declaração UPDATE
E: obter o número de linhas em um conjunto de resultados
5.
<?php
$db = new mysqli("localhost", "login", "senha", "Globo");
$db->autocommit(FALSE);
$db->begin_transaction();
$db->savepoint("se_asia");
$db->query("INSERT INTO Paises VALUES ('Vietnã')");
$db->commit();
Banco de Dados 309
$db->close();
?>
Respostas do simulado
1.
O método fetchAll() da classe PDOStatement recebe, como primeiro argumento, o estilo de busca
a ser utilizado. Você pode optar por trazer os dados em um result set onde o índice é o nome do
campo (PDO::FETCH_ASSOC) ou a ordem em que os campos foram consultados (PDO::FETCH_NUM).
A questão utiliza uma outra constante chamada PDO::FETCH_BOTH que traz o resultado da consulta
associando em ambos estilos: via matriz associativa e numérico.
Portanto a resposta correta é C: O valor de $linha é 'Array ( [nome] => Ari [0] => Ari [email]
=> ari@certificamp.com [1] => ari@certificamp.com ).
2.
Resposta correta é B: Não.
A query submetida via prepared statement é tratada a nível de banco de dados, portanto não há
como inspecionar a query SQL resultante via PHP - exceto com bibliotecas de terceiros.
3.
Observe que a primeira chamada do método exec() não está encapsulada numa transação. Como
não há erros de sintaxe, nem infringe alguma constraint de banco de dados, tal query será executada
normalmente - inserindo o “Renato” na tabela usuarios.
Perceba que o segundo SQL está confinado em uma transação, disparado através do método
begin(). Antes que houvesse um commit() foi invocado deliberadamente uma exceção (linha throw
Banco de Dados 310
new Exception). Essa ação foi capturada pela instrução catch que tratou de desfazer, através do
método rollBack(), a inserção de “Thiago” na tabela usuarios.
Portanto a alternativa correta será a letra A: O usuário "Renato" será inserido, mas o usuário
"Thiago" não.
4.
Embora seja considerada uma questão fácil, essa questão pode gerar uma série de dúvidas. Ao
consultar a documentação oficial no endereço <www.php.net> teremos a informação de que a função
mysqli_affected_rows() retorna o número de linhas afetadas pela ultima consulta INSERT, UPDATE,
REPLACE ou DELETE. Portanto B e D são as alternativas corretas.
5.
Resposta é B: Os valores Noruega, Inglaterra e Vietnã serão adicionados a uma nova ta-
bela Paises.
Arrays
Array é a estrutura que armazena uma coleção de dados, organizados em pares chave-valor.
A vantagem do array é que ele pode armazenar quaisquer outros tipos de dados, tais como números
de ponto flutuante, textos, objetos e inclusive outros arrays internamente.
Existem dois tipos de indexação: onde as chaves são compostas por valores inteiros e a indexação
associando strings como chaves - também conhecidos como arrays associativos.
Arrays associativos
Onde a string é o índice associado ao valor:
1 $a = array(
2 'ZCPE' => 'Zend Certified PHP Engineer',
3 'PHP' => 'PHP: Hypertext Preprocessor'
4 );
Arrays enumerados
Onde um número inteiro é o referido índice:
Perceba que na primeira linha, a associação do valor com sua respectiva chave é feita automatica-
mente. Inicia-se no índice 0 (zero), depois 1, 2 e assim sucessivamente.
Na segunda linha, podemos deliberadamente associar um índice ao valor. Nesse caso, a key 0
armazena o valor a, a chave 1 guarda o valor b e 2 o valor c.
Lembrando sempre que jamais pode-se armazenar diferentes valores numa mesma chave. O PHP
sobrescreve o dado com a última associação realizada.
<?php
$myArray = array("Three" => 3, "Two" => 2, "One" => 1);
sort($myArray);
Arrays 312
$keys = array_keys($myArray);
echo $keys[0];
A: echo $a[3];
B: echo array_values($a, 3);
C: echo $a[5];
D: $r = array_reverse($a, true);
echo $r[0];
A: 0
B: 3
C: 13
D: 12
E: Runtime error, pois 7.0 é maior que 5.0
Importante salientar que a função range() funciona também na ordem reversa, do maior para o
menor. Então já se descartam as alternativas A e E.
A alternativa B induz que a função desconsidera decimais (que não é o caso), então também é
descartada.
As opções mais prováveis ficam entre C e D. Nesses casos é importante utilizarmos o erasable board
para não haverem dúvidas. Fazendo as contas teremos 7, 6.84, 6.68, 6.52, 6.36, 6.2, 6.04, 5.88,
5.72, 5.56, 5.4, 5.24 e 5.08. A resposta correta é C: 13, já que existem 13 elementos no array -
sempre contando os elementos de início e fim, inclusive.
Outra função importante é o array_slice(array, offset) que extrai parte do array. O primeiro
parâmetro é o array em questão, o segundo argumento é que elemento começará a sequência
que fatia o array (que aparece na documentação como offset), o terceiro parâmetro (opcional) é
a quantidade de itens que serão percorridos e o quarto item (também opcional) informa se devemos
ou não preservar os índices originais.
1 $a = array(1, 2, 3, 4, 5);
2 $s = array_slice($a, 2); //array(3, 4, 5)
3 $s = array_slice($a, 1, 3); //array(2, 3, 4)
4 $s = array_slice($a, 1, 3, true); //array(1 => 2, 2 => 3, 3 => 4)
A função array_slice() permite que o offset e o número de elementos sejam negativos. O efeito
do offset negativo é semelhante ao comportamento da função substr(): devolve o número de itens
do fim para o início. Ex.: se o offset for -3 retorna os três últimos elementos do array.
Já o length (tamanho) negativo remove o últimos itens do array de acordo com o número
especificado. No exemplo abaixo o tamanho -2 retira os respectivos elementos do fim do array(3,
4, 5), retornando um array contendo apenas o elemento 3.
Arrays 314
1 $a = array(1, 2, 3, 4, 5);
2 $s = array_slice($a, -3); //array(3, 4, 5)
3 $s = array_slice($a, -3, -2); //array(3)
Para adicionarmos itens no array utilizamos a função array_push() que recebe, como primeiro
argumento, o array passado por referência. A variável $n, que aparece no exemplo, recebe o número
de elementos do array resultante (nesse caso 5). O vetor original era array(1, 2, 3) que adicionou
os elementos 4 e 5 ao final, consequentemente o array resultante ficou array(1, 2, 3, 4, 5).
1 $a = array(1, 2, 3);
2 $n = array_push($a, 4, 5); //$n == 5
Uma forma alternativa e mais eficiente de adicionar valores no array é através da construção:
1 $a[] = 4;
2 $a[] = 5;
1 $a = array(3, 4, 5);
2 $n = array_unshift($a, 1, 2); // $n == 5
Já a função array_shift() retira do vetor o primeiro elemento e o devolve como saída da função,
sendo que os valores existentes são movidos para a frente. Em contra-partida a função array_pop()
retira o elemento do fim.
Ações realizadas array_push() array_unshift() array_pop() array_shift()
Adiciona no array X X
Reduz o array X X
Retorna o novo tamanho X X
Retorna o elemento removido X X
Trabalha no fim do array X
Trabalha no início do array X
<?php
$vetor = array(7 => 1, 2, 3);
Arrays 315
$vetor[] = 4;
$vetor[2] = 5;
array_pop($vetor);
Ao atribuirmos o valor 5 ao $vetor sob o índice 2, embora numericamente esteja antes dos demais,
na prática o PHP encadeia ao fim do array. Isso significa que se fôssemos inspecionar o conteúdo de
$vetor após a linha 4, ele teria a seguinte formação: array(7 => 1, 8 => 2, 9 => 3, 10 => 4, 2
=> 5). Diferente do que poderia se pensar, já que a chave 2 deveria vir antes de todos.
Quando executamos a função array_pop(), ele remove justamente o elemento 5, associado ao índice
2, pois ele está no fim. Portanto a alternativa correta é B: array(7 => 1, 8 => 2, 9 => 3, 10 =>
4).
A: 2
B: 1
C: null
D: 5
Essa pergunta leva em conta o conhecimento adquirido na questão anterior, embora aqui haja um
agravante: o array_unshift() reorganiza o array, modificando completamente os índices, partindo
do 0 (zero) como primeiro elemento, 1 para o segundo valor e assim por diante.
Significa que ao escrever o conteúdo da chave 7 (que antes constava o valor 1), agora já não existe
mais. O array ficou, ao término da execução, nesse formato: array(0 => 6, 1 => 1, 2 => 2, 3
=> 3, 4 => 4, 5 => 5). Portanto o índice 7 é null - alternativa C é a correta.
Arrays 316
Iteração de arrays
Há diversas formas de percorrer um array, seja através da construção for e foreach ou de funções
como array_walk(). Veremos em detalhes cada uma das maneiras.
Quando os índices são numéricos e consecutivos, o laço for funciona perfeitamente:
Quando os índices forem baseados em string (arrays associativos), o loop foreach é o mais
apropriado para realizar tal tarefa:
E quando os índices são importantes para o lógica do programador, o foreach tem uma sintaxe
diferenciada que oferece a possibilidade de resgatar as chaves:
A função array_walk() percorre os itens do array e invoca uma função callback para cada elemento.
No exemplo abaixo a função escreveItens() é executada para cada item do array $certificacoes.
1 $certificacoes = array("ZCPE" => "Zend", "CSM" => "Scrum Alliance", "MCP" => "Mi\
2 crosoft");
3 function escreveItens($valor, $chave)
4 {
5 echo $chave." ".$valor."<br>";
6 }
7
8 array_walk($certificacoes, 'escreveItens');
O callback também aceita argumentos. Basta passar como parâmetro(s) extra(s) na função array_-
walk().
Arrays 317
1 $certificacoes = array("ZCPE" => "Zend", "CSM" => "Scrum Alliance", "MCP" => "Mi\
2 crosoft");
3 function escreveItens($valor, $chave, $nome)
4 {
5 echo $nome." ".$chave." ".$valor."<br>";
6 }
7
8 array_walk($certificacoes, 'escreveItens', 'Ari');
Uma construção interessante é passar um objeto como segundo parâmetro da função array_walk(),
fazendo com que seja executado como $obj(), invocando o método mágico __invoke():
Array multi-dimensional
As matrizes são quando um array possui internamente um ou mais arrays. Ou seja, quando um
elemento do array na verdade é outro array.
Para acessar um elemento específico você precisa de mais índices: dois quando for bi-dimensional,
três para tri-dimensional e assim por diante.
1 $frameworks = array
2 (
3 array("ZendFramework", "ZF2 Certified Architect", "ZFTool"),
4 array("Symfony", "Symfony Certified Developer", "Console"),
5 array("CakePHP", "Certified CakePHP Developer", "Bake"),
6 array("Laravel", "Laravel Certified Developer", "Artisan")
7 );
8
9 echo "Framework: ".$frameworks[1][0]."<br>";
10 echo "Certificação: ".$frameworks[1][1]."<br>";
11 echo "Linha de comando: ".$frameworks[1][2];
Framework: Symfony
Certificação: Symfony Certified Developer
Linha de comando: Console
O PHP comporta encadear arrays dentro de arrays, mas estruturas muito “profundas” (com mais de
três níveis) tendem a ter grande complexidade na manipulação e na lógica empregada.
1 $frameworks = array
2 (
3 array("nome" => "ZF2", "console" => "ZFTool", "psr" => array (7)),
4 array("nome" => "Symfony", "console" => "Console", "psr" => array (1, 2, 4)),
5 array("nome" => "CakePHP", "console" => "Bake", "psr" => array (1, 2)),
6 array("nome" => "Laravel", "console" => "Artisan", "psr" => array (2))
7 );
8
9 echo "Framework: ".$frameworks[1]['nome']."<br>";
10 echo "Nível de PSR: ".$frameworks[1]['psr'][2];
Framework: Symfony
Nível de PSR: 4
Importante alertar que a função sort() altera os índices do array. Caso não seja essa a sua intenção,
utilize a função asort() para preservar a relação chave-valor.
Abaixo seguem mais funções de ordenação. Ex.:
Funções Descrição
arsort() Ordena o array de forma decrescente, mantendo os índices
ksort() Ordena pelas chaves
krsort() Ordena pelas chaves, de trás para frente
rsort() Ordena o array de forma reversa
usort() Ordenação baseado numa função de usuário, disparada por callback
Arrays 319
Outro método relevante de ordenação chama-se ordenação natural. Imagine que você queira ordenar
nomes de arquivos, para ter a sequencia correta:
Veja que não reflete exatamente o que pretendíamos, pois a função sort() realiza uma ordenação
ASCII, baseada nos caracteres que formam os nomes. Significa que a string arquivo10 vem antes da
arquivo9, já que 1 < 9.
Para corrigir esse problema, podemos passar a constante SORT_NATURAL como argumento extra na
função sort(). Ex.:
Outras funções específicas para essa finalidade, que utilizam heurísticas e outras técnicas, são:
natsort(), natcasesort(), strnatcmp() e strnatcasecmp(). Mais informações em https://github.
com/sourcefrog/natsort.
Para compararmos arrays usamos a família de funções array_diff($a, $b) que retorna todos os
valores que existam em $a, mas que não aparecem em $b, independentemente do índice na qual os
valores estão endereçados.
No exemplo acima, o array $d possuiu apenas um elemento que é MongoDB. Mas veja que o array $b
possui o elemento HTML que não existe em $a, mesmo assim ele não é retornado em decorrência da
forma como a função trabalha.
Observe também que o valor PHP ora aparece associado a chave a (em vetor $a), ora aparece
vinculado a chave b (em vetor $b). Nesse caso o array_diff() não distingue o valor.
Arrays 320
Abaixo segue uma breve lista de outras funções da mesma família, para aumentar o seu repertório
para a prova:
Funções Descrição
array_diff_assoc() Compara valores e chaves
array_diff_key() Compara somente as chaves
array_diff_uassoc() Compara valores e chaves através de uma função de usuário
array_diff_ukey() Compara somente as chaves através de uma função de usuário
A: 2
B: 3
C: 4
D: 5
E: 6
Primeiramente observe os valores do array $a e veja que todos começam com o dígito 0 (zero).
Significa que estão em base octal.
Para algum desavisado, possivelmente seria levado a crer que o PHP faria um type juggling
transformando 073 em 73, mas não é como acontece.
Relembrando as conversões, vamos transformar 73 em base octal para o seu correspondente em base
decimal:
Arrays 321
Assim sendo o valor 123 octal representa o 83 em base decimal, que consta no array $b.
E perceba que o elemento 073 aparece DUAS vezes no vetor $a, e a função array_diff() conta
também.
Analisando elemento por elemento, teremos: 073 que não existe no array $b, portanto entra no diff,
o 0103 (67 decimal) também difere, 0113 (75 decimal) não existe em $b e novamente o 073. Contando
teremos 4 elementos, portanto alternativa correta é C:.
O valor 0123 tem seu equivalente em $b(83 decimal), mas não difere e também não contabiliza no
count(). Ex.:
Arrays 322
Da mesma forma que podemos dividir um array, podemos mesclar. Para tanto utilizamos a função
array_merge(). Veja um exemplo:
1 $a = array(1, 2, 4, 8);
2 $b = array(0, 2, 4, 6, 8, 10);
3 $m = array_merge($a, $b);
4 print_r($m);
Observe que existem valores iguais tanto em $a quanto em $b, como é o caso do 2, 4 e 8. Mesmo
assim a função array_merge() adiciona elementos repetidos quando o índice for numérico, então
o resultado seria o array a seguir, contendo 10 itens:
Arrays 323
1 Array
2 (
3 [0] => 1
4 [1] => 2
5 [2] => 4
6 [3] => 8
7 [4] => 0
8 [5] => 2
9 [6] => 4
10 [7] => 6
11 [8] => 8
12 [9] => 10
13 )
Acontece diferentemente quando trabalhamos com índices como string, no contexto de arrays
associativos. Nesses casos a função array_merge() sobrescreve os valores do segundo array, caso
conflitam:
1 Array
2 (
3 [A] => 1
4 [B] => 2
5 [C] => 3
6 )
Desde a versão 4 do PHP, essa associação só seria possível substituindo a linha [$a, $b, $c] =
$vetor; por list($a, $b, $c) = $vetor;. Lembrando que list muitas vezes é chamada errone-
amente de função, sendo que tecnicamente é uma construção da linguagem PHP.
Outra flexibilidade interessante que torna o código mais clean é:
1 $identidade = ['nome' => 'Ari', 'idade' => 40, 'nacionalidade' => 'brasileiro'];
2 ['nome' => $nome, 'nacionalidade' => $origem, 'idade' => $idade] = $identidade;
3 echo $nome; //Ari
Perceba a associação da variável $origem e $idade que não seguem necessariamente a ordem em
que os índices foram dispostos inicialmente.
Nessa questão lidamos com operações em arrays. A primeira porção atribui os valores Ari, Ariel e
Ariane nos seus respectivos índices, respectivamente 1, 3 e 5.
Ao realizar a união (sinal de +) com o segundo array, os índices já ocupados são preservados. Então
o valor Neto assume a chave 0 e Laci associa ao índice 2, enquanto que Paloma está vinculada em
1, porém esse elemento já existe no primeiro array (que é Ari).
Array
(
[1] => Ari
[3] => Ariel
[5] => Ariane
[0] => Neto
[2] => Laci
)
⁹²http://php.net/manual/en/class.arrayaccess.php
Arrays 326
Quando tivermos uma implementação que dependa de um array de tamanho fixo, convém
utilizarmos a classe SplfixedArray já que é mais eficiente do que o uso de array PHP nativo. Deve
ser digitado no campo a resposta SplfixedArray.
Array constante
A partir da versão 7.0 do PHP temos a possibilidade de criar arrays constantes utilizando define(),
o que antes só era possível com a função const(). Ex.:
1 define('FRAMEWORKS', [
2 'ZF',
3 'Laravel',
4 'Phalcon'
5 ]);
6 echo FRAMEWORKS[1]; //Laravel
Simulado
1.
<?php
$array = array(0.1 => 'a', 0.2 => 'b');
echo count($array);
?>
A: 1
B: 2
C: 0
D: Nada
E: 0.3
2.
Qual é a forma mais eficiente para determinar se uma chave está presente em um a\
rray, assumindo que o array não tem valores nulos?
A: in_array('key', array_keys($a))
B: isset($a['key'])
Arrays 327
C: array_key_exists('key', $a)
D: Nenhuma das alternativas
3.
<?php
class minha_classe
{
public $meu_valor = array();
function minha_classe($value)
{
$this->meu_valor[] = $value;
}
function set_valor($value){
$this->meu_valor = $value;
}
}
$a = new minha_classe('a');
$a->meu_valor[] = 'b';
$a->set_valor('c');
$a->minha_classe('d');
?>
A:
Array
(
[0] => d
)
C:
Array
(
[0] => a
[1] => b
[2] => d
)
D: c
Arrays 328
E:
Array
(
[0] => c
[1] => d
)
4.
A: ArrayAccess
B: SeekableIterator
C: RecursiveIteratorIterator
D: FilterIterator
5.
Qual função pode reverter a ordem dos valores de um array, preservando os índice\
s?
A: array_flip()
B: array_reverse()
C: rsort()
D: krsort()
E: array_multisort()
Respostas do simulado
1.
Os índices do array não aceitam float, portanto 0.1 será convertido para integer(0). O mesmo
acontece com 0.2, consequentemente o valor b vai sobrescrever a.
Significa que vai sobrar apenas um elemento no array, então a função count() contará 1 elemento.
Resposta: A.
Arrays 329
Diferentemente de array('0.1' => 'a', '0.2' => 'b') que mantém os índices como string e
portanto diferencia ambos os índices, contando 2.
Pegando um exemplo da documentação oficial https://secure.php.net/manual/pt_BR/language.types.
array.php, veja que todos os índices serão convertidos automaticamente para integer(1) prevale-
cendo o último valor inserido, nesse caso string("d"). Ex.:
1 $array = array(
2 1 => "a",
3 "1" => "b",
4 1.5 => "c",
5 true => "d",
6 );
7 var_dump($array);
array(1) {
[1]=>
string(1) "d"
}
2.
As alternativas A, B e C funcionam, diante do enunciado. A pergunta é: qual o mais performático?
Sabe-se que a função isset() é bastante eficiente, mesmo assim vamos realizar um benchmark
simplório e averiguar:
15 $inicio = microtime(true);
16 isset($a[5]);
17 $fim = microtime(true);
18 $total = $fim - $inicio;
19 echo number_format($total, 12)."<br>";
No código acima vamos usar a função microtime() que devolve o timestamp corrente em micro-
segundos. Obtivemos o tempo antes e depois da execução e mostramos a diferença com doze casas
decimais.
O resultado do código será:
0.000004053116
0.000001907349
0.000000953674
Portanto vemos que a instrução isset($a[5]); leva menos tempo de execução e, portanto, mais
rápida que a demais opções. Então a resposta será a alternativa B:.
3.
Primeiramente teremos o método de mesmo nome da classe que será executado assim que ela
for instanciada, agindo como o método construtor, para garantir a compatibilidade com códigos
antigos. Trata-se de um recurso obsoleto que será extinto nas futuras versões, mas que ainda funciona
no PHP 7.1.
Na primeira instrução a ser executada (linha 14) o valor a será armazenado no índice 0 do array
meu_valor[]. Depois b será atribuído no índice 1 de meu_valor[], já que é um atributo público
e portanto acessível externamente perante a classe. Ao executar set_valor('c') (na linha 16), o
método modifica o array para uma variável e armazena c. Ao invocar o método minha_classe('d'),
a estrutura de meu_valor já não é mais um vetor e portanto ocasiona um: Fatal error: Uncaught
Error: [] operator not supported for strings in.... Nesse caso a alternativa B é a resposta
correta.
4.
Primeiramente temos que lembrar que somente a classe SeekableIterator, RecursiveIteratorI-
terator e FilterIterator pertencem a SPL (Standard PHP Library), e portanto ArrayAccess está
descartada. Para contextualizar, ArrayAccess é uma interface que provê o acesso a objetos como se
fossem arrays. A classe RecursiveIteratorIterator percorre arrays multi-dimensionais, iterando
como árvores de dados. A classe FilterIterator é usada para filtrar itens devolvidos na iteração.
A classe SeekableIterator estende a interface Iterador padrão e adiciona o método seek() que
proporciona a capacidade de recuperar um item específico de uma fonte de dados interna. Mais
informações em http://php.net/manual/pt_BR/book.spl.php.
5.
A resposta é B: array_reverse(), desde que o segundo parâmetro seja true. Ex.:
Arrays 331
Array
(
[0] => Ari
[1] => 7.1
[2] => Array
(
[0] => ZCE
[1] => ZCPE
)
)
Array
(
[0] => Array
(
[0] => ZCE
[1] => ZCPE
)
Podemos estipular quais os tipos de erros gostaríamos de evidenciar para que nosso código fique
melhor, evitando bugs.
Temos 16 tipos de erro dentro do PHP:
Você pode modificar os níveis de erro que lhe interessa analisar, o fazendo globalmente configu-
rando diretivas no php.ini:
Cláusula Características
error_reporting Configure via constante ou diretamente pelo valor (potências de 2)
display_erros Mostra ou não os erros em tela. O padrão é ligado, ou seja, on ou 1
log_errors Aceita 1 (on) e 0 (off). O padrão é desligado, ou seja, off ou 0
log_errors_max_len Tamanho das mensagens de erro em bytes. O padrão é 1024 bytes, sendo que 0 é
ilimitado
error_log Indica o caminho do arquivo de log. O padrão é NULL, ou seja, direciona para os
logs de erro do servidor web
1 error_reporting(E_ALL | E_STRICT);
2 ini_set('display_errors', 1);
3 ini_set('log_errors', 1);
4 ini_set('log_errors_max_len', 0);
5 ini_set('error_log', '/RaizZendServer/php_errors.log');
6
7 //Alterando artificialmente a capacidade de memória
8 ini_set('memory_limit', '1K');
As parametrizações de erros são formadas por operações bitwise, dentre elas |, ∼, ^ e &. Ex.:
Cláusula Descrição
E_ALL & ∼E_NOTICE Todos os erros E_ALL exceto as notificações em tempo de execução
E_NOTICE
E_ALL ^ E_NOTICE Mesmo efeito citado acima
E_ERROR | E_RECOVERABLE_ERROR Mostrar apenas erros e situações recuperáveis
1 $valor = @(2/0);
Em condições normais, esse código teria a sua execução abortada. Porém, ao utilizar @, o PHP
continua executando. Embora seja uma prática desaconselhada, pode ser eventualmente útil.
Você pode customizar os erros implementando uma função própria e registrando-a através da
função set_error_handler(). Mas apenas alguns tipos de erro permitem que se sobrescreva o
manipulador de erros nativo, dentre eles: E_WARNING, E_NOTICE, E_USER_ERROR, E_USER_WARNING,
E_USER_NOTICE, E_RECOVERABLE_ERROR, E_DEPRECATED e E_USER_DEPRECATED. Os erros que não
permitem customização são: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR,
E_COMPILE_WARNING e E_STRICT.
1 error_reporting(E_ALL | E_STRICT);
2
3 trigger_error("Mensagem customizada de aviso.", E_USER_NOTICE);
4 trigger_error("Mensagem customizada de advertência.", E_USER_WARNING);
5 trigger_error("Mensagem customizada de erro.", E_USER_ERROR);
6
7 echo "Essa string não será mostrada";
O PHP 7.0 mudou significativamente a manipulação de erros e exceções, como veremos mais adiante.
Exceções
Existem exceções mais específicas, como é o caso da classe PDOException, invocada quando houver
uma violação nas instruções que utilizam a classe PDO.
Considere um banco de dados fictício chamado zcpe operando em localhost, tendo uma tabela
usuarios já devidamente populada. Essa tabela contém um registro onde o campo id (primary key)
consta o valor 1.
Manipulação de erros 335
Ao tentar adicionar o registro com mesma chave primária 1, o script abaixo tenta executar a
prepared statement e consequentemente exibe Cadastrado com êxito. Porém a instrução não é
executada de fato, pois viola a constraint de integridade do banco de dados, já que o SGBD proíbe
que seja inserido uma chave duplicada - definida como PRIMARY KEY.
1 $dsn = 'mysql:host=localhost;dbname=zcpe';
2 $pdo = new PDO($dsn, 'usuario', 'senha');
3 try {
4 $sql = "INSERT INTO usuarios (id, nome, email) VALUES (:id, :nome, :email)";
5 $stmt = $pdo->prepare($cmd);
6 $stmt->bindValue('id', 1);
7 $stmt->bindValue('nome', 'Ari Stopassola Junior');
8 $stmt->bindValue('email', 'ari@certificamp.com');
9 $stmt->execute();
10 echo "Cadastrado com êxito";
11 }
12 catch (PDOException $e) {
13 echo "PDOException";
14 throw $e;
15 }
O problema é que o objeto $e da classe PDOException deveria ser capturado, o que estranhamente
não acontece. Para isso, é necessário especificar o atributo PDO::ATTR_ERRMODE da conexão PDO para
PDO::ERRMODE_EXCEPTION. Ex.:
1 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Throwables
Agora as classes Error e Exception implementam a interface Throwable, seguindo a estrutura
hierárquica ilustrada abaixo (inspirado pelo artigo de Davey Shafik⁹³):
⁹³https://www.daveyshafik.com/archives/69237-an-exceptional-change-in-php-7-0.html
Manipulação de erros 336
Throwable
|-- Exception (implements Throwable)
| |-- LogicException (extends Exception)
| | |-- BadFunctionCallException (extends LogicException)
| | | |-- BadMethodCallException (extends BadFunctionCallException)
| | |-- DomainException (extends LogicException)
| | |-- InvalidArgumentException (extends LogicException)
| | |-- LengthException (extends LogicException)
| | |-- OutOfRangeException (extends LogicException)
| |-- RuntimeException (extends Exception)
| |-- OutOfBoundsException (extends RuntimeException)
| |-- OverflowException (extends RuntimeException)
| |-- RangeException (extends RuntimeException)
| |-- UnderflowException (extends RuntimeException)
| |-- UnexpectedValueException (extends RuntimeException)
|-- Error (implements Throwable)
|-- AssertionError (extends Error)
|-- ParseError (extends Error)
|-- TypeError (extends Error)
Abaixo seguem algumas situações onde erros específicos da classe Error são ocasionados:
Tanto a classe Error quanto Exception implementam a interface Throwable, que é interna da
linguagem e não pode ser implementada por uma classe customizada. Caso queira desenvolver a
sua própria classe para manipulação de erros, você deve extender Error ou Exception.
O time de desenvolvimento do core do PHP recomenda que se manipule erro ou exceção em blocos
catch separados. Ex.:
1 try {
2 //Código que pode lançar um erro ou exceção
3 } catch (Exception $e){
4 //Manipula a exceção
5 } catch (Error $e){
6 //Manipula o erro
7 }
Manipulação de erros 337
Uma abordagem mais simplificada, agrupando erros e exceções no mesmo catch, seria utilizando a
interface Throwable. Ex.:
1 try {
2 //código suscetível a erros ou exceções
3 } catch (Throwable $e){
4 //Manipula erros e exceções
5 }
Observe o código abaixo e perceba que a classe Cronometro implementa os métodos mágicos
__construct() e __destruct(). Vamos forçar deliberadamente um erro na linha 15, invocando
um método inexistente: $tarefa->dispara();. Ao executarmos esse script, o PHP gera um Fatal
error impedindo que o método __destruct seja executado, podendo ocasionar efeitos colaterais na
execução do código.
Veja que o PHP expõe um erro não capturado (Uncaught Error) ao invés de uma exceção e a
mensagem Finaliza cronômetro não será exibida - o quê pode ocasionar instabilidade na aplicação
como um todo. Ex.:
1 class Cronometro
2 {
3 public function __construct()
4 {
5 echo "Inicia cronômetro<br>";
6 }
7 public function __destruct()
8 {
9 echo "Finaliza cronômetro<br>";
10 }
11 }
12
13 try {
14 $tarefa = new Cronometro;
15 $tarefa->dispara();
16 } catch (Exception $e){
17 echo $e->getMessage()."<br>";
18 } finally {
19 echo "Bloco finally executado<br>";
20 }
O código acima propositadamente lança (throw) um erro não capturado, gerando uma interrupção
abrupta no script, sem chamar o destrutor da classe, tão pouco redirecionar ao fluxo de finally.
Como o PHP 7.0 desmembrou a classe Error, faz-se necessário um catch específico para ela.
Manipulação de erros 338
Significa que, com um pequeno ajuste, podemos melhorar o fluxo do código finalizando graciosa-
mente a execução do objeto $tarefa. Mantendo a mesma definição de classe, observe novamente
as linhas 6 e 7 onde adicionamos mais um catch, mas desta vez interceptando um objeto da classe
Error:
1 try {
2 $tarefa = new Cronometro;
3 $tarefa->dispara();
4 } catch (Exception $e){
5 echo $e->getMessage()."<br>";
6 } catch (Error $e){
7 echo $e->getMessage()."<br>";
8 } finally {
9 echo "Bloco finally executado<br>";
10 }
Significa que agora o script vai capturar adequadamente erros e exceções, mostrando:
Inicia cronômetro
Call to undefined method Cronometro::dispara()
Bloco finally executado
Finaliza cronômetro
Tanto a classe Error quanto Exception disponibilizam uma série de métodos para rastreamento de
erros e exceções:
Métodos Descrição
getMessage() Obtém a mensagem do erro ou exceção
getCode() Retorna um código de erro interno associado ao respectivo erro ou exceção
getFile() Resgata o nome do arquivo na qual gerou o erro/exceção
getLine() Devolve a linha na qual o erro ou exceção ocorreu
getTrace() Obtém a pilha de rastreamentom informando nome de arquivo, função que provocou o erro etc
getTraceAsString() O mesmo que getTrace(), porém devolve uma string com tais informações
getPrevious() Retorna o erro disparado anteriormente, caso exista
__toString() Devolve a string que representa o erro ou exceção
No PHP 7.0 os erros E_STRICT foram reclassificados para outras categorias de erros.
As chamadas static de métodos não estáticos foram realocadas como obsoletas (deprecated),
indicando que esse tipo de erro deixará de funcionar nas próximas versões do PHP. Nas versões
anteriores ao PHP 7.0 elas estavam categorizadas como Strict Standards. Ex.:
Manipulação de erros 339
1 class BoasVindas
2 {
3 public function quem($nome)
4 {
5 echo "<hr>Olá ".$nome."!</hr>";
6 }
7 }
8
9 BoasVindas::quem("Ari");
Esse código gera um aviso indicando que essa possibilidade será descontinuada, embora ainda funci-
one: Deprecated: Non-static method BoasVindas::quem() should not be called statically
in....
A partir do PHP 7.0 foram incorporadas novas palavras reservadas restringindo o uso em nomes
de classe, interface, trait e namespace.
• PHP 7.0: int, float, bool, string, true, false, null, resource, mixed e numeric;
• PHP 7.1: void e iterable;
• PHP 7.2: object - a título de conhecimento, pois não cairá no teste, já que o exame engloba
conteúdos até a versão 7.1.
1 trait Iterable {
2 function obtemAlgo() {
3 return "OK";
4 }
5 }
6
7 class minhaClasse {
8 use Iterable;
9 }
10
11 $obj = new minhaClasse;
12 echo $obj->obtemAlgo();
A saída do código acima será um erro fatal em decorrência da palavra reservada iterable:
Observe que a mensagem de erro ("Etapa pendente") é igual para MosturaException, LavagemEx-
ception e FervuraException e justifica simplificarmos o código encadeando as exceções com pipe,
agrupando-as dessa forma:
Manipulação de erros 341
1 try {
2 $producao->operacao('mosto');
3 } catch (MosturaException | LavagemException | FervuraException $e) {
4 $msg_erro = "Etapa pendente";
5 } catch (MaturaException $e) {
6 $msg_erro = "Maturação incompleta";
7 }
Simulado
1.
A: 2:MinhaExcecao
B: 1:
C: 1:MinhaExcecao
D: 2:
E: Parser error, try cannot be followed by multiple catch
F: 1:Exception
2.
Manipulação de erros 342
A:
Throwable
|
|--Error
|
|--Exception
B:
Error
|
|--Throwable
|
|--Exception
C:
Throwable
|
|--Exception
|
|--Error
D:
Error
|
|--Exception
|
|--Throwable
3.
try {
Manipulação de erros 343
precoTotal(12);
}
catch (Exception $e) {
echo "A";
}
catch (ArgumentCountError $ace) {
echo "B";
}
catch (DivisionByZeroError $dbze) {
echo "C";
}
A:C
B:A
C:B
4.
5.
Em versões anteriores ao PHP 7.0 havia um bug que permitia encadear várias decla\
rações default dentro de um mesmo switch, assumindo como fluxo de execução padrã\
o o último default estabelecido.
Considere o seguinte trecho de código:
$a = 0;
switch(true){
default:
$a += 1;
break;
default:
Manipulação de erros 344
$a += 2;
break;
}
echo $a;
A: 0
B: 1
C: 2
D: 3
E: Fatal error: Switch statements may only contain one default clause
Respostas do simulado
1.
Dentro do try mais interno, observe que o código instancia deliberadamente um objeto de
MinhaExcecao.
Segundo a comportamento descrito na documentação oficial⁹⁴, o primeiro catch (da classe pai
Exception) é quem se encarrega de tratar o referido erro, mostrando 1:, disparando o erro
novamente para o catch posterior.
Ao exibir o nome da classe, através da função get_class(), daí obtém-se MinhaExcecao que é o
nome da classe filha.
Portanto a resposta correta é C: 1:MinhaExcecao.
2.
Resposta é C:
Throwable
|
|--Exception
|
|--Error
3.
O PHP 7.1 incorporou exceções com a classe ArgumentCountError que trata problemas de discre-
pância de argumentos perante a assinatura definida em funções e métodos.
Ao tentar (try) executar a função precoTotal(12) o catch de ArgumentCountError é disparado pois
falta um parâmetro, escrevendo B na tela. Portanto alternativa C é a correta.
4.
A alternativa correta é B, pois classes customizadas NÃO podem implementar Throwable dire-
tamente. Caso queira estender Throwable é preciso criar uma interface filha que implemente
(implements) essa primeira interface através da classe filha de Error ou Exception. Ex.:
5.
Antes do advento do PHP 7.0 poderíamos executar esse código resultando no valor 2, sem
provocar erros. Hoje o PHP identifica as múltiplas declarações default e dispara um erro fatal,
interrompendo imediatamente a execução. Portanto a alternativa E é a resposta correta.
Recursos extras
Além deste guia, há uma série de outros recursos que lhe ajudarão na conquista da certificação PHP:
• Simulados
– uCertify⁹⁵
– BrainBench⁹⁶
– CertBest⁹⁷
– ZendExam⁹⁸
– Vcampus⁹⁹
– My PHP Quiz¹⁰⁰
– Assessmentee¹⁰¹
– AIO TestKing¹⁰²
– Quiz desenvolvido por Taylor Lopes¹⁰³
• Apps
– PHP quiz ZCE versão Android¹⁰⁴ e versão iOS¹⁰⁵ por Fabricio Bedeschi
– PHP Quiz Lite¹⁰⁶ (gratuito)
– PHP Questions¹⁰⁷
– PHP Expert Lite¹⁰⁸ (gratuito)
– PHP Expert¹⁰⁹
• Vídeo-aulas
– CBT Nuggets – Zend Web Development PHP 5 ZCE¹¹⁰
• Guias e resumos
– Zend – PHP 5.3 Study Guide (gratuito)
– PHP Certification Study Guide 2017-PHP¹¹¹ (baseado na versão 7.1 do PHP)
⁹⁵http://www.ucertify.com/certifications/Zend/zend-php-5-3-certification.html
⁹⁶http://www.brainbench.com/xml/bb/common/testcenter/taketest.xml?testId=2862
⁹⁷http://www.certbest.com
⁹⁸http://www.zendexam.com
⁹⁹http://vcampus.co/quizzes/php
¹⁰⁰http://www.myphpquiz.com
¹⁰¹http://www.assessmentee.com/test/module/PHP
¹⁰²http://www.aiotestking.com/zend/category/exam-200-550-zend-certified-php-developer/
¹⁰³http://taylorlopes.com/util/quiz/php/
¹⁰⁴https://play.google.com/store/apps/details?id=com.quiz.php
¹⁰⁵https://itunes.apple.com/us/app/php-quiz-zce/id954188281?ls=1&mt=8
¹⁰⁶http://itunes.apple.com/br/app/php-quiz-lite/id397103578?mt=8
¹⁰⁷http://itunes.apple.com/br/app/php-questions/id407222202?mt=8
¹⁰⁸http://itunes.apple.com/br/app/phpexpert-lite/id545290910?mt=8
¹⁰⁹http://itunes.apple.com/br/app/php-expert/id531106572?mt=8
¹¹⁰http://www.cbtnuggets.com/it-training-videos/series/zend-web-development-php-5zce
¹¹¹http://www.zend.com/en/services/certification/php-certification-study-guide
Recursos extras 347
Importante salientar que 90% de todo o material citado aqui é em língua inglesa.
¹³¹http://zend-php-certification.com
¹³²http://www.diogocatapreta.com.br/certificacao-php-inicio-da-maratona/
¹³³http://dorianneto.com.br/certificacao/guia-de-sobrevivencia-de-um-zcpe-zce/
¹³⁴https://drive.google.com/open?id=0Bww0PIjye2viNkRFTURNVHh5SGM
¹³⁵http://www.rumoacertificacaophp.com