Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Boas práticas com PHP: novidades e como melhorar a escrita de código

Boas práticas com PHP: novidades e como melhorar a escrita de código

Workshop apresentado no Locaweb Digital Conference 2019 em Belo Horizonte, MG, Brasil. (aprox. 2h de duração).

Resumo: O PHP tem mudado muito nos últimos anos. Neste workshop vamos passar brevemente pelas principais mudanças dos últimos 10 anos e discutir algumas boas práticas para tornar seu código mais limpo e aumentar sua produtividade, afinal, código melhor, produto melhor!

Junior Grossi

August 06, 2019
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

  1. PARTE 1 PHP ANTES E DEPOIS Os últimos 10 anos

    da linguagem mais usada na Web!
  2. PHP EM 2019 Linguagem mais usada na Web (~80%) Só

    o WordPress é responsável por ~34% Muito problema pra resolver (mais empregos)
  3. BOOM DE FW: ~2005-2007 O PHP já aprendeu e ainda

    aprende com os erros Muita coisa já veio e já foi Qualquer semelhança com JS é mera coincidência
  4. PHP 5.3 (2009) O DIVISOR DE ÁGUAS NAMESPACES + LAMBDA

    FUNCTIONS/CLOSURES (Mudanças no processo interno de releases do PHP)
  5. NAMESPACES Classes não podiam ter o mesmo nome HtmlPrinter, Zend_HtmlPrinter

    class Zend_HtmlPrinter { public function print(); }
  6. namespace DevStarter\Talks; use Foo\Exporter\Exporter; use Zend\Html\Exporter as ZendExporter; class PhpTalk

    { public function export() { $fooExporter = new Exporter(); $zendExporter = new ZendExporter(); // ... } } https://wiki.php.net/rfc/namespacecurlies
  7. LAMBDA FUNCTIONS/CLOSURES Antes do PHP 5.3 A partir do PHP

    5.3 $array = array_map('append_x', $array); function append_x($item) { return $item . 'x'; } $array = array_map(function ($item) { return $item . 'x'; }, $array);
  8. Nova geração de frameworks (surgimento dos primeiros micro-frameworks) (execução tardia)

    $router­>get('/users/{id}', function ($id) { return User::find($id); }); $function = function ($word) { echo $word . ' World!'; } $function('Hello'); // "Hello World!"
  9. PHP 5.4 Traits Shortened array syntax Melhoria de performance +

    menos consumo de memória Built-in Web Server
  10. TRAITS trait Flyable { public function fly() { // Go,

    fly! } } trait Walkable { public function walk() { // Go, walk! } } class Bird { use Flyable, Walkable; } class Chicken { use Walkable; } $bird = new Bird(); $chicken = new Chicken(); $bird­>walk(); $bird­>fly(); $chicken­>walk();
  11. SHORTENED ARRAY SYNTAX Antes do PHP 5.4 Depois do PHP

    5.4 $numbers = array(1, 2, 3); $associative = array('name' => 'Junior', 'age' => 34); $numbers = [1, 2, 3]; $associative = ['name' => 'Junior', 'age' => 34];
  12. BUILT-IN WEB SERVER Não é necessário mais um Web server

    pra desenvolvimento > php ­S localhost:8000
  13. COMPOSER Resolveu o problema dos require > composer require cakephp/database

    require 'database.php'; require 'pdf.php'; require 'mail.php'; require 'mail­2.php'; require 'mail­98.php'; require 'mail­xp.php'; require 'mail­millenium.php'; $database = db_query('SELECT * FROM users');
  14. /composer.json { "name": "jgrossi/corcel", "description": "Use WordPress backend with Laravel

    or any PHP framework", "homepage": "http://github.com/corcel/corcel", "authors": [ { "name": "Junior Grossi", "email": "[email protected]" } ], "require": { "php": ">=7.1.3", "illuminate/database": "5.7.*", "hautelook/phpass": "0.3.*", "thunderer/shortcode": "^0.6.2" }, "require­dev": { "phpunit/phpunit": "^7.0", "mockery/mockery": "^1.0" }, "autoload": { "psr­4": { "Corcel\\": "src/", "Corcel\\Tests\\": "tests/" } } }
  15. PSR-7 PSR-11 namespace MyFramework\Http; use Psr\Http\Message\RequestInterface; class Request implements RequestInterface

    { } namespace MyFramework\Container; use Psr\Container\ContainerInterface; class Container implements ContainerInterface { }
  16. PHP 5.5 E 5.6 ANTES DA GRANDE MUDANÇA (2013 e

    2014) Adiciona scalar class name resolution via ::class Adiciona finally para exceptions Melhoria de performance (Zend OPcache) Outras mudanças...
  17. PHP 7 (2015) Nova Zend Engine (v3) 2x mais rápido

    que PHP 5 50% menos de consumo de memória
  18. PHP 7.0 UNIFORM VARIABLE SYNTAX // Before $foo = new

    Foo(); $baz = $foo­>bar(); // After $baz = (new Foo())­>bar(); // Before $foo = $foo­>bar(); echo $foo[0]; // After echo $foo­>bar()[0];
  19. PHP 7.0 NULL COALESCE OPERATOR: ?? // Before $age =

    isset($data['age']) ? $data['age'] : 100; // After $age = $data['age'] ?? 100;
  20. PHP 7.0 ⭐ RETURN TYPE DECLARATIONS ⭐ ⭐ SCALAR TYPE

    DECLARATIONS ⭐ (opcional) // Before public function create($sum, $title) { // your code here } // After public function create(float $sum, string $title): float { // your code here }
  21. STRICT TYPES = 1 declare(strict_types=1); namespace Foo\Printer; class CsvPrinter implements

    PrinterInterface { public function __construct(string $foo, float $bar) { // ... } }
  22. PHP 7.0 GROUP USE DECLARATIONS // Before use Package\Http\Request; use

    Package\Http\Response; use Package\Http\Middleware; use Package\Http\BaseController as Controller; // After use Package\Http\{ Request, Response, BaseController as Controller };
  23. PHP 7.1, 7.2 E 7.3 void return type class constant

    visibility modifiers nullable types the object parameter and return type Libsodium extension abstract method overriding
  24. class User { private const TYPE_ADMIN = 'admin'; protected const

    TYPE_MEMBER = 'member'; public const TYPE_GUEST = 'guest'; } public function save(object $object, ?int $age): void { // do something and return nothing }
  25. NOVIDADES: PHP 7.4 NOV/2019 Preloading* Short closures Typed properties Improved

    type variance The null coalescing assignment operator muito mais!
  26. PHP 7.4 SHORT CLOSURES array_map(function (User $user) use ($prefix) {

    return $prefix . $user­>id; }, $users) array_map(fn(User $user) => $prefix . $user­>id, $users)
  27. PHP 7.4 TYPED PROPERTIES ANTES class Foo { /** @var

    int $bar */ private $bar; /** @var callable $bar */ private $function; /** @var string|null $bar */ protected $baz; /** @var array $bar */ public $list; }
  28. PHP 7.4 TYPED PROPERTIES DEPOIS class Foo { private int

    $bar = 4; private callable $function; protected ?string $baz = null; public array $list = [1, 2, 3]; }
  29. CONSIDERAÇÕES FINAIS PHP é uma das linguagens mais maduras Excelente

    performance (e vai melhorar!) Desenvolvimento muito rápido!
  30. LIVRO Clean Code A Handbook of Agile So ware Cra

    smanship Robert C. Martin (Uncle Bob)
  31. AGENDA Resumo dos principais tópicos Não dá pra cobrir tudo

    Focado em PHP quando possível! Alguns detalhes vão ser explicados mais à frente
  32. CAPÍTULO 1 Clean Code "You are reading this book for

    two reasons. First, you are a programmer. Second, you want to be a better programmer. Good. We need better programmers."
  33. "CODE-SENSE" "A programmer without "code-sense" can look at a messy

    module and recognize the mess but will have no idea what to do about it. A programmer with "code-sense" will look at a messy module and see options and variations." Uncle Bob
  34. SEGUNDO UNCLE BOB: Escrever código limpo requer disciplina O "code-sense"

    é a chave! Alguns de nós nascem com o "code-sense" Outros só com a prática!
  35. ALGUNS PONTOS Passamos mais tempo lendo código Sucesso relacionado com

    o entendimento (o entendimento diminui ao longo do tempo)
  36. CAPÍTULO 2 Meaningful Names "Names are everywhere in so ware.

    We name our variables, our functions, our arguments, classes, and packages. We name our source files and the directories that contain them. We name and name and name."
  37. class DtaRcrd102 { private DateTime $genymdhsm; private DateTime $modymdhms; private

    string $pszqint = "102"; } class Customer { private DateTime $generationTimestamp; private DateTime $modificarionTimestamp; private string $recordId = "102"; }
  38. NOMES "BUSCÁVEIS" / CONSTANTES for ($i = 0; $i <

    5; $i++) { // ... } for ($i = 0; $i < WORK_DAYS_PER_WEEK; $i++) { // ... }
  39. EVITE PREFIXOS/SUFIXOS DESNECESSÁRIOS (As pessoas geralmente "aprendem" a ignorar prefixos

    e sufixos muito rapidamente.) $txtFirstName = $controller­>request()­>get('txt_FirstName');
  40. INTERFACES E IMPLEMENTAÇÕES Evitar informação demais pro usuário (dev) (estou

    passando uma interface ou classe concreta?) PSR Naming Conventions | Symfony UserInterface, AbstractResolver, ReusableTrait
  41. EVITE O "MAPA MENTAL" Não obrigue o usuário a tentar

    entender o que está escrito Buscar entender pelo contexto é muito mais demorado!
  42. NOMES DE CLASSES "Classes and objects should have noun or

    noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb." (resumindo: nome de classe = substantivo)
  43. NOMES DE MÉTODOS Devem ser verbos Utilize Named Constructors (mais

    significado) Accessors (get), Mutators (set) e predicates (is)
  44. UMA PALAVRA POR CONCEITO Nunca misture palavras de conceitos similares

    fetch, retrieve, get DeviceController, DeviceManager
  45. USE NOMES PRESENTES EM PADRÕES Todo dev sabe (ou deveria)

    saber Design Patterns Facilita o entendimento sobre a solução implementada AccountVisitor, ConsoleDecorator, UserFactory
  46. NÃO ADICIONE CONTEXTO DESNECESSÁRIO Contexto deve ficar no namespace Nome

    de classe deve resumir o que ela faz GSDAMailingAddress, GSDAAccountAddress
  47. CAPÍTULO 3 Functions "In the early days of programming we

    composed our systems of routines and subroutines. Nowadays only the functions survives from those early days. Functions are the first line of organization in a any program."
  48. BLOCOS E INDENTAÇÃO Cada bloco de uma linha (chamada de

    método) Métodos com 1 ou 2 níveis de indentação Muito mais fáceis de entender
  49. FAÇA UMA COISA Devem fazer apenas uma coisa (e fazer

    bem) Nomes muito grande podem necessitar ser refatorados Palavras de alerta: And, Or, If
  50. "STEPDOWN RULE" Leitura fluente como texto if (!$user = $this­>findUser())

    { // ... } $this­>save($user); $user = $this­>findUser(); if (!$user) { // ... } $this­>save($user);
  51. private function something(): bool { return !$this­>foo() || $this­>bar(); }

    private function something(): bool { if (!$this­>foo()) { return false; } if ($this­>bar()) { return true; } return false; }
  52. switch Tolerados se aparecerem apenas uma vez Pensar em AbstractFactory

    se mais de uma vez Cuidado para não ferir SRP
  53. USE NOMES DESCRITIVOS Word's principle: "You know you are working

    on clean code when each routine turns out to be pretty much what you expected." Não tenha medo de escrever nomes longos Escreva exatamente o que está sendo feito
  54. ARGUMENTOS Nenhum é sempre melhor 1 ou 2 aceitáveis /

    3 deve ser evitado (aumento do número de argumentos dificulta escrever testes)
  55. "Flag Arguments" nunca (boolean) Deixa claro que faz mais de

    uma coisa Refatore para um método para cada coisa
  56. Encapsule argumentos em objetos (dá mais significado ao argumento) private

    function makeCircle(float $x, float $y): Circle { // ... } private function makeCircle(Point $point): Circle { // ... }
  57. Verbos e palavras-chave (dá mais significado ao argumento) public function

    find(Email $email): User { // ... } public function findUserByEmail(Email $email): User { // ... }
  58. PREFIRA EXCEPTIONS São melhores que códigos de erros try/catch em

    método separado $user = $this­>getLoggedUser(); public function getLoggedUser(): ?User { try { return $this­>userResolver­>user(); } catch (EmptyTokenException $e) { return null; } }
  59. "Truth can only be found in one place: the code.

    Only the code can truly tell you what it does."
  60. /** * Returns the day of the month. * *

    @return int the day of the month. */ public function getDayOfMonth(): int { return $this­>dayOfMonth; }
  61. /** The first name */ private string $firstName; /** The

    last name */ private string $lastName; /* Added by Junior */ $response = new InputStreamResponse(); $response­>setBody($formatter­>getResultStream()); // $resultStream = $formatter­>getResultStream(); // $reader = new StreamReader($resultStram);
  62. Escolhe um code style e o siga Agrupe blocos de

    linhas no mesmo contexto Declare variáveis próximo da utilização Variáveis de instância sempre no topo da classe
  63. Funções dependentes próximas Não alinhe horizontalmente class FitNesseExpediter implements ResponseSender

    { private Socket $socket; private InputStream $input; private OutputStream $output; private Request $request; private Response $response; private FitNesseContext $context; private float $requestParsingTimeLimit; protected float $requestProgress; }
  64. CAPÍTULO 6 Objetos e Estrutura de Dados "There is a

    reason that we keep our variables private. We don't want anyone else to depende on them. We want to keep the freedom to change their type or implementation on a whim or an impulse."
  65. Esconder implementação expões abstrações Manipular a essência dos dados por

    abstrações OOP permite adicionar novas classes sem mudar as existentes
  66. LEI DE DEMETER "Fale com amigos, não com estranhos." Encapsule

    o máximo possível. Exponha apenas o necessário.
  67. class Address implements ResponseSender { private string $street; private string

    $city; private Country $country; public function __construct( string $street, string $city, Country $country ) { $this­>street = $street; $this­>city = $city; $this­>country = $country; } // Only getters }
  68. CAPÍTULO 7 Error Handling "...things can go wrong, and when

    they do, we as programmers are responsible for making sure that our code does what it needs to do."
  69. Exceptions sempre. Código de erro nunca! Escreva o bloco try/catch

    primeiro (facilita pensar em todas as possíveis exceções)
  70. Exceptions como primeira opção Trate a exceção na origem public

    function resolveUser(): User { $user = $this­>findInRequest(); if (!$user) { throw new UserNotFoundException(); } return $user; } try { $user = $resolver­>resolveUser(); } catch (UserNotFoundException $e) { return new Response(/**/); }
  71. DEFINA CONTEXTO public static function byEmail(string $email): self { return

    new self( sprintf('User not found with the email %s', $email) ); } throw UserNotFoundException::byEmail($user­>getEmail());
  72. CAPÍTULO 9 Unit Tests "The Agile and TDD movements have

    encouraged many programmers to write automated unit tests, and more are joining their ranks every day. But in the mad rush to add testing to our discipline, many programmers have missed one of the more subtle, and important, points of writing good tests."
  73. AS 3 LEIS DO TDD 1. Você não deve escrever

    código antes de ter escrito um teste pra ele que irá falhar. 2. Você não deve escrever mais de um teste unitário que seja suficiente para passar. 3. Você não deve escrever mais código do que o necessário para fazer o teste passar.
  74. TESTES ANTES OU DEPOIS? Antes! Ciclo 1, 2, 3 que

    irá cobrir todos os casos possíveis, incluindo de falhas. Escreva um teste e o faça falhar! Adicione aquele if maroto e faça o teste passar!
  75. CLEAN TESTS? Clean Code também para testes Crie "helpers" em

    forma de trait para adicionar funcionalidades extras trait JwtAuthenticationTrait { use HttpRequestsTrait; public function loginUser(User $user): void { $serializedToken = $this­>generateTokenForUser($user); $this­>request­>withHeader( 'HTTP_AUTHORIZATION', sprintf('Bearer %s', $serializedToken) ); } }
  76. DICAS PARA CLEAN TESTS Apenas um assert por teste (customize!)

    (nomes de testes mais claros) Apenas um conceito por teste (cada caso possível é um novo teste)
  77. Teste com usuário admin + teste com não admin public

    function test_only_admins_can_change_user_type(): void {} public function test_admin_users_can_change_user_type(): void {} public function test_non_admin_users_cannot_change_user_type(): void
  78. "FIRST" RULE Fast: testes devem ser rápidos Independent: não devem

    compartilhar detalhes Repeatable: independentes de ambiente Self-Validating: ou passou ou não passou (boolean) Timely: escreva testes sempre antes (depois + difícil de testar)
  79. BÔNUS! Regra dos 3As (Arrange, Act, Assert) public function test_admin_users_can_change_user_type():

    void { // Arrange $admin = $this­>makeAdminUser(); $user = $this­>makeNormalUser(['type' => User::TYPE_MEMBER]); $this­>loginAsUser($admin); // Act $formatter = new Formatter(); $user = $formatter­>format($user); // Assert $this­>assertEquals(User::TYPE_MEMBER, $user­>getType()); }
  80. ENCAPSULAMENTO private por padrão protected quando necessário (herança ou testes)

    Foco sempre em encapsular primeiro! (use abstrações)
  81. CLASSES PEQUENAS Evite "God Classes" (SRP) Tamanho é determinado pelo

    nome! (evite Manager ou Processor) Caso feliz: ter um único método público (SRP) (facilita muito abstração)
  82. COESÃO Busque sempre alta coesão (alto relacionamento entre métodos e

    propriedades) Poucas variáveis de instância por classe (favorece SRP!)
  83. COESÃO ENTRE CLASSES Use e abuse de Dependency Injection (favorece

    SRP!) Favorece DRY (Don't Repeat Yourself)
  84. Nada de TokenManager ou coisas do tipo! OCP - Open-close

    Principle final class JwtTokenGenerator implements TokenGeneratorInterface {} final class IntegrationTokenGenerator implements TokenGeneratorInterface { private TokenGeneratorInterface $tokenGenerator; public function __construct(TokenGeneratorInterface $tokenGenerator) { $this­>tokenGenerator = $tokenGenerator; } }
  85. CAPÍTULO 12 Emergent Design "What if there were four simple

    rules that you could follow that would help you create good designs as you worked?"
  86. REGRA 1 RODE TODOS OS TESTES Continuamente (Loop 1, 2,

    3) Incrementalmente (Continuous Integration)
  87. REGRA 2 A 4 REFATORE Remova código, não só adicione!

    "The fact that we have these tests eliminates the fear that cleaning up the code will break it!" TDD (testes ANTES! coverage!)
  88. REGRA 2: ELIMINE DUPLICIDADE "Duplication is the primary enemy of

    a well-designed system!" Use e abuse de SRP!
  89. REGRA 3: GARANTA EXPRESSIVIDADE Escolha de bons nomes Utilização de

    Design Patterns Facilite a vida do próximo dev! Ele pode ser você!
  90. REGRA 4: MINIMIZE CLASSES E MÉTODOS Regra menos importante! Não

    exagere na SRP! Não tenha classes ou métodos demais
  91. CONCLUSÕES Qualidade é um execício diário Comece a praticar (experiência)

    Melhoria de processos Diminuição de complexidade