Criando APIs
usando o micro-framework
Respect
Ivan Rosolen
Graduado em Sistemas de Informação
Pós-graduado em Gerência de Projetos
Desenvolvedor a 14+ anos
Autor de vários PHPT (testes para o PHP)
Entusiasta de novas tecnologias
Head of Innovation @ Arizona
CTO @ Mokation
@ivanrosolen
API
Vantagens
- Troca de informações entre sistemas
- Múltiplas interfaces (web, mobile, CLI)
- Módulos/Componentes
- HTTP Status Codes
- Controle de Acesso
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
REST BEER
API com informações de Ceveja!
GET - http://hostname/cerveja/
POST - http://hostname/cerveja/
cerveja[nome]
cerveja[estilo]
PUT - http://hostname/cerveja/NOME
cerveja[nome]
cerveja[estilo]
DELETE - http://hostname/cerveja/NOME
RESPECT
Micro-framework para PHP 5.3+
construido por Alexandre Gaigalas
(alganet) e comunidade.
Como?
Composer
- Controle de dependências
curl -s http://getcomposer.org/installer | php
php composer.phar install
https://getcomposer.org
{
"name": "RestBeer Jelastic Demo",
"authors": [
{
"name": "Ivan Rosolen",
"email": "ivanrosolen@gmail.net"
}
],
"require": {
"respect/rest": "0.5.x",
"respect/relational": "0.5.x",
"respect/config": "0.3.x",
"respect/validation": "0.4.x"
}
}
Respect/Config
- Apenas arquivos .INI
- Usa o mesmo parser nativo e rápido do php.ini
- Extende o arquivo .INI com seu próprio “dialeto”
- Implementa lazy loading para instâncias de objeto
https://github.com/Respect/Config
db_server = "127.0.0.1"
db_name = "restbeer"
db_user = "user"
db_pwd = "pwd"
dsn_mysql = "mysql:host=[db_server];dbname=[db_name]"
<?php
use RespectConfigContainer;
// Ler arquivo de configuração
$config = new Container('config.ini');
echo $config->dsn_mysql; // mysql:host=127.0.0.1;dbname=restbeer
Respect/Relational
- Quase zero de configuracão (convenção)
- Fluent interface: $mapper->author[7]->fetch();
- Diferentes tipos de banco de dados
- Registros são tratados como Plain Data Object
https://github.com/Respect/Relational
<?php
use RespectRelationalMapper;
// criar instância PDO com o banco
$mapper = new Mapper(new PDO('seudsn'));
// buscar todos os autores
$authors = $mapper->author->fetchAll();
// criar objeto de um registro
$obj = new stdClass;
$obj->name = 'Ivan Rosolen';
// "gravar" informação no banco
$mapper->author->persist($obj);
$mapper->flush();
Respect/Validation
- Fluent/Chained interface: v::numeric()->positive()->between(1,
256)->validate($num)
- 100+ validadores testados
- Fácil extender ou criar novas regras (Concrete API)
- Php 7 ( Quase pronto )
https://github.com/Respect/Validation
use RespectValidationValidator as v;
// validar número simples
v::numeric()->validate(42); //true
// validar em cadeia
$v = v::arr() // validar se é array
->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) // validar a key 'nome'
->key('estilo', $rule) // utilizando a mesma regra da key de cima
->validate($_POST['cerveja']);
// negação de qualquer regra
$v = v::not(v::int())->validate(10); // false
// operadores lógicos
v::allOf(v::numeric(), v::hexa(), v::min(1)); // numeric, hexadecimal e pelo menos 1
v::oneOf(v::nullValue(), v::numeric()); // null ou numeric
Respect/Router
- Thin and lightweight controller para aplicações
RESTful e APIs
- “Don't try to change PHP, small learning curve.”
- If/Before/After/Accept/Auth/Any/By …
https://github.com/Respect/Rest
<?php
use RespectRestRouter;
// Criar instância do router
$router = new Router; // raiz http://example.com/
// instância para trabalhar em uma subpasta
$router = new Router('/pasta'); // raiz http://example.com/pasta
// Olá mundo
$router->get('/', function() {
return 'Hello World';
});
// Separando regras das rotas :D
$router->get('/api/uri/*/*', 'NamespaceCoolClass');
$router->post('/api/uri/', 'NamespaceCoolClass');
$router->put('/api/uri/', 'NamespaceCoolClass');
$router->delete('/api/uri/*', 'NamespaceCoolClass');
Projeto
<?php
require_once realpath(__DIR__ . '/vendor/autoload.php');
use RespectRestRouter;
use RespectConfigContainer;
use RespectValidationValidator as v;
use RespectRelationalMapper;
//Ler arquivo de configuração
$config = new Container('config.ini');
// Criar instância PDO com o SQLite usando as configs
$mapper = new Mapper(new PDO($config->dsn_sqlite));
// Criar instância do router
$router = new Router();
//Rota para qualquer tipo de request (any)
$router->any('/', function () {
return 'RestBeer!';
});
GET Cerveja
$router->get('/cerveja/*', function ($data = null) use ($mapper) {
if ( !isset($data) ) {
$cervejas = $mapper->cervejas->fetchAll();
header('HTTP/1.1 200 Ok');
return $cervejas;
}
$data = filter_var( $data, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( v::not(v::alnum()->notEmpty())->validate($data) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$cerveja = $mapper->cervejas(array( 'nome' => $data ))->fetch();
if ( !$cerveja ) {
header('HTTP/1.1 204 No Content');
return;
}
header('HTTP/1.1 200 Ok');
return $cerveja;
});
POST Cerveja
$router->post('/cerveja', function () use ($mapper) {
if ( !isset($_POST) || !isset($_POST['cerveja']) || v::not(v::arr())->validate($_POST['cerveja']) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$valid = v::arr()->key('nome', $rule = v::alnum()->noWhitespace())->key('estilo', $rule)->validate($_POST['cerveja']);
if ( !$valid ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$cerveja = new stdClass();
$cerveja->nome = filter_var($_POST['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$cerveja->estilo = filter_var($_POST['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$check = $mapper->cervejas(array( 'nome' => $cerveja->nome ))->fetch();
if ( $check ) {
header('HTTP/1.1 409 Conflict');
return 'Cerveja já existe no sistema';
}
$mapper->cervejas->persist($cerveja);
$mapper->flush();
if ( !isset($cerveja->id) || empty($cerveja->id) ) {
header('HTTP/1.1 422 Unprocessable Entity');
return 'Erro ao inserir cerveja';
}
header('HTTP/1.1 201 Created');
return 'Cerveja criada';
});
PUT Cerveja
$router->put('/cerveja/*', function ($nome) use ($mapper) {
parse_str(file_get_contents('php://input'), $data);
if ( !isset($data) || !isset($data['cerveja']) || v::not(v::arr())->validate($data['cerveja']) )
{
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( v::not(v::alnum()->notEmpty())->validate($nome) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch();
if ( !$cerveja ) {
header('HTTP/1.1 204 No Content');
return;
}
$newNome = filter_var( $data['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$newEstilo = filter_var( $data['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$cerveja->nome = $newNome;
$cerveja->estilo = $newEstilo;
$mapper->cervejas->persist($cerveja);
$mapper->flush();
header('HTTP/1.1 200 Ok');
return 'Cerveja atualizada';
}); * removido parte do código para ficar melhor no slide
DELETE Cerveja
$router->delete('/cerveja/*', function ($nome) use ($mapper) {
$nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( !isset($nome) || v::not(v::alnum()->notEmpty())->validate($nome) ) {
header('HTTP/1.1 400 Bad Request');
return 'Faltam parâmetros';
}
$cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch();
if ( !$cerveja ) {
header('HTTP/1.1 422 Unprocessable Entity');
return 'Erro ao validar cerveja';
}
$mapper->cervejas->remove($cerveja);
$mapper->flush();
header('HTTP/1.1 200 Ok');
return 'Cerveja removida';
});
Formatar Resultado
<?php
$jsonRender = function ($data) {
header('Content-Type: application/json');
if ( v::string()->validate($data) ) {
$data = array($data);
}
return json_encode($data,true);
};
$router->always('Accept', array('application/json' => $jsonRender));
Basic Auth
<?php
// do not use this!
function checkLogin($user, $pass) {
return $user === 'admin' && $pass === 'admin';
}
$router->get('/admin', function () {
return 'RestBeer Admin Protected!';
})->authBasic('Secret Area', function ($user, $pass) {
return checkLogin($user, $pass);
});
Deploy
Jelastic
- Locaweb?
- Git
- Barato
- 14 Dias Grátis
Aplicações PHP no Jelastic https://t.co/aHi3ZixLon
Referências / Links
Github
https://github.com/ivanrosolen/RestBeer-Jelastic
Respect
http://respect.github.io
Jelastic
http://www.locaweb.com.br/cloud/jelastic
https://twitter.com/kemelzaidan
http://pt.slideshare.net/ivanrosolen/jelastic-53375753
PHPSP
http://phpsp.org.br
Elton Minetto
https://github.com/eminetto/restbeer
Dúvidas?
OBRIGADO!
Avalie esta palestra
joind.in/15207 Visite phpsp.org.br

Rest Beer v2

  • 1.
    Criando APIs usando omicro-framework Respect
  • 2.
    Ivan Rosolen Graduado emSistemas de Informação Pós-graduado em Gerência de Projetos Desenvolvedor a 14+ anos Autor de vários PHPT (testes para o PHP) Entusiasta de novas tecnologias Head of Innovation @ Arizona CTO @ Mokation
  • 3.
  • 4.
  • 5.
    Vantagens - Troca deinformações entre sistemas - Múltiplas interfaces (web, mobile, CLI) - Módulos/Componentes - HTTP Status Codes - Controle de Acesso https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
  • 6.
  • 7.
    API com informaçõesde Ceveja! GET - http://hostname/cerveja/ POST - http://hostname/cerveja/ cerveja[nome] cerveja[estilo] PUT - http://hostname/cerveja/NOME cerveja[nome] cerveja[estilo] DELETE - http://hostname/cerveja/NOME
  • 8.
  • 9.
    Micro-framework para PHP5.3+ construido por Alexandre Gaigalas (alganet) e comunidade.
  • 10.
  • 11.
    Composer - Controle dedependências curl -s http://getcomposer.org/installer | php php composer.phar install https://getcomposer.org
  • 12.
    { "name": "RestBeer JelasticDemo", "authors": [ { "name": "Ivan Rosolen", "email": "ivanrosolen@gmail.net" } ], "require": { "respect/rest": "0.5.x", "respect/relational": "0.5.x", "respect/config": "0.3.x", "respect/validation": "0.4.x" } }
  • 13.
    Respect/Config - Apenas arquivos.INI - Usa o mesmo parser nativo e rápido do php.ini - Extende o arquivo .INI com seu próprio “dialeto” - Implementa lazy loading para instâncias de objeto https://github.com/Respect/Config
  • 14.
    db_server = "127.0.0.1" db_name= "restbeer" db_user = "user" db_pwd = "pwd" dsn_mysql = "mysql:host=[db_server];dbname=[db_name]" <?php use RespectConfigContainer; // Ler arquivo de configuração $config = new Container('config.ini'); echo $config->dsn_mysql; // mysql:host=127.0.0.1;dbname=restbeer
  • 15.
    Respect/Relational - Quase zerode configuracão (convenção) - Fluent interface: $mapper->author[7]->fetch(); - Diferentes tipos de banco de dados - Registros são tratados como Plain Data Object https://github.com/Respect/Relational
  • 16.
    <?php use RespectRelationalMapper; // criarinstância PDO com o banco $mapper = new Mapper(new PDO('seudsn')); // buscar todos os autores $authors = $mapper->author->fetchAll(); // criar objeto de um registro $obj = new stdClass; $obj->name = 'Ivan Rosolen'; // "gravar" informação no banco $mapper->author->persist($obj); $mapper->flush();
  • 17.
    Respect/Validation - Fluent/Chained interface:v::numeric()->positive()->between(1, 256)->validate($num) - 100+ validadores testados - Fácil extender ou criar novas regras (Concrete API) - Php 7 ( Quase pronto ) https://github.com/Respect/Validation
  • 18.
    use RespectValidationValidator asv; // validar número simples v::numeric()->validate(42); //true // validar em cadeia $v = v::arr() // validar se é array ->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) // validar a key 'nome' ->key('estilo', $rule) // utilizando a mesma regra da key de cima ->validate($_POST['cerveja']); // negação de qualquer regra $v = v::not(v::int())->validate(10); // false // operadores lógicos v::allOf(v::numeric(), v::hexa(), v::min(1)); // numeric, hexadecimal e pelo menos 1 v::oneOf(v::nullValue(), v::numeric()); // null ou numeric
  • 19.
    Respect/Router - Thin andlightweight controller para aplicações RESTful e APIs - “Don't try to change PHP, small learning curve.” - If/Before/After/Accept/Auth/Any/By … https://github.com/Respect/Rest
  • 20.
    <?php use RespectRestRouter; // Criarinstância do router $router = new Router; // raiz http://example.com/ // instância para trabalhar em uma subpasta $router = new Router('/pasta'); // raiz http://example.com/pasta // Olá mundo $router->get('/', function() { return 'Hello World'; }); // Separando regras das rotas :D $router->get('/api/uri/*/*', 'NamespaceCoolClass'); $router->post('/api/uri/', 'NamespaceCoolClass'); $router->put('/api/uri/', 'NamespaceCoolClass'); $router->delete('/api/uri/*', 'NamespaceCoolClass');
  • 21.
  • 22.
    <?php require_once realpath(__DIR__ .'/vendor/autoload.php'); use RespectRestRouter; use RespectConfigContainer; use RespectValidationValidator as v; use RespectRelationalMapper; //Ler arquivo de configuração $config = new Container('config.ini'); // Criar instância PDO com o SQLite usando as configs $mapper = new Mapper(new PDO($config->dsn_sqlite)); // Criar instância do router $router = new Router(); //Rota para qualquer tipo de request (any) $router->any('/', function () { return 'RestBeer!'; });
  • 23.
  • 24.
    $router->get('/cerveja/*', function ($data= null) use ($mapper) { if ( !isset($data) ) { $cervejas = $mapper->cervejas->fetchAll(); header('HTTP/1.1 200 Ok'); return $cervejas; } $data = filter_var( $data, FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( v::not(v::alnum()->notEmpty())->validate($data) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = $mapper->cervejas(array( 'nome' => $data ))->fetch(); if ( !$cerveja ) { header('HTTP/1.1 204 No Content'); return; } header('HTTP/1.1 200 Ok'); return $cerveja; });
  • 25.
  • 26.
    $router->post('/cerveja', function ()use ($mapper) { if ( !isset($_POST) || !isset($_POST['cerveja']) || v::not(v::arr())->validate($_POST['cerveja']) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $valid = v::arr()->key('nome', $rule = v::alnum()->noWhitespace())->key('estilo', $rule)->validate($_POST['cerveja']); if ( !$valid ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = new stdClass(); $cerveja->nome = filter_var($_POST['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); $cerveja->estilo = filter_var($_POST['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); $check = $mapper->cervejas(array( 'nome' => $cerveja->nome ))->fetch(); if ( $check ) { header('HTTP/1.1 409 Conflict'); return 'Cerveja já existe no sistema'; } $mapper->cervejas->persist($cerveja); $mapper->flush(); if ( !isset($cerveja->id) || empty($cerveja->id) ) { header('HTTP/1.1 422 Unprocessable Entity'); return 'Erro ao inserir cerveja'; } header('HTTP/1.1 201 Created'); return 'Cerveja criada'; });
  • 27.
  • 28.
    $router->put('/cerveja/*', function ($nome)use ($mapper) { parse_str(file_get_contents('php://input'), $data); if ( !isset($data) || !isset($data['cerveja']) || v::not(v::arr())->validate($data['cerveja']) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( v::not(v::alnum()->notEmpty())->validate($nome) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch(); if ( !$cerveja ) { header('HTTP/1.1 204 No Content'); return; } $newNome = filter_var( $data['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $newEstilo = filter_var( $data['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $cerveja->nome = $newNome; $cerveja->estilo = $newEstilo; $mapper->cervejas->persist($cerveja); $mapper->flush(); header('HTTP/1.1 200 Ok'); return 'Cerveja atualizada'; }); * removido parte do código para ficar melhor no slide
  • 29.
  • 30.
    $router->delete('/cerveja/*', function ($nome)use ($mapper) { $nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( !isset($nome) || v::not(v::alnum()->notEmpty())->validate($nome) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch(); if ( !$cerveja ) { header('HTTP/1.1 422 Unprocessable Entity'); return 'Erro ao validar cerveja'; } $mapper->cervejas->remove($cerveja); $mapper->flush(); header('HTTP/1.1 200 Ok'); return 'Cerveja removida'; });
  • 31.
  • 32.
    <?php $jsonRender = function($data) { header('Content-Type: application/json'); if ( v::string()->validate($data) ) { $data = array($data); } return json_encode($data,true); }; $router->always('Accept', array('application/json' => $jsonRender));
  • 33.
  • 34.
    <?php // do notuse this! function checkLogin($user, $pass) { return $user === 'admin' && $pass === 'admin'; } $router->get('/admin', function () { return 'RestBeer Admin Protected!'; })->authBasic('Secret Area', function ($user, $pass) { return checkLogin($user, $pass); });
  • 35.
  • 36.
    Jelastic - Locaweb? - Git -Barato - 14 Dias Grátis Aplicações PHP no Jelastic https://t.co/aHi3ZixLon
  • 37.
  • 38.
  • 39.
  • 40.