In the last Desymfony conference I was speaking with Luis Cordova and he introduced me “Stack” (I must admit Stack was in my to-study-list but only marked as favorite). The idea behind Stack is really cool. (In fact every project where Igor Wiedler appears is brilliant, even the chicken one :)).
Nowadays almost every modern framework/applications implements HttpKernelInterface (Symfony, Laravel, Drupal, Silex, Yolo and even the framework that I’m working in ;)) and we can build complex applications mixing different components and decorate our applications with an elegant syntax.
The first thing than come to my mind after studying Stack is to join different Silex applications in a similar way than Symfony (the full stack framework) uses bundles. And the best part of this idea is that it’s pretty straightforward. Let me show you one example:
Imagine that we’re working with one application with a blog and one API. In this case our blog and our API are Silex applications (but they can be one Symfony application and one Silex application for example).
That’s our API application:
use Silex\Application;
$app = new Application();
$app->get('/', function () {
return "Hello from API";
});
$app->run();
And here our blog application:
use Silex\Application;
$app = new Application();
$app->get('/', function () {
return "Hello from Blog";
});
$app->run();
We can organize our application using mounted controllers or even using RouteCollections but today we’re going to use Stack and it’s cool url-map.
First we are going to create our base application. To do this we’re going to implement the simplest Kernel in the world, that’s answers with “Hello” to every request:
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class MyKernel implements HttpKernelInterface
{
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
return new Response("Hello");
}
}
Stack needs HttpKernelInterface and Silex\Application implements this interface, so we can change our Silex applications to return the instance instead to run the application:
// app/api.php
use Silex\Application;
$app = new Application();
$app->get('/', function () {
return "Hello from API";
});
return $app;
// app/blog.php
use Silex\Application;
$app = new Application();
$app->get('/', function () {
return "Hello from API";
});
return $app;
And now we will attach those two Silex applications to our Kernel:
use Symfony\Component\HttpFoundation\Request;
$app = (new Stack\Builder())
->push('Stack\UrlMap', [
"/blog" => include __DIR__ . '/app/blog.php',
"/api" => include __DIR__ . '/app/api.php'
])->resolve(new MyKernel());
$request = Request::createFromGlobals();
$response = $app->handle($request);
$response->send();
$app->terminate($request, $response);
And that’s all. I don’t know what you think but with Stack one big window just opened in my mind. Cool, isn’t it?
The previous post was about how to use AngularJS resources with Silex. AngularJS is great and when I need to switch back to jQuery it looks like I go back 10 years in web development, but business is business and I need to live with jQuery too. Because of that this post is about how to use the Silex RestFull resources from the previous post, now with jQuery. Let’s start:
We’re going to write a simple javascript object to handle the RestFull resource using jQuery:
This days I’m playing with AngularJS. Angular is a great framework when we’re building complex front-end applications with JavaScript. And the best part is that it’s very simple to understand (and I like simple things indeed). Today we are going to play with Resources. Resources are great when we need to use RestFull resources from the server. In this example we’re going to use Silex in the backend. Let’s start.
First of all we must realize that resources aren’t included in the main AngularJS js file and we need to include angular-resource.js (it comes with Angular package). We don’t really need resources. We can create our http services with AngularJS without using this extra js file but it provides a very clean abstraction (at least for me) and I like it.
We’re going to create a simple application with CRUD operations in the table. In the example we will use one simple SqlLite database (included in the github repository)
CREATE TABLE main.messages (
id INTEGER PRIMARY KEY NOT NULL ,
author VARCHAR NOT NULL ,
message VARCHAR NOT NULL );
As we can see we will use ng-app=”MessageService” defined within the js/services.js file:
angular.module('MessageService', ['ngResource']).factory('Message', ['$resource', function ($resource) {
return $resource('/api/message/resource/:id');
}]);
And our controller in js/controllers.js:
function MessageController($scope, Message) {
var currentResource;
var resetForm = function () {
$scope.addMode = true;
$scope.author = undefined;
$scope.message = undefined;
$scope.selectedIndex = undefined;
}
$scope.messages = Message.query();
$scope.addMode = true;
$scope.add = function () {
var key = {};
var value = {author: $scope.author, message: $scope.message}
Message.save(key, value, function (data) {
$scope.messages.push(data);
resetForm();
});
};
$scope.update = function () {
var key = {id: currentResource.id};
var value = {author: $scope.author, message: $scope.message}
Message.save(key, value, function (data) {
currentResource.author = data.author;
currentResource.message = data.message;
resetForm();
});
}
$scope.refresh = function () {
$scope.messages = Message.query();
resetForm();
};
$scope.deleteMessage = function (index, id) {
Message.delete({id: id}, function () {
$scope.messages.splice(index, 1);
resetForm();
});
};
$scope.selectMessage = function (index) {
currentResource = $scope.messages[index];
$scope.addMode = false;
$scope.author = currentResource.author;
$scope.message = currentResource.message;
}
$scope.cancel = function () {
resetForm();
}
}
Now the backend part. As we said before we will use Silex. We’re going to use also RouteCollections to define our routes (you can read about it here). So our Silex application will be:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;
$app = new Silex\Application();
$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
$loader = new YamlFileLoader(new FileLocator(__DIR__ . '/config'));
$collection = $loader->load('routes.yml');
$routes->addCollection($collection);
return $routes;
}
);
$app->register(
new Silex\Provider\DoctrineServiceProvider(),
array(
'db.options' => array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/db/app.db.sqlite',
),
)
);
$app->run();
<?php
namespace Message;
use Silex\Application;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class MessageController
{
public function getAllAction(Application $app)
{
return new JsonResponse($app['db']->fetchAll("SELECT * FROM messages"));
}
public function getOneAction($id, Application $app)
{
return new JsonResponse($app['db']
->fetchAssoc("SELECT * FROM messages WHERE id=:ID", ['ID' => $id]));
}
public function deleteOneAction($id, Application $app)
{
return $app['db']->delete('messages', ['ID' => $id]);
}
public function addOneAction(Application $app, Request $request)
{
$payload = json_decode($request->getContent());;
$newResource = [
'id' => (integer)$app['db']
->fetchColumn("SELECT max(id) FROM messages") + 1,
'author' => $payload->author,
'message' => $payload->message,
];
$app['db']->insert('messages', $newResource);
return new JsonResponse($newResource);
}
public function editOneAction($id, Application $app, Request $request)
{
$payload = json_decode($request->getContent());;
$resource = [
'author' => $payload->author,
'message' => $payload->message,
];
$app['db']->update('messages', $resource, ['id' => $id]);
return new JsonResponse($resource);
}
}
And that’s all. Our prototype is working with AngularJS and Silex as REST provider. We must take care about one thing. Silex and AngularJS aren’t agree in one thing about REST services. AngularJS removes the trailing slash in some cases. Silex (and Symfony) returns HTTP 302 moved temporaly when we’re trying to access to the resource without the trailing slash but when we’re working with mounted controllers we will obtain a 404 page not found (bug/feature?). That’s because my REST service is /api/message/resource/:id instead of /api/message/:id. If I chose the second one, when angular tries to create a new resource, it will POST /api/message instead of POST /api/message/. We’re using mounted routes in this example:
With one simple Silex application (without mounted routes) in one file it doesn’t happen (we will see HTTP 302 and a new request with the trailing slash). Because of that I use this small hack to bypass the problem.
You can see the full code of the example in my github account
Last week Google announced the PHP support for Google App Engine (GAE). PHPStorm, the great IDE for PHP development, also announced support for Google App Engine PHP. Because of that now is time to hack a little bit with this new toy.
I’ve worked in a couple of projects with Google App Engine in the past (with Python). With PHP the process is almost the same. First we need to define our application in the app.yaml file. In our example we are going to redirect all requests to main.php, where our Silex application is defined.
To build a simple Silex application over Google App Engine is pretty straightforward (more info here). Because of that we’re going to go a little further. We are going to use the log-in framework provided by GAE to log-in with our Goggle account within our Silex application. In fact we can use the standard OAuth authentication process but Google provides a simple way to use our gmail account.
Now we’re going to build a LoginProvider to make this process simpler. Our base Silex application will be the following one:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Silex\Application;
use Gae\LoginProvider;
use Gae\Auth;
$app = new Application();
$app->register(new LoginProvider(), array(
'auth.onlogin.callback.url' => '/private',
'auth.onlogout.callback.url' => '/loggedOut',
));
/** @var Auth $auth */
$auth = $app['gae.auth']();
$app->get('/', function () use ($app, $auth) {
return $auth->isLogged() ?
$app->redirect("/private") :
"<a href='" . $auth->getLoginUrl() . "'>login</a>";
});
$app->get('/private', function () use ($app, $auth) {
return $auth->isLogged() ?
"Hello " . $auth->getUser()->getNickname() .
" <a href='" . $auth->getLogoutUrl() . "'>logout</a>" :
$auth->getRedirectToLogin();
});
$app->get('/loggedOut', function () use ($app) {
return "Thank you!";
});
$app->run();
Our LoginProvider is a simple Class that implements Silex\ServiceProviderInterface
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\UserService;
use Gae\Auth;
use Silex\Application;
use Silex\ServiceProviderInterface;
class LoginProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['gae.auth'] = $app->protect(function () use ($app) {
return new Auth($app, UserService::getCurrentUser());
});
}
public function boot(Application $app)
{
}
}
As you can see our Provider class proviedes us an instance of Gae\Auth class
<?php
namespace Gae;
require_once 'google/appengine/api/users/UserService.php';
use google\appengine\api\users\User;
use google\appengine\api\users\UserService;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Silex\Application;
class Auth
{
private $user = null;
private $loginUrl;
private $logoutUrl;
private $logged;
public function __construct(Application $app, User $user=null)
{
$this->user = $user;
if (is_null($user)) {
$this->loginUrl = UserService::createLoginUrl($app['auth.onlogin.callback.url']);
$this->logged = false;
} else {
$this->logged = true;
$this->logoutUrl = UserService::createLogoutUrl($app['auth.onlogout.callback.url']);
}
}
/**
* @return RedirectResponse
*/
public function getRedirectToLogin()
{
return new RedirectResponse($this->getLoginUrl());
}
/**
* @return boolean
*/
public function isLogged()
{
return $this->logged;
}
/**
* @return string
*/
public function getLoginUrl()
{
return $this->loginUrl;
}
/**
* @return string
*/
public function getLogoutUrl()
{
return $this->logoutUrl;
}
/**
* @return \google\appengine\api\users\User|null
*/
public function getUser()
{
return $this->user;
}
}
And that’s all. Full code is available in my github account and you can also use composer to include this provider within your projects.
I’ve working in a pet-project with Silex and I wanted to perform a Sign-in with Twitter. Implementing Sign in with Twitter is pretty straightforward and it’s also well explained in the Twitter’s developers site. Now we only need to implement those HTTP client requests within PHP. We can create the REST client with curl but nowadays I prefer to use the great library called Guzzle to perform those kind of opperations. So let’s start.
The idea is to create something reusable. I don’t want to spend too much time including the Sign-in with Twitter in my proyects, so my first idea was to create a class with all the needed code and mount this class as group of Silex controllers (as it’s defined here). I also want to keep the class as standard as possible and avoiding the usage of any other external dependencies (except Guzzle)..
Imagine a simple Silex application:
<?php
// www/index.php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->get('/', function () {
return 'Hello';
});
$app->run();
Now I want to use a Sign-in with Twitter, so I will change the application to:
<?php
include __DIR__ . "/../vendor/autoload.php";
$app = new Silex\Application();
$app->register(new Silex\Provider\SessionServiceProvider());
$consumerKey = "***";
$consumerSecret = "***";
$twitterLoggin = new SilexTwitterLogin($app, 'twitter');
$twitterLoggin->setConsumerKey($consumerKey);
$twitterLoggin->setConsumerSecret($consumerSecret);
$twitterLoggin->registerOnLoggin(function () use ($app, $twitterLoggin) {
$app['session']->set($twitterLoggin->getSessionId(), [
'user_id' => $twitterLoggin->getUserId(),
'screen_name' => $twitterLoggin->getScreenName(),
'oauth_token' => $twitterLoggin->getOauthToken(),
'oauth_token_secret' => $twitterLoggin->getOauthTokenSecret()
]);
});
$twitterLoggin->mountOn('/login', function () {
return '<a href="/login/requestToken">login</a>';
});
$app->get('/', function () use ($app){
return 'Hello ' . $app['session']->get('twitter')['screen_name'];
});
$app->run();
The application will redirects all requests (without the correct session) to the route “/login”. The login page has a simple link to the route: “/login/requestToken” (we can create a fancy template with Twig if we want, indeed). This route redirects the request to Twitter’s login page and after a successful login it will redirects back to the route that we have defined within our Twitter application. The library assumes that this callback’s url is “/login/callbackUrl”. All this default routes can be defined by the user using the proper setters of the class.
When the sign-in is finished the application will trigger the callback defined in registerOnLoggin function and will redirects to the route “/”. This route (called internally “redirectOnSuccess”) is also customizable with a setter.
In the post Scaling Silex applications I wanted to organize a one Silex application. In one comment Igor Wiedler recommended us to use RouteCollections instead of define the routes with a Symfony’s Dependency Injection Container. Because of that I started to hack a little bit about it and here I show you my outcomes:
I want to build an imaginary application with silex. This application has also one Api and one little blog. I want to organize those parts. Our index.php file
<?php
// www/index.php
require_once __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Silex\Application;
$app = new Application();
$app['routes'] = $app->extend('routes', function (RouteCollection $routes, Application $app) {
$loader = new YamlFileLoader(new FileLocator(__DIR__ . '/../config'));
$collection = $loader->load('routes.yml');
$routes->addCollection($collection);
return $routes;
});
$app->run();
<?php
// lib/Gonzalo123/AppController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
use Silex\Application;
class AppController
{
public function homeAction()
{
return new Response("AppController::homeAction");
}
public function helloAction(Application $app, $name)
{
return new Response("Hello" . $app->escape($name));
}
}
<?php
// lib/Gonzalo123/ApiController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class ApiController
{
public function listAction()
{
return new Response("AppController::listAction");
}
}
<?php
// lib/Gonzalo123/BlogController.php
namespace Gonzalo123;
use Symfony\Component\HttpFoundation\Response;
class BlogController
{
public function homeAction()
{
return new Response("BlogController::homeAction");
}
}
And that’s all. Here also the needed dependencies within our composer.json file
In my humble opinion Silex is great. It’s perfect to create prototypes, but when our application grows up it turns into a mess. That was what I thought until the last month, when I attended to a great talk about Silex with Javier Eguiluz. OK. Scaling Silex it’s not the same than with a Symfony application, but it’s possible.
It’s pretty straightforward to create a Silex application with composer:
But there’s a better way. We can use the Fabien Potencier’s skeleton. With this skeleton we can organize our code better.
We also can use classes as controllers instead of using a closure with all the code. Igor Wiedler has a great post about this. You can read it here.
Today I’m playing with Silex and I want to show you something. Let’s start:
Probably you know that I’m a big fan of Symfony’s Dependency Injection Container (you can read about it here and here), but Silex uses Pimple. In fact the Silex application extends Pimple Class. My idea is the following one:
In the Igor’s post we can see how to use things like that:
My idea is to store this information within a Service Container (we will use Symfony’s DIC). For example here we can see our routes.yml:
routes:
video_info:
pattern: /video/{id}
controller: Gonzalo123\ApiController::initAction
requirements:
_method: GET
As we can see we need to implement one Extension for the alias “routes”. We only will implement the needed functions for YAML files in this example.
<?php
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SilexRouteExtension implements ExtensionInterface
{
/**
* Loads a specific configuration.
*
* @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
* @throws InvalidArgumentException When provided tag is not defined in this extension
*
* @api
*/
public function load(array $config, ContainerBuilder $container)
{
}
/**
* Returns the namespace to be used for this extension (XML namespace).
*
* @return string The XML namespace
*
* @api
*/
public function getNamespace()
{
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*
* @api
*/
public function getXsdValidationBasePath()
{
}
/**
* Returns the recommended alias to use in XML.
*
* This alias is also the mandatory prefix to use when using YAML.
*
* @return string The alias
*
* @api
*/
public function getAlias()
{
return "routes";
}
}
And now we only need to prepare the DIC. According to Fabien’s recommendation in his Silex skeleton, we only need to change the src/controllers.php
<?php
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
// Set up container
$container = new ContainerBuilder();
$container->registerExtension(new SilexRouteExtension);
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../config/'));
// load configuration
$loader->load('routes.yml');
$app['container'] = $container;
$app->mount('/api', include 'controllers/myApp.php');
$container->compile();
$app->error(function (\Exception $e, $code) use ($app) {
if ($app['debug']) {
return;
}
$page = 404 == $code ? '404.html' : '500.html';
return new Response($app['twig']->render($page, array('code' => $code)), $code);
});
and now we define the config/routes.yml
routes:
video_info:
pattern: /video/{videoId}
controller: Gonzalo123\ApiController::initAction
requirements:
_method: GET
And finally the magic in our controllers/myApp.php:
The class for this example is: src/Gonzalo123/ApiController.php
<?php
namespace Gonzalo123;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
class ApiController
{
public function initAction(Request $request, Application $app)
{
return new JsonResponse(array(1, 1, $request->get('id')));
}
}
As you can see the idea is to use classes as controllers, define them within the service container and build the silex needed code iterating over the configuration. What do you think?
Last days I’ve playing with Behat. Behat is a behavior driven development (BDD) framework based on Ruby’s Cucumber. Basically with Behat we defenie features within one feature file. I’m not going to crate a Behat tutorial (you can read more about Behat here). Behat use Gherkin to write the features files. When I was playing with Behat I had one idea. The idea is simple: Can we use Gherking to build a Silex application?. It was a good excuse to study Gherking, indeed ;).
Here comes the feature file:
Feature: application API
Scenario: List users
Given url "/api/users/list.json"
And request method is "GET"
Then instance "\Api\Users"
And execute function "listUsers"
And format output into json
Scenario: Get user info
Given url "/api/user/{userName}.json"
And request method is "GET"
Then instance "\Api\User"
And execute function "info"
And format output into json
Scenario: Update user information
Given url "/api/user/{userName}.json"
And request method is "POST"
Then instance "\Api\User"
And execute function "update"
And format output into json
Our API use this simple library:
<?php
namespace Api;
use Symfony\Component\HttpFoundation\Request;
class User
{
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function info()
{
switch ($this->request->get('userName')) {
case 'gonzalo':
return array('name' => 'Gonzalo', 'surname' => 'Ayuso');
case 'peter':
return array('name' => 'Peter', 'surname' => 'Parker');
}
}
public function update()
{
return array('infoUpdated');
}
}
<?php
namespace Api;
use Symfony\Component\HttpFoundation\Request;
class Users
{
public function listUsers()
{
return array('gonzalo', 'peter');
}
}
The idea is simple. Parse the feature file with behat/gherkin component and create a silex application. And here comes the “magic”. This is a simple working prototype, just an experiment for a rainy sunday.
<?php
include __DIR__ . '/../vendor/autoload.php';
define(FEATURE_PATH, __DIR__ . '/api.feature');
use Behat\Gherkin\Lexer,
Behat\Gherkin\Parser,
Behat\Gherkin\Keywords\ArrayKeywords,
Behat\Gherkin\Node\FeatureNode,
Behat\Gherkin\Node\ScenarioNode,
Symfony\Component\HttpFoundation\Request,
Silex\Application;
$keywords = new ArrayKeywords([
'en' => [
'feature' => 'Feature',
'background' => 'Background',
'scenario' => 'Scenario',
'scenario_outline' => 'Scenario Outline',
'examples' => 'Examples',
'given' => 'Given',
'when' => 'When',
'then' => 'Then',
'and' => 'And',
'but' => 'But'
],
]);
function getMatch($subject, $pattern) {
preg_match($pattern, $subject, $matches);
return isset($matches[1]) ? $matches[1] : NULL;
}
$app = new Application();
function getScenarioConf($scenario) {
$silexConfItem = [];
/** @var $scenario ScenarioNode */
foreach ($scenario->getSteps() as $step) {
$route = getMatch($step->getText(), '/^url "([^"]*)"$/');
if (!is_null($route)) {
$silexConfItem['route'] = $route;
}
$requestMethod = getMatch($step->getText(), '/^request method is "([^"]*)"$/');
if (!is_null($requestMethod)) {
$silexConfItem['requestMethod'] = strtoupper($requestMethod);
}
$instance = getMatch($step->getText(), '/^instance "([^"]*)"$/');
if (!is_null($instance)) {
$silexConfItem['className'] = $instance;
}
$method = getMatch($step->getText(), '/^execute function "([^"]*)"$/');
if (!is_null($method)) {
$silexConfItem['method'] = $method;
}
if ($step->getText() == 'format output into json') {
$silexConfItem['jsonEncode'] = TRUE;
}
}
return $silexConfItem;
}
/** @var $features FeatureNode */
$features = (new Parser(new Lexer($keywords)))->parse(file_get_contents(FEATURE_PATH), FEATURE_PATH);
foreach ($features->getScenarios() as $scenario) {
$silexConfItem = getScenarioConf($scenario);
$app->match($silexConfItem['route'], function (Request $request) use ($app, $silexConfItem) {
function getConstructorParams($rClass, $request) {
$parameters =[];
foreach ($rClass->getMethod('__construct')->getParameters() as $parameter) {
if ('Symfony\Component\HttpFoundation\Request' == $parameter->getClass()->name) {
$parameters[$parameter->getName()] = $request;
}
}
return $parameters;
}
$rClass = new ReflectionClass($silexConfItem['className']);
$obj = ($rClass->hasMethod('__construct')) ?
$rClass->newInstanceArgs(getConstructorParams($rClass, $request)) :
new $silexConfItem['className'];
$output = $obj->{$silexConfItem['method']}();
return ($silexConfItem['jsonEncode'] === TRUE) ? $app->json($output, 200) : $output;
}
)->method($silexConfItem['requestMethod']);
}
$app->run();
You can see the source code in github. What do you think?