silex meets soap & rest
DESCRIPTION
Silex is a brand new PHP 5.3 micro framework built on top of the Symfony2 de decoupled components. In this session, we will discover how to build and deploy powerful REST web services with such a micro framework and its embedded tools. The first part of this talk will introduce the basics of the REST architecture. We fill focus on the main concepts of REST like HTTP methods, URIs and open formats like XML and JSON. Then, we will discover how to deploy REST services using most of interesting Silex tools like database abstraction layer, template engine and input validation. We will also look at unit and functional testing frameworks with PHPUnit and HTTP caching with Edge Side Includes and Varnish support to improve performances.TRANSCRIPT
![Page 1: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/1.jpg)
Hugo Hamon @hhamon
![Page 2: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/2.jpg)
Silex meets REST and SOAP
![Page 3: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/3.jpg)
What is Silex?
![Page 4: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/4.jpg)
![Page 5: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/5.jpg)
![Page 6: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/6.jpg)
![Page 7: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/7.jpg)
http://silex.sensiolabs.org
![Page 8: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/8.jpg)
Why choosing Silex?
![Page 9: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/9.jpg)
What’s inside?
![Page 10: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/10.jpg)
The Silex Philosophy
![Page 11: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/11.jpg)
silex.phar
cache/
logs/
src/
tests/
vendor/
web/
![Page 12: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/12.jpg)
Silex Mantra
namespace Symfony\Component\HttpKernel;
interface HttpKernelInterface
{
(Response) function handle(Request $request);
}
![Page 13: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/13.jpg)
Request Handling
require_once __DIR__.'/silex.phar'; $app = new Silex\Application(); $app->get('/hello/{name}', function($name) use($app) { return 'Hello '. $app->escape($name); }); $app->run();
![Page 14: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/14.jpg)
Request Handling
require_once __DIR__.'/silex.phar';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$app = new Silex\Application();
$app->get('/hello/{name}', function(Request $request) use($app) {
$name = $request->attributes->get('name');
return new Response('Hello '. $app->escape($name));
});
![Page 15: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/15.jpg)
$app == DIC
![Page 16: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/16.jpg)
Deploying REST Web services
![Page 17: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/17.jpg)
REpresentational State Transfer
![Page 18: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/18.jpg)
Architecture style
![Page 19: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/19.jpg)
Designing REST APIs
![Page 20: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/20.jpg)
$app->get('/events', function (Request $request) { $events = array( array('name' => 'OSIDays', 'venue' => 'Bangalore'), array('name' => 'PHP Tour', 'venue' => 'Lille'), array('name' => 'Confoo', 'venue' => 'Montreal'), // ... ); return new Response(json_encode($events), 200, array( 'Content-Type' => 'application/json' )); });
![Page 21: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/21.jpg)
$app->post('/event', function (Request $request) use ($app) { // Get POST data or 400 HTTP response if (!$data = $request->get('event')) { return new Response('Missing parameters.', 400); } // Persist data to the database $event = new Event() $event->title = $data['title']; $event->venue = $data['venue']; $event->save(); // Trigger redirect to the newly created record URI return $app->redirect('/event/'. $event->id, 201); });
![Page 22: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/22.jpg)
$app->put('/event/{id}', function ($id) use ($app) { if (!$data = $request->get('event')) { return new Response('Missing parameters.', 400); } if (!$event = $app['event_manager']->find($id)) { return new Response('Event not found.', 404); } $event->title = $data['title']; $event->venue = $data['venue']; $event->save(); return new Response('Event updated.', 200); });
![Page 23: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/23.jpg)
$app->delete('/event/{id}', function ($id) use ($app) { $event = $app['event_manager']->find($id); if (!$event) { return new Response('Event not found.', 404); } $event->delete(); return new Response('Event deleted.', 200); });
![Page 24: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/24.jpg)
Advanced routing
![Page 25: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/25.jpg)
$app->get('/archive/{year}/{month}', function ($month, $year) { // ... }) ->bind('archives') // Route name ->value('year', date('Y')) // Default parameter value ->value('month', date('m')) ->assert('year', '\d{4}') // Parameter format ->assert('month', '\d{2}');
![Page 26: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/26.jpg)
Events management
![Page 27: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/27.jpg)
$app->before(function (Request $request) use ($app) { $user = $request->server->get('PHP_AUTH_USER'); $pwd = $request->server->get('PHP_AUTH_PW'); if ( $app['api_user'] !== $user || $app['api_pwd'] !== $pwd ) { return new Response('Unauthorized', 403); } });
![Page 28: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/28.jpg)
$app->after(function (Request $request, Response $response) {
// Get URI parameter to determine requested output format $format = $request->attributes->get('format');
switch ($format) { case 'xml': $response->headers->set('Content-Type', 'text/xml'); break; case 'json': $response->headers->set('Content-Type', 'text/json'); break; default: $response->headers->set('Content-Type', 'text/plain'); break; } });
![Page 29: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/29.jpg)
Exception and error handling
![Page 30: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/30.jpg)
$app->error(function (\Exception $e, $code) { switch ($code) { case 400: $message = 'Bad request.'; break; case 404: $message = 'Page not found.'; break; default: $message = 'Internal Server Error.'; } return new Response($message, $code); });
![Page 31: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/31.jpg)
$app['debug'] = true;
![Page 32: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/32.jpg)
$app->post('/event', function (Request $request) use ($app) {
if (!$event = $request->get('event')) {
$app->abort(400, 'Missing parameters.');
}
// ...
return $app->redirect('/event/'. $event->id, 201);
});
![Page 33: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/33.jpg)
Logging with Monolog
![Page 34: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/34.jpg)
use Silex\Provider\MonologServiceProvider;
$app->register(new MonologServiceProvider(), array(
'monolog.logfile' => __DIR__.'/../logs/app.log',
'monolog.class_path' => __DIR__.'/../vendor/monolog/src',
));
![Page 35: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/35.jpg)
if ($app['debug']) {
$app['monolog']->addInfo('Testing the Monolog logging.');
$app['monolog']->addDebug('Method foo() was called.');
$app['monolog']->addWarning('Missing parameter "bar".');
$app['monolog']->addError('Class Foo does not exist.');
}
![Page 36: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/36.jpg)
Database interactions with Doctrine
![Page 37: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/37.jpg)
use Silex\Provider\DoctrineServiceProvider;
$app->register(new DoctrineServiceProvider(), array(
'db.options' => array(
'driver' => 'pdo_mysql',
'host' => 'localhost',
'user' => 'root',
'dbname' => 'event_demo',
),
'db.dbal.class_path' => __DIR__.'/../vendor/doctrine-dbal/lib',
'db.common.class_path' => __DIR__.'/../vendor/doctrine-common/lib',
));
![Page 38: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/38.jpg)
$app->get('/events', function () use ($app) {
$query = 'SELECT id, title, venue FROM events';
$events = $app['db']->fetchAll($query);
return new Response(json_encode($events));
});
![Page 39: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/39.jpg)
Input validation
![Page 40: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/40.jpg)
use Silex\Provider\ValidatorServiceProvider;
$app->register(new ValidatorServiceProvider());
![Page 41: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/41.jpg)
$app['validator']->validate($object);
![Page 42: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/42.jpg)
namespace Confeet\Model;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MaxLength;
class Event extends Model
{
private $title;
private $venue;
static public function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('title', new NotBlank());
$metadata->addPropertyConstraint('title', new MaxLength(array('limit' => 50)));
$metadata->addPropertyConstraint('venue', new NotBlank());
}
}
![Page 43: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/43.jpg)
$app->post('/event', function (Request $request) use ($app) {
if (!$data = $request->get('event')) {
$app->abort(400, 'Missing parameters.');
}
$event = new Event()
$event->setTitle($data['title']);
$event->setVenue($data['venue']);
if (count($app['validator']->validate($event)) > 0) {
$app->abort(400, 'Invalid parameters.');
}
$event->save();
return $app->redirect('/event/'. $event->id, 201);
});
![Page 44: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/44.jpg)
Template engine
![Page 45: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/45.jpg)
§ Fast
§ Concise and rich syntax
§ Automatic output escaping
§ Modern features
§ Extensible
Twig
![Page 46: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/46.jpg)
use Silex\Provider\TwigServiceProvider;
$app->register(new TwigServiceProvider(), array(
'twig.path' => __DIR__.'/../views',
'twig.class_path' => __DIR__.'/../vendor/twig/lib',
));
![Page 47: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/47.jpg)
$app->get('/events.{format}', function ($format) use ($app) {
$events = array(
array('name' => 'OSIDays', 'venue' => 'Bangalore'),
array('name' => 'PHP Tour', 'venue' => 'Lille'),
array('name' => 'Confoo', 'venue' => 'Montreal'),
// ...
);
return $app['twig']->render('events.'.$format.'.twig', array(
'events' => $events,
));
})
->assert('format', 'xml|json');
![Page 48: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/48.jpg)
<!-- views/events.xml.twig -->
<?xml version="1.0" encoding="utf-8" ?>
<events>
{% for event in events %}
<event>
<title>{{ event.title }}</title>
<venue>{{ event.venue }}</venue>
<begin>{{ event.startAt }}</begin>
<end>{{ event.endAt }}</end>
</event>
{% endfor %}
<events>
![Page 49: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/49.jpg)
HTTP Caching & ESI
![Page 50: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/50.jpg)
Reverse Proxy Caching
![Page 51: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/51.jpg)
use Silex\Provider\HttpCacheServiceProvider;
$app->register(new HttpCacheServiceProvider(), array(
'http_cache.cache_dir' => __DIR__.'/../cache',
));
![Page 52: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/52.jpg)
$app->get('/events', function () use ($app) {
$events = array(
array('name' => 'OSIDays', 'venue' => 'Bangalore'),
// ...
);
$content = $app['twig']->render('events.twig', array(
'events' => $events,
));
return new Response($content, 200, array(
'Cache-Control' => 'public, s-maxage=3600',
'Surrogate-Control' => 'content="ESI/1.0"',
));
});
![Page 53: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/53.jpg)
Edge Side Includes
![Page 54: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/54.jpg)
<!-- views/events.twig -->
<?xml version="1.0" encoding="utf-8" ?>
<events>
<esi:include src="/metadata" />
<!-- ... -->
<events>
![Page 55: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/55.jpg)
$app->get('/metadata', function () use ($app) {
return new Response('<meta>...</meta>', 200, array(
'Cache-Control' => 'public, s-maxage=600',
));
});
![Page 56: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/56.jpg)
Functional testing
![Page 57: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/57.jpg)
Client Crawler PHPUnit
![Page 58: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/58.jpg)
class EventApiTest extends Silex\WebTestCase
{
public function testRecentEvents()
{
$client = $this->createClient();
$crawler = $client->request('GET', '/events.xml');
$response = $client->getResponse();
$this->assertTrue($response->isOk());
$this->assertEquals(5, count($crawler->filter('event')));
$this->assertRegExp('/OSIDays/', $response->getContent());
...
}
}
![Page 59: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/59.jpg)
Integrating Zend Soap
![Page 60: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/60.jpg)
$path = __DIR__.'/../vendor/zend/library';
$app['autoloader']->registerNamespace('Zend', $path);
![Page 61: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/61.jpg)
use Zend\Soap\Server;
use Confeet\Model\EventService;
$app->get('/gateway', function (Request $request) use ($app) {
$server = new Server();
$server->setObject(new EventService($app));
$server->setReturnResponse(true);
return new Response($server->handle(), 200, array(
'Content-Type' => 'text/xml'
));
});
![Page 62: Silex meets SOAP & REST](https://reader034.vdocuments.site/reader034/viewer/2022051412/54b773104a7959df648b4599/html5/thumbnails/62.jpg)
92-98, boulevard Victor Hugo
92 115 Clichy Cedex, France
[email protected] (+33 (0)140 998 211)
sensiolabs.com - symfony.com – trainings.sensiolabs.com
Ques%ons?