Category Archives: Symfony

Google App Engine, PHP and Silex. Setting up a Login Provider

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.

application: silexgae
version: 1
runtime: php
api_version: 1
threadsafe: true

handlers:
- url: .*
  script: main.php

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.

Scaling Silex applications (part II). Using RouteCollection

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();

Now our routes.yml file:

# config/routes.yml

home:
  path: /
  defaults: { _controller: 'Gonzalo123\AppController::homeAction' }

hello:
  path: /hello/{name}
  defaults: { _controller: 'Gonzalo123\AppController::helloAction' }

api:
  prefix: /api
  resource: api.yml

blog:
  prefix: /blog
  resource: blog.yml

As we can see we have separated the main routing file into different files: api.yml (for the Api) and blog.yml (for the blog)

# config/api.yml

api.list:
  path:     /list
  defaults: { _controller: 'Gonzalo123\ApiController::listAction' }
# blog.yml

blog.home:
  path:     /
  defaults: { _controller: 'Gonzalo123\BlogController::homeAction' }

And now we can create our controllers:

<?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

{
    "require":{
        "silex/silex":"1.0.*@dev",
        "symfony/yaml":"v2.2.0",
        "symfony/config":"v2.2.0"
    },
    "autoload":{
        "psr-0":{
            "":"lib/"
        }
    }
}

source code at github.

Scaling Silex applications

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:

{
    "require": {
        "silex/silex": "1.0.*"
    },
    "minimum-stability": "dev"
}

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:

$app->match('/video/{id}', 'Gonzalo123\ApiController::indexAction')->method('GET')->bind('video_info');

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:

<?php

$myApp = $app['controllers_factory'];

foreach ($container->getExtensionConfig('routes')[0] as $name => $route) {
    $controller = $myApp->match($route['pattern'], $route['controller']);
    $controller->method($route['requirements']['_method']);
    $controller->bind($name);
}
return $myApp;

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?

Dependency Injection Containers with PHP. When Pimple is not enough.

Two months ago I wrote an article about Dependency Injection with PHP and Pimple. After the post I was speaking about it with a group of colleagues and someone threw a question:

What happens if your container grows up? Does Pimple scale well?

The answer is not so easy. Pimple is really simple and good for small projects, but it becomes a little mess when we need to scale. Normally when I face a problem like that I like to checkout the Symfony2 code. It usually implements a good solution. There’s something I really like from Symfony2: it’s a brilliant component library and we can use those components within our projects instead of using the full stack framework. So why don’t we only use the Dependency Injection component from SF2 instead Pimple to solve the problem? Let’s start:

We are going to use composer to load our dependencies so we start writing our composer.json file. We want to use yaml files so we need to add “symfony/yaml” and “symfony/config” in addition to “symfony/dependency-injection”:

{
    "require": {
        "symfony/dependency-injection": "dev-master",
        "symfony/yaml": "dev-master",
        "symfony/config": "dev-master"
    },
    "autoload":{
        "psr-0":{
            "":"lib/"
        }
    },
}

Now we can run “composer install” command and we already have our vendors and our autolader properly set.

We are going to build exactly the same example than in the previous post:

<?php
class App
{
    private $proxy;

    public function __construct(Proxy $proxy)
    {
        echo "App::__construct\n";
        $this->proxy = $proxy;
    }

    public function hello()
    {
        return $this->proxy->hello();
    }
}

class Proxy
{
    private $curl;

    public function __construct(Curl $curl)
    {
        $this->curl = $curl;
    }

    public function hello()
    {
        echo "Proxy::__construct\n";
        return $this->curl->doGet();
    }
}

class Curl
{
    public function doGet()
    {
        echo "Curl::doGet\n";
        return "Hello";
    }
}

Now we create our file “services.yml” describing our dependency injection container behaviour:

services:
  app:
    class:     App
    arguments: [@Proxy]
  proxy:
    class:     Proxy
    arguments: [@Curl]
  curl:
    class:     Curl

and finally we can build the script:

<?php
include __DIR__ . "/vendor/autoload.php";

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.yml');

$container->get('app')->hello();

IMHO is as simple a Pimple but much more flexible, customizable and it’s also well documented. For example we can split our yaml files into different ones and load them:

<?php
include __DIR__ . "/vendor/autoload.php";

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services1.yml');
$loader->load('services2.yml');
$container->get('app')->hello();

An we also can use imports in our yaml files:

imports:
  - { resource: services2.yml }
services:
  app:
    class:     App
    arguments: [@Proxy]

If you don’t like yaml syntax and you prefer XML (I know. It looks insane :) ) you can use it, or even programatically with PHP.

What do you think?

Source code in github (see the README to install the vendors)

Follow

Get every new post delivered to your Inbox.

Join 552 other followers