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!.

Running a Successful Open-source Project

Running a successful open-source project is not easy. It takes some good efforts to maintain and make it successful. Let me thank each and everyone who have been taking your time and efforts to maintain open-source projects.

How can you make your project a success?

The word Success differ from people to people. So from the eyes of a developer it may be succesful. To measure success you may want to look similar projects which have been started at the time, and see where your project is.

  • Learn from your mistakes
  • Change ideas when it needs
  • Quick support/help

Open-source and Business

Many of the open-source projects like Wordpress, Drupal, Symfony etc are used to make business out of it. When someone use X open-source project for their business they need things to be resolved faster. The community and paid services plays a wonderful role in the success of an open-source project. If it takes months to resolve a ticket or get feedback, people will leave projects.

  • Start service for business ( Eg : see symfony, acquia etc run this)

Learnings

When a project is released, out of 100, you should expect only 1 person to try it. If he is not impressed, then it is a loss. Make it simple and easy to configure tools. Too much of configuration is a pain in the ass.

Don’t forget “Word of mouth is always the success of an open-source project.”

Happy open-sourcing!

Improving Environment Value in Aura V2

Aura v2 framework probably have missed a better way to handle environment variables. But that doesn’t make you stall. Things can be improved ;–).

Assume you are already using aura framework and is at root of the project.

We are going to make use of vlucas/phpdotenv , alternatives are there if you are interested to experiment.

1
composer require vlucas/phpdotenv

Edit the file config/_env.php and add Dotenv::load(/path/to/.env); to the first line. If you have not modified anything it will look as below

1
2
3
4
5
6
7
8
<?php
// {PROJECT_PATH}/config/_env.php
Dotenv::load(__DIR__);
// set the mode here only if it is not already set.
// this allows for setting via web server, shell script, etc.
if (! isset($_ENV['AURA_CONFIG_MODE'])) {
    $_ENV['AURA_CONFIG_MODE'] = 'dev';
}

Don’t forget to create the .env file.

You are done!

Now you can easily make use of environment variables easily from the configuration files.

Below is an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
namespace Aura\Framework_Project\_Config;
// config/Common.php

use Aura\Di\Config;
use Aura\Di\Container;

class Common extends Config
{
    public function define(Container $di)
    {
        $di->params['Aura\Sql\ExtendedPdo'] = array(
            'dsn' => getenv('dsn'),
            'username' => getenv('username'),
            'password' => getenv('password'),
        );
    }

    // more code

Speedup Dependency Injection Configuration for Aura V2

Aura v2 added auto resolution in-order to help lazy people writing configuration manually. Even though it was introduced to help, it introduced a few issues.

So auto resolution will be disabled in the future. Some of the complains/suggestions are how to easily write the di configuration.

So introducing you FOA.DiConfig

Installation

1
composer require foa/di-config

Usage

1
2
3
vendor/bin/di-config-dump
Usage : vendor/bin/di-config-dump /real/path/to/file.php
Usage : vendor/bin/di-config-dump /real/path/to/directory

Example 1

Let’s assume you have

1
2
3
4
5
6
7
8
9
10
<?php
// src/Vendor/World.php
namespace Vendor;

class World
{
    public function __construct(Baz $baz)
    {
    }
}
1
2
3
4
5
6
7
<?php
// src/Vendor/Baz.php
namespace Vendor;

class Baz
{
}

Now you can make use of

1
vendor/bin/di-config-dump src/Vendor/World.php

will output

1
$di->params['Vendor\World']['baz'] = $di->lazyNew('Vendor\Baz');

You can also pass directory path instead of file. It will read the files and display the configuration.

Example 2

Let us look into another example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
// src/Vendor/Hello.php
namespace Vendor;

class Hello
{
    public function __construct(
        \Aura\Web\Response $response,
        \Aura\Web\Request $request,
        \Aura\Router\Router $router,
        World $word
    ) {
    }
}
1
vendor/bin/di-config-dump src/Vendor/Hello.php

will output

1
2
3
4
$di->params['Vendor\Hello']['response'] = $di->lazyGet('aura/web-kernel:response');
$di->params['Vendor\Hello']['request'] = $di->lazyGet('aura/web-kernel:request');
$di->params['Vendor\Hello']['router'] = $di->lazyGet('aura/web-kernel:router');
$di->params['Vendor\Hello']['word'] = $di->lazyNew('Vendor\World');

If you look carefully the Aura\Web\Response, Aura\Web\Request and Aura\Router\Router are making use of lazyGet which gets the shared instance of the Aura.Web_Kernel .

If you are not using inside the framework just pass something as 2nd argument.

1
2
3
4
5
vendor/bin/di-config-dump src/Vendor/Hello.php h
$di->params['Vendor\Hello']['response'] = $di->lazyNew('Aura\Web\Response');
$di->params['Vendor\Hello']['request'] = $di->lazyNew('Aura\Web\Request');
$di->params['Vendor\Hello']['router'] = $di->lazyNew('Aura\Router\Router');
$di->params['Vendor\Hello']['word'] = $di->lazyNew('Vendor\World');

Please make sure all the files need to be autoloadable in-order to generate this.

If you like to improve something fork and contribute.

I have purposefully left not to make use of Aura.Cli in this library. Not sure if we need to integrate or not.

Getting Started With Aura V2

Yesterday aura framework v2 stable released.

Lots of complains about documentation or missing documentation. So this is a quick start. Probably a five minutes walk through. Learn and change to make it better.

Creating your project

Create the project using composer.

1
2
composer create-project aura/web-project quick-start
cd quick-start

The minimal framework don’t come with any sort of view integrated. Let us use aura/view, the two step templating with the help of foa/html-view-bundle.

1
composer require "foa/html-view-bundle:~2.0"

We will be keeping all the templates in templates folder where views in templates/views and layout in templates/layouts.

1
mkdir -p templates/{views,layouts}

Edit config/Common.php and define service for view.

1
2
3
4
public function define(Container $di)
{
    $di->set('view', $di->lazyNew('Aura\View\View'));
}

add a way to set the path to templates. Assuming you have templates folder in the root. There is no finder in aura/view to increase the performance of loading and rendering templates. For a quick hack let us iterate through the directory and set all the views and layouts to its registry.

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
public function modify(Container $di)
{
    // more code
    $this->defineTemplates($di);
}

public function defineTemplates($di)
{
    $view = $di->get('view');
    $view_registry = $view->getViewRegistry();
    $view_directory = dirname(__DIR__) . '/templates/views/';
    $iterator = new \DirectoryIterator($view_directory);
    foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
            $view_registry->set($fileinfo->getBasename('.php'), $fileinfo->getPathname());
        }
    }

    $layout_registry = $view->getLayoutRegistry();
    $layout_directory = dirname(__DIR__) . '/templates/layouts/';
    $iterator = new \DirectoryIterator($layout_directory);
    foreach ($iterator as $fileinfo) {
        if ($fileinfo->isFile()) {
            $layout_registry->set($fileinfo->getBasename('.php'), $fileinfo->getPathname());
        }
    }
}

Edit modifyDispatcher method to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public function modifyWebDispatcher($di)
{
    $dispatcher = $di->get('aura/web-kernel:dispatcher');

    $view = $di->get('view');
    $response = $di->get('aura/web-kernel:response');
    $request = $di->get('aura/web-kernel:request');
    $dispatcher->setObject('hello', function () use ($view, $response, $request) {
        $name = $request->query->get('name', 'Aura');
        $view->setView('hello');
        $view->setLayout('default');
        $view->setData(array('name' => $name));
        $response->content->set($view->__invoke());
    });
}

Create your basic template templates/views/hello.php

1
2
3
<?php // templates/views/hello.php ?>
<?php $this->title()->set("Hello from aura"); ?>
<p>Hello <?= $this->name; ?></p>

and a very basic layout

1
2
3
4
5
6
7
8
9
10
<?php // templates/layouts/default.php ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
  <head>
    <?php echo $this->title(); ?>
  </head>
  <body>
    <?php echo $this->getContent(); ?>
  </body>
</html>

Let us fire the php server

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

and point your browser to http://localhost:8000 .

Probably very simple way how to use aura as a micro framework!.

You can see the example over github.

What is next?

Read Aura Framework v2 : The missing Manual and report/contribute to the book.

Aura Input Form Inside Slim Framework

Rob Allen wrote about Integrating ZF2 forms into Slim. I did write how you can use Aura.Input and Aura.Html to create standalone form for PHP. This time I felt I should write about integrating aura input inside Slim.

Let us install a few dependencies aura/input for building the form and aura/html for the html helpers. You of-course can skip not to use aura/html and build your own helper. I also purposefully left not integrating the powerful Aura.Filter , but you are not limited to integrate any validator you love inside Aura.Input .

The full composer.json is as below.

1
2
3
4
5
6
7
8
9
10
11
12
{
    "require": {
        "slim/slim": "2.*",
        "aura/html": "2.0.0",
        "aura/input": "1.*"
    },
    "autoload":{
        "psr-0":{
            "": "src/"
        }
    }
}

We will keep ContactForm.php under src folder. ie why you see the autoload in composer.json. The form looks 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
// src/ContactForm.php

use Aura\Input\Form;

class ContactForm extends Form
{
    public function init()
    {
        $this->setField('name')
            ->setAttribs([
                'id' => 'contact[name]',
                'name' => 'contact[name]',
                'size' => 20,
                'maxlength' => 20,
            ]);
        $this->setField('email')
            ->setAttribs([
                'id' => 'contact[email]',
                'name' => 'contact[email]',
                'size' => 20,
                'maxlength' => 20,
            ]);
        $this->setField('url')
            ->setAttribs([
                'id' => 'contact[url]',
                'name' => 'contact[url]',
                'size' => 20,
                'maxlength' => 20,
            ]);
        $this->setField('message', 'textarea')
            ->setAttribs([
                'id' => 'contact[message]',
                'name' => 'contact[message]',
                'cols' => 40,
                'rows' => 5,
            ]);
        $this->setField('submit', 'submit')
            ->setAttribs(['value' => 'send']);

        $filter = $this->getFilter();

        $filter->setRule(
            'name',
            'Name must be alphabetic only.',
            function ($value) {
                return ctype_alpha($value);
            }
        );

        $filter->setRule(
            'email',
            'Enter a valid email address',
            function ($value) {
                return filter_var($value, FILTER_VALIDATE_EMAIL);
            }
        );

        $filter->setRule(
            'url',
            'Enter a valid url',
            function ($value) {
                return filter_var($value, FILTER_VALIDATE_URL);
            }
        );

        $filter->setRule(
            'message',
            'Message should be more than 7 characters',
            function ($value) {
                if (strlen($value) > 7) {
                    return true;
                }
                return false;
            }
        );
    }
}

The entry point web/index.php looks 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
25
<?php
// web/index.php

use Aura\Input\Builder;
use Aura\Input\Filter;

require dirname(__DIR__) . '/vendor/autoload.php';
$app = new \Slim\Slim(array(
    'templates' => dirname(__DIR__) . '/templates'
));
$app->map('/contact', function () use ($app) {
    $form = new ContactForm(new Builder(), new Filter());
    if ($app->request->isPost()) {
        $form->fill($app->request->post('contact'));
        if ($form->filter()) {
            echo "Yes successfully validated and filtered";
            var_dump($data);
            $app->halt();
        }
    }
    $app->render('contact.php', array('form' => $form));
})->via('GET', 'POST')
->name('contact');

$app->run();

The template contact.php resides under templates folder.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?php
// templates/contact.php

use Aura\Html\HelperLocatorFactory;

$factory = new HelperLocatorFactory();
$helper = $factory->newInstance();

function showFieldErrors($form, $name) {
    $errors = $form->getMessages($name);
    $str = '';
    if ($errors) {
        $str .= '<ul>';
        foreach ($errors as $error) {
            $str .= '<li>' . $error . '</li>';
        }
        $str .= '</ul>';
    }
    return $str;
}
?>
<html>
<head>
    <title>Aura input form, inside slim framework</title>
</head>
<body>
    <form method="post" action="<?php echo $app->urlFor('contact'); ?>" enctype="multipart/form-data" >
        <table cellpadding="0" cellspacing="0">
            <tr>
                <td>Name : </td>
                <td>
                <?php
                    echo $helper->input($form->get('name'));
                    echo showFieldErrors($form, 'name');
                ?>
                </td>
            </tr>
            <tr>
                <td>Email : </td>
                <td>
                <?php
                    echo $helper->input($form->get('email'));
                    echo showFieldErrors($form, 'email');
                ?>
                </td>
            </tr>
            <tr>
                <td>Url : </td>
                <td>
                <?php
                    echo $helper->input($form->get('url'));
                    echo showFieldErrors($form, 'url');
                ?>
                </td>
            </tr>
            <tr>
                <td>Message : </td>
                <td>
                <?php
                    echo $helper->input($form->get('message'));
                    echo showFieldErrors($form, 'message');
                ?>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                <?php
                echo $helper->input($form->get('submit'));
                ?>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

NB: If you need to reuse the functionality of showFieldErrors keep it on a separate file and require it.

Thank you Andrew Smith, Christian Schorn for the proof reading and tips provided.

I hope you will find something interesting in the integration!.

Download and play with code from github

Aura Framework V2: The Missing Manual

Aura has an awesome collection of libraries for different purpose.

It has components for authentication, cli, request and response, router, dependency injection container, dispatcher, html, view, event handlers, validation, extended pdo, query builders, sql schema, marshal, build and modify uri, http, internationalization, session, forms, includer.

If you are new to aura, there is probably something you may want to figure out yourself.

Some of the components have version 1 and version 2 releases. There is a question of which branch corresponds to which version.

The v1 packages are in develop branch and v2 over develop-2 branch.

There are a few changes in v1 to v2. It is easy to understand when you look into the composer.json or if you know aura v2 follows psr-4 directory structure than the v1 that followed psr-0.

If you see a package "aura/installer-default": "1.0.0" in the require of composer.json it is for sure v1.

Composer installs every package in the vendor folder. The name of the package installed will be the package name. So basically it installs vendor/aura/<package-name> .

In aura framework v1 we have some specific folder structure and it was before composer becomes a standard. So when composer became a standard we added a way to install the framework specific installations in package folder and the rest of the library installation (other than aura framework) in the same way as composer did.

So was the existence of aura/installer-default in v1 package. In v2 we moved to composer assisted two stage configuration.

v2 Framework

There exists a micro framework/full stack framework for v2. But things are hard to find when you are new to aura and when github organization have more than 30 repositories.

Aura framework is built on top of aura libraries, and the library docs applies to the framework also. But people new to aura may be having hard time to find the specific documentation or may be stuck sometime. I don’t know whether my thoughts are right or wrong.

Documentation is one of the hardest problem when newer versions are released. Say 1.0.0 released, 1.1.0 … although the documentation is there in the installed repo, it is probably hard to make things online.

I was talking with Paul M Jones regarding the documentation lately, and he too shared some concerns. Talking with him gave me some inspiration to start the missing manual for the aura framework.

Goal

  • let people read and learn
  • promote aura with good documentation
  • at the sametime, to make a living

Yes, I am independent freelance php developer. It was a sad story that I don’t want to recall how I became a freelancer by chance.

Before my freelancing, I was down for a few months, not physically but mentally which has impacted my life with some ups and downs. But now I really love working as an independent contractor.

About the book

You can find the book over github licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License

The book is available free to read online. If you find a typo, or feel something can be improved open an issue or send a pull request .

If you find it interesting you should consider buying a copy from leanpub to show your support to the project.

Thank you.