Aura.Di 2.x to 3.x Upgrade Guide

3.x has a very minimal BC break. But if you are not sure what are they, then you may feel the pain. I am trying to document most of them, incase I missed please edit and send a pull request.

I will try to eventually pushed to the main Aura.Di repo.

BC Breaks

Instantiation

The way di container is instantiated has been changed from

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Aura\Di\Container;
use Aura\Di\Factory;
use Aura\Di\ContainerBuilder;

$di = new Container(new Factory);

// or 

$container_builder = new ContainerBuilder();
$di = $container_builder->newInstance(
    array(),
    array(),
    $auto_resolve = false
);

to

1
2
3
4
5
6
7
8
9
10
11
use Aura\Di\ContainerBuilder;

$container_builder = new ContainerBuilder();

// use the builder to create and configure a container
// using an array of ContainerConfig classes
$di = $container_builder->newConfiguredInstance([
    'Aura\Cli\_Config\Common',
    'Aura\Router\_Config\Common',
    'Aura\Web\_Config\Common',
]);

setter vs setters

$di->setter is now $di->setters. Please note there is an additional s in the end. https://github.com/auraphp/Aura.Di/issues/115.

Automatic locking

Automatic locking of container once an object is created by container. So make sure everything is lazy call, else you will run something like Cannot modify container when locked.

Config vs ContainerConfig

Version 2 Aura\Di\Config is now Aura\Di\ContainerConfig

Features

lazyGetCall

Example taken from Radar

1
$di->params['Radar\Adr\Handler\RoutingHandler']['matcher'] = $di->lazyGetCall('radar/adr:router', 'getMatcher');

Here the matcher assigned is taken from the RouterContainer getMatcher method.

Instance Factories

Create multiple instances of the class. You can read the docs

Cakephp ORM and Logging Queries

Working with cakephp/orm library, I needed to log all the queries. Cakephp provides a way to do it via cakephp/log.

1
2
3
4
5
6
7
8
use Cake\Log\Log;

Log::config('queries', [
  'className' => 'File',
  'path' => '/my/log/path/',
  'file' => 'app',
  'scopes' => ['queriesLog']
]);

But you are not limited, if you need to configure it to a PSR-3 logger like monolog/monolog

1
2
3
4
5
6
7
8
9
use Cake\Log\Log;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

Log::config('default', function () {
    $log = new Logger('cli');
    $log->pushHandler(new StreamHandler('php://stdout'));
    return $log;
});

That was pretty simple and it logs to cli.

Thank you José Lorenzo Rodríguez for providing the necessary information.

How about logging the queries to a debugbar?

Install fabfuel/prophiler

1
composer require fabfuel/prophiler

Configuring debugbar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Cake\Log\Log;
use Fabfuel\Prophiler\Profiler;
use Fabfuel\Prophiler\Toolbar;
use Fabfuel\Prophiler\DataCollector\Request;
use Fabfuel\Prophiler\Adapter\Psr\Log\Logger;

$profiler = new Profiler();
$toolbar = new Toolbar($profiler);
// add your data collectors
// $toolbar->addDataCollector(new Request());

Log::config('db', function () use ($profiler) {
  $log = new Logger($profiler);
  return $log;
});

Using a PSR-7 framework like zend-expressive, bitExpert/prophiler-psr7-middleware is your friend.

CakePHP ORM and Illuminate Pagination

Do you know CakePHP version 3 has a lovely ORM which can be used as standalone?

Thank you José Lorenzo Rodríguez and every contributor, for your hard work.

1
composer require cakephp/orm

That’s it.

Working on I noticed I need to do some pagination. Oh, remember we have illuminate/pagination. Why not use it?

Problem, there seems no one have implemented it. How could we achieve it? Lets do it.

1
composer require illuminate/pagination

If you are using a psr-7 request / response here is the middleware for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Illuminate\Pagination\Paginator as IlluminatePaginator;
class PaginatorMiddleware
{
    public function __invoke( ServerRequestInterface $request, ResponseInterface $response, callable $next = null )
    {
        IlluminatePaginator::currentPageResolver(function ( $pageName = 'page' ) use($request )
        {
            $params = $request->getQueryParams();
            return empty($params[$pageName]) ? 1 : $params[$pageName];
        });

        IlluminatePaginator::currentPathResolver(function () use($request )
        {
            return $request->getUri()->getPath();
        });

        return $next($request, $response);
    }
}

What we did above are a few things for Illuminate to give the url path when it is doing the pagination. So for example /article?page=<page-number> will come instead of just ?page=<page-number>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
use Cake\ORM\TableRegistry;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator;

$table = TableRegistry::get('Articles');
$currentPage = 1;
$perPage = 20;
$query = $table->find('all');
$total = $query->count();
$items = $query->page($currentPage, $perPage);
$paginator = new LengthAwarePaginator($items, $total, $perPage, $currentPage, [
  'path' => Paginator::resolveCurrentPath(),
]);
echo $paginator->render();

The above code is querying the articles and rendering the pagination with the returned results.

By default the Presenter is Illuminate\Pagination\BootstrapThreePresenter, but you can create your own.

I hope you will love the integration.

Thank you everyone for your support and hard work on components to make PHP better every day.

Eloquent and Pagination Inside Zend Expressive

Recently working with eloquent (Laravel’s orm), zend expressive and zend view, I wanted to integrate pagination.

It was simple as registering a Paginator middleware.

1
2
3
4
5
6
7
8
9
10
11
use Illuminate\Pagination\Paginator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

IlluminatePaginator::currentPageResolver(function ($pageName) use ($request) {
    $params = $request->getQueryParams();
    return empty($params[$pageName]) ? 1 : $params[$pageName];
});
IlluminatePaginator::currentPathResolver(function () use ($request) {
    return $request->getUri()->getPath();
});

and you can call paginate on the Model.

Eg : Consider you have a Post model.

1
$posts = Post::paginate(20);

and in view you can iterate through the $posts and render the pagination. The $posts is an object of LengthAwarePaginator.

You can also modify the presenter accordingly. Default comes with BootstrapThreePresenter

1
2
3
4
5
6
7
8
<?php
foreach ($this->posts as $post) {
?>
    <?= $post->title . "<br />" ?>
<?php
}
?>
<?= $this->posts->render() ?>

Note : Please be aware of the issues/10909.

Integrating Zend Form in Zend Expressive and View

Example is based using Aura.Di. But the functionality will be same for any containers. First register the service Zend\View\HelperPluginManager, so that we can access the same object.

To register the form helpers, create the object of Zend\Form\View\HelperConfig and pass the Zend\View\HelperPluginManager service.

Example code with Aura.Di version 3 configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
use Aura\Di\ContainerConfig;
use Aura\Di\Container;
use Zend\Form\View\HelperConfig;

class ViewHelper extends ContainerConfig
{
    public function define(Container $di)
    {
        $di->set('Zend\View\HelperPluginManager', $di->lazyNew('Zend\View\HelperPluginManager'));
    }

    public function modify(Container $di)
    {
        $serviceManager = $di->get('Zend\View\HelperPluginManager');
        $helper = new HelperConfig();
        $helper->configureServiceManager($servicemanager);
    }
}

Creating your own zend-view helper

  1. Create your helper class
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace App\View\Helper;

use Zend\View\Helper\AbstractHelper;

class HasError extends AbstractHelper
{
    // add as many parameters you want to pass from the view
    public function __invoke()
    {
        // some code
    }
}
  1. Registering your helper class.

First get the Zend\View\HelperPluginManager service.

2.a ) Registering as a factory

1
2
3
$serviceManager->setFactory('hasError', function () {
    return new \App\View\Helper\HasError();
});

2.b ) As an invokable

1
$serviceManager->setInvokableClass('hasError', 'App\View\Helper\HasError');

Now you can access inside zend-view as $this->hasError(). If your view helper need dependencies don’t use the setInvokableClass method. Use factory and get the object from the container.

1
2
3
$serviceManager->setFactory('hasError', function () use ($di) {
     return $di->get('App\View\Helper\HasError');
});

I wished if $serviceManager can understand the aura’s lazyNew functionality so that we don’t need to register it as a service.

Eg : Below will not work.

1
$serviceManager->setFactory('hasError', $di->lazyNew('App\View\Helper\HasError'));

This is what I love to see it working for this a closure, but with namespaced.

Custom Events in Symfony2 Bundle

In this tutorial we will create a custom event for symfony2 bundle.

Assuming you have downloaded the symfony-standard distribution to play.

Create HktEventBundle via sensio generator bundle.

1
php app/console generate:bundle --namespace=Hkt/EventBundle --dir src --no-interaction

Create the event class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// src/Hkt/EventBundle/Event/PageViewed.php
namespace Hkt\EventBundle\Event;

use Symfony\Component\EventDispatcher\Event;

class PageViewed extends Event
{
    protected $name;

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

    public function getName()
    {
        return $this->name;
    }
}

Add as many methods/properties which are needed from the listener. Instead of creating an event class we can make use of generic event also.

Dispatching Event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
//src/Hkt/EventBundle/Controller/DefaultController.php
namespace Hkt\EventBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Hkt\EventBundle\Event\PageViewed;

class DefaultController extends Controller
{
    /**
     * @Route("/hello/{name}")
     * @Template()
     */
    public function indexAction($name)
    {
        $event = new PageViewed($name);
        $this->get('event_dispatcher')->dispatch('hkt.event.page_viewed', $event);
        return array('name' => $name);
    }
}

Listener

Create the listener to do what you want to do with the dispatched/triggered event.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// src/Hkt/EventBundle/EventListener/PageViewedListener.php
namespace Hkt\EventBundle\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Hkt\EventBundle\Event\PageViewed;

class PageViewedListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'hkt.event.page_viewed' => 'handler',
        );
    }

    public function handler(PageViewed $event)
    {
        // $event->getName();
        // do what you want with the event
    }
}

In order to get the PageViewedListener to be called, we need to register the listener to the event_dispatcher.

Add the below lines in services.yml or services.xml accordingly.

1
2
3
4
5
# src/Hkt/EventBundle/Resources/config/services.yml
hkt.event.page_viewed:
    class: Hkt\EventBundle\EventListener\PageViewedListener
    tags:
        - {name: kernel.event_listener, event:hkt.event.viewed, method:handler} 

or in xml as

1
2
3
4
5
<services>
    <service id="hkt.event.page_viewed" class="Hkt\EventBundle\EventListener\PageViewedListener">
        <tag name="kernel.event_listener" event="hkt.event.page_viewed" method="handler"/>
    </service>
</services>

There may be better ways to do this. If you think so do share your knowledge.

Thank you.

20 Years of PHP

First my apologies being late! I hope you all know PHP is celebrating its 20th years of existence in the web.

There is something that makes PHP unique that helps to standout with other languages.

In this opporchunity I would like to thank the creator, the contributors, the maintainers, and all the users (past/present) to make this memorable.

My Story

I got an exposure to learn and use internet in my college days. In those period I look html as a very complex language. I wasn’t aware there exists editors which help you.

Around 2006, iirc Ganesh sir was the one who introduced me to PHP, and then with the guidance and help of Jose Antony.

College life ended in 2007. The search begins to find a job, and I managed to get one.

In 2009, I wrote a blog post A simple Blog using Zend framework 1.9 which got lots of attraction.

I wasn’t aware phpdeveloper.org and devzone.zend.com was changing my life and getting more closer to the people in the PHP community.

Some people praised, some disagreed which I came across a tool called phpunit.

In 2010, I attended my first conference osidays and met some of the awesome people like David Coallier, Fabien Potencier, Jacob Vrana, Dave Hall and many more.

Into the world of open-source

I would like to thank Paul M Jones the man behind auraphp.com for all the help, support and mentoring.

Started with stupid questions, and slowly as a small contributor.

Contributing to open-source gave me some confidence and it opened some help from the community.

Thank you Beau D Simensen, CalEvans.

Start a habit of learning, you will not regret joining NomadPHP it is awesome!.

Thank you all, and thanks for all the help and support you guys are showing.

Happy PhPing!

Zend Feed and Guzzle

You may have worked with Zend Feed as a standalone component. I don’t know whether you have integrated Zend framework Feed with Guzzle as Http Client.

This post is inspired by Matthew Weier O’Phinney, who have mentioned the same on github.

Our composer.json looks

1
2
3
4
5
6
7
8
9
10
11
12
{
    "require": {
        "guzzlehttp/guzzle": "~5.2",
        "zendframework/zend-feed": "~2.3",
        "zendframework/zend-servicemanager": "~2.3"
    },
    "autoload": {
        "psr-0": {
            "": "src/"
        }
    }
}

Zen\Feed\Reader\Reader have a method importRemoteFeed which accepts an instance of Zend\Feed\Reader\Http\ClientInterface.

The Zend\Feed\Reader\Http\ClientInterface have only one method get which returns Zend\Feed\Reader\Http\ResponseInterface.

So any http client that satisfy the interface will work. Let’s create them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// src/GuzzleClient.php
use GuzzleHttp\Client;
use Zend\Feed\Reader\Http\ClientInterface;

class GuzzleClient implements ClientInterface
{
    protected $guzzle;

    public function __construct(Client $guzzle)
    {
        $this->guzzle = $guzzle;
    }

    public function get($uri)
    {
        $response  = $this->guzzle->get($uri);
        return new GuzzleResponse($response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
// src/GuzzleResponse.php
use GuzzleHttp\Client;
use GuzzleHttp\Message\Response;
use Zend\Feed\Reader\Http\ClientInterface;
use Zend\Feed\Reader\Http\ResponseInterface;
use Zend\Feed\Reader\Reader;

class GuzzleResponse implements ResponseInterface
{
    protected $response;

    public function __construct(Response $response)
    {
        $this->response = $response;
    }

    public function getStatusCode()
    {
        return $this->response->getStatusCode();
    }

    public function getBody()
    {
        return (string) $this->response->getBody();
    }
}

Wiring up,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;
use Zend\Feed\Reader\Reader as FeedReader;

$client = new GuzzleClient(new Client());
$feed = FeedReader::importRemoteFeed('http://feeds.feedburner.com/harikt/YKAJ', $client);

echo 'The feed contains ' . $feed->count() . ' entries.' . "\n\n";
foreach ($feed as $entry) {
    echo 'Title: ' . $entry->getTitle() . "\n";
    // echo 'Description: ' . $entry->getDescription() . "\n";
    echo 'URL: ' . $entry->getLink() . "\n\n";
}

You can run from the command line.

Once PSR-7 is accepted, and if both guzzle and zend framework (zf3) is built on top of it, we may not need to do the same.

Happy PhPing.

Conduit : The Middleware for PHP

Long back, I happened to talk with Beau Simensen about stackphp on #auraphp channel. It was hard for me to digest when I noticed it need symfony/http-kernel and its dependencies.

After a few months, I started to like the middleware approach of slim framework and wanted to push it to aura. But nothing happened there.

Conduit to rescue

Conduit is a Middleware for PHP built by Matthew Weier O’Phinney lead of Zend framework. Conduit supports the current PSR-7 proposal. I believe like the many PSR’s, PSR-7 will be a revolution in the PHP world. Conduit is really a micro framework and can grow with your project.

Starting your project

I hope you know about the tool composer and know about PHP 5.3 to follow the tutorial.

1
2
3
4
mkdir sample-project
cd sample-project
composer require "phly/conduit:0.10.*"
mkdir web

We have created an extra web folder, so it acts as the document root. Create an index.php file in the web folder and lets start serving our first Hello conduit! message.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
require dirname(__DIR__) . '/vendor/autoload.php';

use Phly\Conduit\Middleware;
use Phly\Http\Server;

$app = new Middleware();
$app->pipe('/', function ($request, $response, $next) {
    return $response->write('Hello conduit!')->end();
});
$server = Server::createServer($app,
  $_SERVER,
  $_GET,
  $_POST,
  $_COOKIE,
  $_FILES
);
$server->listen();

Start your web server, or fire your built in PHP server.

1
php -S localhost:8000 web/index.php -t web

Point your browser to http://localhost:8000 and you can see Hello conduit!.

Middlewares

Conduit route is very limited and will not handle dynamic routing. So we need a router middleware to resuce. Let us build our first router middleware. If you check the docs middleware can be a closure, invokable objects, array callback etc. We will stick with closure in the examples.

The idea is same even if you are using a different library.

  1. Get the path via $request->getUri()->getPath()
  2. Check router if the path is matching
  3. If
    • no call the next middleware in stack. ie return $next()
    • yes execute the controller and return back the response

Be sure that if you change something you need to return the response. Because Request and Response are immutable.

In-order to build something like the above, we need a router library which can handle routing, and a dispatcher library which can handle the necessary operation when a route is found.

Install the dependencies.

1
composer require "aura/router:~2.0" "aura/dispatcher:~2.0"

The router middleware will look like as below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$router = new \Aura\Router\Router(
    new \Aura\Router\RouteCollection(new \Aura\Router\RouteFactory),
    new \Aura\Router\Generator
);
$dispatcher = new \Aura\Dispatcher\Dispatcher(array(), 'controller', 'action');

$app->pipe(function ($request, $response, $next) use ($router, $dispatcher) {
    $path = $request->getUri()->getPath();
    $route = $router->match($path, $request->getServerParams());
    if (! $route) {
        return $next();
    }
    $params = $route->params;
    $params['request'] = $request;
    $params['response'] = $response;
    $result = $dispatcher->__invoke($params);
    if ($result instanceof \Psr\Http\Message\ResponseInterface) {
        $response = $result;
    } else {
        $response = $response->write($result)->end();
    }
    return $response;
});

Now your router middleware can handle dynamic things. You can see the full example over gist.

You may need an authentication middleware to check whether the user is authenticated, or a content negotiation middleware to set the corresponding Content-Type header in the response.

I have created a skelton project which have a router middleware, authentication middleware and negotiation middleware with the help of a few libraries. Less libraray means less code to maintain, easy to understand and debug the code behind the scenes.

Today, I noticed one question over reddit Moving to a real framework.. need help with the migration wrote my suggestion how conduit + aura can help.

Play and enjoy!

Experimenting on Different Framework

Whenever I get some time, I try to learn and experiment on different frameworks. I would like to stay away from pin pointing to the frameworks I have looked, so they don’t feel bad.

Long live components

Components are awesome!. You can plug your favourite components to any system. Thank you composer. One strong preference choosing a framework was

  • It should be built from components

Advantage

  • I could use those components on a different project without spending long time learning a different API.

Choosing Symfony as a framework

  • Symfony framework have been used in production for long, big community, and backed by Sensio Labs.
  • Many of the libraries, cms and frameowrks that exists on packagist rely on symfony components.

Look at phinx a db migration tool, drupal 8 etc. These makes use of components like symfony/console, symfony/http-foundation, symfony/router etc.

Some of the good things I like in symfony console component are its helpers.

Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Output\ConsoleOutput;

$output = new ConsoleOutput();
$table = new Table($output);
$table
    ->setHeaders(array('ISBN', 'Title', 'Author'))
    ->setRows(array(
        array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
        array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
        array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
        array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
    ))
;
$table->render();

Progress bar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutput;

$output = new ConsoleOutput();

// create a new progress bar (50 units)
$progress = new ProgressBar($output, 50);

// start and displays the progress bar
$progress->start();

$i = 0;
while ($i++ < 50) {
    // ... do some work

    // advance the progress bar 1 unit
    $progress->advance();

    // you can also advance the progress bar by more than 1 unit
    // $progress->advance(3);
}

// ensure that the progress bar is at 100%
$progress->finish();

Question

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\ChoiceQuestion;

$output = new ConsoleOutput();
$helper = new QuestionHelper();
$input = new StringInput('action');
$question = new ConfirmationQuestion('Continue with this action?', false);

if ($helper->ask($input, $output, $question)) {
    $question = new ChoiceQuestion(
        'Please select your favorite color (defaults to red)',
        array('red', 'blue', 'yellow'),
        0
    );
    $question->setErrorMessage('Color %s is invalid.');

    $color = $helper->ask($input, $output, $question);
    $output->writeln('You have just selected: '.$color);
} else {
    $output->writeln('Exiting !');
}

Hidden Passwords

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Question\Question;

$output = new ConsoleOutput();
$helper = new QuestionHelper();
$input = new StringInput('action');
$question = new Question('What is the database password?');
$question->setHidden(true);
$question->setHiddenFallback(false);

$password = $helper->ask($input, $output, $question);
$output->writeln('Entered password : ' . $password);

and more.. I hope you will also add symfony/console into your list.

Http Foundation

The symfony/http-foundation is another extensively used component. It is actually Aura.Web + Aura.Session with more session handlers. Used by drupal, laravel etc in core. When working on mixed projects like drupal, laravel etc, it is good to be in your list.

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$request = Request::createFromGlobals();
$response = new Response(
    'Hello symfony',
    Response::HTTP_OK,
    array('content-type' => 'text/html')
);
$response->send();

Finder

Another wonderful component is the finder.

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Finder\Finder;

$finder = new Finder();

$iterator = $finder
  ->files()
  ->depth(0)
  ->in(__DIR__);

foreach ($iterator as $file) {
    print $file->getRealpath()."\n";
}

Filesystem

Filesystem is another nice component which helps you to copy, rename, create directories.

Complex components

So far, the above components are easy to use. One of the complex component is the security component. It tries to do authentication and authorization.

As a person who have worked with zend framework, I love how authentication and authorization is done. I do love the latest Aura.Auth library.

If you are using symfony as a framework, there is a bundle to rescue. Yes, FOSUserBundle. I believe most of them who use symfony will be using it.

Part of the complexity may be because symfony security was based on Spring security from Java. I am not sure why Fabien choose Spring when there were good ones in the PHP world, but I hope there is a reason behind it.

Conclusion : Symfony as a framework is nice!.