PHP

O que é Symfony?

Software livre sobre a licença MIT, o Symfony é um Framework web escrito em PHP, lançado em 2005.

Sua comunidade conta com mais de 600.000 desenvolvedores de mais de 120 países, tornando a implementação de novas funcionalidades ao Framework um processo rápido.

É um Framework amplamente conhecido por ter sido um dos primeiros a trabalhar com componentes, possibilitando o reuso do seu código em diversos projetos open sources. Os principais pontos em sua filosofia são:

  • A utilização de melhores práticas de desenvolvimento;
  • A criação de aplicações profissionais, e;
  • A interoperabilidade de aplicações.

Padrão MVC

Utiliza o padrão de projeto MVC, que é basicamente:

  • Model: Parte lógica da aplicação que gerencia o comportamento dos dados. A camada de model apenas tem o necessário para que tudo aconteça, mas não sabe quando irá executar.

  • View: Gerencia a saída gráfica e textual da aplicação ao usuário final, não possuindo a responsabilidade de saber quando vai exibir os dados, apenas como irá exibi-los.

  • Controller: Essa é a camada que sabe quem chamar e quando chamar para executar determinada ação, comandando a visão e o modelo para se alterarem de forma apropriada.

Basicamente, o MVC funciona da seguinte forma:

Ao receber uma requisição, o Controller solicita ao Model as informações necessárias (que provavelmente virão do banco de dados), que as obtém e retorna ao Controller. De posse dessas informações, o Controller as envia para a View que irá renderizá-las.

Vantagens do Symfony

  • Orientado a objetos;
  • Possui ferramentas de depuração;
  • Suporte para ambientes de implantação;
  • Recursos avançados de segurança;
  • Documentação completa e bem explicada;
  • ORM: Fácil integração com Doctrine;
  • Integração com PHPUnit;
  • Versões mantidas por um longo período.

Componentes

Os componentes são um conjunto de bibliotecas PHP desacopladas e reutilizáveis, permitindo que sejam utilizadas em qualquer projeto PHP independentemente do Symfony Framework.

Dentre os vários componentes do Symfony, podemos citar:

  • DependencyInjection – Permite padronizar e centralizar a maneira como os objetos são construídos em sua aplicação;
  • Debug – Fornece ferramentas para facilitar a depuração de código PHP;
  • Form – Fornece ferramentas para facilitar a criação, processamento e reutilização de formulários HTML;
  • Mailer – Ajuda a implementar o envio de e-mails e fornece integração com os principais serviços.

Muitos outros componentes podem ser utilizados para te auxiliar na realização de suas aplicações. A lista de todos os componentes do Symfony você pode acessar neste link.

Os componentes do Symfony são usados nos principais projetos PHP. Alguns projetos importantes que fazem o uso são:

  • Laravel Framework
  • Yii Framework
  • Drupal
  • Joomla
  • Magento

É possível ver a lista completa de projetos que usam componentes do Symfony aqui.

Framework ou Micro-Framework?

A partir da versão 4 o Symfony possui uma ferramenta chamada Flex. Ela realiza a instalação e configuração de pacotes de uma maneira extremamente simples e automática. Isso permite ao desenvolvedor iniciar um projeto com o mínimo de dependências e “plugar” novas funcionalidades quando necessário. Baseado nesse recurso quando vamos instalar o Symfony podemos escolher entre uma aplicação mínima com características de micro-framework ou uma aplicação completa, com características de framework.

Quando usamos a estrutura completa do Symfony temos as principais características de um framework:

  • Completo;
  • Integrado;
  • Padronizado;
  • Fácil de deployar.

Quando usamos a estrutura mínima, podemos citar as seguintes características:

  • Leve;
  • Altamente customizável;
  • “Rápido”;
  • Ideal para cenários com micro-serviços.

Ou seja, desta forma, o Symfony traz o melhor dos dois mundos onde atenderá desde aplicações menores a aplicações mais robustas.

Caso ainda não tenha ficado claro a diferença entre Framework e Micro-Framework, temos aqui no blog dois artigos bem legais sobre o assunto. Basta clicar sobre os links. 🙂

Atualizações e Suporte

O Symfony possui cronogramas muito bem definido de manutenção de correções e suporte para cada uma das versões lançadas. Isso garante uma segurança maior, principalmente para projetos corporativos.

As versões comuns contam com 6 meses de suporte a correções e 1 ano de suporte de segurança, enquanto as versões LTS (long term support) 3 anos de suporte para correção e 4 anos de suporte para segurança, sendo a segunda a versão mais indicada para uso em produção.

A cada 6 meses uma nova versão minor é lançada, conforme o cronograma acima. Atualmente estamos na versão 4.3, a versão LTS atual é a 3.4 e a próxima versão LTS é a 4.4 que será lançada em novembro.

Concluindo

O Symfony é um ótimo framework para a criação de aplicações, independente do seu tamanho. Neste artigo vimos algumas de suas principais características.

Promises no ReactPHP

No último artigo – Introdução à programação assíncrona em PHP usando o ReactPHP – tivemos uma introdução à programação assíncrona com PHP.

No artigo de hoje vamos comentar sobre Promises, que no ReactPHP trata-se de um componente. Promises no ReactPHP são uma implementação da API CommonJS Promises/A. Se você trabalha com JavaScript, vai notar muita similaridade com o que veremos a seguir.

Afinal, o que é uma Promise?

Uma Promise é uma abstração que encapsula um resultado de uma execução assíncrona para ser utilizado quando ele estiver disponível. Promises desempenham a mesma tarefa que os Callbacks dos quais já estamos acostumados, só que de maneira bem mais elegante (principalmente quando lidamos com múltiplos callbacks de forma encadeada).

Dois conceitos importantes que temos que conhecer: Promise e Deferred. Já vimos que uma Promise representa o resultado de um código assíncrono, já Deferred representa a computação que vai gerar esse resultado em algum momento futuro. Um objeto Deferred sempre vai ter uma Promise associada que vai representar (encapsular) o resultado. Usar um objeto Deferred é uma forma de separar a Promise de quem vai resolvê-la ou rejeitá-la (em algum momento futuro).

Uma Promise possui três estados possíveis:

  • unfulfilled: É o estado inicial, pois o valor retornado de Deferred ainda é desconhecido.
  • fulfilled: Esse estado indica que a Promise está devidamente “alimentada” do resultado da computação de Deferred.
  • failed: Houve uma exceção durante a execução do Deferred (o executor pode ter chamado reject()).

Já um objeto Deferred possui dois métodos para mudar o estado da sua Promise:

  • resolve(string $result): Quando o código é executado com sucesso, esse método altera o estado da Promise para fulfilled (enviando para ela o resultado que ela encapsulára);
  • reject(string $reason): A execução do código falhou, altera o estado da Promise para failed.

Uma Promise também possui métodos para que definamos handlers (callbacks a serem executados) para as mudanças de estados dela, são esses: then(), done(), otherwise() e always().

Antes de iniciarmos, certifique-se de instalar o componente React Promise no seu projeto:

$ composer require react/promise

Vamos então ao primeiro exemplo do uso de um objeto Deferred e de sua Promise:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->done(function ($data) {
    echo 'Resultado: ' . $data;
});

$deferred->resolve('Olá mundo!'); // Resultado: Olá mundo!

Criamos um objeto Deferred e definimos um handler para o método done() da Promise associada a ele. Por fim, resolvemos a operação usando o método resolve(), método este que “altera” o estado da Promise (para fulfilled) e fez o nosso handler (a função anônima que definimos no primeiro argumento do método done()) ser executado.

No caso de o objeto Deferred explicitamente rejeitar uma operação alterando o estado da Promise para failed, podemos usar o segundo argumento do método done() para definir um handler a ser executado em caso de rejeição:

O método done() aceita como argumento handlers tanto para o estado fulfilled quanto para o failed:

public function done(callable $onFulfilled = null, callable $onRejected = null)

No primeiro argumento informamos o handler que será executado quando o estado da Promise mudar para fulfilled, no segundo argumento, um handler para ser executado quando o estado da Promise for alterado para failed.

Por exemplo, vamos informar um handler pro segundo argumento da Promise e então vamos executar o método reject() do Deferred:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->done(function ($data) {
    echo 'Resultado: ' . $data;
}, function($reason) {
    echo 'Motivo da falha: ' . $reason;
});

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Portanto, temos a seguinte relação:

Promises podem ser encadeadas, ou seja, o valor de uma promise resolvida pode ser encaminhado para a próxima da sequência. Isso pode ser atingido usando os seguintes métodos: then() e otherwise(). O método then() é parecido com o done(), com a diferença que ele retorna uma nova promise, enquanto o done() sempre retorna null. E o método otherwise() é uma forma de definir um handler para quando o estado da Promise for failed (e ele retorna uma nova Promise).

Poderíamos, então, trocar o done() por then() no exemplo anterior:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise->then(function ($data) {
    echo 'Resultado: ' . $data;
}, function($reason) {
    echo 'Motivo da falha: ' . $reason;
});

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Ou, uma vez que then() retorna uma nova Promise, poderíamos usar o otherwise() de forma encadeada:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();
$promise = $deferred->promise();

$promise
    ->then(function ($data) {
        echo 'Resultado: ' . $data;
    })
    ->otherwise(function($reason) {
        echo 'Motivo da falha: ' . $reason;
    });

$deferred->reject('Erro interno'); // Motivo da falha: Erro interno

Outro exemplo com o encadeamento de Promises:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        return "{$data}Web";
    })->then(function($data) {
        return strtoupper($data);
    })->then(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // OLÁ, TREINAWEB!

Na programação assíncrona faz muito sentido uma Promise retornar outra Promise, pois não temos o resultado da operação de imediato (quando envolve I/O), o que temos é uma “promessa” de que em algum momento ele poderá vir a estar disponível. Nesse sentido, then() é um mecanismo para aplicar uma transformação em uma Promise e gerar uma nova Promise a partir dessa transformação.

Outro exemplo de encadeamento de then() com otherwise():

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        throw new InvalidArgumentException("{$data}Web");
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // OLÁ, TREINAWEB!

Nesse exemplo o nosso handler em otherwise() só vai ser invocado se ele receber uma exceção do tipo InvalidArgumentException (e fizemos ele receber).

Outro exemplo:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        throw new RuntimeException("{$data}Web");
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->otherwise(function(Exception $exception) {
        return strtolower($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // olá, treinaweb!

Nesse exemplo, como a promise lança uma exceção do tipo RuntimeException, apenas o handler que está esperando por uma exceção genérica (do tipo Exception) será invocado:

...
})->otherwise(function(Exception $exception) {
  return strtolower($exception->getMessage());
})
...

E, claro, se nenhuma promise passar pelo estado failed os nossos handlers que lidam com as falhas não serão invocados:

<?php

require './vendor/autoload.php';

use React\Promise\Deferred;

$deferred = new Deferred();

$deferred->promise()
    ->then(function ($data) {
        return "Olá, {$data}";
    })->then(function($data) {
        // throw new RuntimeException("{$data}Web");
        return "{$data}Web";
    })->otherwise(function(InvalidArgumentException $exception) {
        return strtoupper($exception->getMessage());
    })->otherwise(function(Exception $exception) {
        return strtolower($exception->getMessage());
    })->done(function($data) {
        echo "{$data}!";
    });

$deferred->resolve('Treina'); // Olá, TreinaWeb!

Agora que já entendemos o funcionamento do objeto Deferred e como as Promises funcionam, podemos verificar como seria resolver (ou rejeitar) uma Promise sem um objeto Deferred:

<?php

require './vendor/autoload.php';

use React\Promise\Promise;

$promise = new Promise(function(Closure $resolve, Closure $reject) {
    if (\random_int(1, 1000000) % 2 === 0) {
        $resolve('Gerou um número par.');
    } else {
        $reject('Gerou um número ímpar.');
    }
});

$promise->then(function($data) {
    echo 'Sucesso: ' . $data;
})->otherwise(function($reason) {
    echo 'Falha: ' . $reason;
});

Nesse exemplo instanciamos um objeto Promise passando para ele uma função anônima que vai cuidar da computação que precisamos realizar. Apenas para fins didáticos, estamos gerando um número inteiro aleatório e então verificamos se ele é par, se verdadeiro, resolvemos a Promise, caso contrário, a rejeitamos. Nisso que passamos a função anônima no construtor da classe Promise, ela é invocada e recebe duas novas funções anônimas como argumento: $resolve e $reject. São essas funções que invocamos no término da nossa operação para decidir o estado da Promise, se vai ser fulfilled ou failed. Por fim, apenas definimos handlers que serão executados em caso de sucesso ou falha e, para isso, usamos os métodos then() e otherwise().

Observe que a diferença dessa abordagem (de instanciar um objeto Promise diretamente) para a que usa um objeto Deferred, é que nessa última invertemos o controle de quem resolve a Promise, ou seja, essa responsabilidade fica com o objeto Deferred.

Agora que já vimos o essencial do funcionamento das promises, podemos fazer um paralelo de como seria um código que usa callbacks versus um que usa promises (usando JavaScript como linguagem de referência):

request('http://www.treinaweb.com.br', function (error, response) {
    if (error) {
        // Aqui a gente lida com o erro.
    } else {
        request('http://www.treinaweb.com.br/' + response.path, function (error, response) {
            if (serror) {
                // Aqui a gente lida com o erro.
            } else {
                // Aqui lidaríamos com o sucesso da requisição.
            }
        });
    }
});

Nesse modelo estamos sempre aninhando callbacks uns dentro dos outros, o que nos torna suscetíveis ao mal do callback hell. O mesmo exemplo acima usando a biblioteca axios que trabalha com Promises ficaria assim:

axios.get('http://www.treinaweb.com.br')
    .then(function (response) {
        return axios.get('http://www.treinaweb.com.br/' + response.path);
    })
    .then(function (response) {
        // Lida com a resposta da requisição anterior
    })
    .catch(function (error) {
        // Lida com alguma exceção, se existir.
    });

No artigo de introdução ao ReactPHP mostramos um exemplo que usava a library reactphp-buzz. Veremos outro exemplo com ela, pois ela faz uso intensivo de promises. Primeiro instale-a como dependência do projeto:

$ composer require clue/buzz-react:^2.6

O nosso exemplo vai de forma assíncrona imprimir o bairro de alguns CEPs usando a API pública do Postmon:

<?php

require './vendor/autoload.php';

use Clue\React\Buzz\Browser;
use React\EventLoop\Factory;
use Psr\Http\Message\ResponseInterface;

$browser = new Browser(
    $loop = Factory::create()
);

$ceps = [
    '01311200', // Bela Vista
    '70630904', // Setor Militar Urbano
    '70165900', // Zona Cívico-Administrativa
    '32685888', // Erro, cep não existe.
];

foreach ($ceps as $cep) {
    $browser->get("https://api.postmon.com.br/v1/cep/{$cep}")
        ->then(function (ResponseInterface $response) {
            $endereco = \json_decode($response->getBody());

            echo $endereco->bairro . PHP_EOL;
        })
        ->otherwise(function (\Exception $exception) use ($cep) {
            echo 'Erro no CEP: ' . $cep . PHP_EOL;
        });
}

$loop->run();

Esse exemplo demonstra que o método get() que realiza uma requisição HTTP retorna uma Promise. Se você executar esse exemplo várias vezes, verá que não terá uma ordem definida para a impressão dos resultados, ademais, um resultado só é impresso quando ele fica pronto, não seguindo uma ordem linear (que é o que estamos acostumados na programação síncrona).

Para finalizar, é importante pontuar que Promises por si só não fazem nossos códigos serem assíncronos, elas são apenas mecanismos para encapsular os resultados.

Até a próxima!

O que é Laravel?

O Laravel é um dos Frameworks PHP mais utilizado no mercado.

Caso você não saiba:

Um framework é um facilitador no desenvolvimento de diversas aplicações e, sem dúvida, sua utilização poupa tempo e custos para quem o utiliza, pois de forma mais básica, é um conjunto de bibliotecas utilizadas para criar uma base onde as aplicações são construídas, um otimizador de recursos. Tem como principal objetivo resolver problemas recorrentes com uma abordagem mais genérica. Ele permite ao desenvolvedor focar nos “problemas” da aplicação, não na arquitetura e configurações.

Aqui no blog também temos um artigo bem legal sobre: Para que serve um Framework?

História do Laravel

Desenvolvido por Taylor B. Otwell, tendo sua primeira versão beta lançada em meados de Junho de 2011, o Laravel é um Framework Open Source sob a licença MIT, criado com o propósito de ser uma alternativa mais avançada do CodeIgniter. Atualmente, se encontra na versão 5.8, tendo seu código-fonte hospedado no GitHub.

Recursos do Laravel

Dentre os diversos recursos do Laravel, podemos citar como principais:

  • Sistema de template (Blade)

O Laravel possui um sistema de template que facilita a criação da camada de visualização de dados (Páginas HTML). Com ele, podemos facilmente criar páginas simples e intuitivas de forma rápida e eficaz. Dentre alguns dos recursos do Blade, se destacam: Herança de layouts, sistema de tags, seções e uso de código PHP nos templates.

  • Módulo de autenticação

O Laravel possui, por padrão, um módulo de autenticação/autorização completo que provê todos os recursos para sua implementação, como: Autenticação de usuários, autorização de usuários, recuperação de senhas, logout, controle de sessão e cadastro de usuários.

  • Eloquent ORM

Com o Laravel não precisamos criar códigos SQL para manipular ou criar tabelas no Banco de Dados. Todo processo é feito utilizando código PHP que, posteriormente, será convertido em instruções SQL. Implementa o padrão Active Record, onde cada model da aplicação representa uma tabela no banco de dados.

Padrão MVC

O Laravel utiliza o padrão MVC (Model, View e Controller) que, basicamente, funciona da seguinte forma:

  • Model é a camada responsável pela parte lógica da aplicação, ou seja, todos os recursos da sua aplicação (consultas ao BD, validações, notificações, etc), mas ele não sabe quando isso deve ser feito, a camada de model apenas tem o necessário para que tudo aconteça, mas não sabe quando irá executar.

  • View é a camada responsável por exibir dados para o usuário, seja em páginas HTML, JSON, XML, etc. A camada View não possui responsabilidade de saber quando vai exibir os dados, apenas como irá exibi-los.

  • Controller é o famoso “meio-de-campo” da aplicação. Essa é a camada que sabe quem chamar e quando chamar para executar determinada ação.

Basicamente, o MVC funciona da seguinte forma:

Ao receber uma requisição, o Controller solicita ao Model as informações necessárias (que virão do banco de dados), que as obtém e retorna ao Controller. De posse dessas informações, o Controller as envia para a View que irá renderizá-las.

Vantagens em utilizar o Laravel

  • Simples e fácil;
  • Documentação completa;
  • Amplamente utilizado;
  • Comunidade ativa;
  • Gratuito.

Concluindo:

O Laravel é um excelente Framework para desenvolvimento de aplicações web. Neste artigo vimos algumas de suas principais características e funcionalidades, o que o torna uma excelente escolha para a criação dos seus projetos.

Autowiring em Container de Injeção de Dependência

No artigo Entendendo Injeção de Dependência vimos sobre o que é injeção de dependência, seu funcionamento e como se dá a sua aplicação. No artigo Container De Injeção De Dependência (DI Container) vimos como funciona um container para gerenciar o mapa de dependências.

Nesse artigo veremos uma funcionalidade relativamente comum nos containers de dependência de frameworks que é a habilidade de resolver as dependências de um construtor (principalmente) automaticamente, chamada de autowiring.

No artigo Container De Injeção De Dependência (DI Container) implementamos um protótipo (simples, porém funcional) de um container e, em determinado momento, chegamos nesse tipo de caso de uso:

$indexController = new IndexController(
    $container->get('user.repository')
);

$userController = new UserController(
    $container->get('user.repository')
);

$registerController = new RegisterController(
    $container->get('user.repository')
);

Alguns controladores precisavam receber a instância de UserRepository e tivemos que manualmente passá-la para cada um deles. Agora, imagine se cada um desses controladores tivesse que receber outras dependências? Nenhum problema, só que ficaria meio chato e improdutivo ter que ficar repetindo essas construções na instanciação deles, concorda?

E se desenvolvermos uma forma do nosso container automaticamente resolver essas dependências? Desse modo, tudo o que faríamos seria pedir para o Container uma instância desses controladores, sem a necessidade de nos preocuparmos em passar manualmente para o construtor de cada um deles as suas dependências. Teríamos como resultado um código assim:

$indexController = $container->get(IndexController::class);

$userController = $container->get(UserController::class);

$registerController = $container->get(RegisterController::class);

Estamos pedindo as instâncias desses controladores sem nos preocuparmos em alimentá-los de suas dependências, deixamos esse trabalho para o Container.

Estrutura inicial do projeto

Vamos criar o protótipo de uma aplicação para que possamos testar a nossa implementação de Container. A nossa aplicação terá essa estrutura:

- [blog-artigo-di]
- - - [app]
- - - - [Http]
- - - - - - [Controller]
- - - - - - - UserController.php
- - - - [Repositories]
- - - - - LeadRepository.php
- - - - - TagRepository.php
- - - - - UserRepository.php
- - - Container.php
- index.php

O repositório dessa estrutura no GitHub você encontra clicando aqui. Se desejar, você pode fazer download direto dela clicando nesse link: sem-autowiring.zip

Depois de baixar e colocar no local onde normalmente você executa seus projetos, basta que você execute composer install pois o projeto faz uso da PSR-4 para autoloading:

$ ~/D/w/blog-artigo-di> composer install

O nosso Container atualmente possui uma implementação simples e que ainda não suporta a resolução automática de dependências. Abra app/Container.php:

<?php
declare(strict_types=1);

namespace App;

use Closure;

final class Container
{
    private $instances = [];

    public function set(string $id, Closure $closure) : void
    {
        $this->instances[$id] = $closure;
    }

    public function get(string $id) : object
    {
        return $this->instances[$id]($this);
    }

    public function singleton(string $id, Closure $closure) : void
    {
        $this->instances[$id] = function () use ($closure) {
            static $resolvedInstance;

            if (null !== $resolvedInstance) {
                $resolvedInstance = $closure($this);
            }

            return $resolvedInstance;
        };
    }
}

Esse container já foi explicado na indicação de leitura no início desse artigo. Portanto, não vamos entrar novamente em seus pormenores.

O arquivo index.php na raiz do projeto é o responsável pelo bootstrap da nossa aplicação protótipo:

<?php

// Carrega o autoload do Composer
require './vendor/autoload.php';

use App\Repositories\TagRepository;
use App\Repositories\LeadRepository;
use App\Repositories\UserRepository;
use App\Http\Controller\UserController;

// Instancia o container
$container = new App\Container();

// Adiciona referências ao container
$container->set(TagRepository::class, function() {
    return new TagRepository();
});

$container->set(LeadRepository::class, function() {
    return new LeadRepository();
});

$container->set(UserRepository::class, function() {
    return new UserRepository();
});

// Instancia o UserController passando para ele as dependências necessárias
$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

$userController->index();

Para testar o exemplo, na raiz do projeto execute:

$ ~/D/w/blog-artigo-di> php -S localhost:8000

Isso executará o servidor embutido do PHP em cima da raiz do nosso projeto, permitindo que o acessemos pelo navegador através da URL: http://localhost:8000/

O resultado será:

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 1
  'name' => string 'tag' (length=3)

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 2
  'name' => string 'user' (length=4)

/Users/kennedytedesco/Documents/www/blog-artigo-di/app/Http/Controller/UserController.php:31:
array (size=2)
  'id' => int 3
  'name' => string 'lead' (length=4)

Indica que o método index() do UserController foi executado com sucesso. Ou seja, as dependências, os repositórios necessários para o funcionamento dessa classe foram injetados com sucesso. Até aqui tudo bem, ademais, injetamos tais dependências manualmente:

$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

Implementando a resolução automática de dependências

Para que possamos implementar a resolução automática das dependências utilizaremos a API de reflexão do PHP. Essa API nos fornece meios para que façamos engenharia reversa nas classes, extraindo muitas de suas informações internas como quantos métodos possui, quais são públicos, protegidos ou privados, se implementa um construtor, quais são os parâmetros do construtor, se são opcionais ou exigidos, entre outras informações.

Dessa forma, se o usuário pedir para o Container a instanciação do UserController:

$userController = $container->get(UserController::class);

Que tal a gente usar reflexão para obter quais dependências (classes) ele necessita para ser instanciado e então resolver essas dependências automaticamente usando o próprio Container e retornar a instância dele?

É exatamente isso que a nossa implementação de autowiring fará. Para tanto, começaremos alterando o código do nosso container app\Container.php para:

<?php
declare(strict_types=1);

namespace App;

use Closure;
use ReflectionClass;
use ReflectionParameter;

final class Container
{
    private $instances = [];

    public function set(string $id, Closure $closure) : void
    {
        $this->instances[$id] = $closure;
    }

    public function get(string $id) : object
    {
        // Se essa referência existe no mapa do container, então a retorna diretamente.
        if ($this->has($id)) {
            return $this->instances[$id]($this);
        }

        // Se a referência não existe no container, então foi passado uma classe para ser instanciada
        // Façamos então a reflexão dela para obter os parâmetros do método construtor
        $reflector = new ReflectionClass($id);
        $constructor = $reflector->getConstructor();

        // Se a classe não implementa um método construtor, então vamos apenas retornar uma instância dela.
        if (null === $constructor) {
            return new $id();
        }

        // Itera sobre os parâmetros do construtor para realizar a resolução das dependências que ele exige.
        // O método "newInstanceArgs()" cria uma nova instância da classe usando os novos argumentos passados.
        // Usamos "array_map()" para iterar os parâmetros atuais, resolvê-los junto ao container e retornar um array das instâncias já resolvidas pelo container.
        return $reflector->newInstanceArgs(array_map(
            function (ReflectionParameter $dependency) {
                // Busca no container a referência da classe desse parâmetro
                return $this->get(
                    $dependency->getClass()->getName()
                );
            },
            $constructor->getParameters()
        ));
    }

    public function singleton(string $id, Closure $closure) : void
    {
        $this->instances[$id] = function () use ($closure) {
            static $resolvedInstance;

            if (null !== $resolvedInstance) {
                $resolvedInstance = $closure($this);
            }

            return $resolvedInstance;
        };
    }

    public function has(string $id) : bool
    {
        return isset($this->instances[$id]);
    }
}

(Através do GitHub você pode visualizar o que foi adicionado/removido. Basta visualizar esse commit aqui).

A partir do momento que o nosso Container consegue resolver as dependências automaticamente, podemos alterar no index.php a forma de instanciar, que era uma instanciação direta da classe usando new UserController:

$userController = new UserController(
    $container->get(TagRepository::class),
    $container->get(UserRepository::class),
    $container->get(LeadRepository::class)
);

Para uma instanciação que usa o Container (para que ele possa resolver as dependências para nós):

$userController = $container->get(UserController::class);
$userController->index();

(O código completo do exemplo com autowiring encontra-se no branch master do repositório desse exemplo).

Você pode testar o exemplo e terá o mesmo resultado obtido anteriormente quando não usávamos autowiring. O UserController continuará sendo instanciado com sucesso.

Concluindo

Vimos nesse artigo a fundação sobre como a resolução automática de dependências é feita nos containers de injeção de dependência. É por esse caminho que Symfony, Laravel entre outros frameworks (inclusive de outras linguagens) fazem. Obviamente o nosso Container é simples, didático e bem direto ao ponto, não estando 100% pronto para uso em projetos reais. Algumas verificações de segurança (se a classe existe, senão lançar uma exceção etc) precisariam ser implementadas. Na realidade, existem boas implementações de containers por aí e, se você usa um Web Framework, não vai precisar criar a sua própria. No entanto, saber como funciona, é essencial. Essa foi a intenção desse artigo.

Até a próxima!

Os principais Frameworks PHP

De maneira mais simples e objetiva, um Framework é um conjunto de funcionalidades que auxiliam o desenvolvedor a criar aplicações de forma mais rápida.

Caso você ainda não saiba para que serve um framework, aqui no blog possuímos um artigo bem legal que, sem dúvidas, irá te auxiliar no entendimento.

Desta forma, neste artigo falaremos brevemente sobre os principais Frameworks PHP.

Laravel

Framework open source, o Laravel é, sem dúvidas, um dos mais utilizados atualmente. Desenvolvido por Taylor B. Otwell sobre a licença MIT, o Laravel possui seu código hospedado no GitHub e é um framework para os apreciadores de um código bonito, como diz seu slogan: “The PHP Framework For Web Artisans”.

Por possuir uma sintaxe simples e clara, permite que o desenvolvedor trabalhe de maneira mais rápida e extremamente estruturada. Possui uma imensa comunidade ativa, o que o torna ainda mais aceito no mercado.

Utiliza o padrão arquitetural MVC, que divide a aplicação em três camadas distintas, sendo elas o Model, View e Controller, onde:

  • Model: determina as entidades do projeto, ou seja, quais tabelas serão mapeadas;

  • View: camada responsável por exibir informações ao usuário, normalmente páginas HTML;

  • Controller: camada que faz o “meio de campo” entre a view e o model, ou seja, obtém a requisição que o usuário realiza, busca os dados através do model e retorna à view.

Em seu site (https://laravel.com/) é possível encontrar toda sua documentação, novidades do framework (Podcats, blog, tutoriais..), parceiros e muito mais.

CodeIgniter

Com sua primeira versão pública lançada em 2006, o CodeIgniter é um excelente framework para desenvolvimento de aplicações PHP que exijam mais rapidez em seu desenvolvimento.

Assim como dito em seu site, fornece um conjunto de ferramentas simples e elegantes para criar aplicativos web com recursos completos. De código open source e extremamente leve e rápido, é um framework ideal para desenvolvedores iniciantes, por possuir a simplicidade atrelada ao desenvolvimento.

Em seu site (https://codeigniter.com/) é possível encontrar toda documentação necessária para sua utilização, além de links para canais de comunicação entre a comunidade.

Symfony

Framework Open source, o Symfony é ideal para construção de aplicações mais robustas por oferecer ao desenvolvedor total controle de suas configurações. Lançado em 2005, é um framework criado sobre o conceito de aprendizagem rápida sobre os componentes do Symfony.

Utiliza também paradigma MVC e uma comunidade ativa em mais de 120 países (Como informado em seu site).

Em seu site (https://symfony.com/), você poderá encontrar mais informações sobre sua documentação, comunidade, realizar seu download e ficar por dentro de todas as novidades acerca deste framework.

Zend

Lançado em 2005, o Zend é um Framework orientado a objetos de código aberto que permite que o desenvolvedor faça a reutilização de seus componentes. De fácil escrita, implementar códigos mais complexos com o Zend torna-se algo mais acessível.

Possui uma comunidade extremamente ativa e certificação oficial aceita no mercado que, por sinal, é um grande diferencial por ser um framework utilizado por grandes empresas.

Em seu site (http://www.zend.com/) é possível realizar o download do framework, além de encontrar toda sua documentação e tudo sobre sua certificação.

CakePHP

O CakePHP é um framework que possibilita a criação de aplicações robustas por programadores de todos os níveis, sem perder sua flexibilidade. É um framework sobre a licença MIT perfeito para aplicações de uso comercial.

Assim como informado em seu site, oferece uma camada flexível de acesso ao banco de dados e torna a construção de sistemas pequenos e complexos muito mais simples.

Em seu site (https://cakephp.org/), além de se “deliciar” com uma página extremamente fofíssima, para os amantes de bolinhos <3, é possível realizar o download da ferramenta, encontrar sua documentação, sua comunidade e uma loja online de produtos do Framework, com camisetas e lindíssimos elefantinhos (os CakePHP ElePHPant).

Concluindo:

O uso de Frameworks, sem dúvidas, caiu no gosto popular de diversos desenvolvedores, pois permite a criação de aplicações mais profissionais em um tempo muito menor de desenvolvimento, quando comparado ao uso da linguagem “pura”, ou seja, sem framework algum.

E você? Utiliza algum Framework? Conte pra gente sua experiência e até o próximo artigo! =)

O que é PHP?

Desenvolvida para criação de aplicações web, o PHP é uma linguagem de programação vastamente utilizada nos dias atuais, tanto para pequenas quanto para grandes aplicações.

Desenvolvida em meados dos anos 90, o PHP se tornou bastante utilizado por grandes empresas para criação de seus projetos, tais como: Facebook, Wikipedia, Yahoo, WordPress, entre outras.

É uma linguagem server-side, ou em tradução livre, linguagem de servidor, que significa que é processada dentro do servidor a cada requisição feita pelo usuário. Desta forma, o usuário faz uma requisição através de um site qualquer e este pedido é enviado para o servidor, após isso a aplicação desenvolvida em PHP recebe esta requisição, processa e o transforma em um XHTML que será devolvido ao navegador que exibirá a resposta ao usuário.

No mercado de trabalho, é muito comum nos depararmos com vagas para desenvolvedor PHP, visto que é uma linguagem utilizada para vários fins, como aplicações para web, plugins do WordPress, APIs Restfull e até aplicações desktop.

Além disso, o PHP pode se comunicar com os principais banco de dados relacionais e não-relacionais do mercado, o que abrange ainda mais sua utilização.

Características do PHP

Entre diversas características do PHP podemos citar algumas das principais:

  • Linguagem interpretada: Diferente de algumas linguagens, como C e C++ que precisam ser compiladas, o código criado com PHP é interpretado e convertido em um executável;

  • Tipagem dinâmica: As variáveis criadas em PHP não possuem um tipo de dado fixo, permitindo armazenar diferentes tipos de informações;

  • Server-side: Como dito acima, o código criado em PHP é executado diretamente no servidor;

  • Orientado a objetos: O PHP suporta o paradigma orientado a objetos para criação de aplicações, tornando o código muito mais robusto.

Vantagens de estudar PHP

Diversas são as vantagens em começar a estudar PHP. Abaixo, citaremos algumas delas.

  • Facilidade no aprendizado: O PHP possui uma curva de aprendizado muito grande devido a sua sintaxe simples e seu código legível.

  • Comunidade do PHP: Por ser muito utilizado, o PHP possui diversos grupos de discussões sobre a linguagem, onde a comunidade está sempre a disposição para ajudar novos desenvolvedores.

  • Open source: O PHP é uma linguagem de código-fonte aberto, o que permite que a comunidade esteja sempre melhorando a linguagem.

  • Grandes empresas utilizam: Como dito acima, várias empresas utilizam o PHP, desta forma, vale a pena ficar “de olho” em oportunidades de emprego que possam surgir.

  • Multiplataforma: Utilizando PHP, o desenvolvedor pode criar suas aplicações em qualquer sistema operacional do mercado (Windows, Linux e Mac).

  • Valor baixo para manter sua aplicação: Por ser muito utilizado, os preços dos servidores de armazenamento de uma aplicação PHP são os mais baixos, permitindo que sua aplicação custe o menor valor possível.

Concluindo:

Desta forma, vimos neste artigo as principais características e vantagens do PHP. Muito atuante no mercado, o PHP é uma excelente alternativa para se tornar a sua principal linguagem de programação.

Como aliviar seus controllers com os eventos do Eloquent no Laravel

O sonho de todo programador é desenvolver código limpo, organizado, legível, manutenível e muitos outros adjetivos que poderia colocar em uma lista enorme. O Laravel possui vários recursos que nos ajuda a organizar nosso código para conseguir alcançar alguns desses atributos que descrevi acima, se usados de maneira correta. Um desses recursos são os eventos do Eloquent.

Esses eventos são executados sempre que uma ação acontece no model. Isso possibilita removermos responsabilidade dos controllers e executar quando o evento for acionado, com isso, conseguimos um controller mais simples e organizado.

O Eloquent disponibiliza os eventos:

  • Retrieved
  • Creating e Created
  • Updating e Updated
  • Saving e Saved
  • Deleting e Deleted
  • Restoring e Restored

Os eventos com final ing são executados antes da ação no banco de dados e os eventos com final ed são executados após a ação no banco de dados.

Relacionando os eventos no Model

É possível relacionar um evento do Eloquent a um método que fará a ação de três modos diferentes.

Via o método boot

O primeiro modo que podemos adicionar uma ação em um evento do model é através do método estático boot. Por exemplo, para executar uma ação antes da criação podemos fazer:

protected static function boot()
{
    parent::boot();

    static::creating(function ($nomeDoModel) {
        //Ação a ser executada 
    });

}

O Eloquent sempre injeta uma instância do model automaticamente no evento. Nos eventos executados antes da ação no banco é possível alterar os valores do model que serão persistidos.

A utilização dos eventos dentro do próprio model pode não ser a mais aconselhável, pois dependendo da quantidade de eventos pode sobrecarregar, tirando a complexidade dos controllers e transferindo para o models, o que não é bom, pois a solução de um problema criaria outro.

Via classe de evento

Um modo mais limpo de executar os eventos do Eloquent é através de classes específicas para cada um dos eventos. Para isso, basta declarar no model uma propriedade chamada dispatchesEvents com um array contendo o nome do evento e a respectiva classe que será responsável por executar a ação:

protected $dispatchesEvents = [
        'creating' => NomeDoModelCreating::class,
        'deleting' => NomeDoModelDeleting::class,
    ];

Via Observer

Na minha opinião, esse é o modo mais prático de executar ações quando precisamos executar ações em vários eventos do mesmo model.

Essa abordagem consiste basicamente em criar uma classe e declarar métodos públicos com o mesmo nome dos eventos, com isso, o próprio Eloquent verifica se o método existe, se sim, ele executa.

<?php

namespace App\Observers;

use App\NomeDoModel;

class NomeDoModelObserver
{

    public function creating(NomeDoModel $nomeDoModel)
    {
        //Executa antes de criar no banco
    }

    public function deleting(NomeDoModel $nomeDoModel)
    {
        //Executa antes de deletar no banco
    }
}

Único detalhe é que para o Eloquent fazer a referência entre a classe do model e a classe do Observer precisamos declarar dentro do método boot do AppServiceProvider essa relação:

NomeDoModel::observe(NomeDoModelObserver::class);

Note que usamos o método observe() do próprio model que recebe o caminho da classe do observer.

Exemplo prático

Vamos supor que temos uma relação 1 para 1 entre festa e cliente. Baseado nisso, a cada evento do model festa precisamos executar uma ação:

  • Quanto uma festa for criada para um cliente precisamos alterar o status do cliente para com-proposta;
  • Quando uma festa for atualizada, se o atributo valor for maior que zero precisamos alterar o status do cliente para festa-agendada;
  • Por fim, se a festa for excluída, alteramos o status do cliente para sem-proposta;

Nesse caso, podemos criar um observer com os eventos created, updated e deleted para realizar essas ações:

<?php

namespace App\Observers;

use App\Festa;

class FestaObserver
{

    /**
     * Evento executado após a festa ser criada
     *
     * @param Festa $festa
     */
    public function created(Festa $festa)
    {
        $festa->cliente->update(['status' => 'com-proposta']);
    }


    /**
     * Evento executado após a festa ser atualizada
     *
     * @param Festa $festa
     */
    public function updated(Festa $festa)
    {
        if ($festa->valor > 0) {
            $festa->cliente->update(['status' => 'festa-agendada']);
        }
    }

    /**
     * Evento executado após a festa ser deletada
     *
     * @param Festa $festa
     */
    public function deleted(Festa $festa)
    {
        $festa->cliente->update(['status' => 'sem-proposta']);
    }

}

Conclusão

Esse é apenas um dos recursos que podemos usar no Laravel para manter a organização do código. Podemos usar outros como View Composer, Route Model Bind, Policies para permissões, Presenters para remover lógica das views e muito mais.

Quais dos recursos você tem utilizado em seus projetos para melhorar a qualidade do seu código? Fique à vontade para nos contar aqui nos comentários.

Ah, temos um curso muito bom de Laravel no TreinaWeb. Dê uma espiadinha? 🙂

Até a próxima! 🙂

Generators no PHP

Generators foram adicionados no PHP na versão 5.5 (meados de 2013) e aqui estamos nós, quase cinco anos depois, falando sobre eles. É que esse ainda não é um assunto muito difundido, digo, não é trivial encontrar casos e mais casos de uso para eles no contexto padrão de desenvolvimento para o PHP.

Primeiro de tudo, é importante entendermos o que são Iterators, ademais, Iterators e Generators são assuntos intrinsicamente relacionados.

O que é um Iterator?

No contexto prático do PHP, Iterator é um mecanismo que permite que um objeto seja iterado e ele próprio fica no controle dessa iteração. Mas o seu uso não limita a essa “feature”, é o que veremos mais pra frente.

Há de se destacar que é possível iterar um objeto “limpo” (digo, aquele que não implementa nenhuma interface específica) e o que será levado em consideração são os seus atributos públicos.

Um exemplo:

<?php

class Maiable
{
    public $from =  'email@domain.com';
    public $to = 'email@domain.com';
    public $subject = "Assunto";
    protected $type = 1;
}

$maiable = new Maiable();

foreach($maiable as $atributo => $valor) {
    echo "<strong>{$atributo}:</strong> {$valor} <br>";
}

O resultado será:

from: email@domain.com 
to: email@domain.com 
subject: Assunto 

No entanto, temos no PHP a Iterator, uma interface que quando implementada, os objetos provenientes ganham a capacidade de serem iterados com base nas seguintes assinaturas de métodos:

Iterator extends Traversable
{
    abstract public mixed current ( void )
    abstract public scalar key ( void )
    abstract public void next ( void )
    abstract public void rewind ( void )
    abstract public boolean valid ( void )
}
  • current() – Retorna elemento corrente;
  • key() – Obtêm a chave corrente;
  • next() – Avança o cursor para o próximo elemento;
  • rewind() – Retornar o cursor para o início;
  • valid() – Checa se a posição atual existe;

Vamos então a um exemplo:

class BookStore implements Iterator
{
    private $books = [];
    private $index;

    public function __construct()
    {
        $this->index = 0;

        $this->books = [
            'Book 1',
            'Book 2',
            'Book 3',
            'Book 4',
            'Book 5',
        ];
    }

    public function current()
    {
        return $this->books[$this->index];
    }

    public function key()
    {
        return $this->index;
    }

    public function next()
    {
        $this->index++;
    }

    public function rewind()
    {
        $this->index = 0;
    }

    public function valid()
    {
        return array_key_exists($this->index, $this->books);
    }
}

Nota: Esse é um exemplo puramente didático. Estamos inicializando o array de forma estática e ele por si só não justifica o uso de Iterator. A ideia aqui é passar a essência do mecanismo. Adiante veremos casos de usos mais proximos da realidade, usando Generators.

Observe que os atributos são protegidos e que mantemos no $index o “cursor” atual da iteração.

Uma possível forma de iterar sobre esse objeto:

$books = new BookStore();

while ($books->valid()) {
    echo "<strong>[{$books->key()}]</strong> = {$books->current()} <br>";

    $books->next();
}

Resultado:

[0] = Book 1 
[1] = Book 2 
[2] = Book 3 
[3] = Book 4 
[4] = Book 5 

Essa interface faz com que o nosso objeto tenha a capacidade de decidir como e o que iterar. E destaco esses termos em negrito pois eles são a essência dos interators.

Enquanto existir um índice válido a ser recuperado os valores serão impressos. Na última iteração, $books->next() incrementa em mais um o valor de $index chegando ao valor 5, no entanto, no array $books o maior índice existente é o 4, logo, o while é interrompido.

Outra forma possível seria:

for ($books->rewind(); $books->valid(); $books->next()) {
    echo "<strong>[{$books->key()}]</strong> = {$books->current()} <br>";
}

Essas são as formas mais “primitivas” de iterar o nosso objeto iterável. Estamos manualmente cuidando de avançar o cursor usando $books->next();.

Podemos facilitar isso se usarmos a estrutura foreach, ela reconhece quando o objeto implementa a interface Traversable e trata de avançar o cursor e recuperar os valores automaticamente.

A interface Iterator estende a Traversable.

A nossa implementação pode ser simplificada para:

$books = new BookStore();

foreach($books as $key => $value) {
    echo "<strong>[{$key}]</strong> = {$value} <br>";
}

Refatorando para ArrayIterator

O lado chato da abordagem de implementar diretamente a interface Iterator é a necessidade de implementar todos os seus métodos. É util quando a lógica para se iterar ou avançar o cursor precisa ser específica. Agora, se ela é trivial, como é o caso do nosso exemplo, podemos usar a classe ArrayIterator, também nativa do PHP.

A ArrayIterator implementa não só a interface Iterator como várias outras (que não fazem parte do escopo desse artigo):

ArrayIterator implements ArrayAccess, SeekableIterator, Countable, Serializable {

Ela recebe no seu construtor um array e itera sobre ele, abstraindo de nós a necessidade de implementar os métodos necessários da interface Iterator.

Podemos simplificar e alterar a nossa classe para implementar a interface IteratorAggregate que assina um único método, chamado getIterator(). E, nele, tudo o que temos que retornar é um Iterator, no caso, vamos retornar a instância da ArrayIterator:

class BookStore implements IteratorAggregate
{
    private $books;

    public function __construct()
    {
        $this->books = [
            'Book 1',
            'Book 2',
            'Book 3',
            'Book 4',
            'Book 5',
        ];
    }

    public function getIterator()
    {
        return new ArrayIterator($this->books);
    }
}

Qual o principal benefício de se usar um Iterator?

Os exemplos anteriores não tiveram o objetivo de explicitar isso, mas um grande benefício do uso de Iterators trata-se de um melhor aproveitamento da memória. Com eles, não precisamos carregar grandes datasets de uma só uma vez, podemos carregar item a item, sob demanda, quando preciso. Digamos, é uma implementação “lazy loading”.

Nos tópicos seguintes, sobre Generators, veremos alguns casos de uso mais reais e então entenderemos como os Iterators são importantes para lidar com grandes coleções de dados.

O que é um Generator?

Em termos práticos Generators são uma forma prática de se implementar Iterators. Isso quer dizer que, com Generators, podemos implementar complexos Iterators sem que precisemos criar objetos, implementar interfaces e toda àquela complexidade que vimos anteriormente. Generators dão para uma função a capacidade de retornar uma sequência de valores.

Para criar uma função Generator, basta que ela possua a palavra reservada yield. O operador yield é uma espécie de return, só que com algumas particularidades. E uma função desse tipo retorna um objeto da classe Generator, que é uma classe especial e específica para esse contexto, não sendo possível utilizá-la de outra forma. Esse objeto retornado pode ser iterado. É aí que entra a nossa base de Iterators que aprendemos anteriormente. A classe Generator implementa a interface Iterator.

Bom, vamos praticar um pouco? O exemplo mais elementar possível de uma função generator:

function getLinhas() {
    yield "Linha 1";
    yield "Linha 2";
    yield "Linha 3";
    yield "Linha 4";
    yield "Linha 5";
}

var_dump(getLinhas());

O resultado:

object(Generator)#1 (0) { }

Já confirmamos o que anteriormente foi explicado. Sempre que uma função usa o operador yield ela vai retornar um objeto do tipo Generator.

E, se Generator é um Iterator, logo, podemos iterar sobre ele, certo? Sim!

Vejamos:

<?php

function getLinhas() {
    yield "Linha 1";
    yield "Linha 2";
    yield "Linha 3";
    yield "Linha 4";
    yield "Linha 5";
}

foreach (getLinhas() as $linha) {
    echo "{$linha} <br>";
}

Resultado:

Linha 1 
Linha 2 
Linha 3 
Linha 4 
Linha 5 

Não precisamos de um yield para cada registro da nossa coleção. Normalmente o que vamos ver é um yield dentro de um laço como, por exemplo:

<?php

function getLinhas() {
    for ($i = 0; $i < 100; $i++) {
        yield "Linha {$i}";
    }
}

foreach (getLinhas() as $linha) {
    echo "{$linha} <br>";
}

E é possível que o yield retorne um conjunto de chave => valor:

function getLinhas() {
    for ($i = 0; $i < 100; $i++) {
        yield $i => 'Linha ' . $i * 2;
    }
}

foreach (getLinhas() as $chave => $valor) {
    echo "[{$chave}] => {$valor} <br>";
}

E a parte que “toca” a memória?

Comentamos anteriormente que o principal benefício de se usar um Iterator está no baixo consumo de memória associado. Com um Iterator recuperamos a informação sob demanda, sem alocar toda a coleção na memória.

É relativamente comum aplicações que recuperam milhares de dados de uma base de dados (quando não podem por algum requisito usar paginação) precisarem carregar esses dados em um array e em seguida formatar esses dados carregando-os em um novo array. O pico de consumo de memória nesses casos pode ser altíssimo.

Vamos emular uma situação onde possamos ver o benefício de usar Generators?

<?php

function getRegistros() {
    $registros = [];

    for ($i = 0; $i < 10000; $i++) {
        $registros[] = "Registro $i";
    }

    return $registros;
}

function formataRegistros($registros) {
    $new = [];

    foreach ($registros as $index => $registro) {
        $new[] = "[$index] -> {$registro}";
    }

    return $new;
}

$registros = formataRegistros(getRegistros());

echo 'Memória: ' . bcdiv(memory_get_peak_usage(), 1048576, 2) . ' MB<hr><br>';

foreach ($registros as $registro) {
    echo "{$registro} <br>";
}

A getRegistros() aloca na memória (em um array) milhares de registros e os retorna. A formataRegistros() recebe uma coleção de dados, formata-os, aloca-os na memória (em um array) e então os retorna.

O resultado da memória total gasta nessa operação foi:

Memória: 2.19 MB

Faça um paralelo disso com uma aplicação sua, real, que precisa recuperar milhares de registros, depois formatá-los de alguma maneira ou relacioná-los com outros registros etc.

Agora, refatorando o exemplo para que as duas funções se transformem em generators:

<?php

function getRegistros() {
    for ($i = 0; $i < 10000; $i++) {
        yield "Registro $i";
    }
}

function formataRegistros($registros) {
    foreach ($registros as $registro) {
        yield "-> {$registro}";
    }
}

$registros = formataRegistros(getRegistros());

echo 'Memória: ' . bcdiv(memory_get_peak_usage(), 1048576, 2) . ' MB<hr><br>';

foreach ($registros as $registro) {
    echo "{$registro} <br>";
}

O resultado:

Memória: 0.38 MB

Uma diferença muito grande. Quanto maior o dataset, maior a diferença será.

Outro possível caso de uso: Você precisa ler um documento de texto linha a linha e tratar essas informações de alguma maneira. Usando a forma tradicional de lidar com isso, teríamos algo como:

<?php

function getLinhas($arquivo) {
    $handle = fopen($arquivo, 'r');

    $linhas = [];

    while (($buffer = fgets($handle, 4096)) !== false) {
        $linhas[] = $buffer;
    }

    fclose($handle);

    return $linhas;
}

$linhas = getLinhas('file.txt');

foreach($linhas as $linha) {
    // TODO
}

echo 'Memória: ' . bcdiv(memory_get_peak_usage(), 1048576, 2) . ' MB';

Se o seu sistema é unix você pode gerar um arquivo com dados randômicos de 10MB (para testar o exemplo) executando:

base64 /dev/urandom | head -c 10000000 > file.txt

O resultado na minha máquina foi:

Memória: 19.56 MB

Se refatorarmos isso para um Generator:

<?php

function getLinhas($arquivo) {
    $handle = fopen($arquivo, 'r');

    while (($buffer = fgets($handle, 4096)) !== false) {
        yield $buffer;
    }

    fclose($handle);
}

$linhas = getLinhas('file.txt');

foreach($linhas as $linha) {
    // TODO
}

echo 'Memória: ' . bcdiv(memory_get_peak_usage(), 1048576, 2) . ' MB';

O resultado:

Memória: 0.37 MB

Veja que a diferença do consumo de memória entre as duas abordagens é enorme. Se o tamanho do arquivo que você está lendo ou precisa processar for muito grande você pode, inclusive, estourar o limite de memória do PHP se não usar um Iterator.

Com os Generators é possível que processemos arquivos de dezenas ou centenas de GB’s sem estourar o limite de memória disponível para a aplicação.

Concluindo

O uso de Generators é indicado e muitas vezes se faz necessário quando é preciso iterar uma grande coleção de dados de acesso sequencial.

Não são muitos os casos de uso para Generators na programação regular (digo, do dia a dia). Mas, certamente possuem o seu espaço. Generators são, inclusive, a base para programação assíncrona com PHP. E um framework que se destaca utilizando-os é o AMP.

Object Calisthenics em PHP – Parte 2

Este artigo é uma continuação do anterior. Para relembrarmos, abaixo a lista dos assuntos e em negrito os que serão abordados nesse artigo:

  1. Um nível de indentação por método
  2. Não use ELSE
  3. Envolva seus tipos primitivos
  4. Envolva suas collections em classes
  5. Uma chamada de método por linha
  6. Não abrevie
  7. Mantenha as classes pequenas
  8. Não tenha classes com mais de duas variáveis de instância

3. Envolva seus tipos primitivos

Podemos definir esse exercício para os tipos escalares em PHP que são: int, float, bool e string. “Envolver” vem de um significado da programação orientada a objetos, que quer dizer, colocar o tipo “envolta” de uma classe, a fim de trazer mais resultados e funcionalidades do que um tipo comum/escalar.

Essa técnica vêm de uma aplicação do DDD (Domain-Driven Design) chamada de Value Object, onde temos um objeto-valor pequeno, que irá cuidar de um tipo de dado específico. Como o PHP é fracamente tipado, a melhor aplicação será em passagens de parâmetros de métodos ou funções. Veja o código abaixo:

class Customer
{
    protected $name;
    protected $birthday;

    public function __construct(string $name, string $birthday)
    {
        // Validar aqui???
        $this->name = $name;
        $this->birthday = $birthday;
    }
}

Ambos parâmetros são validáveis e não é legal validarmos no construtor da classe, pelo fato de não podermos reaproveitar as validações. Já o fato de forçarmos os atributos como string na entrada, algo errado pode acontecer se o desenvolvedor que está usando a classe não souber, por exemplo, qual padrão de data utilizado para a entrada $birthday, o que pode gerar um problema lá na frente, possivelmente no banco de dados.

Abaixo um exemplo de bom e outro de mau uso da classe:

// Programador que conhece a classe
$customer = new Customer('John Doe', '1983-02-10');

// Programador que não conhece a classe
// pode gerar um 0000-00-00 no Database
$customer = new Customer('John Doe', '10/02/1983'); 

Poderíamos então usar duas classes que farão envolvimento no tipo string, por exemplo:

  • CustomerName: que cuidará de validação de nome de cliente, pode verificar tamanho, fazer trim, e até limpeza.
  • CustomerBithday: Mais importante que o nome, ela vai validar o formato da data ou até mesmo formatar a entrada como, por exemplo, converter 10/02/1983 para 1983-02-10, evitando assim um problema de inconsistência, lembrando que não precisa ser necessariamente uma classe e, seguindo o princípio de inversão de dependência, podemos facilmente trabalhar com interfaces seguindo estratégias.

Como ficaria após a implementação dessas classes:

class Customer
{
    protected $name;
    protected $birthday;

    public function __construct(CustomerName $name, CustomerBirthday $birthday)
    {
        $this->name = $name;
        $this->birthday = $birthday;
    }
}

Usando:

// Programador que conhece a classe
$customer = new Customer(
     new CustomerName('John Doe'), 
     new CustomerBirthday('1983-02-10')
);

// A data será formatada internamente
$customer = new Customer(
    new CustomerName('John Doe'), 
    new CustomerBirthday('10/02/1983')
);

Um possível problema dessa abordagem é que ela adiciona complexidade à base de código. Na tradução dos Object Calisthenics, é colocado que todos os tipos primitivos devem ser envolvidos em classes, porém, sabemos que em PHP isso pode se tornar improdutivo e desnecessário, portanto, analise o quanto aquela entrada ou tipo pode sofrer mudança, se precisa de validação, normalização etc, só aplique-o se tiver uma real justificativa.

4. Envolva suas collections em classes

Semelhante ao exercício anterior, devemos envolver nossas coleções. Isso significa que trabalhar com um CustomerList é melhor do que com um array, neste caso, o uso dará uma melhor flexibilidade para o tratamento da coleção.

Abaixo uma usabilidade com e outra sem coleção em classe:

// A lógica fica fora, o que pode trazer problemas futuros
foreach ($customers as $customer) {
    if ($customer->isGoldAccount()) {
        $customer->addBonus(new Money('R$ 50,00'));
    }
}

Nesse exemplo queremos adicionar um bônus aos clientes do tipo gold. $customers é um array e por isso para modificar a coleção, precisamos iterá-la com um foreach e ainda internamente verificar se o tipo do cliente é gold.

Se usarmos uma classe que envolve a coleção, ou seja, uma CustomerCollection ela poderá ter 2 métodos:

  • Para filtragem de tipos de clientes: filterGoldAccounts.
  • Para adicionar aos clientes o bônus: addBonus.

A usabilidade ficaria assim:

$customersCollection = new CustomersCollection; // Classe com Lazy Loading

// Filtramos os clientes de conta Gold
$goldCustomers = $customersCollection->filterGoldAccounts();

// Adicionamos pela collection o bonus de R$ 50,00
// filtrado pela classe de coleção
$goldCustomers->addBonus(new Money('R$ 50,00'));

// Por fim persistimos
$goldCustomers->persists();

Assim, temos coleções que são específicas e inteligentes o suficiente para melhorar a usabilidade e evitar erros de programação. No PHP temos um conjunto de classes padrão para lidar com listas, a SPL(Standard PHP Library) que tem uma sessão dedicada a iteradores.

5. Uma chamada de método por linha

Devemos sempre fazer uma chamada de método por linha, não se aplicando à bibliotecas que usam do padrão Method Chaining ou DSL(Domain Specific Language).

Para seguir esse exercício não devemos, por exemplo, ao desenvolver um conjunto de Models, relacioná-los com métodos em cadeia, isso pode ser uma péssima ideia, segue um exemplo:

$customer->getById(55988)
         ->getPurchase(18376)
         ->getProducts()
         ->filterById(234);

Queremos resgatar um produto do pedido de um cliente, pode-se parecer muito prático, porém, alguns problemas poderão ocorrer e é muito difícil testar um bloco desses. Como saber se getPurchase encontrou o pedido? E se não encontrou, o que acontece? Nesse caso, vem outra problemática: e se o pedido não contém itens ainda? E temos um retorno null, certamente teremos um erro de método não encontrado.

Por isso, para garantir que tudo ocorreu certo, podemos seguir a Lei de Demeter, ela diz que devemos somente conversar com classes próximas, então, criamos um método para conversar e filtrar o que precisamos, ao invés de percorrer pelos Models que estão distantes. Não focaremos na implementação, porém, o conceito de uso abaixo pode ilustrar essa aproximação:

// Resgatando o model isoladamente
$customer = $customerModel->getById(55988);

// Aproximação: método que pertence a Customer
// sua implementação cuidará de retornos nulls
$product = $customer->getPuchasedProduct(18376, 234);

O principal objetivo desse exercício é não sair percorrendo por objetos retornados em chamadas de métodos, usar chamadas de vários métodos em linha pode gerar muitos problemas de manutenção, dificuldade de entendimento e testes mal escritos. Costuma-se dizer que gera um código que “cheira mal”.

Conclusão

Esses exercícios são mais aprofundados e devem ser estudados com calma, não devemos seguí-los somente por que parece ser o certo, devemos entender a motivação por trás deles, lembrando que nenhuma dessas técnicas são balas de prata e vão servir para toda modelagem, o bom senso é a melhor direção.

Até o próximo artigo da série!

Object Calisthenics em PHP – Parte 1

O conceito de object calisthenic foi criado pelo desenvolvedor Jeff Bay, que teve a visão de que praticar código reutilizável, limpo e de boa leitura é uma prática que necessita de muito exercício, ou seja, muita prática e constante melhoria e evolução através de ações e práticas.

Ele é composto por nove exercícios que constituem a aplicação dos mesmos ao se escrever código em orientação a objetos.

Object vem da programação orientada a objetos, e calisthenics do termo grego, Kales, simplificando, seria a forma de se obter um físico ou, no caso, um resultado a partir da prática de exercícios que deixarão seu código em “forma”.

Ao todo são nove regras básicas, veja abaixo:

  1. Um nível de indentação por método;
  2. Não use ELSE;
  3. Envolva seus tipos primitivos;
  4. Envolva suas collections em classes;
  5. Uma chamada de método por linha;
  6. Não abrevie;
  7. Mantenha as classes pequenas;
  8. Não tenha classes com mais de duas variáveis de instancia;
  9. Sem getters e setters;

Veremos neste artigo as duas primeiras que são exercícios fortíssimos que fazem já muita diferença quando praticados.

1. Um nível de indentação por método

Como já especificado devemos internamente dentro de uma função usar somente um nível de indentação, quanto mais níveis de comandos de decisão ou estruturas de decisão, mais complexo o método fica, minando com a simplicidade do projeto.

Abaixo podemos ver um exemplo disso:

Código sem a regra de indentação:

class Customer
{
    public function getPromoCode(string $promoName)
    {
        // 1.
        if ($this->promoCode) {
            // 2.
            if (false === $this->promoCodeExpired()) {
                // 3.
                if ($this->promoName == $promoname) {
                    return $this->promoCode;
                } else {
                    throw new Exception('Promoção não existe mais');
                }
            } else {
                throw new Exception('Promoção Expirada');
            }      
        } else {
            throw new Exception('Cliente sem código de promoção');
        }
    }
}

Uma forma de “treinar” esse exercício é criando métodos protegidos auxiliares que tenham um motivo e reaproveitamento, atuando na facilitação da escrita e colocando os blocos dentro das funções de apoio.

Veja como resolvemos o problema acima:

class Customer
{
    public function getPromoCode(string $promoName)
    {
        if ($this->promoCode) {
            return $this->getValidPromoCode($promoName);
        } else {
            throw new Exception('Cliente sem código de promoção');
        }
    }

    protected function getValidPromoCode(string $promoName)
    {
        if (false === $this->promoCodeExpired()) {
            return $this->getPromoExists($promoName);
        } else {
            throw new Exception('Promoção Expirada');
        }    
    }

    protected function getPromoExists(string $promoName)
    {
        if ($this->promoName == $promoName) {
            return $this->promoCode;
        } else {
            throw new Exception('Promoção não existe mais');
        }
    }
}

2. Não use ELSE

Essa regra parece ser muito estranha, mas ela funciona muito bem com o conceito early return, que emprega o uso do “retorne seu valor o quanto antes”, ação que só é facilmente implementada dentro de funções, métodos ou loops.

A base deste exercício é sempre trabalhar com o return (ou continue), sabemos que ao cair em um return/continue o código abaixo não será executado o que ajuda na remoção dos “elses” ao inverter ou até modificar a validação antes usada.

Abaixo o código anterior com early return:

class Customer
{
    public function getPromoCode(string $promoName)
    {
        if ($this->promoCode) {
            return $this->getValidPromoCode($promoName);
        } 
        throw new Exception('Cliente sem código de promoção');
    }

    public function getValidPromoCode(string $promoName)
    {
        if (false === $this->promoCodeExpired()) {
            return $this->getPromoExists($promoName);
        }
        throw new Exception('Promoção Expirada'); 
    }

    public function getPromoExists(string $promoName)
    {
        if ($this->promoName == $promoName) {
            return $this->promoCode;
        }
        throw new Exception('Promoção não existe mais');
    }
}

Como trocamos os comandos de decisões para que fique um código limpo removemos os “else’s”, e simplificamos a lógica que passa a ser melhor compreendida e limpa, esse conceito também pode ser aplicador para loops, onde o intuito é usar o continue no lugar do “else”.

Vejamos abaixo um exemplo:

Antes:

// Exibir a lista de membros
// que pagaram a mensalidade
foreach ($members as $member) {
    if ($member->paid()) {
        $report[] = [$member->name => 'Paid'];
    } else {
        $report[] = [$member->name => 'Not Paid'];
    }
}

Depois:

// Sem Else
foreach ($members as $member) {
    if ($member->paid()) {
        $report[] = [$member->name => 'Paid'];
        continue;
    }
    $report[] = [$member->name => 'Not Paid'];
}

Ou até com um pouco mais de limpeza:

// Um pouco mais de limpeza
foreach ($members as $member) {
    $report[] = $member->paid() ? 
                    [$member->name => 'Paid'] : 
                    [$member->name => 'Not Paid'];
}

Conclusão

Veremos em breve as outras regras numa série de posts. O uso do object calisthenics trás muitos benefícios que em grande escala fazem uma grande diferença, comece esses exercícios e terá seu código elogiado e muito mais elegante.

Obviamente nem sempre é possível de imediato “atacar” seu código com os calisthenics, mas com refatorações isso começa a ficar natural e quanto mais prática mais natural fica a aplicação desses exercícios.

Espero que tenham gostado e nos vemos nos próximos posts. Bons estudos!

JUNTE-SE A MAIS DE 150.000 PROGRAMADORES