Save 37% off PRO during our Black Friday Sale! »

Criando Aplicações Robustas em PHP com Tipos

Criando Aplicações Robustas em PHP com Tipos

Os tipos são ferramentas fundamentais para a construção de uma aplicação robusta. Eles são utilizados para a prevenção de erros durante a execução e auxiliam na legibilidade e manutenção do código. Nesta palestra serão apresentadas a definição de tipos e por que utilizá-los, sistema de tipos, tipagem estática e dinâmica, tipagem forte e fraca, como funciona o sistema de tipos e a evolução dos tipos em PHP. Abordaremos as novidades relacionadas a tipos que foram adicionadas ao longo do tempo na linguagem tais como enums e intersection types que estarão disponíveis na versão mais nova do PHP. E, por fim, falaremos como expandir o sistema de tipos do PHP e torná-lo ainda mais poderoso com o auxílio de ferramentas de análise estática como PHPStan e Psalm. Assista esta palestra e aprenda como tirar vantagem dos tipos na linguagem mais querida da web!

52711e2157a6fed933b0361cc06a6953?s=128

Marcel dos Santos

November 22, 2021
Tweet

Transcript

  1. Marcel Gonçalves dos Santos @marcelgsantos T php criando aplicações em

    com tipos robustas
  2. pensandonaweb.com.br desenvolvedor web full-stack Marcel Gonçalves dos Santos @marcelgsantos

  3. @phpsp phpsp.org.br

  4. Interaja nas mídias sociais! 
 
 - fale sobre o

    evento, palestrantes e conteúdo - tire fotos do evento e publique 
 - interaja com outros participantes do evento - tire dúvidas ou dê feedbacks para os palestrantes
  5. O que são tipos?

  6. os tipos podem ser considerados como um conjunto de valores,

    isto é, os valores são agrupados por tipos
  7. eles de fi nem quais operações podem ser realizadas sobre

    um dado
  8. os tipos possuem cardinalidade

  9. eles de fi nem a forma como um valor é

    armazenado é representado
  10. os tipos de fi nem o signi fi cado semântico

    do valor
  11. um tipo é uma classi fi cação do dado que

    de fi ne a operação que pode ser feita sobre aquele dado, o signi fi cado do dado e o conjunto de valores permitidos
  12. Por que utilizar tipos?

  13. a utilização de tipos ajuda a prevenir erros de uma

    categoria especí fi ca
  14. a medida que a complexidade de um software cresce, é

    necessário que o código possua garantias de corretude
  15. os tipos fornecem provas genéricas de que o código funcionará

    de acordo com a entrada
  16. os erros que acontecem em tempo de execução e que

    podem causar problemas na aplicação…
  17. …podem ser transformados em erros em tempo de compilação e

    evitar causar sérios problemas na aplicação
  18. existem esforços para trazer type checking em tempo de compilação

    para linguagens dinamicamente tipadas
  19. o TypeScript é uma linguagem criada para fornecer checagem de

    tipo em tempo de compilação para o JavaScript
  20. os principais benefícios dos tipos são corretude, imutabilidade, encapsulamento, componibilidade

    e legibilidade
  21. O que é sistema de tipos?

  22. um sistema de tipos é um conjunto de regras que

    atribui e impõe tipos para elementos de uma linguagem de programação
  23. um sistema de tipos permite atribuir tipos através de uma

    notação no código ou implicitamente deduzindo o tipo de certos elementos baseados no contexto
  24. ele permite várias conversões de tipos e proíbe outras

  25. utiliza-se o sistema de tipos para projetar código menos propenso

    a erros, melhor componentizável e fácil de entender
  26. um código correto significa que ele se com- porta de

    acordo com a sua especi fi cação, produz resultados esperados e sem criar erros em tempo de execução
  27. ao utilizar um sistema de tipos transforma-se o que seria

    um erro em tempo de execução que poderia acontecer em produção e afetar o cliente final…
  28. …em um inofensivo erro em tempo de compilação que pode

    ser corrigido antes de fazer o deploy do código
  29. um sistema de tipos permite otimização em tempo de execução,

    funciona como documentação e pode ser interno ou externo a linguagem
  30. Como os tipos podem 
 ser classi fi cados?

  31. os tipos podem ser classi fi cados como primitivo, composto

    ou recursivo
  32. os tipos primitivos são aqueles cujos valores são atômicos, ou

    seja, não podem ser decompostos em valores mais simples
  33. os tipos compostos são aqueles cujos valores são compostos por

    valores de tipos primitivos
  34. os tipos recursivos é de fi nido em termos de

    si e a cardinalidade de tipos recursivos é in fi nita
  35. as linguagens podem ter tipagem estática ou tipagem dinâmica

  36. uma linguagem com tipagem estática realiza a veri fi cação

    de tipos em tempo de compilação
  37. uma linguagem com tipagem dinâmica realiza a veri fi cação

    de tipos em tempo de execução
  38. as linguagens podem ter tipagem forte e tipagem fraca

  39. em uma linguagem com tipagem forte a conversão de tipos

    deve ser feita de forma explícita e operações entre valores de tipos diferentes não são permitidas
  40. em uma linguagem com tipagem fraca a conversão de tipos

    é feita de forma implícita e operações entre valores de tipos diferentes são permitidas e feitas de forma automática
  41. o PHP possui tipagem dinâmica e fraca pois a veri

    fi cação de tipos é feita em tempo de execução e é feita a conversão automática de tipos em operações com tipos diferentes
  42. ao utilizar ferramentas de análise estática como Phan, PHPStan e

    Psalm é possível realizar a veri fi cação de tipos em tempo de “compilação” na linguagem
  43. Operações comuns 
 relacionadas à tipos

  44. a veri fi cação de tipo ou type checking é

    um processo de veri fi cação e garantia das restrições de tipos
  45. a verificação de tipo pode ser executada em tempo de

    compilação ou tempo de execução
  46. ela garante a integridade do dado, impõe restrições de acesso

    e interpreta os dados como pretendidos pelo(a) programador(a)
  47. a conversão de tipos pode ser realizada de forma implícita

    e explícita
  48. a conversão implícita ou coerção é realizada de forma automática

    pelo compilador ou interpretador
  49. a conversão explícita ou cast é realizada de forma manual

    pelo(a) programador(a)
  50. a inferência de tipos permite que o(a) programador(a) não declare

    o tipo mas que o compilador ou interpretador conheça o tipo com base no valor informado
  51. um erro de tipo ocorre se o programa executa 


    uma operação com tipos incompatíveis
  52. Como funcionam os tipos no PHP?

  53. Conceitos básicos

  54. os tipos são divididos em tipos escalares, compostos e especiais

  55. escalares boolean, integer, float ou string

  56. compostos array, object, callable ou iterable

  57. especiais resource e null

  58. também existem os pseudo-tipos

  59. pseudo-tipos mixed, number, callback, array|object e void

  60. os tipos são de fi nidos em tempo de execução

  61. / / variables of several different types 
 $a_boolean =

    true; $an_int = 10; $a_float = 3.14; $a_string = 'joe'; $a_null = null; $a_array = []; $a_callable = function() { return 'Hey!'; }; $a_object = new stdClass;
  62. as funções get_debug_type, gettype e var_dump são utilizadas para conhecer

    o tipo da variável ou expressão
  63. / / get variable type using var_dump() function var_dump($a_boolean); /

    / bool(true) var_dump($an_int); / / int(10) var_dump($a_float); / / float(3.14) var_dump($a_string); / / string(3) "joe" var_dump($a_null); / / NULL var_dump($a_array); / / array(0) {} var_dump($a_callable); / / object(Closure)#1 (0) {} var_dump($a_object); / / object(stdClass)#2 (0) {}
  64. / / get variable type using gettype() function echo gettype($a_boolean);

    / / boolean echo gettype($an_int); / / integer echo gettype($a_float); / / double echo gettype($a_string); / / string echo gettype($a_null); / / NULL echo gettype($a_array); / / array echo gettype($a_callable); / / object echo gettype($a_object); / / object
  65. / / get variable type using get_debug_type() / / function

    available on PHP 8.0 echo get_debug_type($a_boolean); / / bool echo get_debug_type($an_int); / / int echo get_debug_type($a_float); / / float echo get_debug_type($a_string); / / string echo get_debug_type($a_null); / / null echo get_debug_type($a_array); / / array echo get_debug_type($a_callable); / / Closure echo get_debug_type($a_object); / / stdClass
  66. as funções is_int, is_boolean, is_string, is_array são utilizadas para veri

    fi car o tipo da variável ou expressão
  67. / / check if variables are from a specif i

    c / / type using is_*() functions var_dump(is_bool($a_boolean)); / / bool(true) var_dump(is_bool($an_int)); / / bool(false) 
 var_dump(is_int($an_int)); / / bool(true) var_dump(is_int($a_float)); / / bool(false) 
 var_dump(is_float($a_float)); / / bool(true) var_dump(is_float($a_string)); / / bool(false)
  68. / / check if variables are from a specif i

    c / / type using is_*() functions var_dump(is_string($a_string)); / / bool(true) var_dump(is_string($a_null)); / / bool(false) 
 var_dump(is_array($a_array)); / / bool(true) var_dump(is_array($a_object)); / / bool(false) 
 var_dump(is_null($a_null)); / / bool(true) var_dump(is_null($a_array)); / / bool(false)
  69. Conversão de tipos

  70. conversão explícita e automática

  71. / / explicit type conversion var_dump((bool) "1.5"); / / bool(true)

    var_dump((int) "1.5"); / / int(1) var_dump((float) "1.5"); / / float(1.5) var_dump((string) 1.5); / / string(3) "1.5"
  72. / / explicit type conversion var_dump((array) 1.5); / / array(1)

    { [0] = > float(1.5) } var_dump((array) "1.5"); / / array(1) { [0] = > string(3) "1.5" } var_dump((object) 1.5); / / object(stdClass)#1 (1) {["scalar"] = > … var_dump((object) "1.5"); / / object(stdClass)#1 (1) {["scalar"] = > …
  73. / / implicit or automatic type conversion / / using

    `+` operator 
 $var = "0"; var_dump($var); / / string(2) "0" $var += 5; var_dump($var); / / int(5) $var = $var + 3.1; var_dump($var); / / float(8.1)
  74. Declaração de tipos

  75. as declarações de tipos garantem que o valor é do

    tipo especi fi cado no momento da chamada
  76. as declarações de tipos podem ser utiliza- das em argumentos

    de funções, retornos de valores e propriedades de classe
  77. caso o valor informado não seja do tipo especi fi

    cado na declaração do tipo a exceção TypeError é lançada*
  78. declarações de tipos 
 
 1. tipos escalares 
 2.

    tipos compostos 
 3. classes e interfaces 
 4. object, mixed, self, parent, never 
 5. union types e intersection type 
 6. enums
  79. / / type declaration using classes class Mammal {} class

    Cat extends Mammal {} class Duck {} function sayHi(Mammal $mammal) { echo 'Hi ' . get_class($mammal) . '!'; } sayHi(new Mammal); / / Hi Mammal! sayHi(new Cat); / / Hi Cat! 
 sayHi(new Duck); 
 / / Uncaught TypeError: Argument 1 passed to / / sayHi() must be an instance of Mammal, instance of Duck given
  80. Modo estrito

  81. o PHP realiza, por padrão, a conversão automática de tipos

    de um valor para o tipo esperado quando necessário
  82. / / not using strict types function sum(int $a, int

    $b) { return $a + $b; } var_dump(sum(1, 2)); / / int(3) var_dump(sum(1.5, 2.5)); / / int(3) var_dump(sum("2", "5")); / / int(7) var_dump(sum(true, true)); / / int(2)
  83. pode-se ativar o modo estrito para garantir que a conversão

    automática de tipos não seja realizada
  84. e, caso o valor tenha um tipo incompatível com o

    tipo esperado, a exceção TypeError será lançada
  85. / / using strict types declare(strict_types=1); / / throws an

    exception when a value / / with incompatible type are provided function sum(int $a, int $b) { return $a + $b; } var_dump(sum(1, 2)); / / int(3) 
 var_dump(sum(1.5, 2.5)); Uncaught TypeError: Argument 1 passed to sum() must be of the type integer, float given
  86. o modo estrito funciona da mesma forma para declarações de

    tipos de retorno
  87. / / the value returned is converted / / to

    the correct type function sum($a, $b) : int { return $a + $b; } var_dump(sum(1, 2)); / / int(3) var_dump(sum(1.7, 2.5)); / / int(4) var_dump(sum(true, true)); / / int(2)
  88. declare(strict_types=1); / / a type exception is thrown function sum($a,

    $b) : int { return $a + $b; } var_dump(sum(1, 2)); / / int(3) var_dump(sum(1.7, 2.5)); / / Uncaught TypeError: sum() : Return value must be of type int, float returned
  89. Propriedades tipadas

  90. as typed properties ou propriedades tipadas foram adicionadas no PHP

    7.4
  91. utilizavam-se docblocks e métodos getters e setters para a garantia

    de tipos
  92. / / code with unnecessary boilerplate to enforce type contracts

    class User { /** @var int $id * / private $id; /** @var string $name * / private $name; public function _ _ construct(int $id, string $name) { $this - > id = $id; $this - > name = $name; } public function getId() : int { return $this - > id; } / / setId, getName and setName implementation . . . }
  93. / / more concise code with same type contracts class

    User { public int $id; public string $name; public function _ _ construct(int $id, string $name) { $this - > id = $id; $this - > name = $name; } }
  94. as propriedades tipadas permitem a garantia de tipos em tempo

    de execução
  95. se uma propriedade tipada não tiver um valor padrão ela

    será considerada não inicializada
  96. / / uninitialized properties and / / default null (PHP

    7.3) class User { public $id; public $name; } $user = new User; var_dump($user); / / class User#1 (2) { / / public $id = > NULL / / public $name = > NULL / / }
  97. / / uninitialized properties and no / / null default

    (PHP 7.4) class User { public int $id; / / no null default public ?string $name; / / also no null default } $user = new User; var_dump($user); / / object(User)#1 (0) { / / ["id"] = > uninitialized(int) / / ["name"] = > uninitialized(?string) / / }
  98. ao tentar fazer a leitura de uma propriedade não inicializada

    será lançado um erro do tipo TypeError
  99. / / try to access a uninitialized property class User

    { public int $id; public string $name; } $user = new User; echo $user - > id; / / Uncaught Error: Typed property User : : $id must 
 / / not be accessed before initialization
  100. Nullable types

  101. um nullable type é um tipo que pode ser de

    um tipo especí fi co ou nulo e é representado por ?Type
  102. / / annotate email property with nullable string type class

    Person { function _ _ construct( public string $name, public ?string $email, ) {} } 
 
 $person1 = new Person('Alice', 'alice@example.com'); $person2 = new Person('Bob', null);
  103. var_dump($person1); var_dump($person2); / / object(Person)#1 (2) { / / ["name"]

    = > string(5) "Alice" / / ["email"] = > string(17) "alice@example.com" / / } / / object(Person)#2 (2) { / / ["name"] = > string(3) "Bob" / / ["email"] = > NULL / / }
  104. Union Types

  105. um union type permite fazer uma anotação de tipos com

    diferentes tipos
  106. o PHP já possui dois tipos especiais de union types:

    nullable types e iterable
  107. o tipo nullable possui a sintaxe ?Type e pode ser

    do tipo Type|null, isto é, Type ou null
  108. o tipo iterable pode ser do tipo array| Traversable, isto

    é, array ou Traversable
  109. declare(strict_types=1); / / using union type to annotate a function

    parameter function power(float|int $number, int $exponent) : int|float { return $number * * $exponent; } echo power(3, 2); / / 9 echo power(3.5, 2); / / 12.25 echo power('3', 2.5); / / Uncaught TypeError: power() : Argument #1 ($number) must be of type int|float, string given
  110. os union types são muito utilizado em projetos open-source e

    funções internas do PHP
  111. o suporte nativo permite a garantia de tipos pelo interpretador

    PHP e reduz a necessida- de de docblocks
  112. Intersection Types

  113. um intersection type permite anotar o tipo de um valor

    que satisfaça múltiplas restrições de tipos ao mesmo tempo
  114. / / using intersection type to annotate a property of

    a test / / class that represents a mocked object class CreateUserTest { private MockObject&UserRepository $userRepositoryMock; }
  115. só é posível utilizar intersection types com classes e interfaces

  116. não é permitido combinar em uma declara- ção union types

    e intersection types
  117. Enums

  118. uma enum de fi ne um novo tipo que possui

    número fi xo e limitado de valores possíveis
  119. / / creates an enum for the suits of a

    deck enum Suit { case Hearts; case Diamonds; case Clubs; case Spades; } $value = Suit : : Hearts; var_dump($value); / / enum(Suit : : Hearts)
  120. por padrão, os casos enumerados não possuem equivalentes escalares, isto

    é, eles são objetos singleton
  121. esse tipo de valor é chamado de pure case e

    uma enum que contém apenas pure cases é chamada de pure enum
  122. uma função ou método podem ser tipados com o tipo

    enum
  123. / / annotate a function parameter with an enum type

    function pick_a_card(Suit $suit) {} pick_a_card($value); / / ok pick_a_card(Suit : : Clubs); / / ok pick_a_card('Spades'); / / Fatal error: Uncaught TypeError: pick_a_card() : Argument #1 ($suit) must be of type Suit, string given
  124. contudo, existem casos de usos em que é necessário ter

    equivalentes escalares para, por exemplo, persistir em uma base de dados
  125. / / create an enum backed with scalar values enum

    Suit: string { case Hearts = 'H'; case Diamonds = 'D'; case Clubs = 'C'; case Spades = 'S'; } var_dump(Suit : : Hearts); / / enum(Suit : : Hearts) var_dump(Suit : : Hearts - > name); / / string(6) "Hearts" var_dump(Suit : : Hearts - > value); / / string(1) "H"
  126. um case que tem um equivalente escalar é chamado de

    backed case pois é “suportado" por um valor mais simples
  127. uma enum que contém backed cases é chamado de backed

    enum
  128. um backed enum implementa a interface interna BackedEnum que expõe

    dois métodos adicionais que são from e tryFrom
  129. o método from() recebe um tipo escalar e retorna o

    case correspondente e, caso o valor não seja encontrado, a exceção ValueError é lançada
  130. o método tryFrom() possui comportamento similar mas, caso o valor

    não seja encontra- do, retorna nulo
  131. / / create an order status enum enum OrderStatus: string

    { case PendingPayment = 'pending_payment'; case Processing = 'processing'; case Completed = 'completed'; case Refunded = 'refunded'; case Cancelled = 'cancelled'; }
  132. / / create an enum value using from() method with

    a valid value $orderStatus = OrderStatus : : from('processing'); var_dump($orderStatus); / / enum(OrderStatus : : Processing) echo $orderStatus - > value; / / processing / / try to create an enum value using from() method with an invalid value $orderStatus = OrderStatus : : from('non_existent'); / / Fatal error: Uncaught ValueError: "non_existent" is not a / / valid backing value for enum "OrderStatus" / / try to create an enum value using tryFrom() method with an invalid value $orderStatus = OrderStatus : : tryFrom('non_existent'); var_dump($orderStatus); / / null
  133. Type Never

  134. o tipo never é um novo tipo de retorno adicionado

    no PHP 8.1
  135. uma função ou método declarados com o tipo de retorno

    never indica que nunca retornará um valor…
  136. …isto é, sempre lançará uma exceção ou terminará com die()

    ou exit()
  137. / / creates a function with never return type function

    redirect(string $url) : never { echo "redirected to $url . . . \n"; header('Location: ' . $url); exit(); } redirect('https: / / w w w .example.com'); / / redirected to https: / / w w w .example.com . . . 
 / / The rest of the code will not be executed! echo 'this will not be shown . . . ';
  138. o objetivo do tipo de retorno never é indicar uma

    função que previne que o resto do código chamado seja executado
  139. se uma função ou método com o tipo de retorno

    never não lançar uma exceção ou não terminar o programa será lançada a exceção TypeError
  140. / / create a function with never return type function

    dispatch(string $message) : never { echo $message; } dispatch('test'); / / Uncaught TypeError: dispatch() : never - returning / / function must not implicitly return
  141. o suporte ao tipo de retorno never torna possível não

    utilizar mais a annotation @return noreturn
  142. Conclusão

  143. o PHP tem tido uma enorme evolução em relação a

    tipos e tem se tornado uma linguagem mais robusta
  144. porém, sem perder a fl exibilidade e a pequena curva

    de aprendizado que o torna uma linguagem tão democrática
  145. as novas funcionalidades ajudarão o seu código a ter mais

    garantias, ser mais expressivo e te dar mais poderes
  146. vá em frente e divirta-se!

  147. Avalie!

  148. @marcelgsantos speakerdeck.com/marcelgsantos Obrigado. Perguntas?